翻译文档

本页面向译者,以及所有在改英文时动到了已有译文段落的人。

这些页面用英文写成,通过 Sphinx 的 gettext 流程翻译,所以译文是挂在源文本的每个段落上,而不是整个文件上。正因为如此,只翻译一部分也是安全的:没有译文的字符串会回退到英文,站点照样能完整构建。

make docs-gettext                      # extract one catalog template per page
make docs-translate DOCS_LANG=zh_CN    # create or update that language's catalogs
# edit docs/developer/locale/zh_CN/LC_MESSAGES/*.po -- fill in msgstr
make docs-lang DOCS_LANG=zh_CN         # build it and read the result

消息目录(catalog)里的每条记录,都由英文原文和对应的译文组成:

#: ../index.rst:4
msgid "HybridInference is an open-source LLM inference gateway."
msgstr "HybridInference 是一个开源的 LLM 推理网关。"

由于英文文本就是查找键,编辑一个段落会自动让它的译文失效:下一次 make docs-translate 会把那条记录标成 #, fuzzy,构建就不再使用它,页面回退到英文,而不是继续提供一份已经和代码对不上的译文。译者只需要回头处理被标记的那些条目。这正是用消息目录、而不另外维护一份 .zh.md 文件的原因:平行文件会悄悄和原文脱节,读者完全看不出自己读的内容已经过时。

.po 文件要提交进仓库;docs/gettext/ 是生成的,已被 git 忽略。

翻译一个页面不必全部翻完,也没有义务让某种语言一直保持完整——没翻译的段落只是回退到英文,不算 bug。

翻译成中日韩语言时

四个坑,全都是静默的——构建照样全绿,页面却是错的。

以数字开头的标题会被丢弃。Sphinx 会重新解析翻译后的标题,而 MyST 把 1. 读成有序列表标记而不是文本。结构与原文对不上,翻译被丢弃,输出的是英文标题,而且没有任何警告——-W 下也没有。把那个点转义掉:

msgstr "1\. 申请一个节点"

在 index.rst 里,紧贴 CJK 字符的行内标记不会被解析。reStructuredText 要求开头的 * 前面是空白或特定标点,而汉字两者都不是,于是 请求的*模型 id* 会把星号原样显示出来。用一个转义空格把它们隔开——在消息目录里写作 \\ ,也就是字符串里的反斜杠加空格:

msgstr "客户端请求的\\ *模型 id*\\ ,与真正服务它的\\ *端点*\\ 是解耦的。"

这条只适用于 index.rst。Markdown 页面不需要转义:CommonMark 认为 CJK 字符既不是空白也不是标点,所以夹在汉字之间的 **模型 id** 是合法的强调。

不过 Markdown 有一种情况确实会出问题:结尾的 ** 如果前面是中文句号、后面紧跟汉字,就不满足 right-flanking 条件,于是 **术语。**后文 会原样留下星号。把句号移到加粗外面,写成 **术语**。后文;这样的排版本来也更好,给标点加粗本身就不对。

过期的 .mo 会掩盖你的改动。当 .mo 比它的 .po 新时,Sphinx 会跳过重新编译,于是你验证的那次构建根本没读到你的修改。每次验证构建之前先执行:

find docs/developer/locale -name '*.mo' -delete

能一次把这四个坑都查出来的办法,是和英文构建结果做结构对比:逐页比较 <code>、<strong>、<em>、<a> 的数量,以及行内代码字面量和链接目标的多重集。丢了一个标记,或者连链接目标也翻译了,只有这项检查能发现。

还有一个,好在它至少会明着报错:make docs-translate 可能会在你已标注 no-python-format 的条目上再追加一个 python-format——源串里出现类似 ≥ 5% 的写法,在 gettext 看来就像格式串——随后 msgfmt -c 会拒绝这对互相矛盾的标记。把新加的 python-format 删掉,保留 no-python-format 即可。

译文如何发布到站点上

make docs 用一次 sphinx-build 构建出所有已发布的语言。DOCS_LANGUAGES(在 conf.py 里声明,默认是 en:English,zh_CN:简体中文)里排第一的语言是根语言,落在输出目录的顶层;其余每种语言由 conf.py 里一个 build-finished 钩子写进 docs/build/html/<code>/。

之所以只调用一次、而不是每种语言各调用一次,是因为已发布站点的构建命令在托管方的设置里,不在本仓库中——发布时没有环境变量可设,所以语言列表只能写在 conf.py 里随代码走。把根语言放在输出目录顶层,是为了保住纯英文站原有的每一个 URL:/routing.html 还是 /routing.html,译文在 /zh_CN/routing.html。

侧边栏的切换器指向其他每种语言的同一页;声明的语言少于两种时它什么都不渲染,所以单语言站点不会出现一个点了没用的控件。只想构建英文时用 DOCS_LANGUAGES="en:English"。

检查译文是否悄悄退回了英文

make docs-verify

它就是 make docs 加上 ops/ci/check_docs_translations.py,和 Docs Build 这个 CI job 跑的内容一样。之所以需要它,是因为上面这些问题 -W 一个都发现不了:Sphinx 遇到翻不了的字符串就回退到英文原文,所以一个已经悄悄退回英文的页面照样能干净地构建通过。它包含五项检查——

  • fuzzy 条目,Sphinx 拒绝使用它们;

  • 缺少消息目录,即新加了页面却没跑 make docs-translate;

  • 过期的消息目录——英文改过了,新的 msgid 在消息目录里找不到对应的条目。这是最常见的情况,而且根本不会带 fuzzy 标记,因为压根没有重新合并过;

  • 以块标记开头——译文以 1. 、- 、# 或 > 开头,会被重新解析成列表或标题,然后被丢弃;

  • 构建产物之间的结构差异,它抓的正是上面那些紧挨 CJK 的标记陷阱。

中文术语

同一个英文术语,在每个页面上都用同一个中文译法;新术语要先在术语表里定义。保留英文的术语,在中文正文里也照写英文。

英文

中文

model id

模型 id

alias

别名

model registry

模型注册表

route

路由

endpoint

端点

upstream

上游

weight

权重

fallback

回退

circuit breaker

熔断器

distribution

发行版

dry run

试运行

console

控制台

operational store

运行数据存储

provider, kind, adapter, router, overlay, manifest

provider、kind、adapter、router、overlay、manifest