翻译文档
本页面向译者,以及所有在改英文时动到了已有译文段落的人。
这些页面用英文写成,通过 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 |