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

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

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

作者:

A Tour of Go 多语言翻译项目

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

图 4:GitHub 与 ChatGPT 已成功连接

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

上一篇文章中,我记录了 A Tour of Go 简体中文 103 个正式课程页面的 ChatGPT 全量重译过程。

整个过程最终形成:

Plaintext
8 个首次全量翻译 Batch
+
3 个 revision Batch
=
11 个 ChatGPT Batch

Batch 001~008 首次完整覆盖 103 页,Batch 009~011 则继续处理后续发现的术语和语言质量问题。

上一篇记录:

https://www.shuijingwanwq.com/2026/08/19/26947/

到这里,一个很容易产生的想法是:

103 页既然已经全部翻译完成,而且都通过了现有 validator,是不是就可以直接覆盖正式 zh-CN?

我最后没有这样做。

因为这一轮让我进一步确认了一件事:

结构正确和语言质量足够好,是两个不同的问题。

自动 validator 能够很好地回答:

Plaintext
present.Section 有没有被破坏?
链接 target 是否保持正确?
代码和 directive 是否保持正确?
保护 token 是否完整?
restore 后结构是否与原文对应?

但它不能完全回答:

Plaintext
技术含义有没有细微偏差?
中文是不是足够自然?
同一个术语是否在不同页面保持一致?
有没有明显的翻译腔?
作为正式 Go 初学者教程是否足够顺畅?

因此,103 页完成 ChatGPT 重译以后,我没有立即进入生产发布。

中间又增加了一道独立的:

完整语义质量审核。

一、自动校验通过,并不代表语义审核结束

A Tour of Go 的 .article 并不是普通 Markdown。

课程页面中可能同时存在:

Plaintext
present.Section
链接
行内代码
预格式化代码
.play
.image
强调结构
其他 present directive

翻译模型只要错误改变其中某些内容,即使生成出来的中文看起来完全正常,最终页面也可能无法正确解析或者渲染。

所以自动 validator 一直都是正式 candidate 的必要条件。

但这一次 103 页 ChatGPT 重译完成以后,我越来越明确:

validator 是必要条件,却不是语言质量证明。

例如下面两种译文:

Plaintext
结构完全正确
技术含义基本正确
中文可以理解

和:

Plaintext
结构完全正确
技术含义准确
术语一致
中文自然
符合正式教程表达

都可能通过相同的结构 validator。

但如果目标是正式替换已经上线的 zh-CN,显然应该尽量达到后一种状态。

因此,这一次没有把:

Plaintext
validator 全部通过

直接等价为:

Plaintext
可以正式发布

二、103 页重新做完整语义质量分级

为了避免只检查少量代表页,我最终把审核范围扩大到了全部:

Plaintext
103 / 103

正式课程页面。

这一轮使用 A/B/C/D 四级来记录每个页面当前的语义和语言质量。

这里的 A/B/C/D 不是 present parser、结构 validator 或自动测试产生的结果,而是一套专门用于本轮完整语义审核的内部质量分级。

四个等级大致表示:

Plaintext
A
可以直接发布。

技术含义准确、忠实原文、中文自然,
术语和教学表达均达到正式发布要求。


B
已经达到发布质量,可以不修改。

只存在比较轻微、可选的表达优化空间。
即使保持当前译文,也不会因此阻塞正式发布。


C
建议发布前修改。

存在比较明显的翻译腔、术语问题、
表达不够清楚或其他能够带来明确质量收益的问题。


D
必须修复。

存在误译、遗漏、技术错误,
或者可能影响读者正确理解原文的明显歧义。

从发布角度,可以进一步理解成:

Plaintext
A → 直接满足正式发布质量
B → 已达到发布质量,修改属于可选优化
C → 建议发布前 revision
D → 必须阻塞发布并修复

这里还有一个很重要的审核原则:

“还能换一种写法”本身,不构成降级理由。

自然语言几乎永远可以继续改写。

如果只要模型还能提出另外一种表达,就不断把页面判为 B 或 C,那么审核永远无法真正收敛。

所以需要判断的是:

当前译文是否存在能够明确指出的问题,以及修改以后是否真的有明确质量收益。

最终审核结果是:

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

最终统计:

Plaintext
Formal course pages : 103

A : 103
B : 0
C : 0
D : 0

Missing pages   : 0
Duplicate pages : 0
Extra pages     : 0

也就是说,到最终冻结这一轮语义审核时:

