现象
告警列表与自动化规则列表各有一个「状态筛选」下拉框,三个选项里有两个走译文、一个写死英文 ,于是 zh-CN 界面下的下拉框是中英混排:
┌─────────────┐
│ All │ ← 写死的字面量
│ 生效中 │ ← t('alerts.status.active')
│ 已暂停 │ ← t('alerts.status.paused')
└─────────────┘
两处(同一个模板、同一个根因):
文件
行
同一 <select> 里已本地化的兄弟选项
packages/ui/src/components/alert/AlertList.tsx
90
t('alerts.status.active')、t('alerts.status.paused')
packages/ui/src/components/automation/AutomationRulesView.tsx
94
t('automation.status.active')、t('automation.status.paused')
< option value = "all" > All</ option > { /* 写死 */ }
< option value = "active" > { t ( 'alerts.status.active' ) } </ option > { /* 已本地化 */ }
< option value = "paused" > { t ( 'alerts.status.paused' ) } </ option > { /* 已本地化 */ }
这不是「暂时没翻译」,而是漏用了已经存在的键 :两个语言的 common 命名空间里早就有这一条 ——
// packages/i18n/src/locales/en-US/common.ts:33
all : 'All' ,
// packages/i18n/src/locales/zh-CN/common.ts:36
all : '全部' ,
复现
新增两个断言文件(各 1 例),用 withI18n(..., 'zh-CN') 以真实 zh-CN 资源 渲染,断言下拉框第一项取自 common.all:
const zh = createSyncI18n ( { locale : 'zh-CN' } ) ;
const expected = zh . t ( 'common.all' ) ;
expect ( expected ) . toBe ( '全部' ) ; // 钉住资源值,避免键被搬走后断言静默通过
...
expect ( option ?. textContent ) . toBe ( expected ) ;
在未修复 的 main 上运行:
(fail) AlertList.test.tsx > renders the status filter "all" option from the shared locale key
Expected: "全部" Received: "All"
(fail) AutomationRulesView.statusFilter.test.tsx > renders the status filter "all" option from the shared locale key
Expected: "全部" Received: "All"
0 pass / 2 fail
根因
issue #86 「Unify currency formatting onto the i18n locale policy」把货币格式化 收敛到了 lib/money.ts → @finagent/i18n,那一轮的迁移遗漏是格式化通路 (同一份数值用不同方式渲染,见 #258 / #278 )。本条是另一类遗漏 :界面文案里残留了字面量,而对应的键在 common 命名空间里早已存在 —— 即「漏用键」,不是「漏迁通路」。
证据是同一个 <select> 内的兄弟选项全部走了 t(...),且 t('common.<key>') 在本仓库是既有约定 (packages/ui/src/components 下 20 余处),其中 packages/ui/src/components/alert/AlertForm.tsx:216 就在 AlertList 的同目录 :
也就是说:同目录、同命名空间、同一种键的用法就在旁边,只有这两行的下拉框第一项被漏掉。
本仓库自带的扫描脚本也独立指向了这两行。bun scripts/i18n-audit.ts 报出 6 个候选,除 4 个品牌名(Folio / Folio Research,属合法不翻译)外,剩下的正是这两处 All :
i18n-audit: scanned 185 files, 6 candidate literals (6 suspicious)
packages\ui\src\components\alert\AlertList.tsx:90 [jsx-text] "All"
packages\ui\src\components\automation\AutomationRulesView.tsx:94 [jsx-text] "All"
修复方案
两行,直接用既有键:
- <option value="all">All</option>
+ <option value="all">{t('common.all')}</option>
两个组件都用裸 useTranslation() (不带命名空间),因此 t('common.all') 直接命中扁平键空间,无需新增 key、无需改 locale 文件、无需改组件签名。
影响面
不新增/不修改任何 i18n 键 :bun run i18n:check 修复前后均为 en-US 1487 keys ≡ zh-CN 1487 keys (0 issues)。
改动面:2 个源文件各 1 行,共 2 行。
测试:新增两个独立文件 ,刻意不 改既有的 AutomationRulesView.test.tsx —— 该文件正被 test(automation): RuleCard 补齐 material_update 与 incomplete 状态展示测试 #194 在尾部追加(@@ -154,6 +154,68 @@),避开可减少合并冲突。AlertList.tsx 此前没有 任何测试文件,本次顺带补上。
未纳入:i18n-audit 的其余 4 个候选(Folio / Folio Research)是品牌名,按该脚本自己的说明属「合法不翻译」,不是缺陷。
验证
TDD 顺序:先写测试 → 未修复代码上 0 pass / 2 fail → 修复 → 2 pass / 0 fail。
# 修复前
(fail) ... Expected: "全部" Received: "All" ×2
0 pass / 2 fail
# 修复后
2 pass / 0 fail / 4 expect() calls
回归对照(全部为 main 上可复现的命令):
命令
修复前
修复后
本 PR 新增的两个断言文件
0 pass / 2 fail
2 pass / 0 fail
bun test packages/ui/src/components/automation/AutomationRulesView.test.tsx --isolate
5 pass / 0 fail
5 pass / 0 fail
bun run i18n:check
passed(1487 ≡ 1487)
passed(1487 ≡ 1487)
bun scripts/i18n-audit.ts
6 候选(含这两处 All)
4 候选 (仅剩品牌名)
bun run typecheck
5/5 exit 0
5/5 exit 0
现象
告警列表与自动化规则列表各有一个「状态筛选」下拉框,三个选项里有两个走译文、一个写死英文,于是 zh-CN 界面下的下拉框是中英混排:
两处(同一个模板、同一个根因):
<select>里已本地化的兄弟选项packages/ui/src/components/alert/AlertList.tsxt('alerts.status.active')、t('alerts.status.paused')packages/ui/src/components/automation/AutomationRulesView.tsxt('automation.status.active')、t('automation.status.paused')这不是「暂时没翻译」,而是漏用了已经存在的键:两个语言的
common命名空间里早就有这一条 ——复现
新增两个断言文件(各 1 例),用
withI18n(..., 'zh-CN')以真实 zh-CN 资源渲染,断言下拉框第一项取自common.all:在未修复的
main上运行:根因
issue #86「Unify currency formatting onto the i18n locale policy」把货币格式化收敛到了
lib/money.ts→@finagent/i18n,那一轮的迁移遗漏是格式化通路(同一份数值用不同方式渲染,见 #258 / #278)。本条是另一类遗漏:界面文案里残留了字面量,而对应的键在common命名空间里早已存在 —— 即「漏用键」,不是「漏迁通路」。证据是同一个
<select>内的兄弟选项全部走了t(...),且t('common.<key>')在本仓库是既有约定(packages/ui/src/components下 20 余处),其中packages/ui/src/components/alert/AlertForm.tsx:216就在 AlertList 的同目录:也就是说:同目录、同命名空间、同一种键的用法就在旁边,只有这两行的下拉框第一项被漏掉。
本仓库自带的扫描脚本也独立指向了这两行。
bun scripts/i18n-audit.ts报出 6 个候选,除 4 个品牌名(Folio/Folio Research,属合法不翻译)外,剩下的正是这两处All:修复方案
两行,直接用既有键:
两个组件都用裸
useTranslation()(不带命名空间),因此t('common.all')直接命中扁平键空间,无需新增 key、无需改 locale 文件、无需改组件签名。影响面
bun run i18n:check修复前后均为en-US 1487 keys ≡ zh-CN 1487 keys (0 issues)。AutomationRulesView.test.tsx—— 该文件正被 test(automation): RuleCard 补齐 material_update 与 incomplete 状态展示测试 #194 在尾部追加(@@ -154,6 +154,68 @@),避开可减少合并冲突。AlertList.tsx此前没有任何测试文件,本次顺带补上。i18n-audit的其余 4 个候选(Folio/Folio Research)是品牌名,按该脚本自己的说明属「合法不翻译」,不是缺陷。验证
TDD 顺序:先写测试 → 未修复代码上
0 pass / 2 fail→ 修复 →2 pass / 0 fail。回归对照(全部为 main 上可复现的命令):
bun test packages/ui/src/components/automation/AutomationRulesView.test.tsx --isolatebun run i18n:checkbun scripts/i18n-audit.tsAll)bun run typecheck