跳到主要内容

i18n - 使用 Crowdin

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

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

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

注意

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

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

提示

请使用此**社区驱动的 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%:译文将保留原始的文件夹/文件层次结构
信息

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

我们建议:

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

访问令牌

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

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

提示

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

注意

个人访问令牌授予对你所有 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 进行身份验证。

提示

你可以临时在 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

提示

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

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

Crowdin UI hide string

翻译一些 JSON 文件。

Crowdin UI to translate a JSON file

信息

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

提示

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

请先使用 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 脚本。

提示

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

注意

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

高级 Crowdin 主题

MDX

注意

请特别注意 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,但理想的设置取决于你的版本之间差异有多大。

注意

不使用 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 有一个上下文内本地化功能。

注意

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

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}`;
},
},
},
],
],
};
备注

目前无法链接到 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'