没有不值得去解决的问题,也没有不值得去学习的技术!

法语版上线后,我没有马上开始韩语:A Tour of Go 新增语言流程的一轮完善与简化

图 1:fr-FR 首次 production 验收完成后,我没有立即开始 ko-KR,而是先连续完成新增语言与首次生产流程的优化

作者:

A Tour of Go 多语言翻译项目

图3:访问过去的 Go Tour 简体中文站 tour.go-zh.org 时,当前已经无法正常建立连接。

(1) 从「A Tour of Go 中文版」这个搜索词开始:我决定做一个持续维护的 Go Tour 中文版

本地运行的 A Tour of Go「Methods continued」课程页面,左侧为课程说明,右侧为 Go 代码编辑器及运行结果。

(2) A Tour of Go 中文版项目设计冻结:从 101 页到 103 页,从 Gin 转向 Cobra CLI

A Tour of Go 中文版开发实战:英文基线、在线运行与 101 页上游同步体系

(3) A Tour of Go 中文版开发实战:英文基线、在线运行与 101 页上游同步体系

go-tour-i18n 项目完成首个简体中文课程页面 welcome/1 的整页翻译、结构校验和本地预览。

(4) A Tour of Go 多语言翻译项目实录:完成首个 zh-CN 页面翻译闭环

图 1:DeepSeek 官方更新日志显示,本次只更新 DeepSeek-V4-Flash,DeepSeek-V4-Pro API 与 APP、Web 模型均未更新

(5) A Tour of Go 中文翻译项目进展:完成前 8 页、修复 present 语法,并暂缓 DeepSeek 对比

图 1:generics/1 简体中文页面本地预览

(6) 一个页面重试五次:使用 GLM-5.2 翻译 A Tour of Go 时遇到的 Token 与 Present 结构问题

图 1:methods/16 简体中文页面浏览器预览

(7) A Tour of Go 中文翻译实录:如何只翻译教学代码注释,而不破坏 Go 代码

图 2:A Tour of Go methods/20「练习:错误」中文候选页面最终渲染效果

(8) A Tour of Go 多语言翻译:当正确的中文语序被受保护标记顺序校验误判

图 3:methods/24 中文页面,静态代码、Bounds 行内代码和链接内的 image.Rectangle 均正确渲染

(9) A Tour of Go 中文翻译完成 7 个代表页校准:最后 3 页又发现了哪些真实问题

图 2:首批 10 个普通页面经过试跑与流程校准后全部进入 ready

(10) A Tour of Go 多语言翻译项目:首批 10 个普通页面试跑,从 2 个 blocked 到全部 ready

图 2:flowcontrol/6 最终中文页面,return 与 v 为适应自然中文语序发生整体换位

(11) A Tour of Go 中文翻译第二批实跑:从 Inline Code 顺序误判到历史响应重新验证

图 1:flowcontrol/6 首次原始输入实验直接通过统一自动校验

(12) A Tour of Go 多语言翻译项目:从 swap 问题重新审视 Token 保护,原始输入与最小保护的一次真实实验

图 7:moretypes/1 最终中文页面预览。左侧课程正文、静态代码块、行内代码和教学注释均正常渲染,右侧官方 Go 示例继续保持原样。

(13) A Tour of Go 多语言翻译实战:第三批 10 页全部 Ready,继续校准 Protected Token 的结构角色

图 4:上游源码确认共有 103 个课程页面,zh-CN 最终达到 ready=103、pending=0、blocked=0

(14) A Tour of Go 多语言翻译项目:zh-CN 103 个课程页面全部 ready,课程正文翻译阶段完成

图 4:GitHub 与 ChatGPT 已成功连接

(15) ChatGPT 连接 GitHub 实测:让 AI 直接读取 go-tour-i18n 仓库参与译文审核

图 3:中文 Go 术语校准完成后,将确定的译法写入 zh-CN glossary

(16) 为什么 channel 最终选择“通道”:A Tour of Go 中文术语统一的一次实践

图 1:技术含义基本正确,但“返回一个返回……”已经明显影响阅读,因此仍被列入 C 类修订