全部 103 个页面都进入了 A 档。

但这里还有一个过程值得特别说明。

三、B 本来可以不改,但最后还是把 2 页改到了 A

语义审核过程中,并不是一开始就得到:

Plaintext
A=103
B=0
C=0
D=0

当时曾经有:

Plaintext
2 个页面

被评为 B。

按照前面的分级标准,B 本身已经达到正式发布质量,因此理论上完全可以不修改。

也就是说,即使最后停在:

Plaintext
A=101
B=2
C=0
D=0

也不会因为这两页而阻塞正式发布。

但最后我仍然决定继续把这两页调整掉。

原因并不是 B “不合格”。

恰恰相反,是因为:

只剩 2 页,而且修改成本已经很低。

到了整个项目接近收尾的时候,需要处理的已经不是几十页,而只是两个非常明确的小范围 revision。

这时候继续修订的边际成本不高,但能够让最终状态从:

Plaintext
101 个 A
+
2 个 B

收敛到:

Plaintext
103 个 A

所以最终还是完成了这两个页面的 revision。

这个决定也体现了我这次使用 A/B/C/D 分级的目的:

等级首先用于判断是否值得阻塞发布,而不是强迫所有页面无限追求同一种措辞。

如果当时还有几十页 B,我未必会为了把数字变成 A=103 而全部重写。

但只剩 2 页,而且修改成本已经很低,那么顺手完成最后的语言优化就比较合理。

因此最终:

Plaintext
A=103
B/C/D=0

并不是因为:

B 也必须修复。

而是因为:

B 本来可以发布,只是最后仅剩 2 页,继续优化的成本很低,因此顺便完成了最终收敛。

四、为什么我把整体评价从约 94 分提高到约 98 分

在前一轮正式 zh-CN 质量评估中,我给出的内部工程判断大约是:

Plaintext
94 / 100

更准确一些,可以理解为:

Plaintext
93~95

它本身已经属于质量比较好的正式译文。

问题主要不是存在大量明显错误,而是仍然可以感觉到:

Plaintext
少量翻译腔
部分表达不够自然
个别术语还有继续统一的空间
部分页面还可以更接近正式中文技术教程

这也是后来继续研究 minimal-protect、Static Context 和 Translation Engine 的原因。

整个实验最后证明,真正明显改善语言质量的关键并不是继续减少结构保护,而是:

更换 Translation Engine。

ChatGPT 完成全部 103 页整页重译以后,再经过后续 revision 和完整语义审核,我现在更愿意把这一版 zh-CN 的整体质量评价放在:

Plaintext
约 98 / 100

这里需要强调:

98 分不是程序计算出来的自动分数,也不是 Go 官方评价。

它仍然是结合整轮翻译、审核和工程验证后得到的内部综合判断。

支持这一判断的主要依据包括:

Plaintext
103/103 完整重新翻译
全部页面统一语义审核
最终 A=103
B/C/D=0
术语继续统一
已知不良术语完成扫描
正常英文自然语言遗漏清理
多轮 revision
相同结构 validator 再次通过

与原来的约 94 分相比,差异已经不再只是几个代表页面上的主观感受,而是最终覆盖到了全部 103 个正式课程页面。

五、A=103 为什么仍然不是 100 分

这里需要区分两个概念:

Plaintext
A 档

100 分完美译文

A 表示:

当前页面已经达到本轮设定的正式发布质量,不存在值得继续阻塞发布的明确语义、术语或表达问题。

而 100 分意味着的是一个明显更高的标准:

Plaintext
几乎不存在任何合理的编辑改进空间

这一轮虽然完成了 103 页页面级语义审核,但并没有进行一种成本更高的流程:

由独立 Go 技术专家和专业中文技术编辑,对 103 页逐句完成出版级最终校对。

而且自然语言本身也不存在唯一正确译法。

同一句英文完全可能存在两个都准确、自然的中文表达。

一个可能更简洁,另一个可能更符合某种编辑风格。

所以剩下的约 2 分,更准确地说是:

对未来编辑级微调和极小概率遗漏保留的不确定性。

它并不意味着已经知道“还有 2% 的句子存在错误”。

因此,我认为:

Plaintext
约 98

比:

Plaintext
100

更适合作为当前这一版 zh-CN 的工程质量评价。

六、A=103 并不是第一次审核就得到的

最终看到:

