feat(core): add i18n.localeConfigs.translate + skip translation process if i18n/<locale> dir doesn't exist (#11304)

This commit is contained in:
Sébastien Lorber 2025-07-07 14:55:46 +02:00 committed by GitHub
parent e0524a5c84
commit 1808945c1f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 1061 additions and 366 deletions

View file

@ -2005,17 +2005,6 @@ exports[`simple website content: route config 1`] = `
]
`;
exports[`simple website getPathToWatch 1`] = `
[
"sidebars.json",
"i18n/en/docusaurus-plugin-content-docs/current/**/*.{md,mdx}",
"docs/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs/current/tags.yml",
"docs/tags.yml",
"docs/**/_category_.{json,yml,yaml}",
]
`;
exports[`site with custom sidebar items generator sidebar is autogenerated according to a custom sidebarItemsGenerator 1`] = `
{
"defaultSidebar": [
@ -3327,23 +3316,6 @@ exports[`versioned website (community) content: route config 1`] = `
]
`;
exports[`versioned website (community) getPathToWatch 1`] = `
[
"community_sidebars.json",
"i18n/en/docusaurus-plugin-content-docs-community/current/**/*.{md,mdx}",
"community/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs-community/current/tags.yml",
"community/tags.yml",
"community/**/_category_.{json,yml,yaml}",
"community_versioned_sidebars/version-1.0.0-sidebars.json",
"i18n/en/docusaurus-plugin-content-docs-community/version-1.0.0/**/*.{md,mdx}",
"community_versioned_docs/version-1.0.0/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs-community/version-1.0.0/tags.yml",
"community_versioned_docs/version-1.0.0/tags.yml",
"community_versioned_docs/version-1.0.0/**/_category_.{json,yml,yaml}",
]
`;
exports[`versioned website content 1`] = `
{
"description": "This is next version of bar.",
@ -5209,32 +5181,3 @@ exports[`versioned website content: withSlugs version sidebars 1`] = `
],
}
`;
exports[`versioned website getPathToWatch 1`] = `
[
"sidebars.json",
"i18n/en/docusaurus-plugin-content-docs/current/**/*.{md,mdx}",
"docs/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs/current/tags.yml",
"docs/tags.yml",
"docs/**/_category_.{json,yml,yaml}",
"versioned_sidebars/version-1.0.1-sidebars.json",
"i18n/en/docusaurus-plugin-content-docs/version-1.0.1/**/*.{md,mdx}",
"versioned_docs/version-1.0.1/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs/version-1.0.1/tags.yml",
"versioned_docs/version-1.0.1/tags.yml",
"versioned_docs/version-1.0.1/**/_category_.{json,yml,yaml}",
"versioned_sidebars/version-1.0.0-sidebars.json",
"i18n/en/docusaurus-plugin-content-docs/version-1.0.0/**/*.{md,mdx}",
"versioned_docs/version-1.0.0/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs/version-1.0.0/tags.yml",
"versioned_docs/version-1.0.0/tags.yml",
"versioned_docs/version-1.0.0/**/_category_.{json,yml,yaml}",
"versioned_sidebars/version-withSlugs-sidebars.json",
"i18n/en/docusaurus-plugin-content-docs/version-withSlugs/**/*.{md,mdx}",
"versioned_docs/version-withSlugs/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs/version-withSlugs/tags.yml",
"versioned_docs/version-withSlugs/tags.yml",
"versioned_docs/version-withSlugs/**/_category_.{json,yml,yaml}",
]
`;

View file

@ -13,6 +13,7 @@ import {
posixPath,
DEFAULT_PLUGIN_ID,
LAST_UPDATE_FALLBACK,
getLocaleConfig,
} from '@docusaurus/utils';
import {getTagsFile} from '@docusaurus/utils-validation';
import {createSidebarsUtils} from '../sidebars/utils';
@ -842,7 +843,11 @@ describe('simple site', () => {
describe('versioned site', () => {
async function loadSite(
loadSiteOptions: {options: Partial<PluginOptions>; locale?: string} = {
loadSiteOptions: {
options?: Partial<PluginOptions>;
locale?: string;
translate?: boolean;
} = {
options: {},
},
) {
@ -851,6 +856,10 @@ describe('versioned site', () => {
siteDir,
locale: loadSiteOptions.locale,
});
// hacky but gets the job done
getLocaleConfig(context.i18n).translate = loadSiteOptions.translate ?? true;
const options = {
id: DEFAULT_PLUGIN_ID,
...DEFAULT_OPTIONS,
@ -1055,6 +1064,43 @@ describe('versioned site', () => {
});
});
it('versioned docs - translate: false', async () => {
const {version100TestUtils} = await loadSite({
translate: false,
});
// This doc is translated, but we still read the original
await version100TestUtils.testMeta(path.join('hello.md'), {
id: 'hello',
sourceDirName: '.',
permalink: '/docs/1.0.0/',
slug: '/',
title: 'hello',
description: 'Hello 1.0.0 !',
frontMatter: {
slug: '/',
tags: ['inlineTag-v1.0.0', 'globalTag-v1.0.0'],
},
version: '1.0.0',
source: '@site/versioned_docs/version-1.0.0/hello.md',
tags: [
{
description: undefined,
inline: true,
label: 'inlineTag-v1.0.0',
permalink: '/docs/1.0.0/tags/inline-tag-v-1-0-0',
},
{
description: 'globalTag-v1.0.0 description',
inline: false,
label: 'globalTag-v1.0.0 label',
permalink: '/docs/1.0.0/tags/globalTag-v1.0.0 permalink',
},
],
unlisted: false,
});
});
it('next doc slugs', async () => {
const {currentVersionTestUtils} = await loadSite();

View file

@ -18,7 +18,7 @@ import {
createConfigureWebpackUtils,
} from '@docusaurus/core/src/webpack/configure';
import {sortRoutes} from '@docusaurus/core/src/server/plugins/routeConfig';
import {posixPath} from '@docusaurus/utils';
import {getLocaleConfig, posixPath} from '@docusaurus/utils';
import {normalizePluginOptions} from '@docusaurus/utils-validation';
import {fromPartial} from '@total-typescript/shoehorn';
@ -219,9 +219,13 @@ describe('empty/no docs website', () => {
});
describe('simple website', () => {
async function loadSite() {
async function loadSite({translate}: {translate?: boolean} = {}) {
const siteDir = path.join(__dirname, '__fixtures__', 'simple-site');
const context = await loadContext({siteDir});
// hacky but gets the job done
getLocaleConfig(context.i18n).translate = translate ?? true;
const sidebarPath = path.join(siteDir, 'sidebars.json');
const options = validateOptions({
validate: normalizePluginOptions as Validate<Options, PluginOptions>,
@ -233,7 +237,20 @@ describe('simple website', () => {
const plugin = await pluginContentDocs(context, options);
const pluginContentDir = path.join(context.generatedFilesDir, plugin.name);
return {siteDir, context, sidebarPath, plugin, options, pluginContentDir};
return {
siteDir,
context,
sidebarPath,
plugin,
options,
pluginContentDir,
getPathsToWatch: () => {
const pathToWatch = plugin.getPathsToWatch!();
return pathToWatch.map((filepath) =>
posixPath(path.relative(siteDir, filepath)),
);
},
};
}
it('extendCli - docsVersion', async () => {
@ -242,8 +259,6 @@ describe('simple website', () => {
.spyOn(cliDocs, 'cliDocsVersionCommand')
.mockImplementation(async () => {});
const cli = new commander.Command();
// @ts-expect-error: in actual usage, we pass the static commander instead
// of the new command
plugin.extendCli!(cli);
cli.parse(['node', 'test', 'docs:version', '1.0.0']);
expect(mock).toHaveBeenCalledTimes(1);
@ -251,25 +266,48 @@ describe('simple website', () => {
mock.mockRestore();
});
it('getPathToWatch', async () => {
const {siteDir, plugin} = await loadSite();
describe('getPathToWatch', () => {
it('translate: false', async () => {
const {getPathsToWatch} = await loadSite({translate: false});
expect(getPathsToWatch()).toMatchInlineSnapshot(`
[
"sidebars.json",
"docs/**/*.{md,mdx}",
"docs/tags.yml",
"docs/**/_category_.{json,yml,yaml}",
]
`);
});
const pathToWatch = plugin.getPathsToWatch!();
const matchPattern = pathToWatch.map((filepath) =>
posixPath(path.relative(siteDir, filepath)),
);
expect(matchPattern).toMatchSnapshot();
expect(isMatch('docs/hello.md', matchPattern)).toBe(true);
expect(isMatch('docs/hello.mdx', matchPattern)).toBe(true);
expect(isMatch('docs/foo/bar.md', matchPattern)).toBe(true);
expect(isMatch('docs/hello.js', matchPattern)).toBe(false);
expect(isMatch('docs/super.mdl', matchPattern)).toBe(false);
expect(isMatch('docs/mdx', matchPattern)).toBe(false);
expect(isMatch('docs/headingAsTitle.md', matchPattern)).toBe(true);
expect(isMatch('sidebars.json', matchPattern)).toBe(true);
expect(isMatch('versioned_docs/hello.md', matchPattern)).toBe(false);
expect(isMatch('hello.md', matchPattern)).toBe(false);
expect(isMatch('super/docs/hello.md', matchPattern)).toBe(false);
it('translate: true', async () => {
const {getPathsToWatch} = await loadSite({translate: true});
expect(getPathsToWatch()).toMatchInlineSnapshot(`
[
"sidebars.json",
"i18n/en/docusaurus-plugin-content-docs/current/**/*.{md,mdx}",
"docs/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs/current/tags.yml",
"docs/tags.yml",
"docs/**/_category_.{json,yml,yaml}",
]
`);
});
it('returns patterns matching docs', async () => {
const {getPathsToWatch} = await loadSite();
const matchPattern = getPathsToWatch();
expect(isMatch('docs/hello.md', matchPattern)).toBe(true);
expect(isMatch('docs/hello.mdx', matchPattern)).toBe(true);
expect(isMatch('docs/foo/bar.md', matchPattern)).toBe(true);
expect(isMatch('docs/hello.js', matchPattern)).toBe(false);
expect(isMatch('docs/super.mdl', matchPattern)).toBe(false);
expect(isMatch('docs/mdx', matchPattern)).toBe(false);
expect(isMatch('docs/headingAsTitle.md', matchPattern)).toBe(true);
expect(isMatch('sidebars.json', matchPattern)).toBe(true);
expect(isMatch('versioned_docs/hello.md', matchPattern)).toBe(false);
expect(isMatch('hello.md', matchPattern)).toBe(false);
expect(isMatch('super/docs/hello.md', matchPattern)).toBe(false);
});
});
it('configureWebpack', async () => {
@ -329,9 +367,13 @@ describe('simple website', () => {
});
describe('versioned website', () => {
async function loadSite() {
async function loadSite({translate}: {translate?: boolean} = {}) {
const siteDir = path.join(__dirname, '__fixtures__', 'versioned-site');
const context = await loadContext({siteDir});
// hacky but gets the job done
getLocaleConfig(context.i18n).translate = translate ?? true;
const sidebarPath = path.join(siteDir, 'sidebars.json');
const routeBasePath = 'docs';
const options = validateOptions({
@ -356,6 +398,13 @@ describe('versioned website', () => {
options,
plugin,
pluginContentDir,
getPathsToWatch: () => {
const pathToWatch = plugin.getPathsToWatch!();
return pathToWatch.map((filepath) =>
posixPath(path.relative(siteDir, filepath)),
);
},
};
}
@ -365,8 +414,6 @@ describe('versioned website', () => {
.spyOn(cliDocs, 'cliDocsVersionCommand')
.mockImplementation(async () => {});
const cli = new commander.Command();
// @ts-expect-error: in actual usage, we pass the static commander instead
// of the new command
plugin.extendCli!(cli);
cli.parse(['node', 'test', 'docs:version', '2.0.0']);
expect(mock).toHaveBeenCalledTimes(1);
@ -374,48 +421,101 @@ describe('versioned website', () => {
mock.mockRestore();
});
it('getPathToWatch', async () => {
const {siteDir, plugin} = await loadSite();
const pathToWatch = plugin.getPathsToWatch!();
const matchPattern = pathToWatch.map((filepath) =>
posixPath(path.relative(siteDir, filepath)),
);
expect(matchPattern).not.toEqual([]);
expect(matchPattern).toMatchSnapshot();
expect(isMatch('docs/hello.md', matchPattern)).toBe(true);
expect(isMatch('docs/hello.mdx', matchPattern)).toBe(true);
expect(isMatch('docs/foo/bar.md', matchPattern)).toBe(true);
expect(isMatch('sidebars.json', matchPattern)).toBe(true);
expect(isMatch('versioned_docs/version-1.0.0/hello.md', matchPattern)).toBe(
true,
);
expect(
isMatch('versioned_docs/version-1.0.0/foo/bar.md', matchPattern),
).toBe(true);
expect(
isMatch('versioned_sidebars/version-1.0.0-sidebars.json', matchPattern),
).toBe(true);
describe('getPathToWatch', () => {
it('translate: false', async () => {
const {getPathsToWatch} = await loadSite({translate: false});
expect(getPathsToWatch()).toMatchInlineSnapshot(`
[
"sidebars.json",
"docs/**/*.{md,mdx}",
"docs/tags.yml",
"docs/**/_category_.{json,yml,yaml}",
"versioned_sidebars/version-1.0.1-sidebars.json",
"versioned_docs/version-1.0.1/**/*.{md,mdx}",
"versioned_docs/version-1.0.1/tags.yml",
"versioned_docs/version-1.0.1/**/_category_.{json,yml,yaml}",
"versioned_sidebars/version-1.0.0-sidebars.json",
"versioned_docs/version-1.0.0/**/*.{md,mdx}",
"versioned_docs/version-1.0.0/tags.yml",
"versioned_docs/version-1.0.0/**/_category_.{json,yml,yaml}",
"versioned_sidebars/version-withSlugs-sidebars.json",
"versioned_docs/version-withSlugs/**/*.{md,mdx}",
"versioned_docs/version-withSlugs/tags.yml",
"versioned_docs/version-withSlugs/**/_category_.{json,yml,yaml}",
]
`);
});
// Non existing version
expect(
isMatch('versioned_docs/version-2.0.0/foo/bar.md', matchPattern),
).toBe(false);
expect(isMatch('versioned_docs/version-2.0.0/hello.md', matchPattern)).toBe(
false,
);
expect(
isMatch('versioned_sidebars/version-2.0.0-sidebars.json', matchPattern),
).toBe(false);
it('translate: true', async () => {
const {getPathsToWatch} = await loadSite({translate: true});
expect(getPathsToWatch()).toMatchInlineSnapshot(`
[
"sidebars.json",
"i18n/en/docusaurus-plugin-content-docs/current/**/*.{md,mdx}",
"docs/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs/current/tags.yml",
"docs/tags.yml",
"docs/**/_category_.{json,yml,yaml}",
"versioned_sidebars/version-1.0.1-sidebars.json",
"i18n/en/docusaurus-plugin-content-docs/version-1.0.1/**/*.{md,mdx}",
"versioned_docs/version-1.0.1/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs/version-1.0.1/tags.yml",
"versioned_docs/version-1.0.1/tags.yml",
"versioned_docs/version-1.0.1/**/_category_.{json,yml,yaml}",
"versioned_sidebars/version-1.0.0-sidebars.json",
"i18n/en/docusaurus-plugin-content-docs/version-1.0.0/**/*.{md,mdx}",
"versioned_docs/version-1.0.0/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs/version-1.0.0/tags.yml",
"versioned_docs/version-1.0.0/tags.yml",
"versioned_docs/version-1.0.0/**/_category_.{json,yml,yaml}",
"versioned_sidebars/version-withSlugs-sidebars.json",
"i18n/en/docusaurus-plugin-content-docs/version-withSlugs/**/*.{md,mdx}",
"versioned_docs/version-withSlugs/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs/version-withSlugs/tags.yml",
"versioned_docs/version-withSlugs/tags.yml",
"versioned_docs/version-withSlugs/**/_category_.{json,yml,yaml}",
]
`);
});
expect(isMatch('docs/hello.js', matchPattern)).toBe(false);
expect(isMatch('docs/super.mdl', matchPattern)).toBe(false);
expect(isMatch('docs/mdx', matchPattern)).toBe(false);
expect(isMatch('hello.md', matchPattern)).toBe(false);
expect(isMatch('super/docs/hello.md', matchPattern)).toBe(false);
it('returns patterns matching docs', async () => {
const {getPathsToWatch} = await loadSite();
const matchPattern = getPathsToWatch();
expect(isMatch('docs/hello.md', matchPattern)).toBe(true);
expect(isMatch('docs/hello.mdx', matchPattern)).toBe(true);
expect(isMatch('docs/foo/bar.md', matchPattern)).toBe(true);
expect(isMatch('sidebars.json', matchPattern)).toBe(true);
expect(
isMatch('versioned_docs/version-1.0.0/hello.md', matchPattern),
).toBe(true);
expect(
isMatch('versioned_docs/version-1.0.0/foo/bar.md', matchPattern),
).toBe(true);
expect(
isMatch('versioned_sidebars/version-1.0.0-sidebars.json', matchPattern),
).toBe(true);
// Non existing version
expect(
isMatch('versioned_docs/version-2.0.0/foo/bar.md', matchPattern),
).toBe(false);
expect(
isMatch('versioned_docs/version-2.0.0/hello.md', matchPattern),
).toBe(false);
expect(
isMatch('versioned_sidebars/version-2.0.0-sidebars.json', matchPattern),
).toBe(false);
expect(isMatch('docs/hello.js', matchPattern)).toBe(false);
expect(isMatch('docs/super.mdl', matchPattern)).toBe(false);
expect(isMatch('docs/mdx', matchPattern)).toBe(false);
expect(isMatch('hello.md', matchPattern)).toBe(false);
expect(isMatch('super/docs/hello.md', matchPattern)).toBe(false);
});
});
it('content', async () => {
const {plugin, pluginContentDir} = await loadSite();
const {plugin, pluginContentDir} = await loadSite({translate: true});
const content = await plugin.loadContent!();
expect(content.loadedVersions).toHaveLength(4);
const [currentVersion, version101, version100, versionWithSlugs] =
@ -453,9 +553,13 @@ describe('versioned website', () => {
});
describe('versioned website (community)', () => {
async function loadSite() {
async function loadSite({translate}: {translate?: boolean} = {}) {
const siteDir = path.join(__dirname, '__fixtures__', 'versioned-site');
const context = await loadContext({siteDir});
// hacky but gets the job done
getLocaleConfig(context.i18n).translate = translate ?? true;
const sidebarPath = path.join(siteDir, 'community_sidebars.json');
const routeBasePath = 'community';
const pluginId = 'community';
@ -479,6 +583,13 @@ describe('versioned website (community)', () => {
options,
plugin,
pluginContentDir,
getPathsToWatch: () => {
const pathToWatch = plugin.getPathsToWatch!();
return pathToWatch.map((filepath) =>
posixPath(path.relative(siteDir, filepath)),
);
},
};
}
@ -488,8 +599,6 @@ describe('versioned website (community)', () => {
.spyOn(cliDocs, 'cliDocsVersionCommand')
.mockImplementation(async () => {});
const cli = new commander.Command();
// @ts-expect-error: in actual usage, we pass the static commander instead
// of the new command
plugin.extendCli!(cli);
cli.parse(['node', 'test', `docs:version:${pluginId}`, '2.0.0']);
expect(mock).toHaveBeenCalledTimes(1);
@ -497,34 +606,67 @@ describe('versioned website (community)', () => {
mock.mockRestore();
});
it('getPathToWatch', async () => {
const {siteDir, plugin} = await loadSite();
const pathToWatch = plugin.getPathsToWatch!();
const matchPattern = pathToWatch.map((filepath) =>
posixPath(path.relative(siteDir, filepath)),
);
expect(matchPattern).not.toEqual([]);
expect(matchPattern).toMatchSnapshot();
expect(isMatch('community/team.md', matchPattern)).toBe(true);
expect(
isMatch('community_versioned_docs/version-1.0.0/team.md', matchPattern),
).toBe(true);
describe('getPathToWatch', () => {
it('translate: false', async () => {
const {getPathsToWatch} = await loadSite({translate: false});
expect(getPathsToWatch()).toMatchInlineSnapshot(`
[
"community_sidebars.json",
"community/**/*.{md,mdx}",
"community/tags.yml",
"community/**/_category_.{json,yml,yaml}",
"community_versioned_sidebars/version-1.0.0-sidebars.json",
"community_versioned_docs/version-1.0.0/**/*.{md,mdx}",
"community_versioned_docs/version-1.0.0/tags.yml",
"community_versioned_docs/version-1.0.0/**/_category_.{json,yml,yaml}",
]
`);
});
// Non existing version
expect(
isMatch('community_versioned_docs/version-2.0.0/team.md', matchPattern),
).toBe(false);
expect(
isMatch(
'community_versioned_sidebars/version-2.0.0-sidebars.json',
matchPattern,
),
).toBe(false);
it('translate: true', async () => {
const {getPathsToWatch} = await loadSite({translate: true});
expect(getPathsToWatch()).toMatchInlineSnapshot(`
[
"community_sidebars.json",
"i18n/en/docusaurus-plugin-content-docs-community/current/**/*.{md,mdx}",
"community/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs-community/current/tags.yml",
"community/tags.yml",
"community/**/_category_.{json,yml,yaml}",
"community_versioned_sidebars/version-1.0.0-sidebars.json",
"i18n/en/docusaurus-plugin-content-docs-community/version-1.0.0/**/*.{md,mdx}",
"community_versioned_docs/version-1.0.0/**/*.{md,mdx}",
"i18n/en/docusaurus-plugin-content-docs-community/version-1.0.0/tags.yml",
"community_versioned_docs/version-1.0.0/tags.yml",
"community_versioned_docs/version-1.0.0/**/_category_.{json,yml,yaml}",
]
`);
});
expect(isMatch('community/team.js', matchPattern)).toBe(false);
expect(
isMatch('community_versioned_docs/version-1.0.0/team.js', matchPattern),
).toBe(false);
it('returns patterns matching docs', async () => {
const {getPathsToWatch} = await loadSite();
const matchPattern = getPathsToWatch();
expect(isMatch('community/team.md', matchPattern)).toBe(true);
expect(
isMatch('community_versioned_docs/version-1.0.0/team.md', matchPattern),
).toBe(true);
// Non existing version
expect(
isMatch('community_versioned_docs/version-2.0.0/team.md', matchPattern),
).toBe(false);
expect(
isMatch(
'community_versioned_sidebars/version-2.0.0-sidebars.json',
matchPattern,
),
).toBe(false);
expect(isMatch('community/team.js', matchPattern)).toBe(false);
expect(
isMatch('community_versioned_docs/version-1.0.0/team.js', matchPattern),
).toBe(false);
});
});
it('content', async () => {

View file

@ -8,7 +8,7 @@
import fs from 'fs-extra';
import path from 'path';
import logger from '@docusaurus/logger';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
import {DEFAULT_PLUGIN_ID, getLocaleConfig} from '@docusaurus/utils';
import {
getVersionsFilePath,
getVersionDocsDirPath,
@ -89,7 +89,7 @@ async function cliDocsVersionCommand(
const localizationDir = path.resolve(
siteDir,
i18n.path,
i18n.localeConfigs[locale]!.path,
getLocaleConfig(i18n, locale).path,
);
// Copy docs files.
const docsDir =

View file

@ -196,7 +196,9 @@ async function doProcessDocMetadata({
locale: context.i18n.currentLocale,
});
} else if (typeof options.editUrl === 'string') {
const isLocalized = contentPath === versionMetadata.contentPathLocalized;
const isLocalized =
typeof versionMetadata.contentPathLocalized !== 'undefined' &&
contentPath === versionMetadata.contentPathLocalized;
const baseVersionEditUrl =
isLocalized && options.editLocalizedFiles
? versionMetadata.editUrlLocalized

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import path from 'path';
import * as path from 'path';
import {fromPartial} from '@total-typescript/shoehorn';
import {DEFAULT_PARSE_FRONT_MATTER} from '@docusaurus/utils/src';
import {readVersionsMetadata} from '../version';
@ -19,7 +19,7 @@ const DefaultI18N: I18n = {
currentLocale: 'en',
locales: ['en'],
defaultLocale: 'en',
localeConfigs: {},
localeConfigs: {en: fromPartial({translate: true})},
};
async function siteFixture(fixture: string) {

View file

@ -6,27 +6,44 @@
*/
import {jest} from '@jest/globals';
import path from 'path';
import * as path from 'path';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
import {readVersionsMetadata} from '../version';
import {DEFAULT_OPTIONS} from '../../options';
import type {I18n, LoadContext} from '@docusaurus/types';
import type {I18n, I18nLocaleConfig, LoadContext} from '@docusaurus/types';
import type {
PluginOptions,
VersionMetadata,
} from '@docusaurus/plugin-content-docs';
const DefaultI18N: I18n = {
path: 'i18n',
currentLocale: 'en',
locales: ['en'],
defaultLocale: 'en',
localeConfigs: {},
};
function getI18n(
locale: string,
localeConfigOptions?: Partial<I18nLocaleConfig>,
): I18n {
return {
path: 'i18n',
currentLocale: locale,
locales: ['en'],
defaultLocale: locale,
localeConfigs: {
[locale]: {
path: locale,
label: locale,
translate: true,
calendar: 'calendar',
htmlLang: locale,
direction: 'rtl',
...localeConfigOptions,
},
},
};
}
const DefaultI18N: I18n = getI18n('en');
describe('readVersionsMetadata', () => {
describe('simple site', () => {
async function loadSite() {
async function loadSite({context}: {context?: Partial<LoadContext>} = {}) {
const simpleSiteDir = path.resolve(
path.join(__dirname, '../../__tests__/__fixtures__', 'simple-site'),
);
@ -39,6 +56,7 @@ describe('readVersionsMetadata', () => {
baseUrl: '/',
i18n: DefaultI18N,
localizationDir: path.join(simpleSiteDir, 'i18n/en'),
...context,
} as LoadContext;
const vCurrent: VersionMetadata = {
@ -73,6 +91,26 @@ describe('readVersionsMetadata', () => {
expect(versionsMetadata).toEqual([vCurrent]);
});
it('works with translate: false', async () => {
const {defaultOptions, defaultContext, vCurrent} = await loadSite({
context: {
i18n: getI18n('en', {translate: false}),
},
});
const versionsMetadata = await readVersionsMetadata({
options: defaultOptions,
context: defaultContext,
});
expect(versionsMetadata).toEqual([
{
...vCurrent,
contentPathLocalized: undefined,
},
]);
});
it('works with base url', async () => {
const {defaultOptions, defaultContext, vCurrent} = await loadSite();
@ -188,7 +226,7 @@ describe('readVersionsMetadata', () => {
});
describe('versioned site, pluginId=default', () => {
async function loadSite() {
async function loadSite({context}: {context?: Partial<LoadContext>} = {}) {
const versionedSiteDir = path.resolve(
path.join(__dirname, '../../__tests__/__fixtures__', 'versioned-site'),
);
@ -202,6 +240,7 @@ describe('readVersionsMetadata', () => {
baseUrl: '/',
i18n: DefaultI18N,
localizationDir: path.join(versionedSiteDir, 'i18n/en'),
...context,
} as LoadContext;
const vCurrent: VersionMetadata = {
@ -436,6 +475,54 @@ describe('readVersionsMetadata', () => {
]);
});
it('works with editUrl and translate=false', async () => {
const {defaultOptions, defaultContext, vCurrent, v101, v100, vWithSlugs} =
await loadSite({
context: {
i18n: getI18n('en', {translate: false}),
},
});
const versionsMetadata = await readVersionsMetadata({
options: {
...defaultOptions,
editUrl: 'https://github.com/facebook/docusaurus/edit/main/website/',
},
context: defaultContext,
});
expect(versionsMetadata).toEqual([
{
...vCurrent,
contentPathLocalized: undefined,
editUrl:
'https://github.com/facebook/docusaurus/edit/main/website/docs',
editUrlLocalized: undefined,
},
{
...v101,
contentPathLocalized: undefined,
editUrl:
'https://github.com/facebook/docusaurus/edit/main/website/versioned_docs/version-1.0.1',
editUrlLocalized: undefined,
},
{
...v100,
contentPathLocalized: undefined,
editUrl:
'https://github.com/facebook/docusaurus/edit/main/website/versioned_docs/version-1.0.0',
editUrlLocalized: undefined,
},
{
...vWithSlugs,
contentPathLocalized: undefined,
editUrl:
'https://github.com/facebook/docusaurus/edit/main/website/versioned_docs/version-withSlugs',
editUrlLocalized: undefined,
},
]);
});
it('works with editUrl and editCurrentVersion=true', async () => {
const {defaultOptions, defaultContext, vCurrent, v101, v100, vWithSlugs} =
await loadSite();

View file

@ -7,7 +7,11 @@
import path from 'path';
import fs from 'fs-extra';
import {getPluginI18nPath, DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
import {
getPluginI18nPath,
getLocaleConfig,
DEFAULT_PLUGIN_ID,
} from '@docusaurus/utils';
import {
VERSIONS_JSON_FILE,
VERSIONED_DOCS_DIR,
@ -186,11 +190,16 @@ export async function getVersionMetadataPaths({
>
> {
const isCurrent = versionName === CURRENT_VERSION_NAME;
const contentPathLocalized = getDocsDirPathLocalized({
localizationDir: context.localizationDir,
pluginId: options.id,
versionName,
});
const shouldTranslate = getLocaleConfig(context.i18n).translate;
const contentPathLocalized = shouldTranslate
? getDocsDirPathLocalized({
localizationDir: context.localizationDir,
pluginId: options.id,
versionName,
})
: undefined;
const contentPath = isCurrent
? path.resolve(context.siteDir, options.path)
: getVersionDocsDirPath(context.siteDir, options.id, versionName);

View file

@ -50,33 +50,47 @@ function getVersionEditUrls({
return {editUrl: undefined, editUrlLocalized: undefined};
}
const editDirPath = options.editCurrentVersion ? options.path : contentPath;
const editDirPathLocalized = options.editCurrentVersion
? getDocsDirPathLocalized({
localizationDir: context.localizationDir,
versionName: CURRENT_VERSION_NAME,
pluginId: options.id,
})
: contentPathLocalized;
// Intermediate var just to please TS not narrowing to "string"
const editUrlOption = options.editUrl;
const versionPathSegment = posixPath(
path.relative(context.siteDir, path.resolve(context.siteDir, editDirPath)),
);
const versionPathSegmentLocalized = posixPath(
path.relative(
context.siteDir,
path.resolve(context.siteDir, editDirPathLocalized),
),
);
const getEditUrl = () => {
const editDirPath = options.editCurrentVersion ? options.path : contentPath;
const editUrl = normalizeUrl([options.editUrl, versionPathSegment]);
return normalizeUrl([
editUrlOption,
posixPath(
path.relative(
context.siteDir,
path.resolve(context.siteDir, editDirPath),
),
),
]);
};
const editUrlLocalized = normalizeUrl([
options.editUrl,
versionPathSegmentLocalized,
]);
const getEditUrlLocalized = () => {
if (!contentPathLocalized) {
return undefined;
}
const editDirPathLocalized = options.editCurrentVersion
? getDocsDirPathLocalized({
localizationDir: context.localizationDir,
versionName: CURRENT_VERSION_NAME,
pluginId: options.id,
})
: contentPathLocalized;
return {editUrl, editUrlLocalized};
return normalizeUrl([
editUrlOption,
posixPath(
path.relative(
context.siteDir,
path.resolve(context.siteDir, editDirPathLocalized),
),
),
]);
};
return {editUrl: getEditUrl(), editUrlLocalized: getEditUrlLocalized()};
}
/**