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

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

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

作者:

在

,

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 韩语版上线复盘

图 2:英文 source 与当时发生 validation failure 的 ko-KR candidate 对比

(59) Validator 报错,到底该改译文还是改规则?一次 A Tour of Go 韩语翻译实战

图 2:法语 A Tour of Go 仓库目前已经归档,页面仍保留 553 Commits 和 61 Contributors 的历史

(60) 从法语 553 次提交到韩语两代离线:A Tour of Go 社区翻译为什么难以长期维护

【图 4:es-ES 首次 Production 收口后连续完成的流程优化 commit】

(61) 从 es-ES 首次上线复盘:我把 A Tour of Go 新增语言的 Production 流程又收敛了一轮

图 1:旧公网验收链路连续出现 curl 28 超时,最终导致 public-machine 阶段失败

(62) A Tour of Go Production 公网验收踩坑:从跨境 SOCKS 链路改为 zgocloud direct

图 1:两次 Production Machine Acceptance 都已经通过,但 Headless Chrome 分别在 / 和 /tour/ 判定页面没有完成渲染

(63) 页面明明能打开,为什么 Headless Chrome 仍然判定 Production 失败?

图 3:Bing 从 Google Search Console 中识别到新的 it-IT 站点,并同时发现已有的 1 个 sitemap

(64) GSC → Bing Webmaster Tools:多语言站点如何减少重复验证

图 3:英文站 3 小时内出现 565 个 Cloudflare 来源 URL

(65) IndexNow 实战:手工提交长期不显示,Cloudflare Crawler Hints 却很快出现在 Bing Webmaster Tools

【图 1:A Tour of Go 项目首页目前的语言版本列表】

(66) 从 nl-NL 到 tr-TR:A Tour of Go 连续上线三门语言后,我又优化了哪些流程

【图 4:Go local 页面中已经出现 French、German、Korean、Simplified Chinese】

(67) 从邮件无人回复到进入 Go 官方 Go local:我的 4 个 A Tour of Go 翻译站终于被收录

图8:最终只保留一把启用的新 API Key

(68) 腾讯云 EdgeOne API 自动化:从 CAM 子用户到最小权限 API Key 的完整配置

【图 7:Production Release Batch 最终汇总】

(69) 从同步 Go 官方上游到一个命令发布 10 个语言站:A Tour of Go Production 流程的再次收敛

【图 3:Cloudflare Smart Tiered Cache 已启用】

(70) Cloudflare 缓存 HIT 很快,MISS 却不稳定:回源阿里云杭州的一次真实优化

【图 3:简体中文站 Support 页面】

(71) 广告之外,社区项目如何长期活下去?我给 A Tour of Go 多语言项目加了 Support

图5:评论成功发布

(72) 从 Go Weekly 到 Reddit、DEV 和 Go Forum:第一次系统推广 A Tour of Go 多语言项目

【图 4:RTL 桌面课程布局修复后的效果】

(73) 第一次把 A Tour of Go 做成 RTL:阿拉伯语版本上线,以及我踩过的几个坑

在前两批普通页面完成之后,我继续使用当前已经稳定下来的默认方案,推进 A Tour of Go 简体中文 zh-CN 翻译。

这一次处理的是第三批 10 个普通页面,范围从 flowcontrol/7 一直到 moretypes/3。最初的批量运行结果并不是全部成功:8 页进入 ready,flowcontrol/10 和 moretypes/1 两页进入 blocked。

如果只是为了尽快增加翻译页数,这两页完全可以暂时跳过,等以后人工处理。但项目现阶段的重点并不只是“翻译多少页”,而是尽可能把自动翻译流程校准到:

GLM-5.2 第一次翻译就尽量生成能够通过完整自动校验的结果。

于是这两个 blocked 页面反而成了很好的真实测试样本。

经过这一轮连续排查和修复,第三批最终实现 10/10 ready,全局状态也更新为:

Plaintext
ready=45
pending=58
blocked=0
图 1:第三批页面最终全部进入 ready,当前全局状态为 ready=45、pending=58、blocked=0。
图 1:第三批页面最终全部进入 ready,当前全局状态为 ready=45、pending=58、blocked=0。

这篇文章记录的重点,并不是这 10 页本身翻译了什么,而是在它们背后连续暴露出的几个更值得解决的问题:

  • Protected Token 不仅有内容,还承担结构角色;
  • 静态预格式化代码块的 token 化曾经隐藏了原始块边界;
  • 教学注释里的 Go 标识符虽然没有丢失,也可能因为词法边界变化而失效;
  • 第一次失败后的自动重试反馈,竟然还存在一次字符串误分类。