Plaintext
A=103
B/C/D=0

很容易让人觉得 103 页第一次检查以后就已经是这个结果。

实际上并不是。

Batch 001~008 完成第一次全量覆盖以后,后续审核仍然发现了一些值得继续调整的地方。

于是又产生了:

Plaintext
Batch 009
Batch 010
Batch 011

这些 Batch 不再是为了补齐缺失页面。

它们专门承担:

Plaintext
术语统一
明确的语言质量 revision
少量 B 级页面最终优化
个别页面最终表达修订

所以历史 Batch 一旦形成以后,我没有回去直接修改原来的 evidence。

而是遵循:

Plaintext
旧 Batch
→ 保持不变

发现质量问题
→ 新建 revision Batch

这让最终页面不仅能知道:

现在使用的译文是什么。

还能够继续追溯:

它为什么从早期版本变成了现在这个版本。

七、最终 canonical 并不都来自第一次全量 Batch

这一点从最终 status.tsv 的 provenance 中可以直接看到。

图 2:部分代表页面的最终 canonical provenance。早期已经达到要求的 welcome/1 仍来自 Batch 001,而 concurrency/1、methods/17、methods/19 分别采用后续 Batch 009 和 Batch 011 的 revision,最终均处于 ready 并通过 canonical validator。
图 2:部分代表页面的最终 canonical provenance。早期已经达到要求的 welcome/1 仍来自 Batch 001,而 concurrency/1methods/17methods/19 分别采用后续 Batch 009 和 Batch 011 的 revision,最终均处于 ready 并通过 canonical validator。

例如:

Plaintext
welcome/1
→ chatgpt-zh-CN-001

说明这个页面的早期版本已经达到要求。

没有必要为了让所有页面都来自最新 Batch,而重新改写本来已经很好的译文。

而:

Plaintext
concurrency/1
→ chatgpt-zh-CN-009

说明它最终采用了后续 revision。

再例如:

Plaintext
methods/17
methods/19
→ chatgpt-zh-CN-011

一直到最后一个 revision Batch,它们才被冻结为最终版本。

所以 canonical promotion 的原则并不是:

Plaintext
找到第一次完整覆盖的 103 页

全部覆盖正式译文

而是:

Plaintext
检查每一个 page

确定该页最终有效 revision

验证完整 provenance

再进入正式 canonical

八、历史 evidence 不能为了正式文件“变漂亮”而修改

真正准备 canonical promotion 时,又遇到了一个看起来很小、实际上很重要的问题。

历史 Batch 中保存的 candidate,文件结尾并不完全统一。

统计以后发现:

Plaintext
23 个 candidate
→ 结尾一个换行

80 个 candidate
→ 结尾存在两个或更多换行

如果直接把这些 historical candidate 按字节复制到正式 canonical:

Plaintext
git diff --check

会出现文件结尾空白相关警告。

最简单的处理方法似乎是:

直接把历史 candidate 的多余换行删掉。

但最后没有这么做。

因为这些历史文件已经承担 evidence 的角色。

如果为了最终提交更整洁,而反过来修改历史 candidate,就会破坏此前已经建立起来的:

Plaintext
raw response

restore

historical candidate

精确对应关系。

最终冻结下来的规则变成:

Plaintext
Historical evidence

raw

restore

batch candidate

保持历史字节不变

然后到了正式 promotion 边界:

Plaintext
Promotion boundary

batch candidate

deterministic EOF canonicalization

validator

canonical candidate

只有跨入正式 canonical 时,才执行确定性的 EOF normalization。

规则非常简单:

Plaintext
只删除文件结尾多余的 \n
最终保证恰好一个 \n

正文和其他字节不改变

这样既保护 historical evidence,又保证正式仓库中的 candidate 文件保持规范。

九、canonical promotion 还要证明译文真的来自对应 ChatGPT evidence

真正准备 promotion 时,我还专门加强了一轮 evidence chain。

因为只知道:

Plaintext
某个 Batch 里存在 candidate

仍然不够。

理论上,如果有人后来手工修改过 candidate,而 promotion 只检查最终结构,那么依然有可能把一个无法证明来源的文件正式提升进去。

所以最终的 promotion 不只是“找到文件并复制”。

还需要重新证明整条来源关系。

大致包括:

Plaintext
manifest 中记录的 input

保存时的 input SHA

当前 Default protector 重新生成 input

字节级比较

ChatGPT raw response

restore

historical candidate

精确字节比较

validator

promotion

这样最终正式 candidate 的 provenance 就不再只是:

这个文件位于 ChatGPT Batch 目录中。

而是:

能够证明它确实来自已经保存的 ChatGPT raw response,并经过确定性的 restore 和相同 validator。

十、存在 retry history 时,还要正确识别最终有效 attempt

上一篇文章记录过两个正式 retry 页面:

Plaintext
moretypes/1
concurrency/1

到了 promotion 阶段,还需要继续解决一个问题:

如果一个页面存在 retry history,promotion 能不能正确识别并选择最终有效的 attempt?

例如:

Plaintext
attempt-001
→ 校验失败

attempt-002
→ 校验成功

那么 promotion 需要证明:

最终被提升的是 attempt-002 对应的有效结果。

不能因为历史目录中仍然保留着 attempt-001,就在后续处理时错误回退到更早的失败结果。

因此后面又加强了 retry provenance 规则。

最终要求类似:

Plaintext
存在 retry history

必须识别最终有效 attempt

attempt 编号必须连续

前序 validation history 必须完整存在

不能凭空出现 attempt-999

不能跳过历史 attempt

不能把 provenance 倒退到已经失败的旧 attempt

这部分看起来很严格,但目的很简单:

正式 promotion 不仅要证明“这个文件能用”,还要证明“它确实来自这条 retry history 中最终确认有效的那一次尝试”。

十一、正式 apply:103 页中 102 页发生变化

全部语义审核和 promotion 机制都冻结以后,才真正执行正式 apply。

结果如下:

图 3:103 页正式 canonical promotion 的执行结果。首次 apply 中 102 个页面发生变化、1 个页面已经与目标 candidate 相同,同时对 80 个历史候选执行确定性的 EOF normalization;promotion 完成后再次 dry-run 得到 0 changed、103 unchanged,确认结果已经收敛。
图 3:103 页正式 canonical promotion 的执行结果。首次 apply 中 102 个页面发生变化、1 个页面已经与目标 candidate 相同,同时对 80 个历史候选执行确定性的 EOF normalization;promotion 完成后再次 dry-run 得到 0 changed、103 unchanged,确认结果已经收敛。

第一次正式 apply:

Plaintext
pages          : 103
changed        : 102
unchanged      : 1
EOF normalized : 80

这里的:

Plaintext
unchanged : 1

并不意味着有一个页面没有使用 ChatGPT 新译文。

而是这个页面当前 canonical candidate 本来就已经与最终目标完全相同,所以不需要产生新的文件差异。

真正值得关注的是:

Plaintext
changed : 102

103 页中有 102 个正式 candidate 实际发生变化。

同时:

Plaintext
EOF normalized : 80

正好对应前面发现的 80 个历史 candidate 文件结尾需要规范化。

十二、为什么 apply 完以后还要再跑一次 dry-run

正式 apply 完成以后,我没有马上提交。

而是重新执行了一次 promotion dry-run。

结果:

Plaintext
changed   : 0
unchanged : 103

这一步验证的是:

promotion 是否已经达到确定、稳定的最终状态。

如果第二次 dry-run 仍然出现:

Plaintext
changed > 0

那就意味着 promotion 本身可能还有:

Plaintext
非确定性处理
状态没有完全落盘
文件生成结果不稳定

之类的问题。

最终:

Plaintext
0 changed
103 unchanged

说明在相同输入和相同 historical evidence 下再次计算,不会继续修改 canonical。

也就是具备了很重要的:

幂等性。

十三、最终正式提交只有 103 个数据文件

promotion 完成以后,又对 Git change set 做了一次最终检查。

图 4:ChatGPT 正式 canonical promotion 最终提交 bfa9f92。本次提交只包含 102 个发生变化的正式 candidate 和 1 个 status.tsv,共 103 个文件,没有任何意外路径;提交完成后工作区保持 clean。
图 4:ChatGPT 正式 canonical promotion 最终提交 bfa9f92。本次提交只包含 102 个发生变化的正式 candidate 和 1 个 status.tsv,共 103 个文件,没有任何意外路径;提交完成后工作区保持 clean。

最终提交:

Plaintext
bfa9f92
data: 提升 ChatGPT 重译候选为 zh-CN 正式候选

统计:

Plaintext
103 files changed
628 insertions(+)
533 deletions(-)

再按路径分类:

Plaintext
canonical candidates : 102
status.tsv           : 1
unexpected paths     : 0

也就是:

Plaintext
102 个正式 candidate
+
1 个状态文件
=
103 个文件

这次提交中没有顺手混入:

Plaintext
promotion 实现代码
测试代码
historical Batch
raw response
其他项目文件

这也是我希望长期保留的一条边界:

正式数据提升和实现 promotion 的代码修改,不应该混在同一个 Git commit 中。

十四、promotion 完成以后,又遇到了一个“测试失败”

正式 candidate 替换完成以后,完整测试并不是第一次就全部通过。

当时有几个测试失败。

继续排查以后发现,并不是 ChatGPT 新译文破坏了程序。

真正原因是:

旧测试把上一版正式中文句子本身当成了固定测试基线。

例如测试曾经直接依赖某一句旧译文。

当 ChatGPT 把它改成技术含义相同、中文更自然的新表达以后:

Plaintext
程序正确
结构正确
页面正确

测试却因为:

Plaintext
中文句子不再逐字相同

而失败。

这其实暴露了另外一个问题:

测试到底应该保护什么?

如果测试目标是确保某个页面仍然保持正确的 publication semantics,那么更适合检查:

Plaintext
稳定语义锚点
页面结构
发布顺序
状态字段
provenance

而不是永久冻结整句中文。

所以后面只更新了与新 canonical 有关的测试基线。

最终:

Plaintext
go test -mod=readonly -count=1 ./...

完整通过。

到这里,新的 ChatGPT canonical 才真正完成仓库级正式验收。

十五、从约 94 到约 98,增加的不只是模型能力

如果只从表面看,这轮变化可以简单总结成:

Plaintext
GLM-5.2

ChatGPT

Translation Engine 的变化当然是语言质量明显提升的关键原因。

但真正把原来的约 94 分推进到我愿意评价为约 98 分的,并不只是模型本身。

还包括后面的:

Plaintext
103 页全量覆盖

完整语义审核

A/B/C/D 分级

术语统一

revision Batch

最终 A=103

provenance 验证

retry provenance

canonical promotion

EOF canonicalization

再次 dry-run

完整测试

换句话说:

更强的 Translation Engine 提供更好的原始译文,而完整工程流程负责证明这些译文最终值得进入正式项目。

缺少前者,语言质量很难明显提高。

缺少后者,即使模型翻得很好,也很难放心地一次替换 103 个正式页面。

十六、103 页翻完和 103 页正式可发布,是两个里程碑

回头来看,这次最值得保留下来的经验之一,就是不要把:

Plaintext
translation completed

和:

Plaintext
release ready

当成同一个状态。

上一篇文章结束时,我们已经做到:

Plaintext
103/103 ChatGPT 重译完成

但真正到这一篇结束,才完成:

Plaintext
103/103 语义审核完成

曾有 2 页 B,但已经达到发布质量

因修改成本很低,继续优化

最终 A=103、B/C/D=0

最终 revision 确认

canonical promotion

102 candidate changes

103/103 promotion 收敛

完整测试通过

到这里,我才愿意说:

新的 ChatGPT zh-CN 已经不再只是实验 candidate,而是正式 canonical。

结合旧版本和这轮 103 页完整审核结果,我对当前 A Tour of Go 简体中文整体质量的内部工程评价,也从此前大约:

Plaintext
94 / 100

提高到了:

Plaintext
约 98 / 100

但即便做到这里,还有最后一个问题没有回答:

仓库中的正式 canonical 已经替换成 ChatGPT 译文,是否就等于用户打开网站时看到的一定也是这套新译文?

答案仍然是否定的。

正式 release、生产部署、公网页面、CDN 缓存和真实 Run / Format,都属于下一层。

所以第三篇不再继续讨论 Translation Engine,也不会重复之前已经写过的 deployment script 设计过程。

只回答最后一个问题:

怎样证明生产环境中的 103 个正式课程页面,真的已经全部变成这一轮经过完整语义审核和 canonical promotion 的 ChatGPT 新译文。

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

A Tour of Go 多语言翻译项目

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

项目入口:
✅ 在线学习:A Tour of Go 简体中文版
✅ 项目源码:GitHub:shuijingwan/go-tour-i18n

当前第一阶段已完成简体中文版本。项目属于非官方社区多语言翻译项目,与 Go 官方无隶属或授权关系。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理