最近整理 Go Learning & Documentation v2 的 /translation/ 页面时,我遇到了一个开始看起来很奇怪的问题。
这个页面不是上游 go.dev 的官方文档。
它本来就是项目自己的页面。
内容也是我自己写的。
现在我想把它重新整理一下:
- 调整段落顺序;
- 重组现有说明;
- 合并一些旧内容;
- 增加新的项目介绍;
- 让整个页面更适合现在已经从单纯 Go Tour 扩展到 Learn、Docs 和更多内容包之后的项目定位。
从普通网站维护的角度看,这件事非常简单。
打开:
data/site-shell/translation.html直接改正文就行了。
但真正进入正式工作流以后,我发现:
这个文件虽然是我自己的,却已经不能再被当成一份普通 HTML 随便修改。
因为它已经成为:
被正式语言流程消费过的 canonical source。
这两个状态差别非常大。
最后这个问题甚至不能靠:
“既然是自己的网站,直接改一下。”
来解决。
反而需要专门建立:
consumed-source replacement successor authority。
这篇就整理为什么会这样,以及为什么“追加新内容”和“重写已经被消费的正文”其实是两种完全不同的生命周期操作。
一份文件为什么会从“普通文件”变成正式 source authority
最开始,translation.html 当然只是一份源文件。
这时候修改它没有什么特殊含义。
例如:
旧正文
→ 编辑
→ 新正文只要页面显示正确就行。
但一旦它进入正式翻译生命周期,情况就变了。
它会逐步参与:
- source extraction;
- semantic slots;
- unified corpus;
- glossary review;
- Generation;
- structured output;
- validation;
- package closure;
- Integrated Surface Review;
- Activation。
到了这个阶段以后,一系列正式证据都会绑定:
当时的 exact source bytes。
也就是说,一个已经接受的 target 并不是悬空存在的。
它实际证明的是:
对于某一份确定的 source,在某一个确定的语言 authority 下,这一份 target 曾经被正式生成、验证和接受。
所以如果后来直接把 source 原地修改:
source A
→ 原路径直接覆盖成 source B历史证据就会马上产生一个问题:
过去那些 PASS 到底是在审核 A,还是 B?
答案只能是 A。
但磁盘上原来的 A 已经不存在了。
immutable 不是说“永远不能更新内容”
这里很容易产生一个误解。
既然 source immutable,是不是代表:
页面发布以后就永远不能修改?
当然不是。
这里的 immutable 真正表达的是:
已经被正式证据消费过的历史 source,不能偷偷改写成另一个内容,再假装那些旧证据仍然对应新内容。
这是“历史不可改写”。
不是“产品不能继续演进”。
正确的演进方式应该是:
旧 source authority
→ 保留
新的 source successor
→ 建立新的正式 authority然后新 authority 再走它需要的后续生命周期。
这样才能同时得到两件事:
历史证据仍然可信。
以及:
当前产品可以继续变化。
/translation/ 的问题恰恰不是简单“新增两段文字”
这次真正让我停下来的地方,就是我想做的修改已经超出了 append。
假设旧页面是:
第一部分:项目介绍
第二部分:Tour 说明
第三部分:参与方式现在如果只是希望在最后增加:
第四部分:Learn / Docs这属于一种比较容易描述的变化:
旧内容全部保留,只在后面增加新内容。
但是这次真正想做的更接近:
重新写项目介绍
→ 合并旧 Tour 说明
→ 把 Learn / Docs 提到前面
→ 调整参与方式
→ 删除已经不合适的旧描述
→ 增加新的项目范围说明这不是:
append。
而是:
replacement / reorganization。
也就是旧正文内部本身需要被重新组织。
为什么项目已经有 structured-source-adopt,却还是不能直接解决
项目此前已经建立了一条 structured source successor 路径:
site-content structured-source-adopt它确实允许:
在已有 structured source 基础上增加新的 canonical prose。
但这个能力是故意受限的。
当前合同要求:
旧的 semantic slots 全部保持不变。
而新增正文只能放在:
所有既有 semantic slots 之后。
换句话说,它可以合法表达:
旧正文 A
旧正文 B
旧正文 C
新增正文 D却不能表达:
旧正文 A
→ 改写
旧正文 B
→ 删除
旧正文 C
→ 移到前面
新增正文 D
→ 插入中间这不是工具能力不够。
而是它的 authority 本来就不是为了做这件事。
append successor 的价值就在于“旧内容可以继续精确继承”
为什么要把 append 限制得这么严格?
因为它有一个很大的优势:
旧内容的语言结果可以精确 carry。
假设旧 source 有 10 个 semantic slots。
它们已经完成:
slot 1 → accepted target 1
slot 2 → accepted target 2
...
slot 10 → accepted target 10现在只追加两个新 slot:
slot 11
slot 12只要能够证明:
- 旧 source bytes 不变;
- 旧 coordinates 不变;
- 旧 semantic roles 不变;
- 旧 protected representation 不变;
- 旧 target 仍然合法;
那么就可以安全地说:
1~10 是 exact carry。
真正需要进入 Generation 的只有:
11~12。
这就是 append successor 的最大价值。
一旦重组正文,旧 coordinate 和语义边界可能全部变化
但 replacement 就不一样了。
例如旧正文里有:
段落 A
段落 B
段落 C新正文变成:
段落 C 的改写
新段落 D
段落 A 的部分内容这时候不能简单说:
A 好像还在,所以 carry。
因为正式流程真正需要证明的是:
它是不是同一个 semantic input。
不仅仅是:
中文意思看起来差不多。
或者:
某些字符串重复出现。
尤其 structured source 还有:
- coordinate;
- slot identity;
- role/context;
- protected structure;
- source hash;
这些正式边界。
如果正文发生重组,再靠字符串相似度猜:
这个新 slot 大概就是以前那个。
风险会很大。
“意思一样”不能作为机器 carry authority
比如旧 source 有一句:
Learn about Go.
新 source 重组以后仍然出现:
Learn about Go.
肉眼看起来完全相同。
但它可能已经:
- 出现在另一 section;
- 承担不同标题下的解释;
- 对应不同链接;
- 周围上下文发生变化。
如果 workflow 只因为 source string 一样就自动继承旧 target,那么可能把:
旧上下文中的合法译文
错误地搬到:
新上下文。
所以正式 carry 不能依赖:
文本看起来一样。
而需要依赖:
可证明的 source identity 和语义位置关系。
这和之前 books.yaml 10→12 的问题刚好形成对照
前面写过一次 books.yaml 的 semantic input expansion。
那个案例里:
原始文件 bytes 没有变化。
但 parser 从以前识别 10 个正式语言输入,变成现在识别 12 个。
所以旧 Generation 不能继续冒充 current complete result。
这次 /translation/ 刚好是另一个方向。
不是:
bytes 不变,但正式 semantic inputs 变多。
而是:
我主动想改变 canonical source body 本身。
这两种情况共同说明:
判断旧翻译还能不能继续使用,不能只看某个文件路径是不是还叫同一个名字。
真正需要比较的是:
语言模型当初被要求翻译什么。
和:
现在正式要求它处理什么。
是不是仍然同一个任务。
为什么不能在 renderer 里偷偷加一段中文
发现 source authority 不允许直接改以后,还有一个很诱人的捷径:
那我不改 language source。
直接让 renderer 在
/translation/页面额外输出一段中文不就行了吗?
从页面效果看,确实能做到。
例如 renderer 直接:
原正式 target
+
一段新中文项目说明浏览器看起来完全正常。
但这样做会产生一个更严重的问题:
这段中文从来没有进入正式 language authority。
它没有经过:
- corpus;
- glossary;
- Generation;
- validation;
- Independent QC;
- package closure;
- Surface language review。
也就是说:
renderer 开始偷偷承担语言生成职责。
这会绕开整个正式工作流。
renderer 应该渲染正式内容,不应该创造正式语言内容
这个边界后来让我觉得非常重要。
Renderer 可以负责:
- layout;
- HTML composition;
- route presentation;
- CSS;
- shared chrome;
- canonical;
- sitemap;
- runtime behavior。
但它不应该负责:
凭工程代码新增一段从未经过正式语言流程的中文正文。
否则以后看到页面上的一句中文,根本无法回答:
它来自哪个 canonical source?
哪次 Generation?
哪个 Reviewer?
哪个 target SHA?
哪个 closure?
甚至:
其他 locale 应该怎么办?
所以:
renderer injection 不能成为 source replacement 的旁路。
也不能因为“这是中文原生页面”就跳过语言 authority
这里还有一个很自然的想法:
/translation/本来就是项目自己的页面。
我直接写中文,为什么还需要 Generation?
问题在于这个项目不是:
只有 zh-CN 的网站。
它是:
多 locale 的正式内容系统。
如果 canonical source 模型允许某个 locale 直接在 renderer 里获得特殊正文,那么其他语言以后就会面临:
zh-CN:工程代码直接写中文
fr-FR:走 Generation
de-DE:走 Generation
ja-JP:走另一套?这样语言 authority 很快就会分叉。
所以即使这个页面是项目自己拥有的,也应该继续保持:
canonical source → locale Generation / carry → validation → review。
而不是按 locale 手工硬编码目标语言正文。
source ownership 和 source lifecycle 是两回事
这也是这次最容易混淆的一点。
“这是我自己的页面”解决的是:
谁有权决定 source 应该写什么。
但正式 lifecycle 解决的是:
source 改变以后,已有语言结果还能不能继续被信任。
两者完全不同。
我当然可以决定:
/translation/新版本应该写成什么样。
但是一旦决定了新的 canonical source,接下来仍然需要回答:
新 source 和旧 source 是什么关系?
哪些旧语言结果能够精确 carry?
哪些必须重新 Generation?
glossary corpus 是否发生变化?
旧 Review 是否还覆盖 current corpus?
新 target 是否需要新的独立 QC?
这些问题不会因为 source owner 是自己就消失。
为什么“直接修改原文件”会破坏可恢复性
除了审计,原地修改还有一个很实际的问题:
recovery。
假设直接把旧正文覆盖掉。
然后新流程执行到一半失败。
例如:
新 corpus 建立
→ glossary review 还没完成
→ Generation 尚未执行这时候当前仓库里:
旧 source 已经没了。
但新 source 又:
还没有完成语言生命周期。
那么系统很难准确回答:
当前 Production 应该继续信任哪个 source?
如果旧 source authority 本身保持 immutable,而新 replacement successor 只是:
一个尚未完成的 successor,
恢复就简单很多:
current accepted authority
→ 继续保持旧版本
new successor
→ pending直到新链条真正完成以后,再切换 current authority。
这其实和数据库 migration 很像
后来我觉得,可以把它理解成一种 content migration。
不应该做:
UPDATE current_content
SET body = new_body然后希望以前所有 evidence 自动继续有效。
更合理的是:
旧版本
→ immutable
新版本
→ successor
完成 validation / review / closure
→ 再正式切换 current这样每一个历史状态都有可追溯性。
如果过程中失败,也知道:
哪一个状态仍然是最后可信版本。
source adoption 本身也不是 language acceptance
即使新的 source successor 已经成功建立,也不能马上说:
页面更新完成了。
Source adoption 真正证明的是:
新的 canonical source authority 合法存在。
它并不证明:
target 已经生成。
更不证明:
target 质量通过。
所以 formal source successor 后面仍然可能需要:
source successor
→ corpus successor
→ glossary refresh / review
→ Generation
→ validation
→ Independent QC
→ A-only finalization
→ package closure
→ Integrated Surface
→ Activation具体哪些阶段需要重新执行,要由 source delta 和正式 currentness 来决定。
但 source adoption 本身绝不能跳过这些阶段。
为什么 corpus 也必须跟着 source successor 前进
Unified corpus 的作用之一,就是告诉 glossary workflow:
当前正式内容里真正存在什么语言上下文。
如果 /translation/ source 已经大规模重写,但 corpus 仍然保留:
旧版本 contributor contexts,
那么 glossary review 实际审核的仍然是:
过去的语言世界。
这时候即使 glossary file 本身一个字没变,也不能自动说:
current Review 仍然覆盖新 source。
因此 replacement successor 之后通常还需要:
corpus successor。
然后重新判断:
新 corpus 是否带来了术语影响。
glossary 没改,也不等于 Review 一定可以直接复用
这也是容易误解的一点。
假设新 /translation/ 只重新组织项目介绍,没有出现任何新术语。
最终 glossary.yaml 也许真的一个字都不需要改。
但仍然需要证明:
当前 Unified Review 覆盖的是新的 corpus identity。
因为 Independent Glossary Review 审核的并不只是 YAML bytes。
它还意味着:
当前 glossary 是否足以覆盖当前正式 corpus。
所以:
glossary bytes unchanged
和:
glossary review current
不是同一个概念。
replacement successor 为什么比 append successor 成本高
现在就很容易理解这两者为什么成本差这么多。
Append successor
旧正文保持 exact。
所以可以:
大量 carry。
主要处理:
新增 slots。
Replacement successor
旧正文允许:
- 改写;
- 删除;
- 重排;
- 合并;
- 拆分。
于是系统必须重新判断:
哪些旧 semantic results 仍然有合法继承关系。
无法证明的部分就不能 carry。
因此 replacement successor 天然比 append:
更昂贵。
但这是正确性成本。
不是工具设计失误。
如果实际需求只是追加,就不要升级成 replacement
反过来,这也意味着:
不应该所有 source 更新都走最重的 replacement lifecycle。
如果真实需求只是:
在正文最后增加一个新的独立 section。
并且所有旧 semantic slots 可以逐字节证明没有变化。
那么 append successor 显然更合理。
因为它能最大程度保留:
已经正式审核过的旧语言成果。
所以正确原则不是:
replacement 更高级,所以统一用 replacement。
而是:
mutation 类型必须与真实 source 变化类型匹配。
append 和 replace 应该在 source authority 层就区分
不能等到 Generation 阶段才发现:
原来旧内容被重排了。
因为那个时候 formal input identity 已经改变。
更合理的是在 source 层先分类。
如果变化满足:
旧正文完全保持,只新增后缀 semantic content。
那么进入:
append successor。
如果变化需要:
删除、改写、重排或重新组织已经消费的正文。
那么进入:
replacement successor。
这是一种 source authority decision。
不是 Generation 自己决定。
为什么不能让 Generation 自己“顺便重新组织”
还有一种看起来很省事的想法:
把旧 target 给 Generation,让它根据新需求直接重写中文版。
这样实际上会把两个不同任务混在一起。
第一件事是:
canonical source 应该变成什么。
第二件事是:
当前 locale 应该如何翻译这份 canonical source。
Generation 只应该负责第二件。
否则它会开始决定:
- 哪段 source 应删除;
- 哪段内容应新增;
- 项目结构应该怎么重新写;
这已经不是 translation Generation。
而是:
source authoring。
角色就混乱了。
canonical source 必须先独立成立
所以正确顺序应该是:
先决定新的 canonical source。
让新的 source 本身在目标语言之外也成立。
然后再问:
对 zh-CN 应该生成什么 target?
对未来其他 locale 应该生成什么 target?
这保证所有语言仍然共享:
同一个 source authority。
而不是每个 locale 各自发展一份越来越不同的项目说明。
历史 evidence 为什么必须继续指向旧 source
假设旧 /translation/ 已经经历过:
- Generation;
- validation;
- package closure;
- Surface;
- activation。
这些 evidence 是真实发生过的。
新的 replacement successor 建立以后,不应该把这些历史记录修改为:
它们其实审核的是新 source。
因为那是假的。
正确状态应该是:
旧 source A
→ 旧 Generation
→ 旧 accepted target
→ 旧 closure
→ 旧 Surface
新 source B
→ 新 successor lifecycle如果 B 最终成功:
current authority 切到 B。
但 A 的历史链仍然保持完整。
这也解释了为什么 replacement 不能覆盖旧 receipt
正式 receipt 应该是:
content-addressed / immutable。
如果新的 proposal 失败,不能回头把旧 receipt 改成:
新的内容。
否则同一个 receipt identity 在不同时间会代表不同事实。
那历史引用就失去意义。
所以:
新 source、新 proposal、新 receipt。
旧 artifact 保留。
这是可恢复性的基础。
source draft 尚未被消费时,情况可以不同
这里还有一个很重要的边界。
假设刚建立了一个新的 structured source proposal。
但它:
- 还没有进入 corpus;
- 没有被任何 immutable corpus successor 引用;
- 没有进入 Generation;
- 没有语言 accepted result。
这时候发现:
新正文里有个笔误。
那么它仍然属于:
尚未被消费的 source draft correction。
这和已经被正式语言流程消费以后的 replacement 不一样。
前者可以在严格 CAS 下建立新的 proposal/body/receipt 并替换 current source pointer。
后者则不能再使用这个轻量纠正路径。
因为一旦已经被消费:
历史依赖存在了。
“有没有被消费”比“有没有上线”更重要
这个边界也很有意思。
很多时候我们会直觉认为:
只要还没 Production,就随便改。
其实不一定。
即使页面还没正式上线,只要新的 source 已经被:
- corpus successor;
- Glossary Review;
- Generation;
- QC;
正式消费,那么它已经形成历史依赖。
这时候再原地修改 source,同样会破坏 lineage。
所以真正关键的问题不是:
上线了吗?
而是:
有没有产生正式不可变 evidence 来引用这份 source?
fail-closed 在这里意味着“宁可暂时不能改,也不能猜”
这次 /translation/ 改版最终停下,其实很符合整个项目一直坚持的 fail-closed 原则。
当时如果没有正式 replacement authority,可以有两个选择。
第一个选择:
直接改文件,继续推进。
第二个选择:
停下来,先建立能够合法表达 replacement 的 authority。
第二个明显更慢。
但它避免了很多未来更难解释的问题。
因为一旦直接改:
到底哪些旧 evidence 还可信?
很快就会变得模糊。
所以:
无法证明旧语言结果如何映射到新 source 时,不猜 carry。
同样:
当前 authority 无法合法表达正文替换时,不通过旁路修改。
这就是 fail-closed 在内容生命周期里的实际含义。
这次我越来越觉得“内容”本身也需要 migration contract
以前谈 migration,容易想到:
- database;
- schema;
- API;
- filesystem。
但做到现在以后,我发现正式多语言内容同样需要 migration contract。
尤其当它已经有:
- identity;
- review evidence;
- dependency;
- currentness;
- historical lineage;
以后,它本质上已经是一种:
版本化数据。
既然是版本化数据,就不适合:
直接覆盖。
而应该:
predecessor → successor。
内容工作流真正难的不是“生成新文字”
模型重新生成一版中文其实很容易。
真正困难的是回答:
为什么这次需要重新生成?
哪些旧结果可以保留?
哪些不能保留?
新结果继承哪一个 predecessor?
新 candidate 对应哪一个 source?
old A 是否仍然合法?
新 target 何时才可以取代 current target?
这些问题都不是:
“模型翻译得好不好”
能够解决的。
它们属于:
内容状态管理。
/translation/ 这次让我区分了三种完全不同的变化
现在我更愿意把 structured source 变化分成三类。
第一类:semantic inputs 扩展
例如此前的 books.yaml 10→12。
特点是:
raw source 可能没变,但 current parser 识别出新的正式语言输入。
需要处理:
semantic input expansion。
第二类:append-only source successor
特点是:
旧正文和旧 semantic slots 全部保持 exact,只在允许位置新增新的 canonical content。
优势是:
旧 accepted slots 可以精确 carry。
第三类:consumed-source replacement successor
特点是:
已被正式流程消费的正文需要改写、删除、重排或者重新组织。
这种情况下不能假装旧 slot identity 仍然完整成立。
必须进入:
正式 replacement lifecycle。
这三个问题看起来都叫:
“内容变了。”
但 machine authority 完全不同。
不能用一个通用“内容更新”按钮处理所有情况
如果把上面三类变化全部塞进一个命令:
update-content然后内部自动猜:
- carry;
- replace;
- append;
- retranslate;
就会非常危险。
因为机器需要基于:
精确证明
而不是:
heuristic。
所以宁可不同 mutation 有不同 authority。
这样失败时也知道:
到底哪一层没有证明成立。
replacement successor 最终仍然应该回到共享 TranslationUnit 生命周期
另一个很重要的设计点是:
replacement 不应该建立第二套翻译状态机。
Source replacement 解决的是:
新的 canonical source 如何合法取代旧 source。
一旦新 source authority 成立,真正的翻译部分仍然应该尽量回到已有共享生命周期:
Generation
→ import / restore
→ validation
→ Candidate Snapshot
→ Independent QC
→ revision / re-QC
→ A-only finalization
→ package closure这样 replacement 只是:
新 predecessor/current source 的 authority bridge。
而不是再发明:
ReplacementTranslationStatus、ReplacementQC、ReplacementFinalization。
否则状态机会越来越多。
Integrated Surface 仍然不能被跳过
即使 replacement target 最终重新获得 A,也不代表:
页面已经可以直接上线。
因为 /translation/ 本身还是 Site shell 的一部分。
正文重新组织以后,可能影响:
- 页面链接;
- navigation;
- local coverage 说明;
- 项目入口;
- 与 Homepage 的关系;
- 其他 package 的展示。
所以最终仍然需要:
Integrated Surface Review。
它负责重新检查:
新页面放回完整站点以后是否仍然成立。
Source replacement PASS 也不等于 Production ready
因此整个证明链要继续严格分层。
Source replacement authority PASS 只能证明:
新 canonical source 合法成立。
Corpus successor PASS 只能证明:
当前 corpus 已经正式更新。
Glossary Review PASS 只能证明:
当前 glossary 对当前 corpus 获得独立语言审核。
TranslationUnit A 只能证明:
当前 target 的语言质量通过。
Package Closure 只能证明:
当前内容包完整。
Surface PASS 只能证明:
站点整合通过。
Activation、Preview 和 Production 仍然有各自的证明范围。
这和前面 wrong renderer 那篇其实又连起来了:
一个 PASS 永远不能替代它没有检查过的下一层。
为什么这一整套流程看起来比“直接改 HTML”麻烦很多
因为“直接改 HTML”解决的只有:
页面现在看起来对不对。
正式 lifecycle 还需要解决:
历史能不能解释。
失败以后能不能恢复。
其他 locale 能不能复用同一 source。
旧 accepted result 能不能安全继承。
新 source 的语言影响是否被完整审核。
current authority 能不能确定。
这些都不是普通静态站点编辑需要回答的问题。
一旦项目进入:
多 locale + 独立 Reviewer + immutable evidence + incremental update,
复杂度就自然会上升。
但这份复杂度换来的是“旧成果不用全部推倒”
它的另一面其实是:
正因为 predecessor / successor 关系被严格保存,才有可能安全增量更新。
如果没有这些 identity 和 authority,最简单的处理方式当然是:
页面有变化
→ 整个 locale 全部重新翻译
→ 全部重新 Reviewer
→ 全部重新 Surface那实现非常简单。
但成本也非常高。
现在真正想实现的是:
只有 source replacement 真正影响的部分重新进入语言生命周期。
未受影响的 package、Page 和历史结果继续 carry。
所以严格 source authority 的目标,不只是:
增加限制。
它同时也是:
允许安全复用。
append successor 和 replacement successor 其实是在回答同一个问题
它们共同回答的是:
旧结果到底还能复用多少?
append successor 的回答通常是:
几乎全部旧 slots 都能 exact carry,只生成新增加的部分。
replacement successor 的回答则更谨慎:
只能 carry 能够正式证明仍然具有相同 source/semantic identity 的部分;其余进入新的语言生命周期。
区别不在:
哪个工具更强。
而在:
source mutation 的性质不同。
这次最值得保留的一条判断标准
以后再碰到类似内容改版,我觉得首先不应该问:
能不能直接编辑这个文件?
而应该先问:
这份 source 有没有已经被正式 evidence 消费?
如果没有:
还属于普通 source authoring 或受限 draft correction。
如果已经消费:
再问:
这次变化是 append,还是 replacement?
如果旧正文能够全部保持 exact,只增加新内容:
append successor。
如果需要改写、删除、重排或者重新组织旧正文:
replacement successor。
这个判断一旦做对,后面的 lifecycle 才不会走错。
我现在更认可的几条原则
经过 /translation/ 这次改版,我觉得可以把 source evolution 的规则浓缩成几条。
第一:
source ownership 不等于可以绕过 source lifecycle;自己拥有的页面,一旦被正式语言证据消费,同样必须保持历史可追溯。
第二:
immutable 的含义不是“永远不能改”,而是“旧事实不能被新事实原地覆盖”。
第三:
只在旧 semantic slots 之后增加新内容,属于 append successor;改写、删除、重排已经消费的正文,则属于 replacement。
第四:
append 能安全 carry 旧结果的前提,是旧 source、slot identity、coordinate、role 和 protection 都可以精确证明保持不变。
第五:
不能因为字符串相同或语义相近,就让机器猜旧 target 应该 carry 到哪个新 slot。
第六:
renderer 只能渲染正式语言内容,不能偷偷注入未经 corpus、Generation 和 Review 的目标语言正文。
第七:
source adoption 只建立 canonical source authority,不等于语言接受,更不等于 Production ready。
第八:
新的 source successor 进入 current corpus 后,必须重新判断 Unified Glossary Review 是否仍然 current。
第九:
尚未被正式流程消费的 source draft,可以使用受限纠正路径;一旦已经被 corpus、Generation 或其他 immutable evidence 消费,就不能再假装它只是普通草稿。
第十:
replacement 应该解决 source authority 变化,但后续语言处理仍然回到共享 TranslationUnit 生命周期,不再建立另一套 QC 状态机。
最终我觉得这次最值得记住的一句话是:
页面属于我,不代表它的历史可以被我改写。
当一份内容还只是普通文件时:
编辑它就是编辑文件。
但当它已经被翻译、审核、封存并进入正式站点 authority 以后:
修改它就不再只是“改几段 HTML”。
而是在做一次:
内容版本迁移。
这时候真正需要保护的,不只是今天页面上显示的文字。
还包括:
昨天那些已经被证明为真的历史。
Go 学习与文档多语言本地化项目
本系列完整记录 Go 学习与文档多语言本地化项目从 A Tour of Go 起步,逐步扩展到站点首页、项目说明页、学习内容、文档以及未来内容包的实际开发过程,包括架构设计、统一术语治理、整页翻译、结构保护、自动校验、独立质量审核、生产发布与长期维护。
项目源码:
✅ GitHub:shuijingwan/go-tour-i18n
语言版本:
当前跨项目目标语言池共包含 66 个语言版本。Go 项目本身目前提供 65 个社区 Production 语言版本,下面每一门 Go 语言版本都直接链接到其当前公开站点。语言列表沿用项目首页现有的展示顺序。
英语(美国)仅作为跨项目目标语言纳入列表,并不是单独的 Go Production 语言版本。当前 Go 的英文源内容仍为官方 generic English,因此英语(美国)链接到 Go 官方站点,而不是不存在的本地社区部署。
- 阿姆哈拉语
- 阿拉伯语
- 孟加拉语
- 葡萄牙语(巴西)
- 保加利亚语
- 加泰罗尼亚语
- 克罗地亚语
- 捷克语
- 丹麦语
- 荷兰语
- 英语(美国)(Go 官方英文站点)
- 英语(澳大利亚)
- 英语(加拿大)
- 英语(印度)
- 英语(新加坡)
- 英语(南非)
- 英语(英国)
- 爱沙尼亚语
- 菲律宾语
- 芬兰语
- 法语(法国)
- 法语(加拿大)
- 德语(德国)
- 德语(奥地利)
- 德语(瑞士)
- 希腊语
- 古吉拉特语
- 希伯来语
- 印地语
- 匈牙利语
- 印度尼西亚语
- 意大利语
- 日语
- 卡纳达语
- 哈萨克语
- 韩语
- 西班牙语(拉丁美洲)
- 拉脱维亚语
- 立陶宛语
- 马来语
- 马拉雅拉姆语
- 马拉地语
- 书面挪威语(Bokmål)
- 波斯语
- 波兰语
- 葡萄牙语(葡萄牙)
- 旁遮普语(果鲁穆奇文,印度)
- 罗马尼亚语
- 俄语
- 塞尔维亚语(西里尔文)
- 简体中文(中国大陆)
- 简体中文(新加坡)
- 斯洛伐克语
- 斯洛文尼亚语
- 西班牙语(西班牙)
- 斯瓦希里语(坦桑尼亚)
- 瑞典语
- 泰米尔语
- 泰卢固语
- 泰语
- 繁体中文(台湾)
- 繁体中文(香港)
- 土耳其语
- 乌克兰语
- 乌尔都语
- 越南语
项目正在持续扩展新的 Go 学习与文档内容,并长期维护各语言版本的术语一致性、翻译质量、独立审核、生产发布与后续更新。
本项目属于非官方社区多语言本地化项目,与 Go 官方无隶属、授权或背书关系。

