跳转至

PR 57 国际化深审与翻译交接

历史记录 / Historical record:本文保留当时的计划、研究或验收证据,不代表 0.11 当前能力全集。现行说明见 文档入口;英文入口见 English overview。

日期:2026-10-20。审查对象:PR 57 head 974e862088a4e986cd4eafb88fe5a88e905761c6;当前主工作线为 exp,本轮开始时 f3ba4dd6f9d1961475d908e7b8fc9dc59503be0d。除明确标为“当前工作树”的段落外,下文路径与行号均指 PR head,不是当前工作树同名文件。

结论与本轮边界

国际化值得继续,PR 提供了可复用的语言注册、回落、持久化和部分标签解析基础;它目前是提取工作的起点,不能按“翻译已完成”验收。建议从最新 exp 单独承接国际化工作,先修订阅、消息边界和护栏,再分模块翻译。本轮不合并、实现或补译国际化代码,只提交本报告。

不能整文件取用 PR 的 Learn.tsx、ChatDock.tsx、暂存箱、组织或导览组件。PR 与当前 exp 的共同祖先是 8d24630f082d30a8dd0e51fdc181533d9f60780f,PR 自祖先起涉及 192 个文件;其历史还混合了白板布局/吸附等功能,不是独立的纯翻译补丁。翻译提交 80edf22d 可作来源索引,不能因此免去逐块迁移与回归。

本审查使用 /tmp/onethu-pr57-i18n-review 的 git archive 副本,没有切换或覆盖工作区,没有发远程 review/comment,没有调用学校 API。现有 i18n 护栏、统计、故障注入和一个隔离 React/Chromium 订阅探针均在该副本运行;没有把它们表述成全应用多语言验收。

1. 数量核验:不能把“键数”当成“完成比例”

在 PR head 执行:

node --import ./tools/ts-resolve-register.mjs tools/i18n-test.mjs

自然退出 0,原始输出:

i18n: 2 locales, 2856 keys, 2778 used, all assertions passed
  覆盖:en-US 160/2856(未翻部分回落 zh-CN)
指标 核实结果 含义
默认中文键 2,856 “2,852”不是当前 head 的准确数值
英文键 160 占默认词典 5.602%,可以说“约 5.6%”
缺英文键 2,696 回落中文,不代表运行时缺键报错
自动提取的 auto.* 键 2,626 英文中 0 个对应键
其余语义键 230 160 个已给英文;另外 70 个是 navreg.*
中文不同值 2,214 302 组值重复;相同中文不保证同一个英文语境
含 {name} 等占位符的中文键 9 大量动态句子仍被拼接或留在源码中
现有中英文同键占位符不一致 0 这是本次实查结果,现有护栏并不保护它
词典重复属性键 0 本次 AST 检查未发现;运行时 Object.keys 本身查不出覆盖后的重复属性

英文与中文完全相同的 3 个值是 GitLab、THUBook、OnePage 等名称,不应当为提高“翻译率”强行改写。

自动提取键分布如下,方便估算任务,而不是承诺工作量:

前缀 键数
auto.components 949
auto.pages 1,358
auto.gallery 195
auto.plugins 54
auto.state 39
auto.lib 21
auto.im 9
auto.App 1

“还剩 5,500 处”不能当成准确的待翻任务数。本次对 apps/desktop/src/**/*.{ts,tsx,js,jsx} 做 TypeScript AST 扫描,跳过 i18n/、vendor/、node_modules/,不计注释,结果是:

  • 含汉字的普通 StringLiteral:5,786 个节点。
  • 含汉字的模板头、中段、尾段:817 + 264 + 429 = 1,510 个片段。
  • 含汉字的无插值模板:6 个节点。
  • 合计 7,302 个语法节点/片段,不是 7,302 句待翻译 UI。

这些节点包含展示文案、业务比较值、外部数据匹配、校名/课程名、搜索别名、示例数据、存档标识和类型字面量。单一模板可能贡献多个片段。同一中文又可能出现多次。因此“约 5,500 个引号字面量”数量级接近,但既遗漏模板,又混入禁翻值;不得把扫描结果直接全量替换。

残留节点较多的文件有 lib/infoApps.ts(575)、pages/Settings.tsx(484)、state/atoms.tsx(279)、state/navigation.ts(268)、pages/zhjwxk/Courses.tsx(220)。这些是优先分类的文件,不能推断其中每一项都该翻译。

2. 先修的工程问题

以下优先级表示后续国际化工程的处理顺序,不表示这些问题已在当前未合并的 exp 上发生。

E1:仅订阅 App 不能保证已挂载界面跟随切换(已最小复现)

证据:

  • apps/desktop/src/App.tsx:171 在 App 中调用 useLocale(),注释将其解释为整棵树刷新。
  • apps/desktop/src/main.tsx:120 的 App、ConfirmHost、FormModalHost 是同级节点。
  • apps/desktop/src/lib/formModal.tsx:36 只订阅表单 pending store,按钮在 :81、:87 调用非订阅的 i18n。
  • apps/desktop/src/lib/confirm.tsx:133 的 PromptHost 只订阅 prompt pending store,按钮在 :159、:160 调用非订阅的 i18n。
  • apps/desktop/src/i18n/index.ts:116 的 useT 才会建立 locale 订阅;直接导入 t 不会。

最小复现使用 PR 的真 FormModalHost、ConfirmHost 和 i18n runtime,保持 main.tsx 的同级树形;React/ReactDOM/Chromium 真实运行,OneGUI 外壳替换为简单 DOM 组件,避免加载业务。仅在临时探针中给四个原本未翻译的按钮补 Cancel/Submit/OK,用于排除“词典没有英文”的干扰:

切换前按钮:取消、提交、取消、确定
调用 setLocale("en-US") 后:
  App 订阅值 = en-US
  t("auto.lib.formModal.1") = Cancel
  已挂载按钮 = 取消、提交、取消、确定

探针自然退出 0。它证明宿主没有随 locale 通知更新;不代表真实用户能在所有模态打开时点击到底下的设置。后续跨窗口/程序化切换同样不应依赖任意业务事件碰巧刷新。

最小修复要求:让实际文案消费组件或公共宿主订阅 locale;仅对已解析的文案缓存把 locale 纳入依赖。不要通过给 App 整体加 key={locale} 重挂来修,这会重置未提交表单、聊天和页面状态。已存用户内容不得在切语言时翻译或改写。

模块常量审查的准确结论:本次未找到明确的顶层 t/i18n 直接求值实例;NAV_REGISTRY 使用键/渲染时解析是正确方向(state/navigation.ts:155),PAGE_TITLES 也已保存键(state/explorer.ts:229)。不要把“所有模块常量被冻结”写成已证实缺陷。仍需单独检查 TabHost.tsx:256 的 useMemo([st.tabs]) 内首页标题、GlobalOrb.tsx:152 的 memo 组件以及独立窗口。useCallback 内在点击时调用 t() 会读到当前语言,不能仅凭 callback 没有 locale 依赖就判为旧译文;DockMsgList 的父级也有新函数 props,不能未经复现认定被 memo 阻断。

E2:缺键与格式护栏有可复现漏检(批量翻译前必修)

证据:tools/i18n-test.mjs:42 明确允许局部译文,:84 的扫描只认若干命名空间、双引号调用和几种固定字符串形态;navreg.* 由 state/navigation.ts:162 动态生成。misc.*、tour.* 的调用不在该 NS 正则里;tOr 也不被调用扫描覆盖。文件头 tools/i18n-test.mjs:7、类型注释 i18n/types.ts:5 仍声称逐语言全键一致,与实际策略矛盾。

在临时副本逐一故障注入、每次恢复原文件后,以下三项 全部仍然自然退出 0:

  1. 把 en-US 的 shell.addToHome 从 Add {name} to home 改为 Add to home。
  2. 从两张词典同时删除静态引用的 tour.mcp.aria。
  3. 从中文词典删除动态引用的 navreg.learn.name。

第一项丢失必要对象名;第二项 UI 会显示原始键;第三项被中文 fallback 隐藏。它们没有被“all assertions passed”识别。

修复要求:

  • 根据 i18n import 绑定和 useT() 变量做 AST 引用检查,不能把任意同名 t() 都当翻译函数。实际源码另有颜色函数 t("primary"),粗扫会误报。
  • 明确枚举动态键族:注册表 id + name/note、PAGE/TAB/CATEGORY 键表等;测试新增一项但漏键时失败。
  • 校验每个已提供译文的占位符名称集合、值类型、非空、重复属性、占位符调用参数。给稳定的 key/params 类型或生成类型。
  • 区分“开发中允许回落”和“该语言发布完成”。开发可以部分翻译;发布清单指定的语言/批次应强制完整,不能一边宣称全键一致一边永久只打印覆盖率。
  • 保留未知 locale 回落/拒绝、订阅、持久化测试;新增第三语言要在真正 Vite 构建中验证自动注册,Node 静态种子测试不等于这一项通过。

当前已有 9 个占位符键且无不一致,不应为制造问题随意改写。tOr 在键完全缺失时直接返回 fallback、不插值(i18n/index.ts:107);tOr("missing", "Hi {name}", {name:"A"}) 实测仍为 Hi {name}。当前导航调用没有传这种模板,故列为 API 合约缺口,不能报成现有导航已坏。

E3:机械切片不能仅靠填英文词典解决(已由源码确认)

典型位置:

PR 位置 当前形态 为什么翻译模型无法仅改值解决
plugins/ChatDock.tsx:1004–1006;中文键 auto.plugins.ChatDock.13/.14/.15 在词典 :2355 前半句 + 查询词 + 后半句,另有硬编码中文引号 无法自由调整整句语序、引号和空格
pages/Schedule.tsx:1085 年数字 + “年”键 + 月数字 + “月”键 改两个单位仍不能得到自然英文月份顺序
pages/Schedule.tsx:1165 “第” + 周数 + “周” 需要完整 Week {week} 消息
pages/TasksPage.tsx:230 一个前缀键 + 数量 + 硬编码 ) 标点与计数句被拆开
components/explorer/PracticalFeatures.tsx:37 数量 + 独立单位键 需要 0/1/多项规则,不是所有语言都能追加同一单位

应将一条完整表达作为消息,动态值为具名参数;含 <b>、链接等结构的整句用可重排的富文本槽位,不能把译文作为 dangerouslySetInnerHTML 注入,也不能将用户文本或服务端错误拼入 HTML。plural/select 与日期/数字格式是工程层工作,不要让翻译模型猜出一套运行时尚不支持的 ICU 语法。

i18n/index.ts:95 当前只支持 /\{(\w+)\}/ 替换,不支持 ICU plural/select。后续可选择引入成熟消息格式或实现明确的有限 plural 接口;选定后统一文档和测试,再翻计数消息。

E4:日期、数字、页面语言与窗口同步未接通(静态确认,未做全窗口实机)

  • apps/desktop/index.html:2 固定 lang="zh-CN";setLocale(i18n/index.ts:81)只改内存、localStorage、通知订阅者,没有更新文档语言。读屏可能按中文发音规则处理英文。
  • pages/Schedule.tsx:691 显式 toLocaleTimeString("zh-CN", ...);pages/Settings.tsx:510 的 toLocaleString() 则走系统默认语言。二者都不由 UI locale 控制。
  • 相同 runtime 没有 storage/native 语言同步订阅。独立悬浮窗使用另一份内存状态;是否跨窗立即生效应明确并实测,不能宣称已经实现。

修复要求:公共 formatDate/formatTime/formatNumber/formatRelative 接 UI locale;保持学校日期、课程周、提交截止时间的时区和计算语义不变。只改显示,不能把 locale 格式化结果写回 API 或日期 key。更新每个窗口的 document.documentElement.lang;若支持 RTL 再按配置处理 dir,不得将所有非中文一概设 RTL。语言码失效、存储不可用、重启与新开窗口都应有测试。

E5:语言入口和导航词库不完整(可确定的可达性/一致性问题)

  • 语言下拉只在完整设置 pages/Settings.tsx:568;SettingsOverlay.tsx:34/:84 对极简/实用渲染 SimpleSettings,其 SimpleSettings.tsx:22–44 没有语言入口。新用户选极简后,必须先切完整模式才能改语言。应把语言选择视为基础偏好,在三种模式的适当位置共用,不要为此恢复被隐藏的高级设置。
  • state/quickAccess.ts:38–39 的五个 OneShot 默认显示名仍硬编码中文。PracticalFeatures.tsx:35 的分类按钮直接显示中文 c,即使标题分组已调用翻译函数。
  • navName 使用 navreg.*,英文没有这 70 个键。本次直接运行真导航模块,setLocale("en-US") 后 navName(learn) 仍为“网络学堂”,而已有 nav.learn 为“Courses”;matchNavQuery("Courses") 返回空数组。这是当前词典/搜索覆盖的实测,不是所有英文检索都失败。

应共享同一份“稳定 id → 本地化显示名/搜索别名”定义,保留中文、学校惯用简称及插件原名检索。不要翻译或迁移快捷入口 id、分类内部值来修显示。尚未用户改名的系统默认名称与用户命名需要区别处理,不可扫描整个 localStorage 把中文替换为英文。

3. 提取安全性:哪些现状要保留,哪些绝不能批量翻

本次抽查新增翻译调用、option 值、导航/设置/选课逻辑,没有发现把上述协议比较值直接改成译文的具体反例。这不等于 192 个文件已通过全部业务回归;尤其不能因为中文扫描还有结果就继续无差别替换。

正确的现状包括:

  • state/navigation.ts:170–192 分类内部值仍是中文,显示经 navCategoryLabel,筛选依旧以稳定值进行。
  • pages/Settings.tsx:221–239 已优先用 settings-group:* 标记控制分节,不把翻译后的标题当分组键;这些 group 值仍不能翻译。旧无 marker 分节的中文匹配兜底要逐个迁移成稳定标记后再翻展示标题。
  • lib/xklogic.ts:37–49 的“体育/必修/限选/任选”等是学校数据的识别条件;FLAG_LABELS(:61)才是可做显示映射的位置。
  • pages/info/CalendarTab.tsx:74–75 option 的 autumn/spring/zh/en 与 pages/Organization.tsx:949–951 的 planned/active/done 保持原值,只有可见内容翻译。
  • plugins/ChatDock.tsx:947–948,确认按钮文字可本地化,但 p.onSend("确认")/p.onSend("取消") 是当前交互协议输入,不能仅翻标签时把协议同时改掉。未来若改为结构化 action,另立工程任务。

交给翻译模型时,以下内容一律保留原样:

禁翻类别 例子/处理
代码与协议标识 page:learn、learn-course、assignments、onethu.* 存储键、CSS class、DOM id、data-* 值、命令名、状态枚举、参数名、runId、事件 kind
机器可读数据 URL/路径/hash、文件名及扩展名、MIME、日期 key、HTTP header、正则、API 比较值、ICalendar 字段、错误码
用户和学校原始内容 姓名、课程/作业/通知标题、文件夹名、组织公告、聊天记录、用户自定义 OnePage/工作区名。可翻外层“打开 {title}”,不能翻 {title}
安全与分享载荷 分享短码、密钥、token、邮箱白名单、邀请 ID、QR/NFC 内容、权限枚举、密文和校验值。只能改呈现文案
品牌/兼容 id OneTHU、OH、OneGUI、OneGO、OnePage、OneShot、MCP、GitLab、THUBook。石墨显示名可以用 Graphite,兼容 id onethu.theme.ivory 不能改
现有中文匹配词 学校数据“体育/必修”等、服务搜索别名、设置分组内部值。先拆 id/显示,再决定是否补新的英文别名

state/data.ts:2135 等本次翻的是示例数据 seed 的标题,不能以此类推真实服务返回值也可本地化。翻译权限提示时不得改变“仅指定账号”“可撤销”“删除记录不撤销分享”“打开/复制并不删除原项”等语义。

4. 与最新工作线的迁移边界

后续承接者必须先读最新 git/记忆,再以最新实现为事实源;本报告只冻结了 PR 证据。

领域 PR 与当前线的差异 承接规则
OH / MadModel PR 的 ChatDock.tsx:1861–1870 仍有无 runId 的 interrupt 和 12 秒强制解锁;当前线已有每轮 runId、可取消 preflight、事件归属、真实生命周期互斥 保留最新行为,只迁移消息;不要用旧 stop/send/session 函数覆盖最新实现,不翻 runId/命令/事件 payload
网络学堂 当前工作树新增 pages/learn/LearnCourseCard.tsx,将课程主按钮与作业/通知/文件动作分开,补独立 aria-label;YktCoursePage.tsx 还调整了分组标签;PR 的旧 Learn.tsx 不含这套结构 翻新卡片的来源、作业/通知/文件按钮、计数与无障碍完整句,及雨课堂分组标签,保留兄弟按钮和独立点击目标;title/yktEntry.title 是原始课程名
首页模板 当前工作树正在处理四种首页模板与布局拥挤,涉及 boardTemplates/board/FreeBoard/Settings i18n 不接管几何、默认条目、持久化迁移或重置行为;以当前调整后的模板中文为准,模板显示名另列 key
暂存箱/组织 当前已有更完整的统一收发、已发出口令管理、权限/加密语义、按钮周边 halo、真实拖动/取消反馈;PR VaultExchangeActions.tsx:19 仍是自动复制等旧流程 从最新组件重新提取,不照旧词典复活已删除按钮/说明;不能将“已复制”用于仅生成口令等不同状态
导览 当前三模式课程、真实页面截图贴图、自动模型动画与清理已重做;PR OnboardingTour.tsx 是旧结构 翻最新 lesson 元数据与 UI。真实截图资产在 assets/tour/real-ui,不能通过手绘英文 UI 假装真实截图;需要英文资产时从真实英文界面重采集并保留来源说明
共用确认层 当前 confirm/formModal/OneGUI 模态返回和焦点契约已经修整,PR confirm 仍是旧 DOM mask 把语言订阅加到最新宿主,不恢复旧 mask、不删除 navStack/焦点/危险确认行为

5. 给翻译模型的分批任务

每批输入应带:最新基线 commit、限定文件、key、中文原文、完整调用句、UI 用途(按钮/标题/提示/危险确认/读屏)、参数含义、长度约束、截图/失败态和禁翻字段。输出必须是可审查 diff,加“待确认术语”清单;不得根据 key 的数字后缀猜上下文。

auto.<文件>.<序号> 可以暂时保留来降低迁移差异,但后续新句优先使用语义键;禁止重新运行提取器给所有序号重排。两个相同“删除”可能分别删记录、撤权、删资源,不能因为中文相同就合并为一个不分语境的 key。

批次 范围与交付 本批独立验收重点
0:工程预修 E1–E5;先落消息边界、注册表键清单、格式/缺键护栏、三个模式语言入口;不必一次翻全站 zh→en→zh 原地刷新、输入和状态不丢;故障注入必须失败;数据 id/请求不变
1:壳层与基础导航 现有 230 个语义键复核;补齐 70 个 navreg;OneShot/OneGO/标签页/搜索、我的、登录和三模式基础设置 用用户看到的英文能搜到相同服务;极简五入口、实用四快捷+更多、完整模式各自结构不变
2:学习与日程 最新 LearnCourseCard、课程/作业/通知/文件/雨课堂、待办、日程/课表;整句计数、日期、空态 0/1/2/大数量;长课程名;每个独立按钮的鼠标/键盘与 aria 名称;学校时区/课表规则不变
3:暂存箱与内容交换 最新 Workspace/Sheet/ExchangeActions/IssuedShares/附件、扫码/NFC、接收/发送/撤销/失败重试 生成≠复制;存入≠发出;取用≠删除;限邮箱和撤销提示准确;取消不报成功
4:组织与我的名片 组织成员/角色/文件/项目/公告、个人名片、邀请、权限 pending/error/retry 不把本地状态说成云端成功;撤权、操作权限、邮箱限制保留;用户输入不被改写
5:OH、插件与连接 最新 ChatDock/OhSettings/IM/语音/外部作业源/插件页/网络错误 401、429、网络故障、取消、忙拒绝的文案区别;不翻模型输出/协议消息;会话/运行归属回归仍通过
6:校园服务与存储 info/life/reserve/选课、云盘、GitLab、邮件、THUBook;先分类学校原始值与 UI 预约筛选、金额与单位、时间、原始校名/课程名;中文协议解析保持一致
7:高级设置、导览和画廊 最新三模式 lesson/可选探索、主题/白板/小组件、可访问性、开发画廊 只翻当前存在的模式能力;真实截图来源清楚;长译文窄屏不遮操作;画廊最后做,195 个画廊键不能优先于主流程

每批完成后提供该批实际键覆盖率和仍回落的条目,不用“全仓中文 grep 为零”作为成功标准。标为部分完成的英文语言不应在发布说明里宣称全界面已完成。

6. 术语建议与文风约束

以下是交接用建议表,不冒充已经获得产品定稿。首次翻译前锁定一版,同一概念在按钮、搜索、标题与读屏中复用。

中文/名称 建议英文 注意
首页 Home 不叫 My Whiteboard
网络学堂 Tsinghua Web Learning / Web Learning PR 的 nav.learn=Courses 与 tab.learn=Learn 不一致;不要把课程实体名也翻译
雨课堂 Rain Classroom 保留已有外部平台标识/URL;可在首次说明保留中文名称
日程 / 课表 Schedule / Timetable 同一个页面的不同入口/模式要按实际用途区分
待办 Tasks 作业未交状态用 Pending assignments,避免混同通用待办
暂存箱 Stash(建议) 工程里的 vault id 不变;若产品选择 Vault,则所有显示统一,不能两套词混用
口令 Share code 分享短码,不是账户 password/passphrase
存入暂存箱 / 取用 Save to Stash / Open 或 Use 打开/复制不会删除原内容,不能译成 Move out
撤销分享 / 删除记录 Revoke share / Delete record 不是同一动作
名片 / 组织名片 Profile card / Organization card 组织名片绑定组织创建,不另造独立创建流程
工作区 / 一级功能区 Workspace / Services 不给 OneGO/OneShot 改产品名
极简 / 实用 / 完整 Minimal / Practical / Full 不改存档值 simple/practical/full
石墨 Graphite 显示改名不改兼容主题 id
寻迹 Trace(暂沿用) 当前 PR 已用 Trace;是否加 Map 说明需统一产品决定

英文采用简洁句式;动作按钮用动词,成功/进行中/失败分别准确表述。危险操作保留后果和作用范围,不为“短一点”删掉不可撤销、原记录仍有效、尚未同步等关键信息。占位符名称、换行策略、URL 和快捷键原样保留;英文标点与空格属于整句译文,不在 JSX 两侧临时补。

7. 验收与可复现命令

本次已经执行的验证

  • PR 原始 tools/i18n-test.mjs:自然 exit 0,数量如第 1 节。
  • AST 词典统计:无重复属性、现有占位符中英文一致,2,856/160;残留节点按第 1 节范围统计。
  • 真 state/navigation.ts:英文 navName(learn) 仍中文;查询 Courses 无匹配。
  • 三个隔离故障注入:护栏均仍 exit 0,证明漏检,之后已恢复临时词典。
  • 真 Host + i18n、轻量 GUI 外壳的 React/Chromium 探针:App 已切英文,现存 Form/Prompt 按钮不刷新。不是全应用渲染/多窗口实测。

