f5774a2a26
New docs must not enlarge the translation backlog: any date-named document (yyyy-mm-dd-*.md, i.e. an RFC) dated on/after the manifest's requiredSince cutoff must merge with its complete pair, independent of the required back-catalog list. Cutoff ships at 2026-07-05, so the existing 2026-07-04 RFC wave is grandfathered into the batch backlog and everything after is bilingual from birth. The date comes from the filename, deliberately: it is deterministic from tree content alone — no git history (shallow CI checkouts hold), no PR base ref — matching the gate's pure-content design. A proposal dated before the cutoff that merges later escapes the rule; that is the graceful grandfathering of in-flight work, not a hole. --list marks such files (required by date); contract and RFC updated in both languages and the pairs re-recorded.
6.4 KiB
6.4 KiB
通过配对兄弟文件与配对门禁实现双语文档
English | 中文
背景
本仓库的 README 与 docs 目录树会被公司内外的人和 agent(智能体)以中英两种语言阅读。没有机制、纯靠手工维护第二语言,正是译文腐烂的方式:一侧继续演进,另一侧默默地说谎,而没有门禁会注意到。对这类不变式,本仓库一贯的答案是把它编码成机械检查(见质量门禁与 doc-sync 强制),因此双语政策随附一道门禁一起交付。
决策
- **配对兄弟文件,两种语言同权。**一对文档是三个兄弟文件:英文
foo.md、中文foo.zh.md,加一份一致性记录foo.i18n.yaml。没有哪种语言是正典——一篇文档可以先用中文撰写和评审、之后再译成英文,反之亦可;约束这对文件的是两侧必须说同样的话,且配对整体合入(两种语言加记录,绝不单独落一侧)。政策见 docs/i18n/README.md;翻译规则见 docs/i18n/translation-rules.md;术语真源见 docs/i18n/terminology.md。 - 旁挂记录两侧 blob hash,使一致性可检查。
foo.i18n.yaml保存两侧文件在上一次确认一致状态下各自的完整 git blob hash。此后改了任一侧而没重新确认配对,都能被机械检测出来——纯内容比较、无需查询历史——而且同一个 PR 里改动的文件也能算出 hash,commit hash 式的记录做不到这一点。重新记录(verify-translation-pairing --write)产生一份可评审的 yaml diff:确认一致在 PR 里是一个显式、可见的动作。 - **
verify-translation-pairing加入doc-sync。**门禁(scripts/verify-translation-pairing.ts)强制执行:required 的配对存在;任何已存在的配对完整(三个文件齐全)且一致(两个 hash 都匹配、切换行双向互链、结构签名一致);被排除的文件(生成物或本身即双语的)保持不配对。scripts/translation-pairing.manifest.json 中的required清单是一个棘轮:每个合入的翻译批次把自己的文件加进去,覆盖面只增不减。 - **翻译是 agent 的工作,由人评审。**进仓的工作流是 .agents/skills/dsh-translate-docs,与 dsh-code-review 同一模式:skill 承载工作流,并把真源让给文档。
曾考虑的替代方案
- 英文为正典源、指纹放在译文内——本 RFC 最初提出的设计:
.zh.md文件携带一条 HTML 注释记录英文源的 blob hash,翻译只沿 EN → ZH 单向流动。评审中修订:团队需要中文先行的撰写方式(先写、先审中文 RFC,再译英文),两种语言同权,而单向正典模型无法表达这一点。覆盖两侧的旁挂记录取代了文件内的单向指纹;blob hash 的机制原样保留。 - 语言目录(
docs/en/+docs/zh/,Kubernetes/ECharts 模式)——否决:本仓库没有把 locale 映射到路由的文档站框架,挪动每个英文文件会搅动所有既有交叉引用,且verify-md-links/verify-doc-refs将需要路径映射逻辑而不是原样工作。 - 独立翻译仓库(PingCAP
docs/docs-cn模式)——否决:适合有独立发布节奏的文档产品,对 monorepo 自己的文档而言过重;还会把译文置于本仓库门禁够不到的地方。 - 中英混排单文件(一个文件、两种语言)——否决:每个 diff 都翻倍,破坏一段一行约定的 diff 工效,且局部不一致不可见。
- Commit hash 式记录(MDN
l10n.sourceCommit模式)——否决,改用 blob hash:同一个 PR 内的改动还没有 commit hash,MDN 模式无法表达「与本 PR 引入的状态一致」,且校验它需要 git 历史而非文件内容。 - 比较配对两侧的 git 时间戳(无记录)——否决:纯格式化的改动会误报,一次无关改动之后提交的另一侧会漏报;只有内容同一性这个信号与门禁的承诺名实相符。
业界先例
带语言后缀的配对兄弟文件是中国大厂的主流约定(ant-design 的 index.zh-CN.md/index.en-US.md;arco-design 的 README.zh-CN.md 加顶部切换行;Apache ShardingSphere 的 387 对 .cn.md/.en.md)——但这些仓库都没有在 CI 里强制配对或一致性;约定纯靠评审维系。一致性自动化存在于中国之外:MDN 的 l10n.sourceCommit front-matter 指纹、Vue 的 Ryu-Cho action(监视上游 commit、为陈旧译文自动开 issue/PR)、Kubernetes 的本地化漂移脚本、微软 Azure co-op-translator(CI 中由源 hash 驱动的 LLM 重译)。本设计把两者结合:中文生态的文件布局,加 hash 对门禁,再加一个进仓 agent skill(技能)替代 bot 服务。
后果
- 修改已配对文档的任一侧,同一个 PR 就有义务更新另一侧并重新记录配对——门禁把 doc-sync 规则双语化,不变式由 CI(而非评审者的记忆)承载。
- 每个配对给目录树多添一个文件。记录由机器写入(
--write),代价是目录噪音而非维护负担;换来的是「谁在何时确认过这对一致」可以从 yaml 的 git blame 直接回答。 - 两侧说法冲突时,没有机械规则裁决谁赢——由 PR 评审裁决。这是同权的代价,是有意接受的:另一个选项(正典语言)禁止中文先行撰写。
- 生成文档(
cordis-catalog/、tool-catalog/、module-graph.md)暂被排除;计划中的后续工作是让它们的生成器在输出英文的同时输出中文,届时移出排除清单。 - 推进天然是渐进的:
required之外的文档是可见的 backlog(--list),不是红的 CI,因此配对按可评审的批次落地,无需一个巨型 PR。新文档是例外——文件名日期在 manifestrequiredSince当天或之后的文档,要么连同配对一起合入,要么不合入,因此 backlog 只会缩小。 - 记录的 hash 兼作更新工具(
git cat-file -p <hash>能还原任一侧上次确认的文本,用于基于 diff 的最小更新),所以这套机制从不强迫整篇重译。