mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-02 03:37:48 +02:00
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:
parent
3bcd0a6ab1
commit
b743edf5fb
5 changed files with 170 additions and 121 deletions
|
@ -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'));
|
||||||
|
});
|
||||||
|
});
|
|
@ -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({
|
||||||
|
|
56
packages/docusaurus-utils/src/codeTranslationsUtils.ts
Normal file
56
packages/docusaurus-utils/src/codeTranslationsUtils.ts
Normal 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 {};
|
||||||
|
}
|
|
@ -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(
|
||||||
|
|
Loading…
Add table
Reference in a new issue