Skip to content

ui: 诊断页金融提供方行把 {{list}} 占位符原样渲染,列表被拼在第二个冒号之后 #294

Description

@wxrbyte

问题与复现

设置 → 诊断页的「金融提供方(Financial providers)」区块,每个提供方卡片会渲染两行覆盖范围。这两行把 {{list}} 占位符原样打印给用户,并把真正的列表拼在自己的第二个冒号之后:

packages/ui/src/components/settings/DiagnosticsTab.tsx:210 / :213(行号基于 3a17eca6):

{t('diagnostics.markets')}: {provider.coverage.markets.join(', ') || '—'}
{t('diagnostics.capabilities')}: {provider.coverage.capabilities.join(', ') || '—'}

而这两个键的文案本身已经带了占位符与分隔符:

  • packages/i18n/src/locales/en-US/diagnostics.ts:40 = 'Markets: {{list}}',:41 = 'Capabilities: {{list}}'
  • packages/i18n/src/locales/zh-CN/diagnostics.ts:41 = '市场:{{list}}',:42 = '能力:{{list}}'

调用点没有传插值参数,i18next 因此保留原样占位符,再被调用点自己补一个 : + 列表。实测渲染结果(用一个 markets: ['US','HK']、capabilities: ['market.quote','market.kline'] 的提供方):

en-US: … Financial providers longbridge connected
       Markets: {{list}}: US, HK
       Capabilities: {{list}}: market.quote, market.kline

zh-CN: … 金融提供方 longbridge connected
       市场:{{list}}: US, HK
       能力:{{list}}: market.quote

覆盖范围为空时同样带着占位符:Markets: {{list}}: —。

根因是类型把修复路径堵死了:FinancialProviderRow 把 t 的 prop 类型收窄成 t: (key: string) => string,而父组件传进来的是真实的 TFunction(DiagnosticsTab.tsx:138 的 <FinancialProviderRow … t={t} />)。收窄后的签名不允许传第二个参数,于是调用点既没法传 { list },author 只能手工拼字符串 —— 缺陷被类型系统保护住了。

全仓枚举(不是孤例,也不是普遍问题)

用脚本把两件事对齐后枚举:

  1. 从 packages/i18n/src/locales/en-US/*.ts 抽出所有文案,得到 637 个键,其中 67 个含 {{…}} 占位符;
  2. 在 packages/{ui,shared,core,skill-hub,pi-runtime}/src + apps/electron/src 里正则找所有 t('字面量键')(不带第二个参数)的调用点。

结果:未传插值参数却命中占位符键的调用点全仓恰好 2 处,都在同一个函数里(就是上面两行);同一批键里正确传参的调用点有 61 处。这 61 处里就有 8 处在本文件内部:

DiagnosticsTab.tsx:239  diagnostics.summaryFolio
DiagnosticsTab.tsx:244  diagnostics.summaryAgentRuntime
DiagnosticsTab.tsx:248  diagnostics.summaryLlm
DiagnosticsTab.tsx:252  diagnostics.summaryBroker
DiagnosticsTab.tsx:258  diagnostics.summaryLongbridgeCli
DiagnosticsTab.tsx:259  diagnostics.summarySkills
DiagnosticsTab.tsx:263  diagnostics.summaryResources
DiagnosticsTab.tsx:267  diagnostics.summaryRecentErrors

也就是说同一份文件、同一种 {{name}} 写法,8 处对、2 处错 —— 这两行是漏网,不是口径分歧。(脚本里另有 31 行动态键 t(\…`)` 无法静态判定,本问题不涉及。)

预期与修复方向

把插值交给 i18next,并让类型允许这样做:

t: TFunction;                       // 原来是 t: (key: string) => string
…
{t('diagnostics.markets', { list: provider.coverage.markets.join(', ') || '—' })}
{t('diagnostics.capabilities', { list: provider.coverage.capabilities.join(', ') || '—' })}

删掉调用点自带的 : (分隔符已经写在文案里)。TFunction 从 i18next 导入,与仓库既有写法一致(components/today/TodayView.tsx:5,42 的 formatWhen(timestamp, t: TFunction))。不改任何 locale 文案、不新增键,i18n:check 的 1487 ≡ 1487 保持不变。

复现证据

  • 环境:Windows(10.0.26200.9457,x64)、Bun 1.4.2、main 3a17eca6(2026-10-03T16:16:35Z)。
  • 新增守卫 packages/ui/src/components/settings/DiagnosticsTab.test.tsx(3 个用例:en-US 插值、空覆盖的 — 回退、zh-CN 插值)。用真实资源包(createSyncI18n)渲染真实组件,只把 client.diagnostics.collect 换成固定 bundle。
  • 修复前(把 DiagnosticsTab.tsx 还原为 3a17eca6 的原始字节后重跑):
$ bun test packages/ui/src/components/settings/DiagnosticsTab.test.tsx
 0 pass / 3 fail

  Expected to contain: "Markets: US, HK"
  Expected to contain: "Markets: —"
  Expected to contain: "市场:US, HK"
  (实际渲染里出现 `Markets: {{list}}: US, HK`、`市场:{{list}}: US, HK`)

范围

Activity

  1. wxrbyte commented on Oct 6, 2026

    @wxrbyte
    ContributorAuthor

    /claim

  2. added
    claimedClaimed by a contributor and currently in progress
    on Oct 6, 2026
  3. github-actions commented on Oct 6, 2026

    @github-actions

    @wxrbyte 已认领该 Issue,并已自动设为 Assignee。提交 PR 时请在描述中写 Closes #294。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

claimedClaimed by a contributor and currently in progress

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions