mirror of
https://github.com/facebook/docusaurus.git
synced 2025-12-25 17:22:50 +00:00
fix(core): optimize i18n integration for site builds + improve inference of locale config (#11550)
This commit is contained in:
parent
6a38ccdfb0
commit
9c85f8689a
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['20.0', '20', '22', '24', '25']
|
||||
node: ['20.0', '20', '22', '24', '25.1']
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['20.0', '20', '22', '24', '25']
|
||||
node: ['20.0', '20', '22', '24', '25.1']
|
||||
steps:
|
||||
- name: Support longpaths
|
||||
run: git config --system core.longpaths true
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['20.0', '20', '22', '24', '25']
|
||||
node: ['20.0', '20', '22', '24', '25.1']
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
dist
|
||||
node_modules
|
||||
.yarn
|
||||
build
|
||||
**/build/**
|
||||
coverage
|
||||
.docusaurus
|
||||
.idea
|
||||
|
|
@ -11,6 +11,8 @@ coverage
|
|||
|
||||
jest/vendor
|
||||
|
||||
argos/test-results
|
||||
|
||||
packages/lqip-loader/lib/
|
||||
packages/docusaurus/lib/
|
||||
packages/docusaurus-*/lib/*
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@
|
|||
import fs from 'fs-extra';
|
||||
import logger, {PerfLogger} from '@docusaurus/logger';
|
||||
import {mapAsyncSequential} from '@docusaurus/utils';
|
||||
import {loadContext, type LoadContextParams} from '../../server/site';
|
||||
import {loadI18n} from '../../server/i18n';
|
||||
import {type LoadContextParams} from '../../server/site';
|
||||
import {loadI18nLocaleList} from '../../server/i18n';
|
||||
import {buildLocale, type BuildLocaleParams} from './buildLocale';
|
||||
import {isAutomaticBaseUrlLocalizationDisabled} from './buildUtils';
|
||||
import {loadSiteConfig} from '../../server/config';
|
||||
|
||||
export type BuildCLIOptions = Pick<LoadContextParams, 'config' | 'outDir'> & {
|
||||
locale?: [string, ...string[]];
|
||||
|
|
@ -81,27 +81,21 @@ async function getLocalesToBuild({
|
|||
siteDir: string;
|
||||
cliOptions: BuildCLIOptions;
|
||||
}): Promise<[string, ...string[]]> {
|
||||
// TODO we shouldn't need to load all context + i18n just to get that list
|
||||
// only loading siteConfig should be enough
|
||||
const context = await loadContext({
|
||||
const {siteConfig} = await loadSiteConfig({
|
||||
siteDir,
|
||||
outDir: cliOptions.outDir,
|
||||
config: cliOptions.config,
|
||||
automaticBaseUrlLocalizationDisabled: isAutomaticBaseUrlLocalizationDisabled(cliOptions),
|
||||
customConfigFilePath: cliOptions.config,
|
||||
});
|
||||
|
||||
const i18n = await loadI18n({
|
||||
siteDir,
|
||||
config: context.siteConfig,
|
||||
currentLocale: context.siteConfig.i18n.defaultLocale, // Awkward but ok
|
||||
automaticBaseUrlLocalizationDisabled: false,
|
||||
});
|
||||
|
||||
const locales = cliOptions.locale ?? i18n.locales;
|
||||
const locales =
|
||||
cliOptions.locale ??
|
||||
loadI18nLocaleList({
|
||||
i18nConfig: siteConfig.i18n,
|
||||
currentLocale: siteConfig.i18n.defaultLocale, // Awkward but ok
|
||||
});
|
||||
|
||||
return orderLocales({
|
||||
locales: locales as [string, ...string[]],
|
||||
defaultLocale: i18n.defaultLocale,
|
||||
defaultLocale: siteConfig.i18n.defaultLocale,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,9 +123,11 @@ describe('defaultLocaleConfig', () => {
|
|||
});
|
||||
|
||||
describe('loadI18n', () => {
|
||||
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const consoleWarnSpy = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
beforeEach(() => {
|
||||
consoleSpy.mockClear();
|
||||
consoleWarnSpy.mockClear();
|
||||
});
|
||||
|
||||
it('loads I18n for default config', async () => {
|
||||
|
|
@ -397,8 +399,67 @@ describe('loadI18n', () => {
|
|||
},
|
||||
currentLocale: 'it',
|
||||
});
|
||||
expect(consoleSpy.mock.calls[0]![0]).toMatch(
|
||||
/The locale .*it.* was not found in your site configuration/,
|
||||
expect(consoleWarnSpy.mock.calls[0]![0]).toMatch(
|
||||
/The locale .*it.* was not found in your Docusaurus site configuration/,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when trying to load undeclared locale that is not a valid locale BCP47 name', async () => {
|
||||
await expect(() =>
|
||||
loadI18nTest({
|
||||
i18nConfig: {
|
||||
path: 'i18n',
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'fr', 'de'],
|
||||
localeConfigs: {},
|
||||
},
|
||||
currentLocale: 'x1',
|
||||
}),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
||||
"Docusaurus couldn't infer a default locale config for x1.
|
||||
Make sure it is a valid BCP 47 locale name (e.g. en, fr, fr-FR, etc.) and/or provide a valid BCP 47 \`siteConfig.i18n.localeConfig['x1'].htmlLang\` attribute."
|
||||
`);
|
||||
});
|
||||
|
||||
it('throws when trying to load declared locale that is not a valid locale BCP47 name', async () => {
|
||||
await expect(() =>
|
||||
loadI18nTest({
|
||||
i18nConfig: {
|
||||
path: 'i18n',
|
||||
defaultLocale: 'fr',
|
||||
locales: ['en', 'fr', 'de'],
|
||||
localeConfigs: {x1: {}},
|
||||
},
|
||||
currentLocale: 'x1',
|
||||
}),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
||||
"Docusaurus couldn't infer a default locale config for x1.
|
||||
Make sure it is a valid BCP 47 locale name (e.g. en, fr, fr-FR, etc.) and/or provide a valid BCP 47 \`siteConfig.i18n.localeConfig['x1'].htmlLang\` attribute."
|
||||
`);
|
||||
});
|
||||
|
||||
it('loads i18n when trying to load declared locale with invalid BCP47 name but valid BCP47', async () => {
|
||||
const result = await loadI18nTest({
|
||||
i18nConfig: {
|
||||
path: 'i18n',
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'fr', 'x1'],
|
||||
localeConfigs: {
|
||||
x1: {htmlLang: 'en-US'},
|
||||
},
|
||||
},
|
||||
currentLocale: 'x1',
|
||||
});
|
||||
expect(result.localeConfigs.x1).toEqual({
|
||||
baseUrl: '/x1/',
|
||||
calendar: 'gregory',
|
||||
direction: 'ltr',
|
||||
htmlLang: 'en-US',
|
||||
label: 'American English',
|
||||
path: 'en-US',
|
||||
translate: false,
|
||||
url: 'https://example.com',
|
||||
});
|
||||
expect(consoleWarnSpy).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,7 +10,12 @@ import fs from 'fs-extra';
|
|||
import logger from '@docusaurus/logger';
|
||||
import combinePromises from 'combine-promises';
|
||||
import {normalizeUrl} from '@docusaurus/utils';
|
||||
import type {I18n, DocusaurusConfig, I18nLocaleConfig} from '@docusaurus/types';
|
||||
import type {
|
||||
I18n,
|
||||
DocusaurusConfig,
|
||||
I18nLocaleConfig,
|
||||
I18nConfig,
|
||||
} from '@docusaurus/types';
|
||||
|
||||
function inferLanguageDisplayName(locale: string) {
|
||||
const tryLocale = (l: string) => {
|
||||
|
|
@ -95,12 +100,33 @@ export function getDefaultLocaleConfig(
|
|||
};
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Docusaurus couldn't get default locale config for ${locale}`,
|
||||
`Docusaurus couldn't infer a default locale config for ${logger.name(
|
||||
locale,
|
||||
)}.
|
||||
Make sure it is a valid BCP 47 locale name (e.g. en, fr, fr-FR, etc.) and/or provide a valid BCP 47 ${logger.code(
|
||||
`siteConfig.i18n.localeConfig['${locale}'].htmlLang`,
|
||||
)} attribute.`,
|
||||
{cause: e},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function loadI18nLocaleList({
|
||||
i18nConfig,
|
||||
currentLocale,
|
||||
}: {
|
||||
i18nConfig: I18nConfig;
|
||||
currentLocale: string;
|
||||
}): [string, ...string[]] {
|
||||
if (!i18nConfig.locales.includes(currentLocale)) {
|
||||
logger.warn`The locale name=${currentLocale} was not found in your Docusaurus site configuration.
|
||||
We recommend adding the name=${currentLocale} to your site i18n config, but we will still try to run your site.
|
||||
Declared site config locales are: ${i18nConfig.locales}`;
|
||||
return i18nConfig.locales.concat(currentLocale) as [string, ...string[]];
|
||||
}
|
||||
return i18nConfig.locales;
|
||||
}
|
||||
|
||||
export async function loadI18n({
|
||||
siteDir,
|
||||
config,
|
||||
|
|
@ -114,14 +140,10 @@ export async function loadI18n({
|
|||
}): Promise<I18n> {
|
||||
const {i18n: i18nConfig} = config;
|
||||
|
||||
if (!i18nConfig.locales.includes(currentLocale)) {
|
||||
logger.warn`The locale name=${currentLocale} was not found in your site configuration: Available locales are: ${i18nConfig.locales}
|
||||
Note: Docusaurus only support running one locale at a time.`;
|
||||
}
|
||||
|
||||
const locales = i18nConfig.locales.includes(currentLocale)
|
||||
? i18nConfig.locales
|
||||
: (i18nConfig.locales.concat(currentLocale) as [string, ...string[]]);
|
||||
const locales = loadI18nLocaleList({
|
||||
i18nConfig,
|
||||
currentLocale,
|
||||
});
|
||||
|
||||
async function getFullLocaleConfig(
|
||||
locale: string,
|
||||
|
|
@ -131,7 +153,7 @@ Note: Docusaurus only support running one locale at a time.`;
|
|||
I18nLocaleConfig,
|
||||
'translate' | 'url' | 'baseUrl'
|
||||
> = {
|
||||
...getDefaultLocaleConfig(locale),
|
||||
...getDefaultLocaleConfig(localeConfigInput.htmlLang ?? locale),
|
||||
...localeConfigInput,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue