Skip to main content

i18n - 使用 Crowdin

道格龙(Docusaurus)的 i18n 系统与任何翻译软件都是解耦的。

你可以将道格龙(Docusaurus)与你选择的工具和 SaaS 集成,只要你将翻译文件放置在正确的位置即可。

我们以 Crowdin 为例,来说明一种可能的集成方式。

warning

这并非认可 Crowdin 是翻译道格龙(Docusaurus)网站的唯一选择,但 Facebook 已成功使用它来翻译 Jest、道格龙(Docusaurus)和 ReasonML 等文档项目。

如需帮助,请参阅 Crowdin 文档 和 Crowdin 支持。

tip

请使用此**社区驱动的 GitHub 讨论**来讨论与道格龙(Docusaurus)+ Crowdin 相关的任何问题。

Crowdin 概览​

Crowdin 是一种翻译 SaaS,为开源项目提供免费计划。

我们推荐以下翻译工作流:

  • 上传源文件到 Crowdin(未翻译的文件)
  • 使用 Crowdin 翻译内容
  • 从 Crowdin 下载译文(本地化的翻译文件)

Crowdin 提供了一个 CLI 来上传源文件和下载译文,从而实现翻译过程的自动化。

crowdin.yml 配置文件对于道格龙(Docusaurus)非常方便,它允许将本地化的翻译文件下载到预期的位置(在 i18n/[locale]/.. 中)。

请阅读**官方文档**以了解更多关于高级功能和不同翻译工作流的信息。

Crowdin 教程​

本教程将引导你使用 Crowdin 将一个新初始化的英文道格龙(Docusaurus)网站翻译成法语,并假设你已经完成了 i18n 教程。

最终结果可以在 docusaurus-crowdin-example.netlify.app 查看(仓库地址)。

准备道格龙(Docusaurus)站点​

初始化一个新的道格龙(Docusaurus)站点:

npx create-docusaurus@latest website classic

为法语添加站点配置:

docusaurus.config.js
export default {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
themeConfig: {
navbar: {
items: [
// ...
{
type: 'localeDropdown',
position: 'left',
},
// ...
],
},
},
// ...
};

翻译主页:

src/pages/index.js
import React from 'react';
import Translate from '@docusaurus/Translate';
import Layout from '@theme/Layout';

export default function Home() {
return (
<Layout>
<h1 style={{margin: 20}}>
<Translate description="The homepage main heading">
Welcome to my Docusaurus translated site!
</Translate>
</h1>
</Layout>
);
}

创建一个 Crowdin 项目​

在 Crowdin 上注册并创建一个项目。

使用英语作为源语言,法语作为目标语言。

Create a Crowdin project with english as source language, and french as target language

你的项目已创建,但目前是空的。我们将在接下来的步骤中上传要翻译的文件。

创建 Crowdin 配置​

此配置(文档)为 Crowdin CLI 提供了一个映射,使其能够理解:

  • 在哪里找到要上传的源文件(JSON 和 Markdown)
  • 翻译后将文件下载到哪里(在 i18n/[locale] 中)

在 website 目录下创建 crowdin.yml:

crowdin.yml
project_id: '123456'
api_token_env: CROWDIN_PERSONAL_TOKEN
preserve_hierarchy: true
files:
# JSON 翻译文件
- source: /i18n/en/**/*
translation: /i18n/%two_letters_code%/**/%original_file_name%
# 文档 Markdown 文件
- source: /docs/**/*
translation: /i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%
# 博客 Markdown 文件
- source: /blog/**/*
translation: /i18n/%two_letters_code%/docusaurus-plugin-content-blog/**/%original_file_name%

Crowdin 有自己的语法来声明源/翻译路径:

  • **/*:子文件夹中的所有内容
  • %two_letters_code%:Crowdin 目标语言的双字母变体(在我们的例子中是 fr)
  • **/%original_file_name%:译文将保留原始的文件夹/文件层次结构
info

Crowdin CLI 的警告信息有时不易理解。

我们建议:

  • 一次只更改一件事
  • 每次更改配置后重新上传源文件
  • 使用以 / 开头的路径(./ 不起作用)
  • 避免使用复杂的 globbing 模式,如 /docs/**/*.(md|mdx)(这不起作用)

访问令牌​

api_token_env 属性定义了 Crowdin CLI 读取的环境变量名称。

你可以在你的个人资料页面获取一个 Personal Access Token。

tip

你可以保留默认值 CROWDIN_PERSONAL_TOKEN,并在你的计算机和 CI 服务器上将此环境变量设置为生成的访问令牌。

warning

个人访问令牌授予对你所有 Crowdin 项目的读写权限。

你应该不要提交它,并且最好为你的公司创建一个专用的 Crowdin 个人资料,而不是使用个人账户。

其他配置字段​

  • project_id:可以硬编码,可以在 https://crowdin.com/project/<MY_PROJECT_NAME>/settings#api 找到
  • preserve_hierarchy:在 Crowdin UI 上保留你文档的文件夹层次结构,而不是将所有内容扁平化

安装 Crowdin CLI​

本教程使用 CLI 版本 3.5.2,但我们预计 3.x 版本会继续工作。

将 Crowdin CLI 作为 npm 包安装到你的道格龙(Docusaurus)站点:

npm install @crowdin/cli@3

添加一个 crowdin 脚本:

package.json
{
"scripts": {
// ...
"write-translations": "docusaurus write-translations",
"crowdin": "crowdin"
}
}

测试你是否可以运行 Crowdin CLI:

npm run crowdin -- --version

在你的计算机上设置 CROWDIN_PERSONAL_TOKEN 环境变量,以允许 CLI 使用 Crowdin API 进行身份验证。

tip

你可以临时在 crowdin.yml 中使用 api_token: 'MY-TOKEN' 硬编码你的个人令牌。

上传源文件​

为默认语言在 website/i18n/en 中生成 JSON 翻译文件:

npm run write-translations

上传所有 JSON 和 Markdown 翻译文件:

npm run crowdin upload

Crowdin CLI uploading Docusaurus source files

你的源文件现在可以在 Crowdin 界面上看到:https://crowdin.com/project/<MY_PROJECT_NAME>/settings#files

Crowdin UI showing Docusaurus source files

翻译源文件​

在 https://crowdin.com/project/<MY_PROJECT_NAME> 上,点击法语目标语言。

Crowdin UI showing French translation files

翻译一些 Markdown 文件。

Crowdin UI to translate a Markdown file

tip

使用 Hide String 功能确保翻译人员不会翻译不应翻译的内容:

  • Front matter: id, slug, tags ...
  • 提示框: :::, :::note, :::tip ...

Crowdin UI hide string

翻译一些 JSON 文件。

Crowdin UI to translate a JSON file

info

JSON 翻译文件的 description 属性在 Crowdin 上可见,以帮助翻译字符串。

tip

预翻译你的站点,并手动修复预翻译的错误(首先在设置中启用全局翻译记忆库)。

请先使用 Hide String 功能,因为 Crowdin 的预翻译有时过于激进。

下载译文​

使用 Crowdin CLI 下载翻译好的 JSON 和 Markdown 文件。

npm run crowdin download

翻译后的内容应被下载到 i18n/fr 目录中。

在法语语言环境下启动你的站点:

npm run start -- --locale fr

确保你的网站现在已经在 http://localhost:3000/fr/ 被翻译成法语。

使用 CI 实现自动化​

我们将配置 CI 以在构建时下载 Crowdin 译文,并将它们排除在 Git 之外。

将 website/i18n 添加到 .gitignore。

在你的 CI 上设置 CROWDIN_PERSONAL_TOKEN 环境变量。

创建一个 npm 脚本来 sync Crowdin(提取源文件、上传源文件、下载译文):

package.json
{
"scripts": {
"crowdin:sync": "docusaurus write-translations && crowdin upload && crowdin download"
}
}

在你的 CI 中,在构建道格龙(Docusaurus)站点之前调用 npm run crowdin:sync 脚本。

tip

为了保持部署预览的快速,不要下载译文,并为功能分支使用 npm run build -- --locale en。

warning

Crowdin 不太支持多个并发上传/下载:最好只将译文包含在你的生产部署中,并保持部署预览为未翻译状态。

高级 Crowdin 主题​

MDX​

warning

请特别注意 MDX 文档中的 JSX 片段!

Crowdin 并未正式支持 MDX,但他们增加了对 .mdx 扩展名的支持,并将此类文件解释为 Markdown(而不是纯文本)。

MDX 的问题​

Crowdin 认为 JSX 语法是嵌入式 HTML,并且在下载译文时可能会弄乱 JSX 标记,导致站点因无效的 JSX 而构建失败。

使用简单字符串 props 的简单 JSX 片段,如 <Username name="Sebastien"/>,可以正常工作;而使用对象/数组 props 的更复杂的 JSX 片段,如 <User person={{name: "Sebastien"}}/>,则更有可能因为语法不像 HTML 而失败。

MDX 的解决方案​

我们建议将复杂的嵌入式 JSX 代码提取为独立的组件。我们还增加了一个 mdx-code-block 的"应急"语法:

# 如何部署道格龙(Docusaurus)

要部署道格龙(Docusaurus),请运行以下命令:

````mdx-code-block 

import Tabs from '@theme/Tabs';

import TabItem from '@theme/TabItem';

<Tabs>
<TabItem value="bash" label="Bash">

```bash
GIT_USER=<GITHUB_USERNAME> yarn deploy
```

</TabItem>
<TabItem value="windows" label="Windows">

```batch
cmd /C "set "GIT_USER=<GITHUB_USERNAME>" && yarn deploy"
```

</TabItem>
</Tabs>
````

这将:

  • 被 Crowdin 解释为代码块(下载时不会弄乱标记)
  • 被道格龙(Docusaurus)解释为常规 JSX(就好像它没有被任何代码块包裹一样)
  • 不幸的是,会使 MDX 工具(IDE 语法高亮、Prettier...)失效

文档版本控制​

为 website/versioned_docs 文件夹配置翻译文件。

当创建一个新版本时,源字符串通常与当前版本(website/docs)非常相似,你不想一遍又一遍地翻译新版本的文档。

Crowdin 提供了一个 Duplicate Strings 设置。

Crowdin Duplicate Strings option setting

我们建议使用 Hide,但理想的设置取决于你的版本之间差异有多大。

warning

不使用 Hide 会导致配额中 源字符串 数量大大增加,并将影响定价。

多实例插件​

你需要为每个插件实例配置翻译文件。

如果你有一个 ID 为 ios 的文档插件实例,你将需要也为这些源文件进行配置

  • website/ios
  • website/ios_versioned_docs(如果已版本化)

维护你的站点​

有时,你会在 Git 上移除或重命名一个源文件,而 Crowdin 会显示 CLI 警告:

Crowdin CLI: download translation warning

当你的源文件被重构时,你应该使用 Crowdin UI 手动更新你的 Crowdin 文件:

Crowdin UI: renaming a file

VCS (Git) 集成​

Crowdin 有多个 VCS 集成,用于 GitHub、GitLab、Bitbucket。

TL;DR

我们建议避免使用它们。

能够在 Git 和 Crowdin 中同时编辑译文,并在两个系统之间进行双向同步,这本可以很有帮助。

实际上,由于一些原因,它工作得并不可靠:

  • Crowdin -> Git 的同步工作正常(通过 pull request)
  • Git -> Crowdin 的同步是手动的(你必须按一个按钮)
  • Crowdin 用于将现有 Markdown 译文与现有 Markdown 源文件匹配的启发式方法并非 100% 可靠,每次从 Git 同步后你都必须在 Crowdin UI 上验证结果
  • 两个用户在 Git 和 Crowdin 上并发编辑可能导致翻译丢失
  • 它要求 crowdin.yml 文件位于仓库的根目录

上下文内本地化​

Crowdin 有一个上下文内本地化功能。

warning

不幸的是,由于技术原因,它尚不能工作,但我们很有希望这个问题可以被解决。

Crowdin 会用像 crowdin:id12345 这样的技术 ID 替换 Markdown 字符串,但它做得过于激进,包括了隐藏的字符串,并且弄乱了 front matter、提示框、JSX...

本地化编辑 URL​

当用户浏览 /fr/doc1 页面时,编辑按钮默认会链接到未本地化的文档 website/docs/doc1.md。

你可能更希望编辑按钮链接到 Crowdin 界面,可以通过使用 editUrl 函数来按语言环境自定义编辑 URL。

docusaurus.config.js
const DefaultLocale = 'en';

export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
editUrl: ({locale, versionDocsDirPath, docPath}) => {
// 为法语文档链接到 Crowdin
if (locale !== DefaultLocale) {
return `https://crowdin.com/project/docusaurus-v2/${locale}`;
}
// 为英语文档链接到 GitHub
return `https://github.com/facebook/docusaurus/edit/main/website/${versionDocsDirPath}/${docPath}`;
},
},
blog: {
editUrl: ({locale, blogDirPath, blogPath}) => {
if (locale !== DefaultLocale) {
return `https://crowdin.com/project/docusaurus-v2/${locale}`;
}
return `https://github.com/facebook/docusaurus/edit/main/website/${blogDirPath}/${blogPath}`;
},
},
},
],
],
};
note

目前无法链接到 Crowdin 中的特定文件。

配置示例​

道格龙(Docusaurus)的配置文件是使用版本控制和多实例的一个很好的例子:

crowdin.yml
# 虚构示例
project_id: '123456'
api_token_env: CROWDIN_PERSONAL_TOKEN
preserve_hierarchy: true
files:
- source: /docs/**/*
translation: /i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%

机器翻译 (MT) 问题:链接/图片处理​

Crowdin 最近对其 Markdown 文件格式进行了一些重大更改,现在链接的处理方式与以前不同了。以前它们被视作标签,但现在它们显示为纯文本。由于这些更改,纯文本链接被传递给 MT 引擎,引擎会尝试翻译目标,从而破坏翻译(例如:字符串 Allez voir [ma merveilleuse page](/ma-merveilleuse-page) 被翻译为 Check out [my wonderful page](/my-wonderful-page),这破坏了道格龙(Docusaurus)的 i18n 工作流,因为页面名称不应该被翻译)。

截至 2023 年 12 月 7 日,他们不打算改变链接的处理逻辑,所以如果你计划将 Crowdin 与 MT 一起使用,你应该记住这一点。

也可以在你的 Crowdin 配置中添加一个 base_url:

crowdin.yaml
// ...
base_url: 'https://my-domain.crowdin.com'