国际化 - 教程
本教程将引导您了解 Docusaurus 国际化 (i18n) 系统 的基础知识。
我们将向一个 新初始化的英文 Docusaurus 网站 添加 法语 翻译。
使用 npx create-docusaurus@latest website classic
初始化一个新站点(例如 这个 )。
配置您的站点
修改 docusaurus.config.js
以添加对法语的 i18n 支持。
站点配置
使用 站点 i18n 配置 来声明 i18n 本地化设置:
export default {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'fa'],
localeConfigs: {
en: {
htmlLang: 'en-GB',
},
// 如果您不需要覆盖默认值,可以省略一个语言环境(例如 fr)
fa: {
direction: 'rtl',
},
},
},
};
语言环境名称用于翻译文件的路径,以及您翻译的语言环境的基本 URL。构建所有语言环境时,只有默认语言环境的名称会在基本 URL 中省略。
Docusaurus 使用语言环境名称提供 合理的默认值 : <html lang="...">
属性、语言环境标签、日历格式等。您可以使用 localeConfigs
自定义这些默认值。
主题配置
添加一个类型为 localeDropdown
的 导航栏项目 ,以便用户可以选择他们想要的语言环境:
export default {
themeConfig: {
navbar: {
items: [
{
type: 'localeDropdown',
position: 'left',
},
],
},
},
};
您可以传递一个查询参数,当用户使用下拉菜单更改语言环境时,该参数将附加到 URL(例如 queryString: '?persistLocale=true'
)。
这对于在您的服务器上实现自动语言环境检测很有用。例如,您可以使用此参数将用户的首选语言环境存储在 Cookie 中。
启动您的站点
使用您选择的语言环境在开发模式下启动您的本地化站点:
- npm
- Yarn
- pnpm
npm run start -- --locale fr
yarn run start --locale fr
pnpm run start --locale fr
您的站点可在 http://localhost:3000/fr/
访问。
我们还没有提供任何翻译,因此该站点大部分未翻译。
Docusaurus 为通用主题标签提供 默认翻译 ,例如分页的“下一页”和“上一页”。
请帮助我们完成这些 默认翻译 。
每个语言环境都是一个 独立的单页应用程序 : 无法同时启动所有语言环境下的 Docusaurus 站点。
翻译您的站点
法语语言环境的所有翻译数据都存储在 website/i18n/fr
中。每个插件在其对应的文件夹下获取其自身的翻译内容,而 code.json
文件定义了 React 代码中使用的所有文本标签。
在复制文件后,请使用 npm run start -- --locale fr
重新启动您的站点。编辑现有文件时,热重载效果更好。
翻译您的 React 代码
对于您自己编写的任何 React 代码:React 页面、React 组件等,您将使用 翻译 API 。
找到您 React 代码中所有对用户可见的文本标签,并使用翻译 API 标记它们。API 有两种:
<Translate>
组件将字符串包装为 JSX 元素;translate()
回调函数接受一条消息并返回一个字符串。
使用最符合语义上下文的 API。例如,<Translate>
可用作 React 子元素,而对于期望字符串的 props,可以使用回调函数。
JSX 元素是一个_对象_,而不是字符串。在期望字符串的上下文中使用它(例如 <option>
的子元素)会 将其强制转换为字符串 ,它返回"[object Object]"
。虽然我们鼓励您将 <Translate>
用作 JSX 子元素,但只有在实际有效时才使用元素形式。
- Before
- After
import React from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
export default function Home() {
return (
<Layout>
<h1>Welcome to my website</h1>
<main>
You can also visit my
<Link to="https://docusaurus.io/blog">blog</Link>
<img
src="/img/home.png"
alt="Home icon"
/>
</main>
</Layout>
);
}
import React from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import Translate, {translate} from '@docusaurus/Translate';
export default function Home() {
return (
<Layout>
<h1>
<Translate>Welcome to my website</Translate>
</h1>
<main>
<Translate
id="homepage.visitMyBlog"
description="The homepage message to ask the user to visit my blog"
values={{
blogLink: (
<Link to="https://docusaurus.io/blog">
<Translate
id="homepage.visitMyBlog.linkLabel"
description="The label for the link to my blog">
blog
</Translate>
</Link>
),
}}>
{'You can also visit my {blogLink}'}
</Translate>
<img
src="/img/home.png"
alt={
translate({
message: 'Home icon',
description: 'The homepage icon alt message',
})
}
/>
</main>
</Layout>
);
}
Docusaurus 故意提供了一个 非常小巧轻便的翻译运行时 ,并且只支持使用 ICU 消息格式 的子集进行基本的 占位符插值 。
大多数文档网站通常是 静态的 ,不需要高级 i18n 功能( 复数 、 性别 等)。对于更高级的用例,请使用像 react-intl 这样的库。
docusaurus write-translations
命令将静态分析您的站点中使用的所有 React 代码文件,提取对这些 API 的调用,并将它们聚合到 code.json
文件中。翻译文件将被存储为从 ID 到翻译消息对象的映射(包括翻译后的标签和标签的描述)。在您对翻译 API (<Translate>
或 translate()
) 的调用中,您需要指定默认的未翻译消息或 ID,以便 Docusaurus 正确地将每个翻译条目与 API 调用相关联。
docusaurus write-translations
命令只对您的代码进行 静态分析 。它实际上并没有运行您的站点。因此,无法提取动态消息,因为消息是一个_表达式_,而不是一个_字符串_:
const items = [
{id: 1, title: 'Hello'},
{id: 2, title: 'World'},
];
function ItemsList() {
return (
<ul>
{/* DON'T DO THIS: doesn't work with the write-translations command */}
{items.map((item) => (
<li key={item.id}>
<Translate>{item.title}</Translate>
</li>
))}
<ul>
);
}
这在运行时仍然可以正常工作。但是,将来我们可能会提供一种“无运行时”机制,允许通过 Babel 转换将翻译直接内联到 React 代码中,而不是在运行时调用 API。因此,为了面向未来,您应该始终优先考虑可静态分析的消息。例如,我们可以将上面的代码重构为:
const items = [
{id: 1, title: <Translate>Hello</Translate>},
{id: 2, title: <Translate>World</Translate>},
];
function ItemsList() {
return (
<ul>
{/* The titles are now already translated when rendering! */}
{items.map((item) => (
<li key={item.id}>{item.title}</li>
))}
<ul>
);
}
您可以将对翻译 API 的调用视为纯粹的_标记_,它们告诉 Docusaurus“这是一个将被替换为翻译消息的文本标签”。
复数形式
运行 write-translations
时,您会注意到某些标签已进行复数形式处理:
{
// ...
"theme.blog.post.plurals": "One post|{count} posts"
// ...
}
每种语言都将有一系列 可能的复数类别 。Docusaurus 将按照 ["zero", "one", "two", "few", "many", "other"]
的顺序排列它们。例如,因为英语 (en
) 有两种复数形式(“one” 和 “other”),所以翻译消息有两个用管道 (|
) 分隔的标签。对于波兰语 (pl
),它有三种复数形式(“one”、“few” 和 “many”),您将按此顺序提供三个标签,并用管道连接。
您也可以对您自己代码的消息进行复数形式处理:
import {translate} from '@docusaurus/Translate';
import {usePluralForm} from '@docusaurus/theme-common';
function ItemsList({items}) {
// `usePluralForm` 将为当前语言环境提供复数选择器
const {selectMessage} = usePluralForm();
// 根据 `items.length` 选择合适的复数形式标签
const message = selectMessage(
items.length,
translate(
{message: 'One item|{count} items'},
{count: items.length},
),
);
return (
<>
<h2>{message}</h2>
<ul>{items.map((item) => <li key={item.id}>{item.title}</li>)}<ul>
</>
);
}
Docusaurus 使用 Intl.PluralRules
来解析和选择复数形式。为 selectMessage
提供正确数量的复数形式,并按正确的顺序排列非常重要。
翻译插件数据
JSON 翻译文件用于代码中穿插的所有内容:
- React 代码,包括上面标记的翻译标签
- 主题配置中的导航栏和页脚标签
sidebars.js
中的文档侧边栏类别标签- 插件选项中的博客侧边栏标题
- ...
运行 write-translations 命令:
- npm
- Yarn
- pnpm
npm run write-translations -- --locale fr
yarn write-translations --locale fr
pnpm run write-translations --locale fr
它将提取并初始化您需要翻译的 JSON 翻译文件。根目录下的 code.json
文件包含从源代码中提取的所有翻译 API 调用,这些调用可能是您编写的,也可能是主题提供的,其中一些可能已默认翻译。
{
// <Translate> 组件没有 ID:默认消息用作 ID
"Welcome to my website": {
"message": "Bienvenue sur mon site web"
},
"home.visitMyBlog": {
"message": "Vous pouvez également visiter mon {blog}",
"description": "Le message de la page d'accueil pour inviter l'utilisateur à visiter mon blog"
},
"homepage.visitMyBlog.linkLabel": {
"message": "Blog",
"description": "L'étiquette du lien vers mon blog"
},
"Home icon": {
"message": "Icône d'accueil",
"description": "Le message alternatif de l'icône de la page d'accueil"
}
}
插件和主题也将编写他们自己的 JSON 翻译文件,例如:
{
"title": {
"message": "Mon Site",
"description": "Le titre dans la barre de navigation"
},
"item.label.Docs": {
"message": "Docs",
"description": "Élément de la barre de navigation avec l'étiquette Docs"
},
"item.label.Blog": {
"message": "Blog",
"description": "Élément de la barre de navigation avec l'étiquette Blog"
},
"item.label.GitHub": {
"message": "GitHub",
"description": "Élément de la barre de navigation avec l'étiquette GitHub"
}
}
翻译 i18n/fr
中 JSON 文件的 message
属性,您的站点布局和主页现在应该已翻译。
翻译 Markdown 文件
官方的 Docusaurus 内容插件广泛使用 Markdown/MDX 文件,并允许您翻译它们。
翻译文档
将您的文档 Markdown 文件从 docs/
复制到 i18n/fr/docusaurus-plugin-content-docs/current
,并进行翻译:
mkdir -p i18n/fr/docusaurus-plugin-content-docs/current
cp -r docs/** i18n/fr/docusaurus-plugin-content-docs/current
请注意,docusaurus-plugin-content-docs
插件总是按版本划分其内容。./docs
文件夹中的数据将在 current
子文件夹和 current.json
文件中进行翻译。有关“current”的含义,请参阅 文档版本控制指南 。
翻译博客
将您的博客 Markdown 文件复制到 i18n/fr/docusaurus-plugin-content-blog
,并进行翻译:
mkdir -p i18n/fr/docusaurus-plugin-content-blog
cp -r blog/** i18n/fr/docusaurus-plugin-content-blog
翻译页面
将您的页面 Markdown 文件复制到 i18n/fr/docusaurus-plugin-content-pages
,并进行翻译:
mkdir -p i18n/fr/docusaurus-plugin-content-pages
cp -r src/pages/**.md i18n/fr/docusaurus-plugin-content-pages
cp -r src/pages/**.mdx i18n/fr/docusaurus-plugin-content-pages
我们只复制 .md
和 .mdx
文件,因为 React 页面已通过 JSON 翻译文件进行翻译。
默认情况下,Markdown 标题 ### Hello World
将具有生成的 ID hello-world
。其他文档可以使用 [link](#hello-world)
来链接它。但是,翻译后,标题变为 ### Bonjour le Monde
,ID 为 bonjour-le-monde
。
生成的 ID 并不总是适合本地化站点,因为它要求您本地化所有锚链接:
- [link](#hello-world) .
+ [link](#bonjour-le-monde)
对于本地化站点,建议使用 显式标题 ID 。
部署您的站点
您可以选择将您的站点部署到 单个域 或使用 多个(子)域 。
单域部署
运行以下命令:
- npm
- Yarn
- pnpm
npm run build
yarn build
pnpm run build
Docusaurus 将为每个语言环境构建 一个单页应用程序 :
website/build
: 用于默认的英文语言website/build/fr
: 用于法语
您现在可以 部署 build
文件夹到您选择的静态托管解决方案。
Docusaurus 网站使用此策略:
根据惯例,静态托管提供商通常会将 /unknown/url
重定向到 /404.html
,始终显示 英文 404 页面 。
通过将主机配置为将 /fr/*
重定向到 /fr/404.html
来 本地化您的 404 页面 。
这并非总是可行,并且取决于您的主机:GitHub Pages 无法做到这一点, Netlify 可以。
多域部署
您也可以为单个语言环境构建您的站点:
- npm
- Yarn
- pnpm
npm run build -- --locale fr
yarn build --locale fr
pnpm run build --locale fr
Docusaurus 将不会添加 /fr/
URL 前缀。
在您的 静态托管提供商 上:
- 为每个语言环境创建一个部署
- 使用
--locale
选项配置相应的构建命令 - 为每个部署配置您选择的(子)域
此策略对于 GitHub Pages 不可行 ,因为它只能 进行单一部署 。
混合模式
一些语言环境可以使用子路径,而其他语言环境可以使用子域。
也可以将每个语言环境部署为单独的子域,在 CDN 层面将子域组合到单个统一域中:
- 将您的站点部署为
fr.docusaurus.io
- 配置 CDN 以从
docusaurus.io/fr
提供服务
管理翻译
Docusaurus 不关心您如何管理翻译:它只需要在构建过程中所有翻译文件(JSON、Markdown 或其他数据文件)都存在于文件系统中即可。但是,作为站点创建者,您需要考虑如何管理翻译,以便您的翻译贡献者能够良好地协作。
我们将分享两种常见的翻译协作策略: 使用 git 和 使用 Crowdin 。