Skip to content

ui: 设置→评估页 10 处错误提示显示 IPC 原始英文 message,而按 code 出本地化文案的 errors.* 机制全仓无人调用 #296

Description

@wxrbyte

问题与复现

设置 → 评估(Evaluation)页的失败提示,把这 10 处都写成「优先用 IPC 信封里的原始 message」:

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

:90   setLoadError(result?.error.message ?? t('settings.evaluation.failedToLoad'));
:118  setSaveError(result?.error.message ?? t('settings.evaluation.couldNotUpdateTracing'));
:137  setSaveError(result?.error.message ?? t('settings.evaluation.couldNotSaveSettings'));
:161  setTestResult({ ok: false, message: result?.error.message ?? t('settings.evaluation.connectionTestFailed') });
:181  setKeyError(result?.error.message ?? t('settings.evaluation.couldNotSaveApiKey'));
:194  setKeyError(result?.error.message ?? t('settings.evaluation.couldNotRemoveApiKey'));
:207  setSaveError(result?.error.message ?? t('settings.evaluation.couldNotUpdateTracing'));
:228  message: result?.error.message ?? t('settings.evaluation.connectionTestFailed'),
:251  setLangfuseKeyError(result?.error.message ?? t('settings.evaluation.couldNotSaveApiKey'));
:265  setLangfuseKeyError(result?.error.message ?? t('settings.evaluation.couldNotRemoveApiKey'));

因为 error.message 通常都在,?? t(...) 那一侧几乎永远不生效 —— 本地化文案被架空。实测(把 evaluation.getSettings 桩成返回 { ok:false, error:{ code:'STORAGE_WRITE_FAILED', message:'STORAGE_WRITE_FAILED: connection is locked' } }、界面语言 zh-CN):

Expected to contain: "无法保存更改"                                    ← errors.storageWriteFailed 的 zh-CN 文案
Received:            "STORAGE_WRITE_FAILED: connection is locked"      ← IPC 原始英文,连 code 一起显示给用户

机制现状:errors.* 这套按 code 出文案的设计全仓没有任何调用方

仓库其实已经为「按稳定 code 出本地化文案」建好了完整机制:

  • packages/i18n/src/locales/{en-US,zh-CN}/errors.ts:34 条用户可见错误文案,docstring 写明
    「User-facing error copy keyed by stable ApiError.code … Resolution: translateErrorCode(code) → this table」;
  • packages/i18n/src/error-messages.ts:ERROR_CODE_TO_KEY(33 条 code → key 静态表)+ errorKeyForCode + translateErrorCode,
    docstring 写明「falls back to a sanitized technical message when no code matches — raw stack traces are never shown to users」。

但全仓检索(packages / apps / docs / scripts,含测试)后,translateErrorCode / errorKeyForCode /
ERROR_CODE_TO_KEY 的引用只有三处,全部在机制自己内部:定义本身、它自己的单测 packages/i18n/src/i18n.test.ts、
以及 barrel packages/i18n/src/index.ts 的导出。没有任何生产代码调用它。

顺带两处文件自述与实际不符:

  1. error-messages.ts 的 docstring 说「translateErrorMessage looks up the mapping」,但该文件里没有这个函数(只有 translateErrorCode);
  2. errors.* 里 34 条文案中,只有 ErrorBoundary.tsx 直接用了 sectionUnexpected / sectionUnexpectedDetail /
    sectionOpenDiagnostics 三条,其余全部只被那个无人调用的解析器引用。

量级对齐(脚本枚举,packages / apps / scripts 下所有 code: 'SCREAMING_SNAKE'、
createCodeError('…')、code = '…' 字面量):

项 数量
全仓发出的 SCREAMING_SNAKE 形态 code 99
ERROR_CODE_TO_KEY 条目 33
表中覆盖到的已发出 code 31
表中有、但全仓从未发出 2(STORAGE_READ_FAILED、SYNTHESIS_TIMEOUT)

即:「按 code 出本地化文案」这条设计路径,从表到解析器都齐备,却没有任何一个渲染面接上它;而 UI 侧大量页面
(不止本页)直接渲染 result.error.message。本 issue 只处理本页这 10 处。

预期与修复方向

让本页的失败提示按「注册过的 code → 本地化文案;未注册的 code → 保留 sanitized message;两者都没有 → 页面自己的兜底文案」
解析,即把已经存在但没人调用的 translateErrorCode 真正接上:

function errorCopy(t: TFunction, error: ApiError | undefined, fallbackKey: string): string {
  return translateErrorCode(error?.code, t) ?? error?.message ?? t(fallbackKey);
}
  • 10 处调用点改为 errorCopy(t, result?.error, '<原有的 settings.evaluation.* 键>');
  • 不删除未注册 code 的原始 message(那是有用的诊断信息),只是不再让它抢占已注册 code 的本地化文案;
  • 零新增 i18n 键(errors.* 与 settings.evaluation.* 都已存在),i18n:check 的 1487 ≡ 1487 不变。

复现证据

  • 环境:Windows(10.0.26200.9457,x64)、Bun 1.4.2、main 3a17eca6。
  • 在既有 packages/ui/src/components/evaluation/evaluation.test.tsx 追加 2 个用例(用 client.prefs.get 把界面语言钉成 zh-CN,
    再把 evaluation.getSettings 桩成失败):
    • 「注册过的 code 走 errors.* 本地化文案」;
    • 「未注册的 code 仍保留原始 message」(防止修复过度)。
  • 修复前:该文件 6 pass / 1 fail,唯一失败即上文的 Expected "无法保存更改" / Received "STORAGE_WRITE_FAILED: connection is locked"
    (另一个新用例本来就通过,说明现有实现「保留原始 message」这一点是对的,缺的只是「已注册 code 没被解析」)。
  • 修复后:该文件 7 pass / 0 fail。
  • 工作区回归:bun test packages/ui --isolate = 344 pass / 0 fail(63 个文件);其中 2 个是本 PR 新增的守卫用例,
    其余为既有用例且全绿(无既有失败项)。
  • tsc --noEmit(packages/ui)exit 0。
  • 上游 PR checks(.github/workflows/pr.yml)当前 state=disabled_manually,新建 PR 不会产生 check run,
    故以上均为本地等价命令结果,非 CI 结论。

范围

No activity

Activity on this issue will appear here.

Activity

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