Skip to content

fix(app-shell): name the language menu's built-in locales from the platform locale table - #31053

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-e104ip
Nov 6, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-e104ip

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread
Fixes #31045

domain:ui#2 · os-tesla · session_01L9qibzTyA5TRApD5ko4rtq

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:控制台语言菜单里内置语言的名称,改为直接读平台语言表 PLATFORM_LOCALES 每行的 label(如 简体中文、繁體中文、Português (Brasil)),删掉 LocaleSwitcher 里按旧代码 zh/pt 手写的第二份名称表。
  • 为什么改:feat(i18n)!: the Console's language packs follow the platform locale table, keyed by its tags, and Traditional Chinese (zh-Hant) ships #30902 把语言包改成按表标签(zh-Hans、zh-Hant、pt-BR)之后,手写表对不上这三个标签,它们的名称落到浏览器的 Intl.DisplayNames,随浏览器与 ICU 版本变化,缺 Intl.DisplayNames 的运行时直接显示裸代码 zh-Hans。
  • 风险与回滚:菜单提供哪些语言不变,只改名称来源。在 Node 22(ICU 78.3)和 Chromium 153 上,11 个名称改前改后逐字相同;变化只出现在 CLDR 不同或缺 Intl.DisplayNames 的运行时。回滚即 revert 本 PR。
  • 先看:ui/packages/app-shell/src/layout/LocaleSwitcher.tsx 的 BUILT_IN_LABEL_BY_TAG 与 localeLabel;LocaleSwitcher.appLocales.test.tsx 末尾新增的 describe。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/layout/__tests__/LocaleSwitcher.appLocales.test.tsx,预期 12 个测试全绿;把手写表放回去重跑,预期「without Intl.DisplayNames」一例转红,点名 zh-Hans。
  • 需要你做的:无,席位落地。
  • 席位意见:ui 席 2 对照 head fe26b46f 的 diff 复核:3 个文件、+87 / −39,无受治理路径。语言菜单的内置语言名改由平台语言表 PLATFORM_LOCALES 的 label 提供(如 简体中文、繁體中文、Português (Brasil)),删去按旧 zh/pt 代码手写的名单;表外代码仍走 Intl.DisplayNames,菜单提供哪些语言不变。新测试含「没有 Intl.DisplayNames」一档,恢复手写名单即红。CI 全绿,Clause-② no,不欠契约复审。同意落地。

What changes

  • localeLabel() names a platform locale table tag by its row's label. The lookup is a Map built from PLATFORM_LOCALES (tag → label), so a code like constructor or __proto__ still cannot pick up a label. BUILT_IN_LABELS, the hand list keyed by the old ten codes (zh, pt among them), is deleted; there is no second list of names.
  • Intl.DisplayNames stays the fallback for every code that is not a table tag (an app-shipped th, or a bare zh / pt an app might still list), with the same capitalization and the same degradation to the bare code.
  • The docblocks no longer call pt-BR an app-shipped locale and say where the built-in names come from.
  • @objectstack/spec was already a dependency of @object-ui/app-shell (useUserLocale.ts imports @objectstack/spec/system), so no manifest or lockfile change.
  • Root changeset .changeset/31045-locale-switcher-table-labels.md: @object-ui/app-shell patch.

The menu's option set is not touched (that is #30514, phase 4). #30514's body carries no separate "drop the dead zh / pt rows" line; the dead zh and pt label rows that PR #30902's acceptance notes pointed at ("Its hand-kept names are keyed zh and pt") are removed here, so nothing of that remains for #30514.

Tests

LocaleSwitcher.appLocales.test.tsx (the existing harness file, new top-level describe):

  • every PLATFORM_LOCALES row's label is what localeLabel(tag) returns;
  • the same with Intl.DisplayNames removed from the runtime (and an app code such as th still degrades to the bare code);
  • constructor and __proto__ answer the bare code.

The fixtures that modelled an app shipping the retired zh tag and expected the hand list's 中文 now ship zh-Hans and expect the table's 简体中文. The phantom-label comment no longer says pt-BR is named by Intl.DisplayNames.

Acceptance notes

  • Premise, measured. The structural defect holds on main@115a43ad: the three tags missed the hand list and were named by the runtime. The visible names did not differ on the two runtimes measured (Node v22.23.4 with ICU 78.3, Chromium 153.0.8010.12): there CLDR happens to give the table's strings after capitalization. They differ where Intl.DisplayNames is missing (bare zh-Hans, zh-Hant, pt-BR) and on any runtime whose CLDR names them otherwise. Table below.
  • Ablation direction. With the hand list restored, the runtime-Intl leg of the new pins stays green on Node 22 / ICU 78.3 (CLDR coincides), and the no-Intl.DisplayNames leg goes red. That leg is the one that proves the name comes from the table.
  • ProfilePage comment. ui/apps/console/src/pages/system/ProfilePage.tsx's nativeLanguageName docblock still mentions app-shell's "hand-kept BUILT_IN_LABELS map". It is outside this card's file surface and is left alone; the profile page's language card names zh-Hans, zh-Hant and pt-BR through Intl.DisplayNames, the same runtime dependence this PR removes from the menu, because localeLabel is not on the @object-ui/app-shell entry. Noted for the seat; not filed.

Verification

Before / after label table (Node v22.23.4, ICU 78.3; base 115a43a, head fe26b46)
tag table label before before, no Intl.DisplayNames after after, no Intl.DisplayNames
en English English English English English
zh-Hans 简体中文 简体中文 zh-Hans 简体中文 简体中文
zh-Hant 繁體中文 繁體中文 zh-Hant 繁體中文 繁體中文
es Español Español Español Español Español
ar العربية العربية العربية العربية العربية
fr Français Français Français Français Français
pt-BR Português (Brasil) Português (Brasil) pt-BR Português (Brasil) Português (Brasil)
ru Русский Русский Русский Русский Русский
de Deutsch Deutsch Deutsch Deutsch Deutsch
ja 日本語 日本語 日本語 日本語 日本語
ko 한국어 한국어 한국어 한국어 한국어

Off-table codes, before → after: zh 中文 → 中文, pt Português → Português, th ไทย → ไทย (all through Intl.DisplayNames after).

Chromium 153.0.8010.12 raw Intl.DisplayNames([tag]).of(tag): zh-Hans 简体中文, zh-Hant 繁體中文, pt-BR português (Brasil) (capitalized by localeLabel).

Ablation (head fe26b46)

node scripts/ablation-replace.mjs replaced the PLATFORM_LOCALES.map(...) initializer with the deleted ten-row hand list (anchor 1 → 0, blob dfc65564 → 7b64fa58) and ran the appLocales file: 1 failed / 11 passed, zh-Hans: expected 'zh-Hans' to be '简体中文' in "names every table tag with its row label on a runtime without Intl.DisplayNames". Restored: blob == HEAD, git diff HEAD empty. The test imports the component by relative source path, so no build leg applies.

Suites, type-check and gates (head fe26b46)
  • pnpm --dir ui exec vitest run --maxWorkers=2 packages/app-shell/src/layout/ packages/app-shell/src/hooks/__tests__/userLocale-10059.test.tsx: 141 files, 938 tests passed.
  • pnpm --filter @object-ui/app-shell run type-check: exit 0.
  • eslint on the two changed sources: exit 0.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 36 derived families, all exit 0, including node scripts/check-sdui-manifest.mjs, pnpm check:undeclared-dep-imports, pnpm check:test-source-alias and pnpm --dir ui exec vitest run scripts/ (170 files passed, 2 skipped). --ran: 36 derived, 36 run, 0 NOT-MEASURED, 0 UNRUN.
  • Change set: 3 files, +87 / -39.

LocaleSwitcher's localeLabel() read a hand-kept name list keyed by the
retired zh and pt pack tags, so zh-Hans, zh-Hant and pt-BR fell through to
Intl.DisplayNames. Index PLATFORM_LOCALES by tag instead and pin that every
row's label is what localeLabel(tag) returns, with and without
Intl.DisplayNames.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzTyA5TRApD5ko4rtq
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 6, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-tesla · session_01L9qibw7DekEe3wx5f3zphf

席位核对 · ACCEPT · 2026-11-06T22:09Z · head fe26b46f9016f8a18fba140ade0e540df9be4c04

Seat domain:ui#2 reviewed the diff of its own dispatch (claim 6695503312 on #31045, card thread session_01L9qibzTyA5TRApD5ko4rtq, report 6696842751). The same seat dispatched and reviews; the code is the thread's.

  1. Shape: 3 files, +87 / -39. BUILT_IN_LABELS is deleted. localeLabel() now reads a ReadonlyMap built from the PLATFORM_LOCALES rows (tag → label), and is still prototype-safe. Codes off the table still go through Intl.DisplayNames, with capitalisation and the bare-code fallback unchanged. The menu's option set is untouched; that remains i18n phase 4 (locale menu): the Console language menu offers the PLATFORM_LOCALES languages, intersected with an app’s supportedLocales #30514. @objectstack/spec was already an app-shell dependency. Fixes #31045.
  2. Premise measured: structurally true on main. The three renamed tags missed the hand list. On Node 22 / ICU 78.3 and Chromium 153 the CLDR names already matched the table, so the visible divergence shows only without Intl.DisplayNames (bare zh-Hans / zh-Hant / pt-BR). The before/after table is in the PR body. The new pin has a no-Intl.DisplayNames leg. Restoring the hand list reds that leg (1 failed / 11 passed).
  3. Gates / check-runs: 55 check runs on this head, 44 success, 11 skipped, 0 red, including Test UI 16/16, Type Check UI and both E2E UI legs. 36 derived gates exit 0, and --ran reconciles (0 NOT-MEASURED). The app-shell type-check is clean, and the layout suite passes 141 files and 938 tests.
  4. Accepted deviation: none.
  5. Out of scope / Skills-docs: the nativeLanguageName docblock in ProfilePage.tsx still names BUILT_IN_LABELS. It's a comment only, so nothing is filed; the next edit of that file fixes it. Metadata-protocol: none; Skills-docs: not owed.
  6. Landing: check-governed-merges --pr 31053 on the final list: not governed, 126 changed lines, so Tier S, with no human approval owed. Clause-②: no, and no packages/spec/src file is touched, so no contract review is owed. Landing on the maintainer's standing words 「按 pm-dispatch 审过的pr你可以自主落地」 (2026-10-30T14:20Z) and 「你应该是使用 pm-dispatch 技能自主审核和落地 pr」 (2026-11-06T14:09Z): relay pr_ready, then automerge_enable. On merge the card closes.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 6, 2026 22:11
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit b5106af Nov 6, 2026
60 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-e104ip branch November 6, 2026 22:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

app-shell LocaleSwitcher still labels built-in languages from a hand list keyed by the retired zh / pt codes

1 participant