跳转至主内容
Version: 2.0.0-beta.16 🚧

i18n - 使用 Crowdin

Docusaurus 的 i18n 系统与其他翻译软件脱钩

只要您将翻译文件放置在正确的位置,您可以将 Docusaurus 与您所选择的任何工具或 SaaS 集成

本文中,我们将使用 Crowdin 作为其中一个可能的集成示例

caution

This is not an endorsement of Crowdin as the unique choice to translate a Docusaurus site, but it is successfully used by Facebook to translate documentation projects such as Jest, Docusaurus, and ReasonML.

请参见 Crowdin 文档Crowdin 支持来寻求帮助。

tip

Use this community-driven GitHub discussion to discuss anything related to Docusaurus + Crowdin.

Crowdin 概述

Crowdin 是一款翻译 SaaS,为开源项目提供了免费套餐

我们推荐以下的翻译流程:

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

Crowdin 提供 CLI 工具来上传资源下载译文,助您自动化翻译流程。

The crowdin.yml configuration file is convenient for Docusaurus, and permits to download the localized translation files at the expected location (in i18n/[locale]/..).

您可阅读官方文档来了解进阶功能及不同的翻译流程。

Crowdin 教程

下方是使用 Crowdin 来翻译新创建的英文版 Docusaurus 站点至简体中文的手把手教程,本文假设您已遵循了 i18n 教程

最终结果可参见 docusaurus-crowdin-example.netlify.app代码仓库)。

准备 Docusaurus 站点

初始化新的 Docusaurus 站点:

npx [email protected] website classic

添加简体中文版网站的配置:

docusaurus.config.js
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'zh-cn'],
},
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 账户,然后创建新项目。

Use English as the source language, and French as the target language.

创建 Crowdin 项目,使用英语作为源语言,简体中文作为目标语言

您的项目创建好了,但暂时空空如也。 我们将在下几步中上传待译文件。

创建 Crowdin 配置

此配置(文档)提供了 Crowdin CLI 能理解的映射条件:

  • 何处寻找要上传的源文件(JSON 及 Markdown)
  • Where to download the files after translation (in i18n/[locale])

website 中创建 crowdin.yml

crowdin.yml
project_id: '123456'
api_token_env: CROWDIN_PERSONAL_TOKEN
preserve_hierarchy: true
files:
# JSON translation files
- source: /i18n/en/**/*
translation: /i18n/%two_letters_code%/**/%original_file_name%
# Docs Markdown files
- source: /docs/**/*
translation: /i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%
# Blog Markdown files
- source: /blog/**/*
translation: /i18n/%two_letters_code%/docusaurus-plugin-content-blog/**/%original_file_name%

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

  • **/*:子文件夹中的所有内容
  • %two_letters_code%:Crowdin 目标语言的两字代码变体(本例中为 zh
  • **/%original_file_name%:翻译文件将保留原始文件夹/文件结构
info

Crowdin CLI 的警告信息有时会晦涩难懂。

我们建议您:

  • 一次改一件事情
  • 配置更改后重新上传资源
  • 使用 / 开头的路径(无法使用 ./
  • 避免像 /docs/**/*.(md|mdx) 一类的花里胡哨表达式(无法使用)

访问令牌

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

您可以在您的个人资料页获得您的个人访问令牌

tip

您也可以保留 CROWDIN_PERSONAL_TOKEN 的默认值,然后在您的电脑和 CI 服务器上将此环境变量设置为您的访问令牌。

caution

个人访问令牌有读写您所有 Crowdin 项目的权限。

不应该提交此令牌,同时我们推荐您创建另外的Crowdin 公司账户来替代您的个人账户。

其他配置字段

  • project_id:您可硬编码此字段,同时您可在 https://crowdin.com/project/<我的项目名称>/settings#api 处找到
  • preserve_hierarchy:是否在 Crowdin UI 上保留您的文档目录结构,而不将所有文件放置于同一文件夹中

安装 Crowdin CLI

This tutorial uses the CLI version 3.5.2, but we expect 3.x releases to keep working.

Install the Crowdin CLI as an NPM package to your Docusaurus site:

npm install @crowdin/[email protected]

添加 crowdin 脚本:

package.json
{
"scripts": {
"crowdin": "crowdin"
}
}

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

npm run crowdin -- --version

在您的计算机上设置 CROWDIN_PERSONAL_TOKEN 环境变量来让 CLI 通过 Crowdin API 进行认证。

tip

您可以暂时在 crowdin.yml 中写入 api_token: '我的令牌' 来硬代码您的个人令牌。

上传源文件

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

npm run write-translations

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

npm run crowdin upload

Crowdin CLI 上传 Docusaurus 源文件

您可在 Crowdin 界面上找到您的源文件:https://crowdin.com/project/<我的项目名称>/settings#files

Crowdin UI 显示 Docusaurus 源文件

翻译源文件

https://crowdin.com/project/<我的项目名称>,点击简体中文语言。

Crowdin UI 显示简体中文翻译文件

翻译 Markdown 文件。

Crowdin UI 翻译 Markdown 文件

tip

使用隐藏字符串以确保翻译人员不翻译不该被翻译的内容:

  • Front matter: id, slug, tags ...
  • 警告: :::, :::note, :::tip...

Crowdin UI 隐藏字符串

翻译 JSON 文件。

Crowdin UI 翻译 JSON 文件

信息

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

tip

预翻译您的网站,再手动修复预翻译错误(请先在设置中启用全局翻译存储)。

预先使用隐藏字符串功能,因为 Crowdin 在预翻译上对自己太自信了。

下载译文

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

npm run crowdin download

翻译的内容应该在 i18n/zh-CN 中下载。

使用简体中文启动您的网站:

npm run start -- --locale fr

确保你的网站现在在这里已经被翻译成简体中文:http://localhost:3000/zh-cn/

使用持续集成 (CI) 来自动化翻译

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

website/i18n 添加到 .gitignore 中。

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

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"
}
}