(17) 103 页逐页审核后,只返修 16 页:A Tour of Go 中文正文发布前质量审计

图 2:中文课程页面以及已经完成本地化的编辑器控制区

(18) A Tour of Go 多语言翻译项目:公共 UI 本地化完成,从课程译文走向完整中文界面

图 2:完整 zh-CN 正式投影成功生成,103 个课程页面被重新组装为 7 个 .article

(19) 从 103/103 ready 到完整站点验收:A Tour of Go 中文版补齐正式投影与课程元数据本地化

图 3:从 production bundle 独立启动的 A Tour of Go 简体中文页面,课程正文、公共 UI 和官方 Go 示例均已进入最终发布形态。

(20) A Tour of Go 多语言翻译项目:103 页全部完成后,我终于生成了可独立部署的 zh-CN Production Bundle

图 1:A Tour of Go 简体中文版正式生产页面,远程运行已经成功输出“Hello, 世界”。

(21) A Tour of Go 多语言翻译项目:zh-CN 正式上线,从生产发布到 go.dev Playground 的完整部署记录

图 1:新生产域名 go-dev.shuijingwanwq.com 已正常访问,Go 示例远程运行成功

(22) A Tour of Go 多语言翻译项目:从 go-tour 到 go-dev,完成 EdgeOne、Nginx 与 HTTPS 生产域名迁移

图 1:A Tour of Go 多语言翻译项目正式生产首页

(23) A Tour of Go 多语言翻译项目正式上线:从项目首页、公共 Footer 到生产发布元数据的完整收尾

图 3:GA4 g/collect 请求返回 HTTP 204,并携带当前课程页面地址,确认 Google Analytics 已产生真实事件上报

(24) A Tour of Go 多语言翻译项目:接入 Google Analytics 与百度统计,从 systemd 注入到 EdgeOne 与真实上报验证

图 1:生产环境 robots.txt 与 sitemap.xml 已正确对外提供,Sitemap 共包含 104 个规范 URL。

(25) A Tour of Go 中文站上线后:补齐 robots.txt、Sitemap,并完成五大搜索引擎提交

图 4:发布生产并刷新 EdgeOne 缓存后,再次通过真实手机访问,顶栏项目名称已经完整保持在一行,最终移动端验收通过。

(26) A Tour of Go 多语言翻译项目:修复手机端首页顶栏标题换行,并完成真机验证

图 3:生产自动部署脚本第一次真实运行时,systemd 已经显示 active,但首次 localhost 探测仍然得到 HTTP 000;随后连续 3 次同时满足 active + HTTP 200 后,脚本才判定部署成功,并继续完成公网 HTTP 200 验收。这次真实运行验证了连续应用层健康检查的必要性。

(27) A Tour of Go 多语言翻译项目:从一次 203/EXEC 故障到生产自动部署脚本落地

图 1:阿里云生产环境访问 go.dev Playground 时出现 TLS 握手超时

(28) A Tour of Go 多语言翻译项目:为 Playground 搭建独立的 ZgoCloud 执行代理

图 3:清除 /tour/script.js 缓存后,Run 已直接请求 ZgoCloud /compile 并返回 HTTP 200

(29) A Tour of Go 多语言翻译项目:将生产 Run / Format 切换到 ZgoCloud 执行节点

【图 1:微信中运行 Go 示例时出现 Error communicating with remote server.】

(30) A Tour of Go 中文版手机端运行与格式化偶发失败排查:从间歇性报错到补强 Nginx 可观测性

【图 4:向 golang-dev 公开邮件列表发送 Playground 使用告知邮件并投递成功】

(31) A Tour of Go 多语言项目:为 Playground 代理增加唯一 User-Agent,并主动告知 Go 官方

【图 3:百度剩余 9 个核心 URL 提交成功,remain 归零】

(32) A Tour of Go 搜索引擎收录实测:从 Google 自然索引到百度主动提交与 Bing IndexNow

【图 1:当前正式中文 methods/24 页面,右侧示例代码运行成功】

(33) A Tour of Go 中文翻译完成后,我为什么重新评估 minimal-protect 翻译模式

