Skip to content

Latest commit

 

History

History
83 lines (56 loc) · 4.01 KB

File metadata and controls

83 lines (56 loc) · 4.01 KB

ADR-0008:以 DOM 适配层实现界面本地化

  • 状态:Accepted
  • 日期:2026-08-12
  • 关联:ADR-0006(保留现有 Web 页面并通过适配层对接 API)

背景

设置中心早就有「界面语言」下拉,但它只是一个不生效的控件。要让英文真正可用,需要在 不破坏界面基线的前提下翻译整个产品外壳。

约束来自现状:14 个页面由 apps/web/src/legacy/openmathmodel-ui.ts 一次性生成中文 markup,该文件超过 3000 行、含约 730 行中文,且属于 ADR-0006 列出的受保护入口。把它 改写成 key 驱动的模板,意味着对每一处文案做机械替换——改动面覆盖全部页面结构,回归 风险与收益完全不成比例。

同时,界面上并存两类文本:一类是产品文案(按钮、标题、状态、提示),另一类是真实数据 (项目名、赛题标题、论文题目、用户输入的论文正文)。本地化只能作用于前者。

决策

1. 在渲染结果上做翻译,不改写页面模板

新增 apps/web/src/i18n/

  • en-US.ts:中文原文 → 英文译文的词典;
  • dom-translator.ts:按整段完全匹配替换文本节点与 placeholdertitlearia-labelaltdata-titledata-subtitle 属性;
  • locale.ts:读写 openmathmodelSettings.interfaceLanguage,应用语言并广播变更。

这与 ADR-0006 的适配层方向一致:后端语义、真实数据和现在的界面语言,都是在既有 DOM 上 做原位适配,而不是另建一套页面。

2. 整段完全匹配是安全边界

只有修剪空白后与词典键完全相等的文本才会被替换。项目名、赛题标题、论文编号这类真实 数据不会出现在词典里,因此不可能被改写。这条规则同时决定了本地化的能力上界:由变量拼接 出的句子匹配不到词典,需要在代码里显式调用 t()

3. 用户可编辑内容与代码块永不翻译

<script><style><code><pre><textarea> 以及任何 contenteditable 子树整体跳过。论文正文是用户数据:若用户恰好写下与词典键相同的词, 替换会直接篡改他的内容。

4. 未命中即回落原文

词典缺项时保留中文,不显示占位符、不报错。宁可少译,不可错译。

5. 语言切换与主题一致

设置中心内选择即时预览,保存后写入 openmathmodelSettings,未保存就关闭会还原为打开 前的语言。启动时在首屏渲染前应用,避免先闪一帧中文。切回中文时用记录的原文还原,不刷新 页面。

6. 内容数据不在本地化范围内

赛题库与论文库中的真实竞赛题面、论文标题,以及方法库的中文技术条目,保持原语言。这些是 数据而不是界面文案,机器翻译它们等于伪造内容。

结果

正向结果:

  • 界面语言开关真实生效,覆盖外壳、设置中心、账户与安全、任务流程六页与知识库页面框架;
  • 受保护的页面结构、样式与路由零改动;
  • 真实数据与用户内容不受影响;
  • 新增页面无需接入 i18n 框架即可工作,补词典即可获得英文。

代价与约束:

  • 词典以中文原文为键,改动界面文案时必须同步更新词典,否则该条回落中文;
  • 由变量拼接的句子必须显式 t(),否则不会翻译;
  • MutationObserver 常驻,需在自身写入时暂停观察以避免回环。

验收要求

  1. 切换到 English 后,侧栏、设置中心、账户与安全、六个流程页面与知识库页面框架显示英文;
  2. 切回简体中文后完全还原,无残留英文、无重复文本;
  3. 项目名、赛题标题、论文题目与用户输入的论文正文在两种语言下都保持原样;
  4. 未保存就关闭设置中心时语言还原;刷新页面后保存过的语言仍然生效;
  5. <html lang> 随语言变化;
  6. 词典质量门禁(en-US.test.mjs)通过:无空译、无原样返回、无重复键、无残留中文。