在构建 Docusaurus 网站之前,在 CI 中调用 npm run crowdin:sync 脚本。

提示

为了维持你的部署预览的速度,不要下载翻译,并且在功能分支上使用 npm run build --locale en

caution

Crowdin 对多个并行上传/下载的支持不太好:最好只将翻译内容包含到生产部署中,并且在部署预览时不要翻译。

Crowdin 进阶议题

MDX

caution

在 MDX 文档中,要格外关注 JSX 片段!

Crowdin 缺少官方 MDX 支持, 但他们支持 .mdx 的文件后缀名,并将它们解释为 Markdown(而不是纯文本)。

MDX 问题

Crowdin thinks that the JSX syntax is embedded HTML and can mess up with the JSX markup when you download the translations, leading to a site that fails to build due to invalid JSX.

Simple JSX fragments using simple string props like <Username name="Sebastien"/> will work fine; more complex JSX fragments using object/array props like <User person={{name: "Sebastien"}}/> are more likely to fail due to a syntax that does not look like HTML.

MDX 解决方案

We recommend extracting the complex embedded JSX code as separate standalone components. We also added an mdx-code-block escape hatch syntax:

# How to deploy Docusaurus

To deploy Docusaurus, run the following command:

````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 重复字符串选项设置

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

caution

不使用 Hide 会导致配额中包含更多的 源字符串 ,从而影响 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。

TL;DR

我们不推荐您使用。

在 Git 和 Crowdin 中同时编辑翻译文件,达成双向同步的效果可能对您有所帮助。

但实际上,这种做法并不可取,原因如下:

  • Crowdin -> Git 同步没有问题(合并请求)
  • Git -> Crowdin 需要手动操作(您需要手动点击)
  • Crowdin 无法做到 100% 准确的将已有的 Markdown 源文件及其译文关联起来,您随后需要在从 Git 同步后前往 Crowdin UI 验证结果
  • 多名用户同时在 Git 和 Crowdin 编辑可能会造成翻译丢失
  • 需要将 crowdin.yml 放置在仓库根目录

语境内本地化

Crowdin 支持语境内本地化功能。

caution

遗憾的是,由于技术原因,此功能尚不能使用。但我们这个问题觉得解决起来并不麻烦。

Crowdin replaces markdown strings with technical ids such as crowdin:id12345, but it does so too aggressively, including hidden strings, and messes up with front matter, admonitions, JSX...

Localize edit URLs

当用户浏览位于 /zh-cn/doc1 的页面时,编辑按钮则将默认指向 website/docs/doc1.md 处的未翻译文档。

You may prefer the edit button to link to the Crowdin interface instead by using the editUrl function to customize the edit URLs on a per-locale basis.

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

module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
editUrl: ({locale, versionDocsDirPath, docPath}) => {
// Link to Crowdin for French docs
if (locale !== DefaultLocale) {
return `https://crowdin.com/project/docusaurus-v2/${locale}`;
}
// Link to Github for English docs
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 v2 配置文件就是一个使用文档分版和多实例功能的好例子:

crowdin.yml
project_id: '428890'
api_token_env: CROWDIN_PERSONAL_TOKEN
preserve_hierarchy: true
languages_mapping: &languages_mapping
two_letters_code:
pt-BR: pt-BR
files:
- 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