feat(v2): default theme translations: locale "pt" should load "pt-BR" translations (#4581)

* code translation utils => locale "pt" should attempt to load "pt-BR" (not "pt-PT")

* useless async test
This commit is contained in:
Sébastien Lorber 2021-04-07 18:28:48 +02:00 committed by GitHub
parent 3bcd0a6ab1
commit b743edf5fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 170 additions and 121 deletions

View file

@ -0,0 +1,112 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import path from 'path';
import fs from 'fs-extra';
import {
codeTranslationLocalesToTry,
readDefaultCodeTranslationMessages,
} from '../codeTranslationsUtils';
describe('codeTranslationLocalesToTry', () => {
test('should return appropriate locale lists', () => {
expect(codeTranslationLocalesToTry('fr')).toEqual(['fr', 'fr-FR']);
expect(codeTranslationLocalesToTry('fr-FR')).toEqual(['fr-FR', 'fr']);
// Note: "pt" is expanded into "pt-BR", not "pt-PT", as "pt-BR" is more widely used!
// See https://github.com/facebook/docusaurus/pull/4536#issuecomment-810088783
expect(codeTranslationLocalesToTry('pt')).toEqual(['pt', 'pt-BR']);
expect(codeTranslationLocalesToTry('pt-BR')).toEqual(['pt-BR', 'pt']);
expect(codeTranslationLocalesToTry('pt-PT')).toEqual(['pt-PT', 'pt']);
});
});
describe('readDefaultCodeTranslationMessages', () => {
const dirPath = path.resolve(
__dirname,
'__fixtures__',
'defaultCodeTranslations',
);
async function readAsJSON(filename: string) {
return JSON.parse(
await fs.readFile(path.resolve(dirPath, filename), 'utf8'),
);
}
test('for empty locale', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: '',
dirPath,
}),
).rejects.toThrowErrorMatchingInlineSnapshot(
`"First argument to Intl.Locale constructor can't be empty or missing"`,
);
});
test('for unexisting locale', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'es',
dirPath,
}),
).resolves.toEqual({});
});
test('for fr but bad folder', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'fr',
dirPath: __dirname,
}),
).resolves.toEqual({});
});
test('for fr', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'fr',
dirPath,
}),
).resolves.toEqual(await readAsJSON('fr.json'));
});
test('for fr-FR', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'fr-FR',
dirPath,
}),
).resolves.toEqual(await readAsJSON('fr-FR.json'));
});
test('for en', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'en',
dirPath,
}),
).resolves.toEqual(await readAsJSON('en.json'));
});
test('for en-US', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'en-US',
dirPath,
}),
).resolves.toEqual(await readAsJSON('en.json'));
});
test('for en-WHATEVER', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'en-WHATEVER',
dirPath,
}),
).resolves.toEqual(await readAsJSON('en.json'));
});
});

View file

