跳到主要内容

i18n - 教程

本教程将引导你了解 道格龙(Docusaurus) i18n 系统 的基础知识。

我们将向一个新初始化的英文道格龙(Docusaurus)网站添加法语翻译。

使用 npx create-docusaurus@latest website classic 初始化一个新站点(就像这个一样)。

配置你的站点

修改 docusaurus.config.js 以添加对法语的 i18n 支持。

站点配置

使用站点 i18n 配置来声明 i18n 语言环境:

docusaurus.config.js
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导航栏项目,以便用户可以选择他们想要的语言环境:

docusaurus.config.js
export default {
themeConfig: {
navbar: {
items: [
{
type: 'localeDropdown',
position: 'left',
},
],
},
},
};
提示

你可以传递一个查询参数,当用户使用下拉菜单更改语言环境时,该参数将被附加到 URL(例如 queryString: '?persistLocale=true')。

这对于在你的服务器上实现自动语言环境检测非常有用。例如,你可以使用此参数将用户的首选语言环境存储在 cookie 中。

启动你的站点

使用你选择的语言环境,在开发模式下启动你的本地化站点:

npm run start -- --locale fr

你的站点现在可以通过 http://localhost:3000/fr/ 访问。

我们还没有提供任何翻译,所以站点大部分是未翻译的。

提示

道格龙(Docusaurus)为通用的主题标签提供了默认翻译,例如分页的"Next"和"Previous"。

请帮助我们完善这些**默认翻译**。

注意

每个语言环境都是一个独立的单页面应用程序 (SPA):无法同时启动所有语言环境的道格龙(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 子元素,但请仅在它确实有效时才使用元素形式。

src/pages/index.js
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>
);
}
信息

道格龙(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>
{/* 不要这样做:这与 write-translations 命令不兼容 */}
{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>
{/* 现在渲染时,标题已经被翻译了! */}
{items.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
);
}

你可以将对翻译 API 的调用纯粹看作是标记,它告诉道格龙(Docusaurus)"这里有一个文本标签,需要用翻译后的消息替换"。

复数形式

当你运行 write-translations 时,你会注意到一些标签是复数形式的:

i18n/en/code.json
{
// ...
"theme.blog.post.plurals": "One post|{count} posts"
// ...
}

每种语言都会有一个可能的复数类别列表。道格龙(Docusaurus)将按 ["zero", "one", "two", "few", "many", "other"] 的顺序排列它们。例如,因为英语(en)有两种复数形式("one" 和 "other"),所以翻译消息有两个用管道符(|)分隔的标签。对于有三种复数形式("one", "few", "many")的波兰语(pl),你需要按该顺序提供三个用管道符连接的标签。

你也可以对你自己代码中的消息进行复数处理:

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 run write-translations -- --locale fr

它会提取并初始化你需要翻译的 JSON 翻译文件。根目录下的 code.json 文件包含了从源代码中提取的所有翻译 API 调用,这些代码可能由你编写,也可能由主题提供,其中一些可能已经有默认翻译。

i18n/fr/code.json
{
// <Translate> 组件没有 ID:默认消息被用作 ID
"Welcome to my website": {
"message": "Welcome to my website"
},
"home.visitMyBlog": {
"message": "You can also visit my {blog}",
"description": "The homepage message to ask the user to visit my blog"
},
"homepage.visitMyBlog.linkLabel": {
"message": "Blog",
"description": "The label for the link to my blog"
},
"Home icon": {
"message": "Home icon",
"description": "The homepage icon alt message"
}
}

插件和主题也会写入它们自己的 JSON 翻译文件,例如:

i18n/fr/docusaurus-theme-classic/navbar.json
{
"title": {
"message": "My Site",
"description": "The title in the navbar"
},
"item.label.Docs": {
"message": "Docs",
"description": "Navbar item with label Docs"
},
"item.label.Blog": {
"message": "Blog",
"description": "Navbar item with label Blog"
},
"item.label.GitHub": {
"message": "GitHub",
"description": "Navbar item with label 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 翻译文件进行翻译了。

使用明确的标题 ID

默认情况下,一个 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 run build

道格龙(Docusaurus)将为每个语言环境构建一个单页面应用程序

  • website/build:用于默认的英语语言
  • website/build/fr:用于法语

你现在可以将 build 文件夹部署到你选择的静态托管解决方案上。

备注

道格龙(Docusaurus)网站使用此策略:

提示

静态托管提供商通常会按照惯例将 /unknown/url 重定向到 /404.html,这总是显示一个英文的 404 页面

通过配置你的主机将 /fr/* 重定向到 /fr/404.html本地化你的 404 页面

这并非总是可行,具体取决于你的主机:GitHub Pages 无法做到这一点,而 Netlify 可以。

多域名部署

你也可以为单个语言环境构建你的站点:

npm run build -- --locale fr

道格龙(Docusaurus)将不会添加 /fr/ URL 前缀。

在你的静态托管提供商上:

  • 为每个语言环境创建一个部署
  • 配置适当的构建命令,使用 --locale 选项
  • 为每个部署配置你选择的(子)域名
注意

这个策略不适用于 GitHub Pages,因为它只可能拥有单个部署

混合部署

可以实现某些语言环境使用子路径,而其他语言环境使用子域名。

也可以将每个语言环境部署为独立的子域名,然后在 CDN 层面将这些子域名组合成一个统一的域名:

  • 将你的站点部署为 fr.docusaurus.io
  • 配置 CDN 从 docusaurus.io/fr 提供服务

管理翻译

道格龙(Docusaurus)不关心你如何管理翻译:它只需要所有翻译文件(JSON、Markdown 或其他数据文件)在构建期间都存在于文件系统中。然而,作为网站创建者,你需要考虑如何管理翻译,以便你的翻译贡献者可以很好地协作。

我们将分享两种常见的翻译协作策略:使用 Git使用 Crowdin