【图 3:5 个页面 Request SHA256 完全一致,但 Response SHA256 全部不同】

(34) A Tour of Go 翻译实验:更多原始上下文为什么没有更好?从 157 个保护标记到 30 次重复盲评

图 3:3 个代表页 × 4 个评审模型,共 12 次匿名评审的完整排名与第一名票数。

(35) A Tour of Go 翻译质量再评估:minimal-protect 未达预期后,我开始比较 ChatGPT、Codex 与 GLM-5.2

图 2:GitHub 中的 chatgpt-zh-CN-008 重译批次。一个 Batch 同时保存 inputs、raw-responses、candidates、validation 和 retry 记录,GitHub 成为 ChatGPT 与本地翻译流水线之间的数据交接层。

(36) 没有 OpenAI API,我如何用 ChatGPT + GitHub 完成 A Tour of Go 103 页全量重译

图 1:103 个正式课程页面完成最终语义质量审核,结果为 A=103、B/C/D=0,同时不存在缺失、重复或额外页面。A/B/C/D 是本轮项目内部语义质量分级,并不是自动结构 validator 的结果。

(37) 103 页翻完还不能发布:A Tour of Go ChatGPT 译文的语义审核与 Canonical Promotion

图 3:7 个 article endpoint 的 production origin 与 EdgeOne 公网响应全部 exact match,同时新的 ChatGPT 译文标记存在,旧版译文标记已经消失。

(38) 103/103 与 7/7 Exact Match:A Tour of Go ChatGPT 新译文的最终线上验收

【图 2:upstream source preview】

(39) A Tour of Go 多语言翻译项目首次实现 upstream 同步机制:从一次性翻译到长期维护

图 2:A/B/C/D 质量评级规则,展示正式质量 rubric 与 Promotion gate。

(40) A Tour of Go 多语言翻译项目引入 Translation Quality Review:从自动验证到 AI 翻译质量控制

图 4:翻译后 Playground Example,展示中文注释与保持不变的 Go 代码结构。

(41) A Tour of Go 多语言翻译项目实现 Playground Example 本地化:从代码注释翻译到完整学习体验

图 5:公网页面验证广告脚本输出

(42) A Tour of Go 多语言翻译项目接入广告系统:从 AdSense 专用配置到通用 HTML 注入架构

图 3:百度统计单页应用设置

(43) go-dev 单页网站接入统计与 Google AdSense Auto Ads 验证记录

图 1:GitHub 仓库中的多语言翻译项目结构

(44) A Tour of Go 多语言翻译项目:选择日语作为第二语言并启动翻译验证

图 5:模型身份描述不一致

(45) A Tour of Go 多语言翻译项目中的 AI 协作异常排查:一次长期工程会话失效分析

【图 1:ChatGPT GitHub 写入流程调整说明截图】

(46) go-tour-i18n 翻译流程调整:从 ChatGPT GitHub 写入到本地 artifact 导入

图 4 展示了本次异常状态。

(47) go-tour-i18n 翻译自动化流程复盘:已验证的 ChatGPT 翻译能力在新任务中的稳定性问题

【图 6:全部评审结束以后才读取 secret key,完成最终揭盲】

(48) A Tour of Go 翻译质量再评估:Codex High 已接近 ChatGPT High,我决定调整默认翻译引擎

图 4:ja-JP 生产课程页实际运行 Go 示例

(49) A Tour of Go 日语版正式上线:第二门语言 ja-JP 完成生产发布

图 1:ja-JP 上线以后,先提交“新增语言标准化流程”

(50) A Tour of Go 多语言扩展复盘:为第三门语言建立标准化流程

图 1:课程正文结束以后,页面左侧仍然存在大块视觉空白,但 AdSense 并没有在这里插入展示广告。

(51) A Tour of Go 明明有大片空白,为什么 AdSense 就是不显示展示广告?

图 2:golang/website 当前的 _content/tour/。课程内容、静态资源和模板都在这个上游目录中持续维护。

(52) 为了 AdSense,要不要放弃 SPA?A Tour of Go 页面架构的一次取舍