@ -6,7 +6,6 @@
*/ */
import path from 'path'; import path from 'path';
import fs from 'fs-extra';
import { import {
fileToPath, fileToPath,
simpleHash, simpleHash,
@ -34,7 +33,6 @@ import {
findFolderContainingFile, findFolderContainingFile,
getFolderContainingFile, getFolderContainingFile,
updateTranslationFileMessages, updateTranslationFileMessages,
readDefaultCodeTranslationMessages,
parseMarkdownHeadingId, parseMarkdownHeadingId,
} from '../index'; } from '../index';
import {sum} from 'lodash'; import {sum} from 'lodash';
@ -722,92 +720,6 @@ describe('updateTranslationFileMessages', () => {
}); });
}); });
describe('readDefaultCodeTranslationMessages', () => {
const dirPath = path.resolve(
__dirname,
'__fixtures__',
'defaultCodeTranslations',
);
async function readAsJSON(filename: string) {
return JSON.parse(
await fs.readFile(path.resolve(dirPath, filename), 'utf8'),
);
}
test('for empty locale', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: '',
dirPath,
}),
).resolves.toEqual({});
});
test('for unexisting locale', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'es',
dirPath,
}),
).resolves.toEqual({});
});
test('for fr but bad folder', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: '',
dirPath: __dirname,
}),
).resolves.toEqual({});
});
test('for fr', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'fr',
dirPath,
}),
).resolves.toEqual(await readAsJSON('fr.json'));
});
test('for fr_FR', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'fr_FR',
dirPath,
}),
).resolves.toEqual(await readAsJSON('fr_FR.json'));
});
test('for en', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'en',
dirPath,
}),
).resolves.toEqual(await readAsJSON('en.json'));
});
test('for en_US', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'en_US',
dirPath,
}),
).resolves.toEqual(await readAsJSON('en.json'));
});
test('for en_WHATEVER', async () => {
await expect(
readDefaultCodeTranslationMessages({
locale: 'en_WHATEVER',
dirPath,
}),
).resolves.toEqual(await readAsJSON('en.json'));
});
});
describe('parseMarkdownHeadingId', () => { describe('parseMarkdownHeadingId', () => {
test('can parse simple heading without id', () => { test('can parse simple heading without id', () => {
expect(parseMarkdownHeadingId('## Some heading')).toEqual({ expect(parseMarkdownHeadingId('## Some heading')).toEqual({

View file

@ -0,0 +1,56 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import path from 'path';
import fs from 'fs-extra';
// Return an ordered list of locales we should try
export function codeTranslationLocalesToTry(locale: string): string[] {
// @ts-expect-error: TODO until available in TS, see https://github.com/microsoft/TypeScript/issues/37326
const intlLocale = Intl.Locale ? new Intl.Locale(locale) : undefined;
if (!intlLocale) {
return [locale];
}
// if locale is just a simple language like "pt", we want to fallback to pt-BR (not pt-PT!)
// see https://github.com/facebook/docusaurus/pull/4536#issuecomment-810088783
if (intlLocale.language === locale) {
const maximizedLocale = intlLocale.maximize(); // pt-Latn-BR`
// ["pt","pt-BR"]
return [locale, `${maximizedLocale.language}-${maximizedLocale.region}`];
}
// if locale is like "pt-BR", we want to fallback to "pt"
else {
return [locale, intlLocale.language];
}
}
// Useful to implement getDefaultCodeTranslationMessages() in themes
export async function readDefaultCodeTranslationMessages({
dirPath,
locale,
}: {
dirPath: string;
locale: string;
}): Promise<Record<string, string>> {
const localesToTry = codeTranslationLocalesToTry(locale);
// Return the content of the first file that match
// fr_FR.json => fr.json => nothing
// eslint-disable-next-line no-restricted-syntax
for (const fileName of localesToTry) {
const filePath = path.resolve(dirPath, `${fileName}.json`);
// eslint-disable-next-line no-await-in-loop
if (await fs.pathExists(filePath)) {
// eslint-disable-next-line no-await-in-loop
const fileContent = await fs.readFile(filePath, 'utf8');
return JSON.parse(fileContent);
}
}
return {};
}

View file

@ -22,6 +22,8 @@ import {
// @ts-expect-error: no typedefs :s // @ts-expect-error: no typedefs :s
import resolvePathnameUnsafe from 'resolve-pathname'; import resolvePathnameUnsafe from 'resolve-pathname';
export * from './codeTranslationsUtils';
const fileHash = new Map(); const fileHash = new Map();
export async function generate( export async function generate(
generatedFilesDir: string, generatedFilesDir: string,
@ -642,39 +644,6 @@ export function updateTranslationFileMessages(
}; };
} }
export async function readDefaultCodeTranslationMessages({
dirPath,
locale,
}: {
dirPath: string;
locale: string;
}): Promise<Record<string, string>> {
const fileNamesToTry = [locale];
if (locale.includes('_')) {
const language = locale.split('_')[0];
if (language) {
fileNamesToTry.push(language);
}
}
// Return the content of the first file that match
// fr_FR.json => fr.json => nothing
// eslint-disable-next-line no-restricted-syntax
for (const fileName of fileNamesToTry) {
const filePath = path.resolve(dirPath, `${fileName}.json`);
// eslint-disable-next-line no-await-in-loop
if (await fs.pathExists(filePath)) {
// eslint-disable-next-line no-await-in-loop
const fileContent = await fs.readFile(filePath, 'utf8');
return JSON.parse(fileContent);
}
}
return {};
}
// Input: ## Some heading {#some-heading} // Input: ## Some heading {#some-heading}
// Output: {text: "## Some heading", id: "some-heading"} // Output: {text: "## Some heading", id: "some-heading"}
export function parseMarkdownHeadingId( export function parseMarkdownHeadingId(