A Tour of Go 的第三门社区语言 de-DE 已经正式上线。
生产地址:
至此,目前项目已经有简体中文、日语和德语三个实际运行的社区语言版本。
这一次德语版仍然不是“翻译完成就上线”。
103 个课程 Page、19 个 eligible Example,共 122 个 TranslationUnit,仍然完整经过 automatic validation、Quality Check、Final Review、promotion、Locale Surface Review、production 部署和真实浏览器验收。
第一次 Quality Check 中,122 个 TranslationUnit 里有 119 个达到 A,另外 3 个被判定为 B,没有直接放行,而是重新进入 revision。修订完成以后,最终 122 个 TranslationUnit 全部达到 A。
Final Review 又重新独立审核了一遍,最终同样是 122 个 A + approved。
也就是说,这次并没有因为已经是第三门语言,就降低原来的质量门槛。

先说明:这 16 小时并不是从零开始
这篇文章的标题里有一个很醒目的数字:
约 16 小时。
按每天 8 小时计算,大约就是两个工作人日。
这个数字如果脱离整个项目的前期投入,很容易产生误解。
它并不是说:
“从零开始完整翻译、审核、开发并上线一门 A Tour of Go 语言,只需要 16 个小时。”
实际上完全不是这样。
我大约从 2026 年 7 月 28 日开始形成比较明确的 A Tour of Go 多语言计划。
真正开始集中推进第一门简体中文,大约是从 8 月 1 日开始。
一直到 8 月 22 日左右,第一门语言的完整翻译、质量审核、工具、生产环境和上线流程才真正走完整。
仅仅从 8 月 1 日到 8 月 22 日这一阶段,我基本上没有完整休息过,每天投入时间通常都超过 10 个小时。
即使只按照每天 10 个小时进行非常保守的计算:
22 天 × 10 小时 = 220 小时
也已经超过 220 个小时。
而这还只是第一门语言最集中的一个阶段。
中文完成以后,第二门语言 ja-JP 又继续承担了一项很重要的任务:验证此前为中文构建的能力到底能不能真正扩展到另一个 locale。
也是在日语完成以后,我才进一步把新增 locale 的完整生命周期、Locale Surface Review、生产服务器基线、首次部署与日常部署等内容固化成正式 Runbook。
所以,当 de-DE 开始的时候,很多非常昂贵的问题已经不需要重新解决了。
TranslationUnit 怎样定义,不需要重新研究。
Quality Check 和 Final Review 怎样执行,不需要重新设计。
glossary 怎样约束全站,不需要重新决定。
服务器使用什么目录结构、systemd 怎么管理、Nginx 怎么接入、Playground 怎么复用,也不需要重新探索。
广告、共享静态资源、生产部署和 CDN 同样已经有现成基线。
因此,更准确的理解应该是:前面用了数百个小时逐渐把“增加一门语言”做成了一套系统,而 de-DE 第一次比较清晰地展示了,在这套基础已经存在以后,再增加一门语言的边际人工成本可以下降到约 16 个小时。
这也是为什么我认为这个 16 小时值得记录。
它不是用来说明翻译工作本身有多轻松。
恰恰相反,它说明的是:
大量工作其实已经提前完成了。
上一轮解决“流程怎么走”,这一轮开始回答“流程到底有多快”
ja-JP 完成以后,我没有直接开始第三门语言,而是先整理了一轮新增 locale 标准化。
当时最重要的问题是:
如果增加第三门语言,还要不要重新理解一遍整个项目?
现在 de-DE 真正走完以后,这个问题已经基本得到回答。
至少从架构层面来看,答案是:
不需要。
de-DE 没有重新设计 TranslationUnit。
没有重新设计 Quality Check。
没有重新设计 Final Review。
没有重新设计共享广告架构。
也没有重新设计 SPA 生命周期和 Playground。
整个过程基本按照已有 Runbook 往前走。
但新的问题也很快出现了:
即使路线已经知道,真正走完这条路线仍然需要不少人工时间。
这就成为第三门语言和第二门语言最大的区别。
第二门语言以后,我主要在解决:
有没有一条正式流程?
第三门语言完成以后,我开始更关心:
这条正式流程中,还有多少时间其实花在了重复操作、历史遗留问题和人工搬运上?
de-DE 的 16 小时,并不全是“正常新增语言成本”
如果这 16 个小时全部都是:
翻译 → 审核 → 发布
那么下一门语言想压缩到 8 小时,难度其实会很大。
但实际执行以后我发现,其中相当一部分时间并不是德语本身必然需要的。
而是在第三次完整执行时,又发现了一批以前没有完全暴露出来的历史问题。
例如 status.tsv。
第一门、第二门语言时已经有状态文件和相关流程,但到了第三门语言,某些生成和同步边界才真正被完整验证。
再例如翻译文件末尾空行和 EOF normalization。
单独看只是非常小的格式问题,但当 TranslationUnit 数量达到 122 个,而且还涉及 candidate、promotion、diff 和 validation 时,一个很小的不一致就可能不断产生额外确认成本。
除此之外,这一轮还遇到了旧 upstream commit/time fixture。
有 4 个测试仍然期待以前的 upstream baseline:
645042eb
而当前正式 FrozenUpstreamCommit 已经更新。
最终确认以后发现,这和 de-DE 本身、SHA-256、language selector 都没有关系,只是测试中的历史 fixture 没有同步。
问题很小,但如果不把根因确认清楚,就很容易在完全错误的方向上浪费时间。
生产部署阶段还出现过一个首次 deployment 特有的问题。
已有 locale 部署时始终存在 old release,但 de-DE 是真正的首次部署,没有 old release。
原来的 SSH 参数传递在这种情况下会丢失一个空 positional argument,最终触发:
unbound variable
后来专门改成显式 sentinel,才彻底区分“没有旧 release”和“参数丢失”。
这些事情都不是“每新增一种语言理应重新处理一次”的工作。
所以我的处理原则基本都是:
这次既然已经发现,就尽量不要让下一门语言重新支付同样的成本。