图 4:最终的手动课程广告真实展示在课程内容区域内,而不是 footer 之后。

(53) 保留 SPA 以后,广告应该放在哪里?从 AdSense 预览到方案 B

图 1:Google Search Console 中,多个不同的 A Tour of Go 课程 URL 被判断为“重复网页,用户未选定规范网页”。

(54) SPA 的 SEO 老问题:为什么最后还是给 103 个 A Tour of Go 课程页生成了 Prerender HTML

图 2:方案 B 生产环境中,源码已经正常,但课程主体高度异常缩短,footer 提前进入第一屏。

(55) 从源码空白到 AdSense 高度污染:A Tour of Go 方案 B 的生产收尾

图 1:A Tour of Go 德语版 de-DE 已正式运行在生产环境

(56) A Tour of Go de-DE 上线复盘:第三门语言用了约 16 小时,下一门如何压到 8 小时?

图 1:fr-FR 首次 production 验收完成后,我没有立即开始 ko-KR,而是先连续完成新增语言与首次生产流程的优化

(57) 法语版上线后,我没有马上开始韩语:A Tour of Go 新增语言流程的一轮完善与简化

图 1:根据 ko-KR 期间 76 次 Git 提交估算实际工作时间

(58) 原本预计 6 小时,最后用了约 16~18 小时:A Tour of Go 韩语版上线复盘

A Tour of Go 法语版完成以后,我原本已经可以马上开始下一门韩语。

毕竟到了这个阶段,“新增一门语言应该怎么做”已经不是未知问题。

在此前的日语、德语和法语过程中,TranslationUnit、automatic validation、Quality Check、Final Review、promotion、Locale Surface Review、production 部署等主要流程都已经逐渐固定下来。

尤其是在上一篇《A Tour of Go de-DE 上线复盘:第三门语言用了约 16 小时,下一门如何压到 8 小时?》里,我已经把下一阶段的目标写得比较明确:

不是继续降低质量标准,而是减少没有判断价值的人工操作。

随后完成的法语版,也进一步验证了这一方向。

按照 Git 记录,从 2026 年 8 月 30 日 11:59 左右出现 fr-FR 第一笔初始化与首轮翻译提交,到当天 21:37 完成首次 production 验收,墙钟跨度大约 9 小时 39 分钟。

我当时大体上把它记作:

约 9 小时。

这个结果已经非常接近此前给下一门语言设定的 8 小时目标。

按正常思路,接下来似乎应该马上开始韩语,看看能不能继续压缩到 6~8 小时。

但我没有这么做。

法语完成以后,我又停下来专门处理了一轮流程问题。

图 1:fr-FR 首次 production 验收完成后,我没有立即开始 ko-KR,而是先连续完成新增语言与首次生产流程的优化
图 1:fr-FR 首次 production 验收完成后,我没有立即开始 ko-KR,而是先连续完成新增语言与首次生产流程的优化

从 Git 时间线看,这一段非常清楚。

2026 年 8 月 30 日 21:37:

Plaintext
docs: 完成 fr-FR 首次生产验收

一个多小时以后,23:15:

Plaintext
feat: 优化新增语言与首次生产流程

第二天上午 09:35:

Plaintext
feat: 自动化新增语言首次生产流程

下午 14:16:

Plaintext
docs: 建立 deferred issue 记录机制

直到 14:58,才真正出现:

Plaintext
feat: 初始化 ko-KR locale

也就是说,法语已经完成以后,我没有继续追求“下一门语言马上开始”。

相反,我先把法语和前几门语言已经验证过的经验,再往工具和正式规则里收了一层。

这一次的重点已经不是:

流程应该怎么设计?

而变成了:

既然流程已经基本确定,为什么还要让我每增加一门语言都重复做这些事情?


从“标准流程”继续往前走一步

这两个阶段其实很容易混在一起。

所谓流程标准化,解决的是:

下一门语言应该按照什么顺序做?

例如:

Plaintext
locale 准备
→ glossary
→ 公共 UI 与 metadata
→ TranslationUnit 翻译
→ automatic validation
→ Quality Check
→ revision
→ Final Review
→ promotion
→ SEO metadata
→ preview
→ Locale Surface Review
→ publish
→ first production
→ production acceptance

这些步骤在前面的几门语言中已经逐渐明确下来。

但有标准流程,并不代表执行成本已经足够低。

举个最简单的例子。

假设每增加一门语言,我都已经知道需要准备:

Plaintext
locale.json
glossary.yaml
UI catalog
article metadata
course metadata inventory
status.tsv
语言 registry
……

那么“知道这些文件需要存在”,属于流程标准化。

但是如果第五门、第六门语言开始时,我仍然需要:

Plaintext
复制旧目录
→ 修改 locale
→ 删除旧语言内容
→ 检查有没有遗漏
→ 创建 status
→ 检查 UI key
→ 再检查有没有把旧 locale 内容带过来

这部分仍然是重复劳动。

而且这种重复劳动不只是慢。

它还有另一个问题:

非常容易把上一门语言的状态、内容或者历史假设一起复制过去。

所以法语完成以后,我首先处理的,就是新增 locale 的“机械骨架”。


新 locale 不再从“复制上一门语言”开始

现在新增语言的正式入口已经变成了一个明确命令:

Plaintext
go run -mod=readonly ./cmd/tour-i18n locale init \
  --locale <locale> \
  --language-name <autonym> \
  --english-name <English-name> \
  --html-lang <html-lang>
图 2:新增 locale 的机械骨架已经收敛为正式 locale init 命令
图 2:新增 locale 的机械骨架已经收敛为正式 locale init 命令

这一点看起来只是增加了一个 CLI 命令,但实际上它改变了新增语言的起点。

以前更接近:

找一门现有语言作为参考,然后人工搭出下一门语言。

现在则变成:

从正式 catalog 和英文 source 出发,由工具生成一套明确处于“未完成”状态的机械骨架。

目前这个初始化过程会准备包括:

Plaintext
locale.json
glossary.yaml 骨架
UI catalog
article-metadata.json
course-metadata.todo.json
status.tsv
.locale-init-incomplete

其中有一个我很喜欢的设计:

Plaintext
.locale-init-incomplete

它的意义不是告诉开发者:

“这里还有点东西没写完,记得以后回来看看。”

而是直接把“还没有完成语言内容”变成机器能够识别的状态。

只要这个标记仍然存在,完整 build、preview 和 publish 就应该 fail closed。

换句话说:

TODO 不再只是人的记忆,而成为发布流程的一部分。

这也避免了一种很危险的情况:

刚创建完语言骨架,里面其实还是 TODO 或英文占位内容,但因为目录结构已经齐全,误以为它已经可以 build 或发布。


自动生成的是骨架,而不是翻译

这里还有一个边界很重要。

locale init 并没有试图“顺便把语言也翻译了”。

它解决的是机械问题:

Plaintext
哪些文件应该存在
哪些 key 应该存在
TranslationUnit inventory 是什么
status 初始状态是什么
哪些内容还没有完成

但是下面这些事情仍然需要正式语言判断:

Plaintext
glossary 应该怎样制定
公共 UI 应该怎样翻译
article metadata 应该怎样本地化
术语在全站应该怎样统一
TranslationUnit 的正式译文是什么

也就是说,我现在越来越倾向于一个原则:

能机械生成的东西尽量由工具生成,但有语言判断价值的东西不要假装自动化。

这也是这一轮“简化”与简单追求“一键生成所有东西”的区别。

我的目标不是让系统帮我猜。

而是尽量让系统不要再逼我做它自己完全可以确定完成的事情。


首次 production 也不应该每门语言重新手工拼命令

另一个明显的重复区域,是首次 production。

新增 locale 的第一次生产部署,和已有语言的日常发布并不是一回事。

第一次需要建立或者确认:

Plaintext
production identity
hostname
data root
service
port
Nginx
TLS
CDN
Playground Origin
AdSense production 配置
首次 release 激活
production acceptance

早期这些东西之所以大量依赖 Runbook 和人工命令,是因为流程本身还没有完全稳定。

如果服务器目录结构、service 命名、Playground 代理、CDN 方案都还可能变化,那么太早把它们全部写死进脚本,反而容易增加维护成本。

但到了法语完成以后,情况已经不同。

中文、日语、德语、法语连续几门语言走完以后,哪些部分是真正共享的,哪些部分只是 locale-specific identity,已经越来越明确。

所以这时候继续让第五门、第六门语言复制几十条命令,就开始显得没有必要了。

法语完成后的第二轮大改动,就是:

Plaintext
feat: 自动化新增语言首次生产流程
图 3:首次 production 从大量人工步骤继续收敛为正式自动化入口
图 3:首次 production 从大量人工步骤继续收敛为正式自动化入口

从提交本身也能看出,这不是只添加一个几行的 shell wrapper。

这一轮涉及 17 个文件:

Plaintext
2085 insertions
135 deletions

其中新增了:

Plaintext
scripts/first-production.py
scripts/first-production.sh
scripts/first-production-test.py

scripts/production-identity.py
scripts/production-identity.sh
scripts/production-identity-test.py

production/identity.json

scripts/verify-production-browser.py

并继续调整:

Plaintext
deploy-production.sh
verify-production.sh
PRODUCTION_RUNBOOK.md
NEW_LOCALE_RUNBOOK.md

其中 first-production.py 本身就有 800 多行。

这也是我认为这轮优化值得单独记录的原因之一。

如果只是把几条常用命令包装成:

Plaintext
./deploy.sh

那很难称得上流程变化。

真正有意义的是:

production identity、首次部署和验收边界开始从“人脑理解 Runbook”继续进入机器可执行状态。


自动化不是把所有 HUMAN GATE 都删除

当然,把首次 production 自动化,也不等于:

从此运行一个脚本,什么都不用管,直接上线。

这一点我反而越来越谨慎。

例如 DNS、CDN、真实浏览器页面、某些生产环境操作,本身就可能存在外部状态。

真正应该自动化的是:

Plaintext
确定性检查
重复命令
固定文件生成
identity 校验
机器能够明确判断 PASS / FAIL 的验收

而不是为了追求“一键部署”这个听起来漂亮的目标,把需要人确认的生产边界偷偷跳过去。

所以我现在对“自动化”的理解更接近:

让机器把问题推进到真正需要人判断的位置。

而不是:

让人彻底退出流程。

这和 TranslationUnit Quality Check 的思路其实是一样的。

我可以自动准备:

Plaintext
source
candidate
glossary
validation evidence
TranslationUnit identity

但最后:

Plaintext
A / B / C / D

仍然属于语言质量判断。

production 也是如此。


命令输出本身也开始成为优化对象

除了减少命令数量以外,还有一个之前比较容易忽略的问题:

命令输出太多,也会消耗人工时间。

CLI 如果每执行一步都打印几百行 JSON,看起来似乎“信息非常完整”。

但如果我真正需要判断的只是:

Plaintext
是否通过
失败在哪个 stage
哪些 TranslationUnit 需要处理
下一步应该执行什么

那么把所有内部信息默认全部倾倒到终端,反而会增加查找成本。

这也是项目后来持续调整的一个方向:

默认输出尽量服务于下一步决策,完整 evidence 则继续保留在正式文件中。

这个方向在后面的韩语实战中还会继续推进,例如一些 retranslation 命令的默认输出后来又继续精简。

所以这里我没有把它理解成:

少打印一点,看起来干净。

而是:

终端是执行界面,evidence 文件才是长期证据。

两者没有必要承担完全相同的职责。


还有一种浪费:发现一个问题,就被迫马上解决

法语完成以后,我还专门增加了一项看起来和“自动化”关系不大的东西:

Plaintext
Deferred Issues

它解决的是另一个长期项目里很实际的问题。

开发、测试和 production 验收过程中,经常会遇到这种情况:

我发现了一个真实问题。

但这个问题:

  • 不阻塞当前 locale;
  • 不属于当前任务;
  • 暂时没有必要立即修复;
  • 或者现在解决反而会把当前流程带偏。

以前这种时候通常只有两个选择。

第一种:

现在就解决。

缺点是很容易 scope 膨胀。

