现象
checkKeyParity()(packages/i18n/src/resources.ts:87-118)外层遍历全部 locale、内层再对 flat 与
otherFlat 各查一遍,于是同一处 en/zh 键差异会产出 2 条 issue;且 extra 分支的 locale
填的是当前 locale 而不是缺键的那一侧,标签与事实相反。
给 zh-CN 删掉 navigation.today(一处差异),跑真实脚本 bun run i18n:check:
i18n:check
supported locales: en-US, zh-CN
✗ [missing zh-CN] navigation.today
✗ [extra zh-CN] navigation.today
en-US: 1487 keys · zh-CN: 1486 keys
i18n:check FAILED — 2 issue(s)
第 2 行读作「zh-CN 多出一个键」,但该键其实存在于 en-US、缺失于 zh-CN——标签正好说反;
同时 failures 计数被放大一倍(scripts/i18n-check.ts:57 对每条 issue 累加一次)。
期望
一处差异产出且仅产出一条 issue,且 locale 指向有问题的那一侧。修复后同样的差异:
i18n:check
supported locales: en-US, zh-CN
✗ [missing zh-CN] navigation.today
en-US: 1487 keys · zh-CN: 1486 keys
i18n:check FAILED — 1 issue(s)
判据(取自源码自述契约)
packages/i18n/src/locales/keys.ts:8-9:exact parity including extras is enforced by i18n:check at CI
packages/i18n/src/resources.ts:4:Both locales must stay key-identical — i18n:check enforces that in CI
scripts/i18n-check.ts:8:key parity — every key in en-US exists in zh-CN and vice versa
scripts/i18n-check.ts:56-58:for (const issue of issues) fail(...) → failures 就是 issue 条数,
并直接出现在用户可见的 i18n:check FAILED — ${failures} issue(s) 与 assertKeyParity() 抛出的
I18N_KEY_PARITY: ${issues.length} issue(s) 里
影响
- CI 日志与
assertKeyParity 的报错计数把 1 处差异说成 2 处,N issue(s) 虚高一倍;
[extra …] 行的 locale 指向错误一侧,排查时会去看根本没有问题的那个 locale;
assertKeyParity 只打印前 8 条样本(resources.ts:125),双报会白白占掉一半样本额度;
- 插值不一致(
interpolation-mismatch,resources.ts:99-110)同样被双报,且两条的 locale 分别是
两个 locale —— 同一对 key 看起来像是「两边都错」。
当前 main 上键完全对齐(en-US 1487 keys ≡ zh-CN 1487 keys),checkKeyParity() 恒返回空数组,
所以该缺陷只在真正出现差异时暴露 —— 也就是 CI 变红、最需要读数准确的那一刻。
复现
# 1) 制造一处差异:在 packages/i18n/src/locales/zh-CN/navigation.ts 里删掉 `today: '今日',`
bun run i18n:check
# 修复前:i18n:check FAILED — 2 issue(s) ([missing zh-CN] + [extra zh-CN])
# 修复后:i18n:check FAILED — 1 issue(s) ([missing zh-CN])
建议修复方向
以 SUPPORTED_LOCALES_FOR_RESOURCES[0] 为基准 locale(reference),只对其余 locale 单向比较,
并把三类 issue 的 locale 统一为「有问题的那一侧」:
missing:该 locale 缺少 reference 里存在的键;
extra:该 locale 多出 reference 里没有的键;
interpolation-mismatch:该 locale 的 {{var}} 集合与 reference 不一致。
这样 issues.length 恰好等于差异处数,locale 语义在三类之间保持一致,同时保留未来增加第三个
locale 时的扩展性。
验证方式
- 新增 3 条回归用例(一处缺失 / 一处多余 / 一处插值不一致):修复前
bun test packages/i18n/src/resources.test.ts --isolate = 5 pass / 3 fail(三条均为
Expected length: 1 / Received length: 2),修复后 = 8 pass / 0 fail;
bun test packages/i18n --isolate = 28 pass / 0 fail;
bun run i18n:check 在键对齐时输出 i18n:check passed ✅、exit 0;
bun run typecheck 五个工作区 exit 0。
现象
checkKeyParity()(packages/i18n/src/resources.ts:87-118)外层遍历全部 locale、内层再对flat与otherFlat各查一遍,于是同一处 en/zh 键差异会产出 2 条 issue;且extra分支的locale填的是当前
locale而不是缺键的那一侧,标签与事实相反。给 zh-CN 删掉
navigation.today(一处差异),跑真实脚本bun run i18n:check:第 2 行读作「zh-CN 多出一个键」,但该键其实存在于 en-US、缺失于 zh-CN——标签正好说反;
同时
failures计数被放大一倍(scripts/i18n-check.ts:57对每条 issue 累加一次)。期望
一处差异产出且仅产出一条 issue,且
locale指向有问题的那一侧。修复后同样的差异:判据(取自源码自述契约)
packages/i18n/src/locales/keys.ts:8-9:exact parity including extras is enforced by i18n:check at CIpackages/i18n/src/resources.ts:4:Both locales must stay key-identical — i18n:check enforces that in CIscripts/i18n-check.ts:8:key parity — every key in en-US exists in zh-CN and vice versascripts/i18n-check.ts:56-58:for (const issue of issues) fail(...)→failures就是 issue 条数,并直接出现在用户可见的
i18n:check FAILED — ${failures} issue(s)与assertKeyParity()抛出的I18N_KEY_PARITY: ${issues.length} issue(s)里影响
assertKeyParity的报错计数把 1 处差异说成 2 处,N issue(s)虚高一倍;[extra …]行的 locale 指向错误一侧,排查时会去看根本没有问题的那个 locale;assertKeyParity只打印前 8 条样本(resources.ts:125),双报会白白占掉一半样本额度;interpolation-mismatch,resources.ts:99-110)同样被双报,且两条的locale分别是两个 locale —— 同一对 key 看起来像是「两边都错」。
当前 main 上键完全对齐(
en-US 1487 keys ≡ zh-CN 1487 keys),checkKeyParity()恒返回空数组,所以该缺陷只在真正出现差异时暴露 —— 也就是 CI 变红、最需要读数准确的那一刻。
复现
建议修复方向
以
SUPPORTED_LOCALES_FOR_RESOURCES[0]为基准 locale(reference),只对其余 locale 单向比较,并把三类 issue 的
locale统一为「有问题的那一侧」:missing:该 locale 缺少 reference 里存在的键;extra:该 locale 多出 reference 里没有的键;interpolation-mismatch:该 locale 的{{var}}集合与 reference 不一致。这样
issues.length恰好等于差异处数,locale语义在三类之间保持一致,同时保留未来增加第三个locale 时的扩展性。
验证方式
bun test packages/i18n/src/resources.test.ts --isolate= 5 pass / 3 fail(三条均为Expected length: 1 / Received length: 2),修复后 = 8 pass / 0 fail;bun test packages/i18n --isolate= 28 pass / 0 fail;bun run i18n:check在键对齐时输出i18n:check passed ✅、exit 0;bun run typecheck五个工作区 exit 0。