本次没有验证:全应用英文布局、完整 Vite 第三语言注册、屏幕阅读器、多窗口同步、所有提取调用的业务行为。不要把本次几个通过的检查扩大成这些结论。

安全取出 PR 的命令

在仓库根目录执行;临时路径每次唯一,不 checkout 当前工作区:

pr57_dir=$(mktemp -d /tmp/onethu-pr57-i18n.XXXXXX)
git archive 974e862088a4e986cd4eafb88fe5a88e905761c6 | tar -x -C "$pr57_dir"
# 在这个独立副本按仓库 lockfile 安装/复用所需依赖后执行:
cd "$pr57_dir"
node --import ./tools/ts-resolve-register.mjs tools/i18n-test.mjs

后续分批合入前的命令

以下前两项是 PR 已有/仓库已有命令;新语言护栏必须先补第 2 节的能力,不能只执行旧版本就签字:

node --import ./tools/ts-resolve-register.mjs tools/i18n-test.mjs
pnpm --filter @onethu/desktop typecheck
pnpm --filter @onethu/desktop build
pnpm guard

浏览器验收应新增专门 locale 流程,并复用当前领域回归。已有例子:

# 需要按各测试说明运行隔离 dev server;不要连接真实用户账号来跑夹具。
node tools/experience-browser-test.mjs
node tools/short-share-browser-test.mjs http://127.0.0.1:5180
ONETHU_TEST_URL=http://127.0.0.1:5180 node tools/private-share-browser-test.mjs
ONETHU_TEST_URL=http://127.0.0.1:5180 node tools/learn-layout-browser-test.mjs

最后一项来自本轮正在集成的新学堂回归,后续使用前以最新脚本说明为准。不得为翻译改名而删减原来的行为断言;稳定 data-testid/语义 id 用于定位,另外独立断言当前语言的可见文案和 accessible name。

必须新增的语言验收矩阵:

  1. 三模式 × zh-CN/en-US,320/390/844 横屏/1280;长文本、100%/200% 文本放大、键盘 Tab 与焦点返回。不能仅截图首页。
  2. 已打开标签/菜单/表单/确认/搜索与语言来回切换;不丢草稿、活动页、选中项或完整模式存档。数据响应前后都测。
  3. 页面标题、tooltip、placeholder、aria-label、状态播报、空态、错误、成功反馈一起核对;document.lang 正确。
  4. 0/1/2/大数量、无数据/加载中/失败、跨午夜日期、学校时区、长课程名/文件名/姓名。占位符不裸露,不把实体名字翻掉。
  5. 语言切换前后,同一选课/预约/分享/组织/插件操作的 request 参数、id、权限和持久化业务值一致;允许 locale 偏好键及展示文案变化。
  6. 伪本地化/长译文探针,加可见缺键报警;仅在测试态覆盖词典,不把伪译文作为正式语言发布。第三语言增删与存储无效码另测。
  7. 故障注入:错 key、丢参数、改参数名、动态键漏项、重复 key 应让护栏失败;先确保测试能抓住错误,再批量补译。

8. 可直接给后续模型的任务说明

在最新 exp 上承接 OneTHU 国际化。先阅读本报告和最新记忆/git。PR 57 的词典和调用位置仅作迁移参考,不整文件覆盖当前实现,不混入 PR 白板/导览/组织/聊天旧行为。先完成批次 0 的工程预修并给独立测试,再按批次提交小 diff。只翻展示层完整句子;不得翻协议值、数据 id、用户/学校内容、权限和分享载荷。每条译文结合真实调用和 UI 状态,保留占位符、安全提示与无障碍名称。遇到需要 plural/富文本/日期格式支持的句子,先修消息边界,不能通过词典拼接硬凑英文。保留最新 LearnCourseCard 的独立按钮和 aria、OH runId/取消/互斥、暂存箱及组织安全语义。每批报告键覆盖率、残留分类、实际验收和未测范围;不以 grep 无中文或旧 i18n-test 通过冒充完成。