本来正在部署一门语言,最后却因为顺手发现的另一个问题,又改了半天完全不同的代码。

第二种:

先不解决。

但问题往往只留在 ChatGPT / Codex 对话里。

几天以后会话换了、上下文变了,这个真实问题也就跟着消失了。

于是我增加了:

Plaintext
docs/DEFERRED_ISSUES.md
图 4:Deferred Issues 用来正式保存“已经真实发现,但本轮明确暂缓”的问题
图 4:Deferred Issues 用来正式保存“已经真实发现,但本轮明确暂缓”的问题

这个文件的边界其实定得比较严格。

不是所有“以后可能优化一下”的想法都往里面扔。

必须同时满足:

  1. 问题已经在真实工作中被发现,有现象、失败结果或者其他 evidence;
  2. 已经明确决定当前任务暂不处理或者延后处理。

普通 TODO、未经验证的怀疑、未来架构设想,都不属于 Deferred Issue。

状态也明确限制为:

Plaintext
open
resolved
accepted
obsolete

这意味着问题即使以后不修,也需要有明确结论。

例如:

Plaintext
resolved

表示已经修复,并且有验证证据。

Plaintext
accepted

表示问题仍然存在,但已经明确接受这个影响,不准备继续修。

Plaintext
obsolete

则代表相关前提已经不存在,问题自然失效。


Deferred Issues 实际上也是一种流程简化

最开始我只是担心问题遗忘。

但后来再看,我觉得 Deferred Issues 对效率还有另一层价值:

它允许当前任务保持当前任务。

以前发现一个问题以后,因为害怕以后忘记,很容易产生一种心理压力:

要不现在顺手解决了吧。

结果一个原本 10 分钟记录就能结束的问题,可能把当前任务拖进另外两个小时的分析。

有正式 Deferred Issues 以后,可以明确做出:

Plaintext
确认问题真实存在
→ 记录 evidence
→ 标记 open
→ 当前任务继续

这并不是逃避问题。

恰恰相反,它比:

“这个以后再说。”

可靠得多。

因为“以后再说”如果没有正式记录,大部分时候其实就等于:

“以后很可能忘掉。”

所以这一轮的“简化”,已经不只是命令数量的简化。

它还包括:

减少不必要的上下文切换。


到这个阶段,我越来越不希望新增 locale 依赖记忆

如果回头看前几门语言,会发现项目一直有一个变化。

最开始很多事情依赖的是:

我记得上一次是这样做的。

随后变成:

文档里已经写清楚应该怎么做。

再往后则开始变成:

只要这是机器能够确定完成的事情,就不应该要求我每次重新读一遍文档再手工执行。

这三种状态其实差别很大。

第一种依赖人的记忆。

第二种依赖人的执行纪律。

第三种才真正开始依赖工具本身保证边界。

例如新 locale:

以前可能需要记得:

Plaintext
不要漏 status.tsv
不要直接复制旧 glossary
别忘了 article metadata
别忘了 UI catalog
正式 metadata 还没生成
现在不能 publish

现在越来越多的东西可以变成:

Plaintext
locale init
→ 明确生成机械骨架
→ 明确留下 incomplete marker
→ 未完成就 fail closed

人的工作就可以集中到:

真正需要判断的语言内容。


“简化流程”不能变成“减少质量 gate”

这里我仍然想特别强调一个边界。

这一轮做了很多:

Plaintext
自动生成
自动初始化
自动部署
自动验收
减少命令
减少默认输出
减少上下文切换

但是有一类东西我并没有打算因为效率而减少:

Plaintext
逐 TranslationUnit Quality Check
A-only
独立 Final Review
Locale Surface Review
production acceptance
必要的 HUMAN GATE

也就是说,我想减少的是:

操作成本。

而不是:

判断成本。

如果一门语言真的出现 20 个 B,那么它就应该 revision 20 个。

如果 Final Review 又发现新的问题,就应该重新回 revision。

如果真实浏览器发现页面异常,就应该修。

这些都不能因为:

“我这一门语言计划 6 小时完成。”

就强行跳过去。