这些问题解决之后,项目的默认方案没有变成更复杂的结构化翻译系统,反而逐渐变得更符合最初目标:保持整页翻译,同时只保护真正危险的结构。


一、第三批第一次运行:8 页成功,2 页 blocked

第三批固定处理:

flowcontrol/7、flowcontrol/9、flowcontrol/10、flowcontrol/11~flowcontrol/14、moretypes/1~moretypes/3。

其中大多数页面表现很好。除了 flowcontrol/7 前三次因为沙箱 DNS/socket 网络失败没有真正拿到模型响应以外,其余多数页面的第一份真实 GLM-5.2 输出就通过了校验。

真正值得分析的是两个 blocked 页面。

flowcontrol/10 连续出现:

Plaintext
preformatted code block mismatch at index 1:
static preformatted block changed

moretypes/1 则连续出现:

Plaintext
font span count mismatch:
expected 5, actual 3

一开始看起来,两者似乎是完全不同的问题。

但随着继续往下分析,我发现它们其实都指向了同一个更大的问题:

模型看到的是 Protected Token,但它并不知道每一种 Token 在原页面中究竟承担什么结构角色。


二、flowcontrol/10:Token 内容保住了,但独立代码块消失了

flowcontrol/10 的英文源中有一个静态预格式化代码块:

Go
switch i {
case 0:
case f():
}

这个代码块本身不应该翻译,因此现有机制会把整个块替换成一个 protectedPreformattedStatic token。

问题恰恰出现在这里。

实际发送给模型的受保护页面中,关键部分变成了类似:

Plaintext
⟪..._000001⟫does not call ...

也就是说,模型看到的并不是:

Plaintext
[一个独立代码块]

[后续普通段落]

而更像是:

Plaintext
[一个普通 Token][后续英文]
图 2:flowcontrol/10 修复前的真实 protected input。000001 代表完整静态代码块,但 token 后面直接续接 does not call,从输入形态上看并不像一个独立 block。
图 2:flowcontrol/10 修复前的真实 protected input。000001 代表完整静态代码块,但 token 后面直接续接 does not call,从输入形态上看并不像一个独立 block。

当时的 Prompt 虽然反复告诉模型“Token 不得删除、修改、复制”,但 000001 只属于普通的“单 token 必须保留”规则。

模型并不知道:

000001 不是可以自由塞进一句中文里的占位符,而是一个完整的块级结构。

因此它曾经生成:

Plaintext
当 `i==0` 时,⟪..._000001⟫ 不会调用 `f`。

Token 本身没有丢,恢复 payload 也完全正确,可一旦把真正的 Go 代码塞回这里,原本独立的预格式化块就被降级成了普通句子中间的一块内容。

validator 正确拒绝了这种结果。


三、第一步不是加强校验,而是让 Prompt 解释“结构角色”

这时我没有选择继续给 validator 增加更强的限制,也没有恢复之前已经放弃的“所有 Token 必须保持全局原始顺序”。

因为这里真正缺少的,不是新的校验能力。

validator 已经正确发现问题了。

真正缺少的是:

模型从第一次请求开始,就应该知道各种 Protected Token 分别代表什么。

于是首次翻译 Prompt 被调整为“payload + 结构角色”的思路。

对于行内代码 pair,允许它们随着中文自然语序整体移动,但要求继续保持为行内结构,不能跨越预格式化块或 directive 等块级边界。

对于 protectedPreformattedStatic,明确告诉模型:

它代表完整、独立的预格式化代码块,必须继续保持为独立 block,不得嵌入普通段落、标题、列表或 directive 行,也不得与相邻自然语言合并。

对于 protectedDirective,则明确说明它代表完整的 present directive 行。

这个修改非常重要的一点是:

结构角色必须保持,不等于全局顺序必须保持。

中文仍然可以调整正常语序,行内代码仍然可以随着语义单元移动;只是不能把一个本来是 block 的东西,重新解释成 inline 内容。

修改 Prompt 后,我重新对 flowcontrol/10 发起了一次完全干净的首次请求,不带任何历史 retry feedback。

结果第一份真实 GLM-5.2 响应就通过了全部校验。

这证明 Prompt 的角色说明确实有效。

