文档对照研究,2026-10-29
范围与证据
本次问题是:OINK 如何为约 30–40 个本地站点提供通用对照阅读模式,覆盖译文、八语言集合、校对书籍、任意语言,以及同一语言的新旧版本。研究检查了 Docusaurus、VitePress、Material for MkDocs 与 mike、Read the Docs、Hugo、Sefaria、MediaWiki ContentTranslation、OmegaT、Weblate、Joplin、VS Code 和 CodeMirror。证据来自官方文档与固定源码版本;没有启动上游应用,也没有把仅凭源码检查推导出的行为称为浏览器验证。未固定修订的官方文档仅记录 2026-10-29 当日所读内容,不是不可变源码快照。OmegaT 证据限于 6.1.0 手册,不代表已检查实现。
| 项目 | 固定文档/源码修订 | 已核实的相关性 |
|---|---|---|
| Hugo | v0.168.0 源码 e27a6760c5547237f87d353aebe0580feadb20f4 |
页面关系与原生版本维度轮换(.Rotate),不提供段落对齐。 |
| Docusaurus、VitePress、Material/mike | Docusaurus v3.10.2、VitePress v1.6.4、Material 9.8.0、mike v2.2.0 | 稳定文档 ID、显式锚点、版本元数据与目标存在性感知导航。 |
| Read the Docs 与 VS Code | RTD 9b012b1df15fc153590b4db5f4da7bd3eb8ae74a、Addons d8f99069d9a8b9647f5bd68fdedb95ffa11e63ce、VS Code d7658021df8289f549bf2af8c8dec131f2580d69 |
真实 HTML/Markdown 渲染对照和映射预览;不能证明通用 OINK 阅读器。 |
| Sefaria 与 ContentTranslation | Sefaria af96a1a1695a6307a3d616493455a6802455be1b、ContentTranslation 8d5dde493528330efcb373dc448f352e81815115 |
独立窗格和严格段落行是两种产品。 |
| Joplin、OmegaT、Weblate、CodeMirror | Joplin b48e75856d81152c32e71f21075e1c043d9ea922、OmegaT Standard Manual 6.1.0、Weblate 503129cadb056e2ea0db629fe8e37907b41ff054、CodeMirror 53c8f7b213332743a1f4f7d26bf40cfe2b512b53 |
逻辑滚动映射、人工对齐、稳定消息上下文和几何留白。 |
| DDIA 消费站样本 | Vonng/ddia a6b2e6f85e1249720c8a349273455190d2fc9515 |
四个 Hugo 成员拥有重复中文 locale 和不同版次。 |
身份与关系建模结论
原始的通用对照范围需要区分候选集合身份、成员身份、Hugo site key、实际 locale 与方向、edition/version、规范 URL 以及锚点对应关系。语言代码不是文档身份。固定修订的 DDIA 配置提供了具体案例:zh、tw、v1、v1_tw 是四个配置成员,包含重复的中文 locale 和不同版次。按 locale 去重会删除合法选择,也会把版次错误标成译文。
Hugo 0.168.0 可以作为可靠起点。.AllTranslations 包含当前页,.Translations 排除当前页;显式 translationKey 可以关联路径不同的页面。.Rotate "version" 可以在保持其它维度(包括语言和 role)的前提下适配原生版本维度。这个适配器对对照是可选的,不要求消费站重组内容,也不能与 OINK 的 params.versions 菜单混淆:后者是导航配置,不是语义伙伴解析器。自定义版本或 role 维度仍需显式适配,避免静默接受含义不明的原生匹配。
自动译文候选应先取实际 Hugo 页面关系,再通过资格过滤:已发布、已构建、可渲染的 HTML 兄弟页面;排除当前页面;排除作为语言导航回退的首页;当集合有边界时排除其它 role 或 version。导航链接证明目标地址存在,却不能证明它是译文或可比较文档。缺失译文必须保持缺失,不能在读者不知情时复制源文。
原始的通用设计为八语言集合提供两个选择器,不需要预生成 28 个配对页面,也不需要同时加载八份文档。同语言 v1/v2 对照使用两个成员 ID 和明确标签。同一身份模型也支持章节结构不同的校对书籍,不会把相同文件名或标题文字误认为对应关系。
现有工具实际比较了什么
Docusaurus 的文档身份、本地化与版本管理将 ID、slug、locale 和版本树分开,其标题指南建议本地化页面固定标题 ID。VitePress 的语言 helper按当前相对路径构造语言链接,并支持自定义锚点,但该 helper 没有建立语义伙伴注册表。Material 使用 mike 版本元数据和目标 sitemap 查找对应 URL;这能确认地址可用,不能确认语义等价。这些是身份和校验模式,不是原生双栏滚动证据。
Read the Docs 提供了本次检查中可以直接核对的渲染对照证据:其 visual-diff 文档 和 Addons 的 docdiff.js 对已渲染 HTML 正文进行比较、标记变化的 DOM 区域,并用 URL 状态保存模式。这是面向审阅流程的单页渲染差异。VS Code 的官方 Markdown diff 文档 和 diffScrollSync.ts 展示了两个渲染预览、逻辑映射和反馈抑制,说明渲染对照与映射滚动可组合,但 OINK 的内容身份和静态托管边界仍需自行解决。
Sefaria 明确区分独立滚动的 Compare Text 和共用 segment 的双语阅读:固定源码 TextRange.jsx 把两种文本放进共享 segment,TextColumn.jsx 则让比较列独立滚动。MediaWiki ContentTranslation 的 alignSectionPair 使用稳定 section ID,并在一个编辑流程中对成对翻译区域进行高度与边距补偿。OmegaT 的 Align Files 手册 把自动匹配与人工拆分、合并、移动和接受结合起来。Weblate 的 Unit 模型 将 message ID、上下文和文本 hash 分开保存。CodeMirror 的 updateSpacers 展示了严格行对齐的几何成本。这些证据说明自动相似度可以提出关系建议,却不能为任意多语言 HTML 静默发布段落映射。
三种阅读语义
设计必须命名三种不同的行为。独立滚动 让每个窗格保留自己的位置,是每个有效配对的可靠回退。按锚点联动 保留两个独立窗格,在共同语义锚点之间估算对应进度;它只是邻近章节参考,不保证永久同行,也绝不能称为“逐段齐行”。严格行对齐 把实际块放入成对行,用最大行高和短侧留白,对应关系还需要一对多映射、缺失块占位和明确顺序策略。这是后续独立功能。
对 OINK,默认策略应检查映射质量:满足下述保守条件时才启用锚点联动,否则使用独立滚动。控制条始终允许读者关闭联动。零个或一个有效内部锚点、重复显式目标、缺失显式目标或移动后顺序非单调的配对,都保持独立阅读。普通未匹配章节属于内容缺口,不是无效目标。即使连续同步不可用,仍可提供“跳到对应章节”。
渲染与隔离结论
OINK 的 v1.4.0 Docs 外壳、正文渲染、Book ID 处理、Tabs、表格阅读器、图表和按能力选择的脚本,使任意正文拼入单 DOM 成为较大的重构。两个同源文档 frame 能保留每页自己的 lang、dir、资源基址、渲染上下文、ID 和阅读器所有权。frame 是文档边界,不是独立进程或安全沙箱;同源内容仍然属于受信内容。参见 iframe 参考与同源策略。
最初考虑的路径先在两个同源 frame 中加载普通合资格 HTML 页面,并为导航、键盘快捷键、analytics、评论和递归对照入口增加守卫,再考虑可选的 content-only HTML view。收窄后的第一阶段实现继续使用普通页面;专用输出只有在测量证明值得时才作为未来优化。Hugo 参数本身不能创建 output format;辅助输出需要明确的 output plumbing、Page Store/能力处理、相对资源规则和 SEO 策略。
滚动模型与有界结论
可复用的引擎模式是 Joplin 的逻辑映射和 VS Code 的渲染预览映射。Joplin 的 scrollmap.js 和 VS Code 的 diffScrollSync.ts 支持收集有效布局锚点、在区间内线性插值并限制滚动边界。收窄后的译文 MVP 从共同稳定标题 ID 自动推导严格单调的映射,不需要另行编写或审阅配对映射。锚点交叉或重排就拒绝该映射;引擎不能静默选择 LIS、丢弃移动锚点或重排文档。字体、图片、图表、Tabs、details 或宽度改变布局时,几何必须失效。状态 owner 与 sequence token 防止程序滚动事件回传。用户应保留焦点和窗格所有权;普通滚动不能打开隐藏内容或移动焦点。
普通未匹配章节在页面和两侧包围它的锚点都有效时属于内容缺口;如果缺口被有效锚点夹住,阅读器可以标明这是近似区间并进行插值。在未来的人工映射功能中,显式目标不存在、重复或与另一个显式目标交叉,则属于无效映射;这会关闭该配对的连续同步,而不是回退到序号匹配。显式跳转仍可报告具体的无效目标。
这些证据支持一份聚焦提案,却不构成实现声明。这轮上游源码调查没有运行候选对照功能、服务器、浏览器、无障碍检查或八语言消费站,也没有证明每个框架都不存在插件或实验性对照模式。这些限制会进入提案的验收矩阵,推荐也因此保持克制:先验证普通 Docs/Book 双栏阅读,再决定更复杂输出或严格行对照是否值得维护成本。
第一阶段范围修正
根据 2026-10-29 的简化要求,配套提案现在假定已有翻译兄弟页和相同标题 ID。
唯一站点设置是 params.ui.compare: true。Hugo 提供现有翻译链接,浏览器选择一个
目标,用两个同源 frame 显示普通页面,并提供同步滚动开关。当前语言固定,仅目标
语言需要选择器。
上文较广泛的身份讨论保留为未来版本对照的研究。第一阶段无需 compare set、member ID、人工锚点映射、CLI、新布局、新输出或预生成语言配对。Frame 加载普通页面就是 推荐的首版实现,不代表后续必须增加正文专用输出。原宿主页保留在阅读面之后,因此 两个子文档加上宿主页共占三个文档;先测量这个成本,再决定是否增加渲染路径。
来源链接
- Hugo 多语言页面、
.AllTranslations、.Rotate和 Hugo v0.168.0 源码。 - Docusaurus 标题 ID、VitePress 自定义锚点 和 mike 版本元数据。
- Read the Docs visual diff、RTD DOM diff 源码 和 VS Code Markdown diff。
- Sefaria 双栏阅读、Joplin sync-scroll 规范 和 MDN iframe 策略。
- OINK v1.4.0 language targets 与 OINK v1.4.0 content rendering。