所以真正理想的 happy path 应该是:

没有问题时非常快;有问题时能够正确变慢。

这比“无论任何语言都必须 6 小时上线”重要得多。


然后我才开始韩语

到 2026 年 9 月 1 日 14:58:

Plaintext
feat: 初始化 ko-KR locale

韩语才正式开始。

当时我的预期其实很乐观。

德语已经把新增 locale 的边际成本降下来。

法语又只用了大约 9 小时。

而且法语结束以后,我还专门继续完成了:

Plaintext
locale 机械骨架初始化
首次 production 自动化
production identity
验收入口继续收敛
Deferred Issues
减少重复操作

按这个趋势,我当时认为:

韩语大约 6 小时左右完成,并不是一个特别激进的目标。

甚至从逻辑上来说,它应该比法语更快。

但实际结果完全不是这样。

韩语最终明显比法语慢得多。虽然中文和日文同样属于非拉丁语系,但韩语这次又暴露出了一批新的语言边界问题,尤其是 Go 标识符与韩语助词直接连接时,validator 会产生误判。

也正因为如此,我后来才越来越清楚一件事:

流程越来越成熟,并不意味着每增加一门语言,耗时就一定线性下降。

工具只能消掉已经理解的问题。

新的自然语言仍然可能带来以前从来没有遇到过的问题。

而这部分,就是下一篇韩语版复盘真正值得记录的内容。


小结

法语完成以后,我没有马上开始韩语。

从时间线上看,中间只有很短的一段间隔。

但就在这段时间里,项目又完成了一轮比较集中的流程调整:

Plaintext
fr-FR production acceptance
→ 新增 locale 初始化能力
→ 首次 production 进一步自动化
→ production identity 收敛
→ Deferred Issues 正式化
→ ko-KR 初始化

这一次和之前“为第三门语言建立标准化流程”最大的区别在于:

标准化解决的是“应该怎么做”。

而这一轮继续解决的是:

既然已经知道怎么做,还有多少事情根本不应该再由人重复做。

我现在越来越觉得,一个长期维护的多语言项目,真正能产生复利的并不是:

下一门语言翻译得比上一门快 10%。

而是:

上一门语言已经解决过的问题,不要再让下一门语言重新支付一次成本。

如果 locale 骨架已经固定,就让工具生成。

如果 production identity 已经有明确 schema,就不要再到多个脚本里分别写一遍。

如果首次 production 的机械步骤已经验证稳定,就把它们收进正式入口。

如果问题已经发现但本轮不处理,就留下可追踪的 evidence,而不是让它消失在聊天记录里。

如果机器能够判断 PASS / FAIL,就不要每次让人重新阅读十几条命令输出。

但与此同时,真正属于语言质量和生产风险判断的 gate,仍然应该保留。

这才是我现在理解的“流程简化”:

不是把流程砍短,而是把流程中没有判断价值的部分拿掉。

法语之后的这一轮优化,本来是为了让韩语更快。

事实证明,韩语最终并没有更快。

但也正因为韩语后来遇到的问题更多,我反而进一步确认:

这轮优化并没有白做。

如果连这些已经可以消掉的重复操作都还保留着,那么韩语最终花费的时间只会更长。

而下一篇,我准备专门整理:

为什么原本预计大约 6 小时完成的 A Tour of Go 韩语版,最终实际投入达到了约 16~18 小时。

A Tour of Go de-DE 上线复盘:第三门语言用了约 16 小时,下一门如何压到 8 小时?

A Tour of Go 多语言翻译项目

本系列完整记录 A Tour of Go 多语言翻译项目从架构设计、整页翻译、结构保护、自动校验,到生产发布与后续维护的实际开发过程。

项目入口:
✅ 简体中文:A Tour of Go 简体中文版
✅ 日语:A Tour of Go 日语版
✅ 德语:A Tour of Go 德语版
✅ 法语:A Tour of Go 法语版
✅ 项目源码:GitHub:shuijingwan/go-tour-i18n

当前已上线简体中文、日语、德语和法语版本。项目属于非官方社区多语言翻译项目,与 Go 官方无隶属或授权关系。