不过很快,moretypes/1 又让我发现:只有 Prompt 还不够。


四、更底层的问题:protected input 自己把 block boundary 吞掉了

重新检查 flowcontrol/10 和 moretypes/1 的精确输入边界后,我发现一个此前忽略的问题。

以 flowcontrol/10 为例,英文源本来是:

Plaintext
(For example,

    switch i {
    case 0:
    case f():
    }

does not call ...

preformattedBlocks 的解析逻辑会把代码块末尾的空行视为完整 Present preformatted block 的一部分。

这在 Present 语义上没有问题。

但翻译保护阶段直接把整个 block替换成 token 后,尾部用于分隔下一段 prose 的换行也一起被 token payload 吞掉了。

于是:

Plaintext
\tcode\n\n

整体变成了:

Plaintext
⟪TOKEN⟫

原来 token 后面的两个换行也随之消失。

这就是为什么模型最终看到:

Plaintext
⟪TOKEN⟫does not call

而不是:

Plaintext
⟪TOKEN⟫

does not call
图 3:static preformatted token 的输入表示修复前后。修复前 token 与后续 prose 直接相连;修复后保留了源页面本来就存在的两个换行。
图 3:static preformatted token 的输入表示修复前后。修复前 token 与后续 prose 直接相连;修复后保留了源页面本来就存在的两个换行。

这个问题让我重新确认了一个很重要的原则:

Prompt 和实际输入必须向模型传达同一个事实。

如果 Prompt 一边说“这是独立 block”,输入另一边却长成 TOKENprose,模型仍然需要自己猜测并重建被隐藏的结构。

因此这里没有继续强化 Prompt,而是做了一个更底层、但范围很小的修复:

仅调整 protectedPreformattedStatic 的翻译输入保护右边界,把 source 中原本存在的 block separator 留在 token 外。

也就是说:

Plaintext
source:
\tcode\n\nThe prose

保护后变成:

Plaintext
⟪TOKEN⟫\n\nThe prose

restore 之后仍然能够逐字节恢复原始 source。

这个修改没有改变 preformattedBlocks 的 Present 语义,没有改变 validator,也没有新增 Token metadata,更没有引入 slot 或全局顺序限制。

它只是让模型能够真正“看到”这个 block 本来就存在的边界。


五、moretypes/1:修完 block,下一层问题又出现了

static block 输入边界修复之后,我再次对 moretypes/1 进行干净首次回归。

这一次,之前最明显的 font span 问题不再首先出现。

模型已经能够正确生成:

Plaintext
[static block token]

[普通 prose + inline pair]

也就是说,static token 与后面的 &、* 行内代码不再紧贴。

但是 validator 随即暴露了下一层问题:

Plaintext
preformatted code block mismatch at index 3:
line comment mismatch at index 1:
referenced Go identifier count mismatch:
expected 1, actual 0

问题出现在教学注释。

原文中有:

Go
fmt.Println(*p) // read i through the pointer p
*p = 21         // set i through the pointer p

这里最后的 p 是同一代码块里真正出现过的 Go 标识符,因此项目已经会将它保护为 protectedPreformattedIdentifier。

也就是说,保护机制其实已经存在。

但 Prompt 并没有告诉模型:

这个 Token 恢复后必须仍然是一个词法上独立的 Go 标识符。

于是模型生成了:

Plaintext
通过指针读取 i⟪...000015⟫

Token 没有丢,数量也没有错。

可恢复 000015 → p 后,结果变成:

Plaintext
通过指针读取 ip
图 4:moretypes/1 中 Protected Token 本身完整保留,但 p 与前面的自然语言字符 i 拼接成 ip,不再是独立的 Go 标识符,因此 validator 正确拒绝。
图 4:moretypes/1 中 Protected Token 本身完整保留,但 p 与前面的自然语言字符 i 拼接成 ip,不再是独立的 Go 标识符,因此 validator 正确拒绝。

这个案例特别有代表性。

它说明:

Protected Token 校验通过,不代表结构校验一定能通过。

Token 机制证明的只是:

  • Token 没有消失;
  • 没有重复;
  • 没有伪造;
  • payload 可以正确恢复。

但恢复以后,它到底处于怎样的词法边界、容器和结构关系中,仍然需要更高一层的规则判断。


六、继续补充的不是新保护器,而是已有 kind 的真实含义

p 已经被正确 token 化,因此这里没有必要再改保护器。

我只给首次 Prompt 增加了 protectedPreformattedIdentifier 的角色说明。

现在模型会明确知道:

这些 Token 代表教学注释中引用的 Go 源码标识符;必须在所属注释中原样保留,并且恢复以后仍要能作为词法上独立的 Go 标识符识别,不能与相邻中文、英文字母、数字或下划线拼接。

与此同时,整条教学注释仍然允许正常翻译和调整中文语序。

再次进行干净首次回归后,模型输出中出现了:

Plaintext
通过指针读取 i ⟪...000015⟫
通过指针设置 i ⟪...000017⟫

这一次 token 前有了明确的边界,恢复后 p 仍然是独立 identifier。

完整自动校验全部通过,moretypes/1 恢复为 ready。

图 5:补充教学注释 Go 标识符的结构角色后,模型为 p 保留了词法边界。最终 candidate 又进一步将中文语序润色为“通过指针 p 读取 i / 设置 i”,并通过全部校验。
图 5:补充教学注释 Go 标识符的结构角色后,模型为 p 保留了词法边界。最终 candidate 又进一步将中文语序润色为“通过指针 p 读取 i / 设置 i”,并通过全部校验。

这里还有一个我觉得很值得保留的细节。

模型自动输出通过校验后,教学注释是:

Go
fmt.Println(*p) // 通过指针读取 i p
*p = 21         // 通过指针设置 i p

结构上完全正确,但中文稍显生硬。

最终我只对 candidate 做了非常小的人工润色:

Go
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21         // 通过指针 p 设置 i

然后重新经过同一套统一校验。

这也符合项目一直坚持的原则:

人工可以改善译文,但不能绕过自动校验。


七、还有一个意外问题:自动重试反馈竟然分错类了

处理这两页的过程中,还发现了另一个比较隐蔽的问题。

项目本来已经实现:

Plaintext
第一次翻译
→ validation 失败
→ 根据失败原因生成 retry feedback
→ 注入下一次完整页面翻译

所以最初 flowcontrol/10 和 moretypes/1 连续失败时,我一度怀疑是不是 retry feedback 根本没有执行。

检查真实 request 后发现:

feedback 确实注入了,但分类错了。

例如真正的错误明明是:

Plaintext
preformatted code block mismatch at index 1:
static preformatted block changed

但下一次模型收到的却是:

Plaintext
上一次输出出现了未受保护的额外 present directive,
或改变了 directive 结构。

原因非常具体。

validator 为结构错误统一追加了一段诊断:

Plaintext
check the named directive or protected content near the first difference

而当前 TranslationValidation 只保存:

Plaintext
Failures []string

没有结构化的 failure kind/code。

retryFeedbackForMode 只能对完整错误文本做字符串匹配。

旧分类逻辑又先检查 directive,于是:

Plaintext
真正的 preformatted failure
+
统一 suffix 中的 directive
=
误命中 directive feedback

moretypes/1 的 font span failure 也受到了完全相同的影响。

图 6:真实 failure 是 preformatted,但 diagnostic suffix 中出现了 directive,旧字符串匹配因此给出了完全错误的重试反馈。
图 6:真实 failure 是 preformatted,但 diagnostic suffix 中出现了 directive,旧字符串匹配因此给出了完全错误的重试反馈。

当前没有为了这个问题引入一套新的结构化错误体系。

这一次仍然采取最小修复:

先剥离固定 diagnostic suffix,再只对真正的 failure core 分类,同时增加 preformatted 专用分支,并确保 font span / emphasis 在 generic directive 之前识别。

修复以后:

Plaintext
preformatted
→ preformatted feedback

font span
→ font / emphasis feedback

真正的 directive
→ directive feedback

这一步虽然不是“首次成功率”的优化,但非常重要。

因为第一份输出再怎么优化,都不可能保证以后所有页面永远一次成功。

只要还存在有限重试,那么:

失败以后告诉模型的原因必须是真的。

否则所谓“智能重试”,实际上只是拿着错误方向再翻译一次。


八、第三批最终 10/10 ready

经过这一轮校准,两个最初进入 blocked 的页面最终都恢复为 ready。

第三批最终完成 10/10 ready,全局状态为:

Plaintext
ready=45
pending=58
blocked=0

status check 同时确认:

Plaintext
status OK: 103 pages for zh-CN

所有 candidate 都重新通过统一 candidate validate,全量测试:

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

通过,git diff --check 也没有问题。

最终改动以提交:

Plaintext
be9413846f54e6679c79c0fc19957ac4e2a3f815
feat(i18n): 完成第三批并校准受保护翻译结构

推送到 origin/main。


九、最终页面不只是校验通过,也能正常渲染

完成所有内部结构校准后,我又重新打开 moretypes/1 的浏览器预览页面。

最终效果如下。

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

这个页面非常适合作为本轮校准的最终结果展示。

因为它恰好包含了这次出现问题的多种结构:

  • 普通中文正文;
  • *T、T、nil、&、* 等行内代码;
  • 多个静态预格式化代码块;
  • 可翻译教学注释;
  • 教学注释中的 Go 标识符;
  • 右侧 .play 引用的官方代码示例。

最终页面看起来很普通。

但也正是这种“普通”,才是自动翻译基础设施应该追求的结果:读者不需要知道背后经历过多少 Token、restore、Present 解析和 validator,只需要看到一页正常、自然、结构完整的中文课程。


十、这一轮最重要的结论

这次第三批最值得记录的,并不是又完成了 10 页。

更重要的是,我对“Protected Token 应该保护什么”有了更清晰的认识。

早期更容易把它理解成:

Token 的主要任务,是让模型不要改动危险内容。

现在这个定义已经不够了。

更准确的说法应该是:

Protected Token 既有 payload,也有结构角色。

对于行内 pair,角色是“仍然属于行内结构”;对于 static preformatted token,角色是“仍然是一个独立 block”;对于 directive,角色是“仍然是一条独立 directive”;对于教学注释里的 Go identifier,角色则是“恢复以后仍然必须是词法上独立的标识符”。

另一方面,结构角色也不能被无限扩大。

我仍然不打算恢复:

Plaintext
所有 Token 必须保持英文原始全局顺序

因为这会重新妨碍正常中文语序调整。

当前更合适的边界是:

允许语言层面的重新组织,但不允许结构类型发生退化。

除此之外,这一轮还有另一个很重要的经验:

不能只要求模型“理解结构”,输入本身也必须把结构展示正确。

TOKENprose 与 TOKEN\n\nprose,在人看来只差两个换行,但对一个本来应该表示独立代码块的 Token 来说,这两个换行就是非常真实的结构信息。

最后,retry feedback 的误分类也再次提醒我:

自动翻译流程里真正需要校准的,不只有模型 Prompt。

从:

Plaintext
输入
→ Prompt
→ 模型输出
→ Restore
→ Present 解析
→ 结构校验
→ Retry feedback

任何一个环节传达了错误的信息,最终都可能表现成“模型翻译失败”。

这也是为什么我现在越来越倾向于,在继续扩大页面数量之前,优先把真实批量运行中暴露出的通用问题解决掉。


十一、下一步

第三批完成后,项目已经有 45 页进入 ready,剩余 58 页仍为 pending,当前没有 blocked 页面。

下一步原本可以立即进入第四批普通页面。

不过在连续完成三批真实运行,并且这一批又完成了 Prompt、protected input、教学注释 identifier 以及 retry feedback 的连续校准之后,我打算先暂时停一下,把这次过程完整记录下来。

接下来再继续第四批时,我更关心的已经不是:

“10 页最后能不能都翻译成功?”

而是:

经过这一轮调整以后,新的普通页面首次通过率究竟能提高到什么程度?

如果后续大批量页面能够稳定维持较高的一次通过率,那么这个项目才算真正从“逐页开发校准”,开始进入可以持续推进的自动翻译阶段。

A Tour of Go 多语言翻译项目:从 swap 问题重新审视 Token 保护,原始输入与最小保护的一次真实实验 A Tour of Go 多语言翻译项目:zh-CN 103 个课程页面全部 ready,课程正文翻译阶段完成

A Tour of Go 多语言翻译项目

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

项目入口:
✅ Brazilian Portuguese — Português (Brasil)
✅ Dutch — Nederlands
✅ French — Français
✅ German — Deutsch
✅ Italian — Italiano
✅ Japanese — 日本語
✅ Korean — 한국어
✅ Simplified Chinese — 简体中文
✅ Spanish — Español
✅ Turkish — Türkçe
✅ 项目源码:GitHub:shuijingwan/go-tour-i18n

项目正在持续扩展更多语言版本,并长期维护翻译质量、生产发布与后续更新。项目属于非官方社区多语言翻译项目,与 Go 官方无隶属或授权关系。