mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-17 19:16:58 +02:00
feat(v2): Add i18n default code translation bundles (#4215)
* Add default code translation bundles * fix tests
This commit is contained in:
parent
1b3c9be530
commit
6a94ad989c
12 changed files with 430 additions and 5 deletions
18
packages/docusaurus-theme-classic/codeTranslations/fr.json
Normal file
18
packages/docusaurus-theme-classic/codeTranslations/fr.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"theme.NotFound.title": "Page introuvable",
|
||||||
|
"theme.NotFound.p1": "Nous n'avons pas trouvé ce que vous recherchez.",
|
||||||
|
"theme.NotFound.p2": "Veuillez contacter le propriétaire du site qui vous a lié à l'URL d'origine et leur faire savoir que leur lien est cassé.",
|
||||||
|
"theme.BlogListPaginator.newerEntries": "Nouvelles entrées",
|
||||||
|
"theme.BlogListPaginator.olderEntries": "Anciennes entrées",
|
||||||
|
"theme.BlogPostItem.readMore": "Lire plus",
|
||||||
|
"theme.BlogPostPaginator.newerPost": "Article plus récent",
|
||||||
|
"theme.BlogPostPaginator.olderPost": "Article plus ancien",
|
||||||
|
"theme.CodeBlock.copied": "Copié",
|
||||||
|
"theme.CodeBlock.copy": "Copier",
|
||||||
|
"theme.DocPaginator.previous": "Précédent",
|
||||||
|
"theme.DocPaginator.next": "Suivant",
|
||||||
|
"theme.EditThisPage.editThisPage": "Éditer cette page",
|
||||||
|
"theme.SkipToContent.skipToMainContent": "Aller au contenu principal",
|
||||||
|
"theme.Playground.liveEditor": "Éditeur en direct",
|
||||||
|
"theme.Playground.result": "Résultat"
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import path from 'path';
|
||||||
import Module from 'module';
|
import Module from 'module';
|
||||||
import postcss from 'postcss';
|
import postcss from 'postcss';
|
||||||
import rtlcss from 'rtlcss';
|
import rtlcss from 'rtlcss';
|
||||||
|
import {readDefaultCodeTranslationMessages} from '@docusaurus/utils';
|
||||||
|
|
||||||
const createRequire = Module.createRequire || Module.createRequireFromPath;
|
const createRequire = Module.createRequire || Module.createRequireFromPath;
|
||||||
const requireFromDocusaurusCore = createRequire(
|
const requireFromDocusaurusCore = createRequire(
|
||||||
|
@ -103,6 +104,13 @@ export default function docusaurusThemeClassic(
|
||||||
getTranslationFiles: async () => getTranslationFiles({themeConfig}),
|
getTranslationFiles: async () => getTranslationFiles({themeConfig}),
|
||||||
translateThemeConfig,
|
translateThemeConfig,
|
||||||
|
|
||||||
|
getDefaultCodeTranslationMessages: () => {
|
||||||
|
return readDefaultCodeTranslationMessages({
|
||||||
|
dirPath: path.resolve(__dirname, '..', 'codeTranslations'),
|
||||||
|
locale: currentLocale,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getClientModules() {
|
getClientModules() {
|
||||||
const modules = [
|
const modules = [
|
||||||
require.resolve(getInfimaCSSFile(direction)),
|
require.resolve(getInfimaCSSFile(direction)),
|
||||||
|
|
6
packages/docusaurus-types/src/index.d.ts
vendored
6
packages/docusaurus-types/src/index.d.ts
vendored
|
@ -240,6 +240,12 @@ export interface Plugin<T, U = unknown> {
|
||||||
|
|
||||||
// translations
|
// translations
|
||||||
getTranslationFiles?(): Promise<TranslationFiles>;
|
getTranslationFiles?(): Promise<TranslationFiles>;
|
||||||
|
getDefaultCodeTranslationMessages?(): Promise<
|
||||||
|
Record<
|
||||||
|
string, // id
|
||||||
|
string // message
|
||||||
|
>
|
||||||
|
>;
|
||||||
translateContent?({
|
translateContent?({
|
||||||
content,
|
content,
|
||||||
translationFiles,
|
translationFiles,
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"id1": "message 1 en",
|
||||||
|
"id2": "message 2 en"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"id1": "message 1 fr",
|
||||||
|
"id2": "message 2 fr"
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"id1": "message 1 fr_FR",
|
||||||
|
"id2": "message 2 fr_FR",
|
||||||
|
"id3": "message 3 fr_FR"
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import fs from 'fs-extra';
|
||||||
import {
|
import {
|
||||||
fileToPath,
|
fileToPath,
|
||||||
simpleHash,
|
simpleHash,
|
||||||
|
@ -33,6 +34,7 @@ import {
|
||||||
findFolderContainingFile,
|
findFolderContainingFile,
|
||||||
getFolderContainingFile,
|
getFolderContainingFile,
|
||||||
updateTranslationFileMessages,
|
updateTranslationFileMessages,
|
||||||
|
readDefaultCodeTranslationMessages,
|
||||||
} from '../index';
|
} from '../index';
|
||||||
import {sum} from 'lodash';
|
import {sum} from 'lodash';
|
||||||
|
|
||||||
|
@ -718,3 +720,89 @@ 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'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -602,3 +602,35 @@ 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
|
||||||
|
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 {};
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import {
|
||||||
writePluginTranslations,
|
writePluginTranslations,
|
||||||
writeCodeTranslations,
|
writeCodeTranslations,
|
||||||
WriteTranslationsOptions,
|
WriteTranslationsOptions,
|
||||||
|
getPluginsDefaultCodeTranslationMessages,
|
||||||
|
applyDefaultCodeTranslations,
|
||||||
} from '../server/translations/translations';
|
} from '../server/translations/translations';
|
||||||
import {extractPluginsSourceCodeTranslations} from '../server/translations/translationsExtractor';
|
import {extractPluginsSourceCodeTranslations} from '../server/translations/translationsExtractor';
|
||||||
import {getCustomBabelConfigFilePath, getBabelOptions} from '../webpack/utils';
|
import {getCustomBabelConfigFilePath, getBabelOptions} from '../webpack/utils';
|
||||||
|
@ -47,7 +49,7 @@ export default async function writeTranslations(
|
||||||
siteDir: string,
|
siteDir: string,
|
||||||
options: WriteTranslationsOptions & {locale?: string},
|
options: WriteTranslationsOptions & {locale?: string},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const context = await loadContext(siteDir);
|
const context = await loadContext(siteDir, {locale: options.locale});
|
||||||
const pluginConfigs = loadPluginConfigs(context);
|
const pluginConfigs = loadPluginConfigs(context);
|
||||||
const plugins = initPlugins({
|
const plugins = initPlugins({
|
||||||
pluginConfigs,
|
pluginConfigs,
|
||||||
|
@ -68,10 +70,19 @@ Available locales=[${context.i18n.locales.join(',')}]`,
|
||||||
isServer: true,
|
isServer: true,
|
||||||
babelOptions: getCustomBabelConfigFilePath(siteDir),
|
babelOptions: getCustomBabelConfigFilePath(siteDir),
|
||||||
});
|
});
|
||||||
const codeTranslations = await extractPluginsSourceCodeTranslations(
|
const extractedCodeTranslations = await extractPluginsSourceCodeTranslations(
|
||||||
plugins,
|
plugins,
|
||||||
babelOptions,
|
babelOptions,
|
||||||
);
|
);
|
||||||
|
const defaultCodeMessages = await getPluginsDefaultCodeTranslationMessages(
|
||||||
|
plugins,
|
||||||
|
);
|
||||||
|
|
||||||
|
const codeTranslations = applyDefaultCodeTranslations({
|
||||||
|
extractedCodeTranslations,
|
||||||
|
defaultCodeMessages,
|
||||||
|
});
|
||||||
|
|
||||||
await writeCodeTranslations({siteDir, locale}, codeTranslations, options);
|
await writeCodeTranslations({siteDir, locale}, codeTranslations, options);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
|
|
|
@ -32,7 +32,10 @@ import {loadHtmlTags} from './html-tags';
|
||||||
import {getPackageJsonVersion} from './versions';
|
import {getPackageJsonVersion} from './versions';
|
||||||
import {handleDuplicateRoutes} from './duplicateRoutes';
|
import {handleDuplicateRoutes} from './duplicateRoutes';
|
||||||
import {loadI18n, localizePath} from './i18n';
|
import {loadI18n, localizePath} from './i18n';
|
||||||
import {readCodeTranslationFileContent} from './translations/translations';
|
import {
|
||||||
|
readCodeTranslationFileContent,
|
||||||
|
getPluginsDefaultCodeTranslationMessages,
|
||||||
|
} from './translations/translations';
|
||||||
import {mapValues} from 'lodash';
|
import {mapValues} from 'lodash';
|
||||||
|
|
||||||
type LoadContextOptions = {
|
type LoadContextOptions = {
|
||||||
|
@ -267,10 +270,15 @@ ${Object.keys(registry)
|
||||||
JSON.stringify(i18n, null, 2),
|
JSON.stringify(i18n, null, 2),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const codeTranslationsWithFallbacks: Record<string, string> = {
|
||||||
|
...(await getPluginsDefaultCodeTranslationMessages(plugins)),
|
||||||
|
...codeTranslations,
|
||||||
|
};
|
||||||
|
|
||||||
const genCodeTranslations = generate(
|
const genCodeTranslations = generate(
|
||||||
generatedFilesDir,
|
generatedFilesDir,
|
||||||
'codeTranslations.json',
|
'codeTranslations.json',
|
||||||
JSON.stringify(codeTranslations, null, 2),
|
JSON.stringify(codeTranslationsWithFallbacks, null, 2),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Version metadata.
|
// Version metadata.
|
||||||
|
|
|
@ -12,11 +12,14 @@ import {
|
||||||
readTranslationFileContent,
|
readTranslationFileContent,
|
||||||
WriteTranslationsOptions,
|
WriteTranslationsOptions,
|
||||||
localizePluginTranslationFile,
|
localizePluginTranslationFile,
|
||||||
|
getPluginsDefaultCodeTranslationMessages,
|
||||||
|
applyDefaultCodeTranslations,
|
||||||
} from '../translations';
|
} from '../translations';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import tmp from 'tmp-promise';
|
import tmp from 'tmp-promise';
|
||||||
import {TranslationFile, TranslationFileContent} from '@docusaurus/types';
|
import {TranslationFile, TranslationFileContent} from '@docusaurus/types';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import {InitPlugin} from '../../plugins/init';
|
||||||
|
|
||||||
async function createTmpSiteDir() {
|
async function createTmpSiteDir() {
|
||||||
const {path: siteDirPath} = await tmp.dir({
|
const {path: siteDirPath} = await tmp.dir({
|
||||||
|
@ -461,3 +464,194 @@ describe('localizePluginTranslationFile', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getPluginsDefaultCodeTranslationMessages', () => {
|
||||||
|
function createTestPlugin(
|
||||||
|
fn: InitPlugin['getDefaultCodeTranslationMessages'],
|
||||||
|
): InitPlugin {
|
||||||
|
return {getDefaultCodeTranslationMessages: fn} as InitPlugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('for empty plugins', async () => {
|
||||||
|
const plugins: InitPlugin[] = [];
|
||||||
|
await expect(
|
||||||
|
getPluginsDefaultCodeTranslationMessages(plugins),
|
||||||
|
).resolves.toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('for 1 plugin without lifecycle', async () => {
|
||||||
|
const plugins: InitPlugin[] = [createTestPlugin(undefined)];
|
||||||
|
await expect(
|
||||||
|
getPluginsDefaultCodeTranslationMessages(plugins),
|
||||||
|
).resolves.toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('for 1 plugin with lifecycle', async () => {
|
||||||
|
const plugins: InitPlugin[] = [
|
||||||
|
createTestPlugin(async () => ({
|
||||||
|
a: '1',
|
||||||
|
b: '2',
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
await expect(
|
||||||
|
getPluginsDefaultCodeTranslationMessages(plugins),
|
||||||
|
).resolves.toEqual({
|
||||||
|
a: '1',
|
||||||
|
b: '2',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('for 2 plugins with lifecycles', async () => {
|
||||||
|
const plugins: InitPlugin[] = [
|
||||||
|
createTestPlugin(async () => ({
|
||||||
|
a: '1',
|
||||||
|
b: '2',
|
||||||
|
})),
|
||||||
|
createTestPlugin(async () => ({
|
||||||
|
c: '3',
|
||||||
|
d: '4',
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
await expect(
|
||||||
|
getPluginsDefaultCodeTranslationMessages(plugins),
|
||||||
|
).resolves.toEqual({
|
||||||
|
a: '1',
|
||||||
|
b: '2',
|
||||||
|
c: '3',
|
||||||
|
d: '4',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('for realistic use-case', async () => {
|
||||||
|
const plugins: InitPlugin[] = [
|
||||||
|
createTestPlugin(undefined),
|
||||||
|
createTestPlugin(async () => ({
|
||||||
|
a: '1',
|
||||||
|
b: '2',
|
||||||
|
})),
|
||||||
|
createTestPlugin(undefined),
|
||||||
|
createTestPlugin(undefined),
|
||||||
|
createTestPlugin(async () => ({
|
||||||
|
a: '2',
|
||||||
|
d: '4',
|
||||||
|
})),
|
||||||
|
createTestPlugin(async () => ({
|
||||||
|
d: '5',
|
||||||
|
})),
|
||||||
|
createTestPlugin(undefined),
|
||||||
|
];
|
||||||
|
await expect(
|
||||||
|
getPluginsDefaultCodeTranslationMessages(plugins),
|
||||||
|
).resolves.toEqual({
|
||||||
|
// merge, last plugin wins
|
||||||
|
b: '2',
|
||||||
|
a: '2',
|
||||||
|
d: '5',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('applyDefaultCodeTranslations', () => {
|
||||||
|
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation() as any;
|
||||||
|
beforeEach(() => {
|
||||||
|
consoleSpy.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('for no code and message', () => {
|
||||||
|
expect(
|
||||||
|
applyDefaultCodeTranslations({
|
||||||
|
extractedCodeTranslations: {},
|
||||||
|
defaultCodeMessages: {},
|
||||||
|
}),
|
||||||
|
).toEqual({});
|
||||||
|
expect(consoleSpy).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('for code and message', () => {
|
||||||
|
expect(
|
||||||
|
applyDefaultCodeTranslations({
|
||||||
|
extractedCodeTranslations: {
|
||||||
|
id: {
|
||||||
|
message: 'extracted message',
|
||||||
|
description: 'description',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultCodeMessages: {
|
||||||
|
id: 'default message',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
id: {
|
||||||
|
message: 'default message',
|
||||||
|
description: 'description',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(consoleSpy).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('for code and message mismatch', () => {
|
||||||
|
expect(
|
||||||
|
applyDefaultCodeTranslations({
|
||||||
|
extractedCodeTranslations: {
|
||||||
|
id: {
|
||||||
|
message: 'extracted message',
|
||||||
|
description: 'description',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultCodeMessages: {
|
||||||
|
unknownId: 'default message',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
id: {
|
||||||
|
message: 'extracted message',
|
||||||
|
description: 'description',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(consoleSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(consoleSpy.mock.calls[0][0]).toMatch(/unknownId/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('for realistic scenario', () => {
|
||||||
|
expect(
|
||||||
|
applyDefaultCodeTranslations({
|
||||||
|
extractedCodeTranslations: {
|
||||||
|
id1: {
|
||||||
|
message: 'extracted message 1',
|
||||||
|
description: 'description 1',
|
||||||
|
},
|
||||||
|
id2: {
|
||||||
|
message: 'extracted message 2',
|
||||||
|
description: 'description 2',
|
||||||
|
},
|
||||||
|
id3: {
|
||||||
|
message: 'extracted message 3',
|
||||||
|
description: 'description 3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultCodeMessages: {
|
||||||
|
id2: 'default message id2',
|
||||||
|
id3: 'default message id3',
|
||||||
|
idUnknown1: 'default message idUnknown1',
|
||||||
|
idUnknown2: 'default message idUnknown2',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
id1: {
|
||||||
|
message: 'extracted message 1',
|
||||||
|
description: 'description 1',
|
||||||
|
},
|
||||||
|
id2: {
|
||||||
|
message: 'default message id2',
|
||||||
|
description: 'description 2',
|
||||||
|
},
|
||||||
|
id3: {
|
||||||
|
message: 'default message id3',
|
||||||
|
description: 'description 3',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(consoleSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(consoleSpy.mock.calls[0][0]).toMatch(/idUnknown1/);
|
||||||
|
expect(consoleSpy.mock.calls[0][0]).toMatch(/idUnknown2/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -8,7 +8,11 @@ import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import {InitPlugin} from '../plugins/init';
|
import {InitPlugin} from '../plugins/init';
|
||||||
import {mapValues, difference} from 'lodash';
|
import {mapValues, difference} from 'lodash';
|
||||||
import {TranslationFileContent, TranslationFile} from '@docusaurus/types';
|
import {
|
||||||
|
TranslationFileContent,
|
||||||
|
TranslationFile,
|
||||||
|
TranslationMessage,
|
||||||
|
} from '@docusaurus/types';
|
||||||
import {getPluginI18nPath, toMessageRelativeFilePath} from '@docusaurus/utils';
|
import {getPluginI18nPath, toMessageRelativeFilePath} from '@docusaurus/utils';
|
||||||
import * as Joi from 'joi';
|
import * as Joi from 'joi';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
@ -259,3 +263,46 @@ export async function localizePluginTranslationFile({
|
||||||
return translationFile;
|
return translationFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPluginsDefaultCodeTranslationMessages(
|
||||||
|
plugins: InitPlugin[],
|
||||||
|
): Promise<Record<string, string>> {
|
||||||
|
const pluginsMessages = await Promise.all(
|
||||||
|
plugins.map((plugin) => plugin.getDefaultCodeTranslationMessages?.() ?? {}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return pluginsMessages.reduce((allMessages, pluginMessages) => {
|
||||||
|
return {...allMessages, ...pluginMessages};
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyDefaultCodeTranslations({
|
||||||
|
extractedCodeTranslations,
|
||||||
|
defaultCodeMessages,
|
||||||
|
}: {
|
||||||
|
extractedCodeTranslations: Record<string, TranslationMessage>;
|
||||||
|
defaultCodeMessages: Record<string, string>;
|
||||||
|
}): Record<string, TranslationMessage> {
|
||||||
|
const unusedDefaultCodeMessages = difference(
|
||||||
|
Object.keys(defaultCodeMessages),
|
||||||
|
Object.keys(extractedCodeTranslations),
|
||||||
|
);
|
||||||
|
if (unusedDefaultCodeMessages.length > 0) {
|
||||||
|
console.warn(
|
||||||
|
chalk.yellow(`Unused default message codes found.
|
||||||
|
Please report this Docusaurus issue.
|
||||||
|
- ${unusedDefaultCodeMessages.join('\n- ')}
|
||||||
|
`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapValues(
|
||||||
|
extractedCodeTranslations,
|
||||||
|
(messageTranslation, messageId) => {
|
||||||
|
return {
|
||||||
|
...messageTranslation,
|
||||||
|
message: defaultCodeMessages[messageId] ?? messageTranslation.message,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue