跳到主要内容

i18n - 使用 Crowdin

Docusaurus 的 i18n 系统是与任何翻译软件解耦的。

只要您将翻译文件放在正确的位置,就可以将 Docusaurus 与您选择的工具和 SaaS 集成。

我们以 Crowdin 为例记录了一种可能的集成方式。

注意

这并不是对 Crowdin 的唯一选择的背书,但它已成功用于翻译 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 上注册,并创建一个项目。

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

使用英语作为源语言,法语作为目标语言创建 Crowdin 项目

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

创建 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 目标语言的 2 个字母变体(我们的例子中是 fr)
  • **/%original_file_name%:翻译将保留原始文件夹/文件层次结构
信息

Crowdin CLI 警告并不总是容易理解。

我们建议:

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

访问令牌​

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

您可以在个人资料页面获取 个人访问令牌。

提示

您可以保留默认值 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"
}
}

Test that you can run the Crowdin CLI:

npm run crowdin -- --version

Set the CROWDIN_PERSONAL_TOKEN env variable on your computer, to allow the CLI to authenticate with the Crowdin API.

提示

Temporarily, you can hardcode your personal token in crowdin.yml with api_token: 'MY-TOKEN'.

Upload the sources​

Generate the JSON translation files for the default language in website/i18n/en:

npm run write-translations

Upload all the JSON and Markdown translation files:

npm run crowdin upload

Crowdin CLI uploading Docusaurus source files

Your source files are now visible on the Crowdin interface: https://crowdin.com/project/<MY_PROJECT_NAME>/settings#files

Crowdin UI showing Docusaurus source files

Translate the sources​

On https://crowdin.com/project/<MY_PROJECT_NAME>, click on the French target language.

Crowdin UI showing French translation files

Translate some Markdown files.

Crowdin UI to translate a Markdown file

提示

Use Hide String to make sure translators don't translate things that should not be:

  • Front matter: id, slug, tags ...
  • Admonitions: :::, :::note, :::tip ...

Crowdin UI hide string

Translate some JSON files.

Crowdin UI to translate a JSON file

信息

The description attribute of JSON translation files is visible on Crowdin to help translate the strings.

提示

Pre-translate your site, and fix pre-translation mistakes manually (enable the Global Translation Memory in settings first).

Use the Hide String feature first, as Crowdin is pre-translating things too optimistically.

Download the translations​

Use the Crowdin CLI to download the translated JSON and Markdown files.

npm run crowdin download

The translated content should be downloaded in i18n/fr.

Start your site on the French locale:

npm run start -- --locale fr

Make sure that your website is now translated in French at http://localhost:3000/fr/.

Automate with CI​

We will configure the CI to download the Crowdin translations at build time and keep them outside of Git.

Add website/i18n to .gitignore.

Set the CROWDIN_PERSONAL_TOKEN env variable on your CI.

Create an npm script to sync Crowdin (extract sources, upload sources, download translations):

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

Call the npm run crowdin:sync script in your CI, just before building the Docusaurus site.

提示

Keep your deploy-previews fast: don't download translations, and use npm run build -- --locale en for feature branches.

注意

Crowdin does not support well multiple concurrent uploads/downloads: it is preferable to only include translations to your production deployment, and keep deploy previews untranslated.

Advanced Crowdin topics​

MDX​

注意

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

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

MDX 问题​

Crowdin 认为 JSX 语法是嵌入的 HTML,并且在下载翻译时可能会破坏 JSX 标记,导致站点由于无效的 JSX 而无法构建。

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

MDX 解决方案​

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

# 如何部署 Docusaurus

要部署 Docusaurus,请运行以下命令:


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 提供了一个 重复字符串 设置。

Crowdin 重复字符串选项设置

我们建议使用 隐藏,但理想的设置取决于您的版本有多大不同。

注意

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

多实例插件​

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

如果您有一个带有 id=ios 的文档插件实例,您还需要配置这些源文件

  • website/ios
  • website/ios_versioned_docs(如果有版本)

维护您的站点​

有时,您将在 Git 上删除或重命名源文件,Crowdin 将显示 CLI 警告:

Crowdin CLI:下载翻译警告

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

Crowdin UI:重命名文件

VCS(Git)集成​

Crowdin 为 GitHub、GitLab、Bitbucket 提供了多种 VCS 集成。

TL;DR

我们建议避免使用它们。

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

实际上,它并不能可靠地工作,原因如下:

  • Crowdin -> Git 同步工作正常(通过拉取请求)
  • Git -> Crowdin 同步是手动的(您必须按下按钮)
  • Crowdin 用于匹配现有 Markdown 翻译和现有 Markdown 源的启发式方法并不 100% 可靠,在任何 Git 同步后,您必须在 Crowdin UI 上验证结果
  • 在 Git 和 Crowdin 上同时编辑的两个用户可能导致翻译丢失
  • 它要求 crowdin.yml 文件位于仓库的根目录

上下文本地化​

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

注意

出于技术原因,它目前还不能正常工作,但我们对解决它抱有很大希望。

Crowdin 将 Markdown 字符串替换为诸如 crowdin:id12345 之类的技术 ID,但这样做太过激进,包括隐藏的字符串,并且会破坏前置内容、警告框、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: "docusaurus-v2"
api_token_env: "CROWDIN_PERSONAL_TOKEN"
preserve_hierarchy: true
commit_message: "New translations %file_name% (%language%)"
append_commit_message: false
languages_mapping: &languages_mapping
two_letters_code:
pt-BR: pt-BR
files:
- source: "/website/docs/**/*.md"
translation: "/website/i18n/%locale%/docusaurus-plugin-content-docs/current/**/%original_file_name%"
- source: "/website/docs/**/*.mdx"
translation: "/website/i18n/%locale%/docusaurus-plugin-content-docs/current/**/%original_file_name%"
- source: /website/i18n/en/**/*
translation: /website/i18n/%two_letters_code%/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/docs/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/community/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs-community/current/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/versioned_docs/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/blog/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-blog/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/src/pages/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-pages/**/%original_file_name%
ignore: [/**/*.js, /**/*.jsx, /**/*.ts, /**/*.tsx, /**/*.css]
languages_mapping: *languages_mapping

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

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

截至 2023 年 12 月 7 日,他们不打算更改链接处理的逻辑,所以如果您计划将 Crowdin 与机器翻译一起使用,请记住这一点。