Add support for locale extensions in pages plugin

This commit is contained in:
sebastienlorber 2024-01-04 18:19:54 +01:00
parent ca09f238f3
commit fc72669528
7 changed files with 90 additions and 14 deletions

View file

@ -11,6 +11,10 @@ module.exports = {
url: 'https://your-docusaurus-site.example.com', url: 'https://your-docusaurus-site.example.com',
baseUrl: '/', baseUrl: '/',
favicon: 'img/favicon.ico', favicon: 'img/favicon.ico',
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
markdown: { markdown: {
parseFrontMatter: async (params) => { parseFrontMatter: async (params) => {
const result = await params.defaultParseFrontMatter(params); const result = await params.defaultParseFrontMatter(params);

View file

@ -0,0 +1,22 @@
/**
* 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 React from 'react';
import Head from '@docusaurus/Head';
export default class Home extends React.Component {
render() {
return (
<div>
<Head>
<title>translated Hello</title>
</Head>
<div>translated TypeScript...</div>
</div>
);
}
}

View file

@ -69,7 +69,7 @@ exports[`docusaurus-plugin-content-pages loads simple pages with french translat
}, },
{ {
"permalink": "/fr/typescript", "permalink": "/fr/typescript",
"source": "@site/src/pages/typescript.tsx", "source": "@site/src/pages/typescript.fr.tsx",
"type": "jsx", "type": "jsx",
}, },
{ {

View file

@ -13,7 +13,6 @@ import {
aliasedSitePath, aliasedSitePath,
docuHash, docuHash,
getPluginI18nPath, getPluginI18nPath,
getFolderContainingFile,
addTrailingPathSeparator, addTrailingPathSeparator,
Globby, Globby,
createAbsoluteFilePathMatcher, createAbsoluteFilePathMatcher,
@ -22,6 +21,7 @@ import {
parseMarkdownFile, parseMarkdownFile,
isUnlisted, isUnlisted,
isDraft, isDraft,
findAsyncSequential,
} from '@docusaurus/utils'; } from '@docusaurus/utils';
import {validatePageFrontMatter} from './frontMatter'; import {validatePageFrontMatter} from './frontMatter';
@ -38,6 +38,48 @@ export function getContentPathList(contentPaths: PagesContentPaths): string[] {
return [contentPaths.contentPathLocalized, contentPaths.contentPath]; return [contentPaths.contentPathLocalized, contentPaths.contentPath];
} }
async function getLocalizedSource({
relativeSource,
contentPaths,
locale,
}: {
relativeSource: string;
contentPaths: PagesContentPaths;
locale: string;
}) {
const {name, dir, ext} = path.parse(relativeSource);
// Lookup in localized folder in priority
const possibleSources = getContentPathList(contentPaths).flatMap(
(contentPath) => [
path.join(contentPath, dir, `${name}.${locale}${ext}`),
path.join(contentPath, relativeSource),
],
);
const localizedSource = await findAsyncSequential(
possibleSources,
fs.pathExists,
);
if (!localizedSource) {
throw new Error('unexpected');
}
return localizedSource;
}
function filterLocaleExtensionFiles(
files: string[],
locales: string[],
): string[] {
const localeExtensions = locales.map((locale) => `.${locale}`);
return files.filter((file) => {
const {name} = path.parse(file);
return !localeExtensions.includes(path.extname(name));
});
}
const isMarkdownSource = (source: string) => const isMarkdownSource = (source: string) =>
source.endsWith('.md') || source.endsWith('.mdx'); source.endsWith('.md') || source.endsWith('.mdx');
@ -62,6 +104,14 @@ export default function pluginContentPages(
); );
const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID); const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID);
async function getPageFiles() {
const files = await Globby(options.include, {
cwd: contentPaths.contentPath,
ignore: options.exclude,
});
return filterLocaleExtensionFiles(files, context.i18n.locales);
}
return { return {
name: 'docusaurus-plugin-content-pages', name: 'docusaurus-plugin-content-pages',
@ -73,28 +123,21 @@ export default function pluginContentPages(
}, },
async loadContent() { async loadContent() {
const {include} = options;
if (!(await fs.pathExists(contentPaths.contentPath))) { if (!(await fs.pathExists(contentPaths.contentPath))) {
return null; return null;
} }
const {baseUrl} = siteConfig; const {baseUrl} = siteConfig;
const pagesFiles = await Globby(include, { const pagesFiles = await getPageFiles();
cwd: contentPaths.contentPath,
ignore: options.exclude,
});
async function processPageSourceFile( async function processPageSourceFile(
relativeSource: string, relativeSource: string,
): Promise<Metadata | undefined> { ): Promise<Metadata | undefined> {
// Lookup in localized folder in priority const source = await getLocalizedSource({
const contentPath = await getFolderContainingFile(
getContentPathList(contentPaths),
relativeSource, relativeSource,
); contentPaths,
locale: context.i18n.currentLocale,
const source = path.join(contentPath, relativeSource); });
const aliasedSourcePath = aliasedSitePath(source, siteDir); const aliasedSourcePath = aliasedSitePath(source, siteDir);
const permalink = normalizeUrl([ const permalink = normalizeUrl([
baseUrl, baseUrl,

View file

@ -0,0 +1,3 @@
# Page i18n test
French version

View file

@ -0,0 +1,3 @@
# Page i18n test
English version

View file

@ -26,6 +26,7 @@ import Readme from "../README.mdx"
### Other tests ### Other tests
- [React 18](/tests/pages/react-18) - [React 18](/tests/pages/react-18)
- [i18n](/tests/pages/i18n)
- [Crash test](/tests/pages/crashTest) - [Crash test](/tests/pages/crashTest)
- [Code block tests](/tests/pages/code-block-tests) - [Code block tests](/tests/pages/code-block-tests)
- [Link tests](/tests/pages/link-tests) - [Link tests](/tests/pages/link-tests)