从图里的提交记录也可以看到,这次 de-DE 并不是只有翻译内容的变化。
上线过程中不断有测试、部署脚本、生产验收、shared-assets、语言列表规则以及历史问题被继续收紧。
一些“看起来很小”的问题,其实很消耗人工时间
这次有一个感受变得越来越明显:
真正影响效率的,往往不是一个需要两小时开发的大功能,而是连续出现十几个需要人判断十分钟的小问题。
比如一个测试为什么失败。
一个目录到底是不是 production profile。
一个广告环境变量名称在文档里是不是已经过期。
shared-assets 是否真的需要重新部署。
Cloudflare 缓存到底应该强制要求 MISS -> HIT,还是只需要确认缓存状态正常。
新增 de-DE 后,是否必须马上重新部署中文和日语,让旧站首页立即出现 Deutsch。
单独一个问题可能并不复杂。
但每一次都需要:
查看代码 → 找文档 → 判断现状 → 执行命令 → 看结果 → 再决定下一步。
这些操作累计起来,时间很快就上去了。
所以 de-DE 这一轮除了“完成第三门语言”,另一项很重要的结果就是:
继续把这些需要临时分析的问题,尽可能变成代码里的明确行为和仓库里的正式规则。
语言列表最终也选择了“最终一致”,而不是每次全站同步
de-DE 上线以后,还有一个很典型的问题。
新的 language registry 已经包含德语。
因此新构建的 de-DE 自然可以看到:
简体中文、English、Deutsch、日语。
但已经运行在 production 的 zh-CN 和 ja-JP,使用的是它们之前构建好的 release。
旧 release 不会因为 Git 仓库里增加了一个语言,就自动获得新的语言列表。
最开始很容易把这理解为:
新增 de-DE 后,是不是应该马上重新 publish 中文和日语?
这次实际上也额外同步了一次 zh-CN 和 ja-JP,并确认两个站点都正确显示了 Deutsch。
但在继续分析以后,我最终决定:
以后不把这种反向同步作为新增 locale 的上线 gate。
首页的 language registry 是 build-time 数据。
因此已有 locale 的语言列表正式采用 eventual consistency。
新语言上线时,必须确认:
新 locale 自己能够正确看到已有语言。
但是已有的旧 locale,不要求在新语言上线当天立即反向显示它。
它们等下一次正常 upstream sync、UI 更新或者日常 release 时,自然获得最新 registry 就可以。
这个决定看起来只是少部署两个站点。
但如果以后变成 10 门、20 门甚至更多语言,意义就完全不同了。
如果每增加一门语言,都必须立即重新构建、部署和清理其他所有 locale 的缓存,那么成本会随着语言数量不断放大。
而 eventual consistency 可以把这部分成本基本消掉。
production 验收开始真正收敛成一个命令
de-DE 这一轮,我认为最有实际效率价值的改进之一,是:
scripts/verify-production.sh
以前 production 部署完成以后,需要继续手动确认很多事情。
例如:
release identity 是否正确。
服务器上的 current 是否真的是目标 release。
localhost 是否正常。
公网几个关键路由是不是 200。
sitemap 到底有多少 URL。
里面有没有错误 hostname。
有没有 HTTP failure。
/socket 是否仍然保持预期边界。
CDN 有没有真正返回缓存状态。
这些检查单独执行都不复杂。
问题是需要不断复制命令、看输出,再人工记住哪些已经检查过。
现在这部分被正式压缩成:
scripts/verify-production.sh <release-dir>de-DE 的最终结果是:
release identity: PASS
remote identity: PASS
source routes: 7/7 PASS
public routes: 7/7 PASS
html identity: PASS
sitemap: 105/105 PASS
socket boundary: PASS
CDN: PASS
PRODUCTION MACHINE ACCEPTANCE: PASS
verify-production.sh 将 de-DE 的生产机器验收收敛为一次执行,并最终全部 PASS这里我很在意的一点是:
自动化并没有减少验收项目。
不是为了快,就少检查几个 URL。
也不是为了快,就不检查 sitemap。
实际上恰恰相反。
把检查变成脚本以后,可以更稳定地每次都执行完整范围。
所以效率提升和质量降低,并不是一回事。
很多时候,自动化以后反而更容易维持严格标准。
最终验收仍然保留人工 HUMAN GATE
当然,也不是所有事情都适合交给脚本。
机器可以判断:
HTTP 是不是 200。
hostname 是否正确。
sitemap 是不是 105/105。
release SHA 是否一致。
但是机器很难完全替代:
页面到底看起来正不正常。
德语标题有没有溢出。
移动端 header 是否被裁切。
按钮宽度是否够用。
footer 有没有和内容重叠。
点击 Weiter 以后 SPA 是否仍然自然。
Run / Format / Reset 是否真的能正常交互。
广告加载机会是否会破坏课程布局。
所以 de-DE 在机器验收 PASS 后,仍然完整进行了真实浏览器验收。
这一轮实际还发现并修复过一些只有 rendered surface 才比较容易暴露的问题。
包括移动端标题、按钮宽度、课程列表 footer、分页居中和页面之间的视觉细节。
这也是为什么项目现在会明确区分:
machine acceptance
和:
browser HUMAN GATE
我的目标不是把 HUMAN GATE 去掉。
而是尽量让人只判断真正需要人看的东西。
de-DE 最终不是“翻译完成”,而是完整 production gate 通过
最终 de-DE 的 Locale Surface Review 得到:
decision = passed并且不是只有 TranslationUnit 通过。
最终 evidence 中记录的是:
A language quality review: passed
preview rendered acceptance: passed
production machine acceptance: passed
production browser acceptance: passed
lightweight ads confirmation: passed同时:
unresolved language blocker: none
unresolved rendered blocker: none
unresolved production blocker: none
这也是我比较看重的一点。
无论译文最初通过什么方式产生,最终决定它能不能进入 production 的,不应该只是“生成完成”。
真正重要的是后面的:
结构验证、完整 source ↔ target 审核、A-only Quality Check、独立 Final Review、Surface Review 和 production acceptance。
这也是为什么我不准备为了把下一门语言压缩到 8 个小时,就删掉这些 gate。
到了第三门语言,最大的瓶颈已经开始变成人工操作
de-DE 做完以后,我现在觉得整个项目的瓶颈已经发生了一次比较明显的变化。
最开始最大的瓶颈是:
能力不存在。
需要先写工具、做架构、建立 workflow。
后来最大的瓶颈变成:
流程不够明确。
所以 ja-JP 完成以后,我专门建立 Runbook 和 Locale Surface Review。
现在到了 de-DE,越来越明显的瓶颈已经变成:
人工审核与人工操作。
特别是 Quality Check 和 Final Review。
正式规则要求每一个 TranslationUnit 都必须单独审核。
这一点我目前并不打算改变。
122 个 TranslationUnit 就应该有 122 个明确的质量结论。
不能因为前面 20 个都很好,就推测后面的也没问题。
不能用 automatic validation 替代语言质量判断。
也不能因为某种翻译方式过去表现不错,就跳过审核。
真正可以优化的是另外一层。
现在人工审核过程中,人不只是在“判断译文”。
还需要做很多辅助操作:
找当前 TranslationUnit。
读取 source。
读取 candidate。
确认 glossary。
确认 validation evidence。
组织审核上下文。
记录结果。
切换到下一个 TranslationUnit。
如果出现 B,再整理 revision unit。
然后重新进入下一轮。
这里真正有质量价值的,是:
判断这个 TranslationUnit 是否达到 A。
其他相当多动作,其实只是信息搬运。
下一步真正应该优化的,是“让人只负责判断”
所以如果下一门语言还要继续提速,我现在最想优化的不是翻译速度。
而是:
减少一个完整 locale 生命周期里人工需要操作多少次。
比如 Quality Check。
理想状态应该逐渐变成:
生成 Candidate Snapshot
→ 自动准备完整 review packet
→ 连续执行逐 TranslationUnit 审核
→ 自动记录每个 A/B/C/D
→ 自动汇总
→ B/C/D 自动形成 revision unit list这里仍然是逐 TranslationUnit 审核。
标准一点都没有降低。
只是把:
122 次找文件和组织上下文
变成:
少数几次启动审核任务。
Final Review 也一样。
仍然独立重新读取:
source、candidate、glossary、validation evidence、TranslationUnit identity。
但是这些材料应该尽可能自动准备。
最终只让人关注:
这个结果是否可以 approved。
生产流程其实已经开始朝这个方向走了。
目前机器部分越来越接近:
deploy
→ CDN purge HUMAN GATE
→ verify-production.sh
→ browser HUMAN GATE我希望 TranslationUnit 审核以后也能逐渐达到类似状态。
下一门语言为什么目标是 8 小时
如果 de-DE 大约花了 16 个小时,那么下一门直接要求 4 小时,我觉得暂时还是有些激进。
因为语言本身可能出现更多 revision。
不同 locale 的 UI 长度也可能暴露新布局问题。
production 同样可能出现真实异常。
这些事情没有必要为了追求一个漂亮数字而忽略。
所以目前比较现实的下一阶段目标是:
完整新增一门语言控制在 8 小时以内。
这里的“8 小时”同样不是指传统意义上的纯翻译工时。
它仍然指:
在已有生产体系基础上的新增 locale 增量执行时间。
而且前提是不降低现在的质量标准。
122 个 TranslationUnit 仍然逐个 Quality Check。
Final Review 仍然 A-only。
Locale Surface Review 仍然执行。
production machine acceptance 和 browser acceptance 仍然保留。
我要减少的是:
重复复制命令。
重复找文件。
重复确认已经有正式答案的问题。
重复执行可以被脚本稳定完成的机器检查。
以及下一门语言再次踩到这一门语言已经修过的坑。
4 小时可以作为更后面的“无异常路径”目标
如果 8 小时以后也能够稳定达到,我觉得才适合继续看 4 小时。
4 小时更适合被理解成:
基础设施稳定、翻译质量较高、没有明显 revision 风暴、没有生产异常时的一条理想 happy path。
而不是给所有语言设置硬指标。
因为如果一个 locale 在 Quality Check 中真的发现十几个 B,那么就应该老老实实 revision。
质量问题不应该因为“4 小时预算已经到了”而被放过去。
所以从长期来看,我更关心的可能不只是:
一门语言用了多少小时还应该开始记录:
需要多少次人工介入
需要多少次异常回流
需要多少个 HUMAN GATE
多少时间真正花在语言判断
多少时间花在操作和等待只有这样,后面的效率优化才会越来越清楚。
第三门语言真正留下的,不只是 de-DE
从最终 Git 记录来看,de-DE 上线以后,这一轮又留下了不少可以继续复用的东西。
首次 production deployment 对新 locale 的支持已经更完整。
production machine acceptance 已经有正式的一键入口。
shared-assets 的生产验收也进一步自动化。
旧 upstream fixture 已经清理。
retranslation batch 的默认命名也同步到了当前正式工作流。
language registry 的 eventual consistency 规则已经明确写进正式文档。
这些事情意味着:
第四门语言开始的时候,项目状态应该比 de-DE 开始的时候更加干净。

最终工作区重新回到 clean。
对我来说,这也是一次新增 locale 真正结束的重要标志。
不是:
“页面已经可以访问,所以先算完成。”
而是:
这轮发现的问题已经尽量回收到代码、测试、脚本和文档里。
小结
A Tour of Go de-DE 已经正式上线。
如果只看这一轮新增语言本身,从开始集中推进到最终 production,大约花了 16 个小时。
但是这个数字不能脱离前面的长期投入理解。
从 7 月 28 日左右开始形成明确计划,到 8 月初集中推进第一门中文,再到 8 月 22 日左右完成第一门语言完整上线,仅中文最密集的阶段就已经投入超过 220 个小时。
随后 ja-JP 又继续验证第二门语言,并把很多经验变成正式 Runbook 和质量流程。
所以 de-DE 的 16 小时,不是整个项目的成本。
它更接近于:
当底层能力已经存在以后,新增第三门语言的边际成本。
而这一轮最重要的收获,也不仅仅是多了一个德语站点。
更重要的是,我开始能够比较清楚地看到:
哪些时间属于真正不可省略的语言质量判断。
哪些时间属于历史遗留问题。
哪些时间属于机械操作。
哪些操作已经可以交给脚本。
以及整个项目下一阶段真正的效率瓶颈在哪里。
目前看来,最大的瓶颈已经越来越不是“怎么部署一门语言”。
也不是“新增 locale 的架构应该怎么设计”。
而是:
怎样在不降低逐 TranslationUnit Quality Check、A-only Final Review、Locale Surface Review 和 production acceptance 标准的前提下,把大量没有判断价值的人工操作从流程中移出去。
所以第四门语言,我现在给自己的目标是:
8 小时以内。
如果后面能够稳定做到,再尝试把没有明显异常的理想路径进一步压到 4 小时左右。
从 200 多个小时建立第一套完整能力,到第三门语言开始出现大约 16 小时的边际成本,我觉得真正值得继续做的事情已经越来越清楚:
不是单纯追求“翻译得更快”,而是让此前投入的工程能力不断产生复利。
A Tour of Go 多语言翻译项目
本系列完整记录 A Tour of Go 多语言翻译项目从架构设计、整页翻译、结构保护、自动校验,到生产发布与后续维护的实际开发过程。
项目入口:
✅ 在线学习:A Tour of Go 简体中文版
✅ 项目源码:GitHub:shuijingwan/go-tour-i18n
当前第一阶段已完成简体中文版本。项目属于非官方社区多语言翻译项目,与 Go 官方无隶属或授权关系。

