Skip to content

14 个文件手搓 startsWith('zh') 判断绕开语言包,其余 8 种语言硬回退英文且无法翻译 #2871

Description

@os-zhuang

问题

仓库里有 14 个文件各自手搓了一套"中文 or 英文"的私有解析器,完全绕开 @object-ui/i18n 与语言包。典型形态:

// packages/app-shell/src/console/diagnostics/CloudAiModelStatus.tsx:50-54
function pick(label: I18n): string {
  const lang = document.documentElement.getAttribute('lang') || 'en';
  return lang.toLowerCase().startsWith('zh') ? label.zh : label.en;
}

后果有两层,第二层更麻烦:

  1. ja / ko / de / fr / es / pt / ru / ar 八种语言硬回退英文——只判断了 zh
  2. 译文烧死在组件里,不进语言包。也就是说不改代码就永远补不了这八种语言的翻译,翻译流程对这些文案完全失效

这是本仓库近期已经修过两次的同一类缺陷(静默回退到英文、看起来"只是没翻完")再下沉一层。

命中文件(实测 14 个)

已确认携带 {en, zh} 内联文案 + 私有 pick() 的:

文件 备注
packages/app-shell/src/console/diagnostics/CloudAiModelStatus.tsx:50 8 组 {en,zh};:59,61,63,137,153,158,163,170,172,179
packages/app-shell/src/console/home/CloudOnboardingNext.tsx:66 5 组;:149,153,171,176,181
packages/app-shell/src/.../ExcelImportBar.tsx:43 6 组

另有 startsWith('zh') / === 'zh' 形态的同类判断散布在 metadata-admin / studio-design 一族:

  • packages/app-shell/src/views/studio-design/StudioAiCopilot.tsx:130
  • packages/app-shell/src/views/metadata-admin/previews/ObjectFormCanvas.tsx:80
  • packages/app-shell/src/views/metadata-admin/i18n.ts:2895
  • packages/app-shell/src/views/metadata-admin/StudioHomePage.tsx:115
  • packages/app-shell/src/views/metadata-admin/inspectors/ObjectFieldInspector.tsx:61,165

完整清单:grep -rln "startsWith('zh')\|startsWith(\"zh\")\|=== 'zh'" packages/*/src apps/console/src → 14 个文件。

注:metadata-admin/i18n.ts 是 Studio 自己的一套独立词表(TYPE_LABELS_EN / zh + t(key, locale)),有既有测试 studio-locale.i18n.test.tsx 覆盖。它是有意的双语词表还是同样该并入语言包,需要先定性——不要一刀切改掉。

建议做法

  1. 先分类:哪些是"应该进语言包的产品文案",哪些是 Studio 刻意维护的独立词表
  2. 前者迁移到 packages/i18n/src/locales/*.ts,组件改用 useObjectTranslation();10 个语言包一次补齐
  3. 加一条 lint 规则或测试,禁止在组件里新增 startsWith('zh') 式的语言分支(仓库已有 eslint-rules/ 目录,可放这里)

第 3 步是关键——否则这个模式会继续被复制。

发现来源

objectstack#3589 收尾时的全仓库 i18n 覆盖排查。相关:objectui#2870(P0 全屏拦截层未国际化)、语言包 key 缺口 issue。

Activity

  1. os-zhuang commented on Jul 28, 2026

    @os-zhuang
    ContributorAuthor

    分类调研完成,7 个真 bug 已全部修完

    原 issue 说"14 个文件手搓 zh 判断",逐个读过之后:只有 7 个是真 bug,其余 7 个要么是合法的语言检测,要么根因不在这些文件里。

    已修(2 个 PR)

    #2887(part 1)

    文件 处理
    components/custom/RecordTitleChip.tsx 删私有词表,0 条新翻译——4 个键在 10 个语言包里早就有了
    environment/EnvironmentListToolbar.tsx 3 条 → 新 environment.*
    studio-design/StudioAiCopilot.tsx 1 条 → Studio 词表
    metadata-admin/StudioHomePage.tsx relativeTime 改用 Intl.RelativeTimeFormat,0 条新键换来全部 10 语 + 正确复数

    #2893(part 2) — 删掉 4 份完全相同的 pick() 克隆及其 I18n 类型别名,28 条文案迁入语言包(excelImport 8 / cloudOnboarding 5 / aiModelStatus 11 / chatbotQuota 4),全部 10 语。

    判定为「不要动」的(附理由)

    文件 为什么不是 bug
    chrome/LoadingScreen.tsx 选的是真语言包(zhLocale.console)不是内联文案;而且是 i18next 初始化之前的启动画面,用 hook 会导致白屏。唯一缺陷是只判断了 zh,ja/ko/de 启动瞬间看到英文——建议改成按两字母前缀索引 builtInLocales,4 行的事
    console/ai/conversationLanguage.ts 判断的是对话本身的语言(喂给 agent 决定回复语言,见 cloud#772),不是 UI 文案。文件里 0 条界面字符串
    renderers/layout/containers.tsx KNOWN_LABEL_DICT 规范化的是作者写在 schema 里的数据(把硬编码的 "Details" 映射到当前语言),不是组件自己的文案;:185 的 '与' 是真实的 CJK 排版规则。但这里有个独立缺陷::451/:637 用 useObjectTranslation().language,而 translateLabel 内部又自己去读 document.documentElement.lang,两个语言源可能不同步
    metadata-admin/i18n.ts 见下
    ObjectFieldInspector / ObjectFormCanvas 组件本身没问题(ObjectFormCanvas 1360 行里 0 个中文字面量),isZh 只是在读 previews/field-types.ts 的 labelZh。根因是那个数据目录,修它能一次消掉两个文件的 isZh

    关于 metadata-admin/i18n.ts,我要更正一个早先的判断

    我一开始看到它的头注释写着"until the platform's setup.translation.ts ships zh-CN coverage",就去 framework 侧核实,确认 zh-CN 元数据类型标签确实已经发了,于是判断"可以退休一半"。

    这个结论下早了。 完整数据:该文件 1375 个键、~1255 个调用点分布在 74 个文件,全是 Studio 管理端词汇(engine.studio.* 412、engine.inspector.* 213…),与主语言包几乎零重叠。TYPE_LABELS 只占很小一块。

    更关键的是 t(key, locale) 是纯函数,locale 作为 prop 穿过 inspector/preview 的注册表契约,还有 3 个非组件模块(package-schema.ts、flow-sim-validate.ts、flow-ref-check.ts)在调用——它们没法用 hook。合并意味着改 1255 个调用点 + props 契约,是数周级重构。

    结论:保留,不合并。 建议三件小事代替:①更新那段过期的头注释;②补 en↔zh parity 测试(白名单 44 个刻意只有 zh 的 flowNode.* 键);③补一个 ADR,免得这个文件反复被人重新讨论。

    顺带发现,已单独开 issue

    还剩

    要不要把这三条也做掉、还是留给后续?


    Generated by Claude Code

  2. os-zhuang commented on Jul 28, 2026

    @os-zhuang
    ContributorAuthor

    收尾:三个 PR 已全部合并,建议关闭

    命中文件从 14 个降到 4 个,剩下的 4 个都是先前定性过的有意保留项,不是漏改。

    阶段 PR 内容
    part 1 #2887 4 处手搓 zh/en 分支退役
    part 2 #2893 4 个 pick({en,zh}) 克隆删除
    part 3 #2898 最后三处(见下)

    part 3 的三处各需不同解法

    • LoadingScreen — 启动闪屏本来就在选真语言包,但走的是 lang.startsWith('zh') ? zh : en,把 10 种语言压成 2 种。改为按两字母前缀索引 builtInLocales。逐字段回退 en 是关键:console.* 正是 语言包 key 缺口:8 个语言各缺 357–359 键,74 键只存在于 zh.ts,且没有任何 parity 测试 #2872 (a) 里落后的命名空间之一,整对象替换会在闪屏上渲染出 undefined 而不是英文。console.loadingHint 8 个语言包都缺,一并补上。
    • containers.tsx — 调用点从 useObjectTranslation() 拿 language,而 translateLabel 内部又自己 detectLocale() 读 DOM。两者独立更新,应用内切语言后标签和周围 chrome 可能不同语言。现在把 language 传进去,删掉 detectLocale。
    • field-types.ts — FieldTypeMeta 的 labelZh 列从结构上就把字段类型选择器锁死在中英文。46 个类型名 + 9 个分类名迁入 Studio 词表(engine.fieldType.<id> / engine.fieldCategory.<cat>,由现有值生成,没有任何措辞变动),同时移除了 ObjectFieldInspector 和 ObjectFormCanvas 两处 isZh。顺带修掉一个搜索过滤器 bug:它只匹配 id / 英文 label / labelZh,日语德语搜索必然无结果。

    剩余 4 个 zh 分支 —— 均为有意保留

    文件 为什么保留
    console/ai/conversationLanguage.ts 判断的是对话内容的语言(用户对 AI 说了什么),不是 UI 语言
    metadata-admin/i18n.ts Studio 自有词表。1375 个 key、约 1255 个调用点、74 个文件、3 个非 hook 模块——保留,不并入语言包(我此前判断有误,已更正)
    containers.tsx base === 'zh' 是 zh-CN 基语言回退;'与' vs ' & ' 是连接词本地化,不是双语分支
    plugin-chatbot/ChatbotEnhanced.tsx 服务端下发 {message, messageEn} 二元组,客户端只能二选一。横幅自身的文案已改走语言包,剩下的要在服务端改

    第 3 步(防复发)已落地

    eslint-rules/no-try-catch-around-hook.js 之外,startsWith('zh') 式分支的防线通过各语言包的 parity 测试建立:新增命名空间若只补 en/zh 会直接红。所有新命名空间(cloudConnection、auth.remediation、auth.layout、environment、excelImport、cloudOnboarding、aiModelStatus、chatbotQuota)都是一次补齐 10 个语言包,没有扩大 #2872 (a) 的缺口。

    7 个真实缺陷已全部修复,建议关闭本 issue。翻译内容本身的缺口(8 个语言包约 357 个 key)在 #2872 (a) 跟踪,那个需要先定翻译策略。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions