Skip to content

fix(example-showcase): translate the app sidebar so a zh-CN session stops mixing locales - #9597

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-9260-showcase-nav-zh-translations
Aug 18, 2026
Merged

fix(example-showcase): translate the app sidebar so a zh-CN session stops mixing locales#9597
os-sam merged 1 commit into
mainfrom
claude/issue-9260-showcase-nav-zh-translations

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#9260

On a zh-CN session the showcase app rendered a mixed-locale shell: view tabs, list columns, field labels and option values in Chinese, the left sidebar in English throughout. The bundle carried no apps.* keys at all, so the sidebar had nothing to fall back on but its English source labels.

Why objects.* could not cover this

Navigation labels are their own key space — apps.APP_NAME.navigation.NAV_ID.label, addressed by the node's stable id regardless of tree depth. It is applied by translateApp (packages/spec/src/system/i18n-resolver.ts) at the /meta boundary in @objectstack/rest, and nothing under objects.* answers for it: a nav entry whose label was authored (Projects, not the bare showcase_project) keeps that authored label, so the object's translated pluralLabel never reaches the menu. That is why 12 object entries were already translated and all 12 still read English in the sidebar.

What changed

examples/app-showcase/src/system/translations/index.ts gains a zh-CN apps.showcase_app block:

surfacefoundtranslated
navigation items (leaves)4949
group headers (grp_*)88
app label (apps.showcase_app.label)11
separator nav_sep_reports1n/a — declares no label, so there is no key

The counts are measured, not eyeballed: os lint --json on the showcase config reported exactly 57 i18n/missing-navigation plus 1 i18n/missing-app, zh-CN only. The translated id set was diffed against that reported set — no missing ids, no extra ids. en needs no entries: the default locale is satisfied by the inline source label (i18n-coverage.ts), and restating it would be the fake translation this bundle's header already warns about.

The app's own label is included beyond the card's literal enumeration: it renders at the top of the same sidebar (useObjectLabel().appLabel), so leaving it English reproduces the mixed shell one line above the tree being fixed. Setup and Account translate theirs, Studio keeps its product name; this app is named for what it does, so it translates.

Wording reuses the vocabulary this bundle already established rather than minting a second word per idea — the object words for the object entries, 任务清单 / 工时按状态分布 / 活动时间线 / 工作地点地图 from showcase_task._views for the entries that open exactly those surfaces, 分流 from showcase_inquiry._views.triage, 排期 from showcase_task._sections, 业务单元 from the platform bundle. Each non-obvious choice carries its reason inline.

The ratchet

scripts/i18n-coverage-baseline.json: examples/app-showcase 451 to 393 (exactly the 58 keys above). check:i18n-coverage rejects an un-ratcheted improvement as well as a regression, and its own remedy is --update; the baseline moves down only, and no other config's entry moved.

The header sentence

The bundle's header declared a columns-only remit ("EVERY field surfaced as a column", "a list never mixes locales") — which is precisely why nothing caught this: the invariant was scoped narrower than the surface it reads as covering. One sentence is added describing what the file now contains, deliberately not widened into a guarantee that every declared surface of the showcase is translated (it is not — the frozen baseline still counts 393), and pointing at that baseline as the measure of what remains.

Verification

Run after the final commit, on the pushed tree (883f9a938):

  • node packages/cli/bin/run.js lint examples/app-showcase/objectstack.config.ts --jsoni18n/ issues 451 to 393; i18n/missing-navigation 57 to 0, i18n/missing-app 1 to 0, zero remaining keys under translations.zh-CN.apps.
  • node scripts/check-i18n-coverage.mjs — before the baseline update it went red on exactly this change ("untranslated declared strings improved 451 to 393 — ratchet DOWN"); after --update: OK (12 config(s), 602 baselined untranslated string(s), none new)
  • pnpm --filter @objectstack/example-showcase --filter @objectstack/lint test — 21 files / 334 tests and 74 files / 2088 tests, all passing (packages/lint imports this bundle in validate-translatable-sections.test.ts)
  • pnpm --filter @objectstack/example-showcase typecheck — clean
  • pnpm check:cross-package-test-inputs — OK (12 packages read outside themselves, all declared)
  • pnpm check:i18n — OK (9 packages, all bundles in sync). It does not cover this file: its population is packages/** directories owning a scripts/i18n-extract.config.ts, and no example has one. Run rather than assumed.
  • pnpm check:app-nav-i18n — OK (10 contributors, 53 merged setup nav ids, 4 locales). Also untouched by this diff, and run for the same reason: it computes its own population, so no path derivation can clear it.
  • node scripts/check-nul-bytes.mjs — OK (6160 files)

Gate union re-derived with node scripts/pm/dispatch-gates.mjs (no path arguments) against the committed diff: check:cross-package-test-inputs and check:i18n-coverage, both green above. The examples/** path pulls in check:cross-package-test-inputs — a packages/** card would not see it — because packages/lint declares this example file as a cross-package test input.

No changeset: nothing published changes (@objectstack/example-showcase is private: true; the other file is repo tooling). Using this repo's real mechanism for that — the skip-changeset label.


Generated by Claude Code

…tops mixing locales (#9260)
The showcase bundle carried no `apps.*` keys at all, so a zh-CN session
rendered translated view tabs, columns, field labels and option values under
an English sidebar. Nav labels are their own key space —
`apps.<app>.navigation.<id>.label`, applied by `translateApp` at the `/meta`
boundary — and nothing under `objects.*` answers for them: a nav entry whose
label was authored keeps that authored label, so the object's translated
`pluralLabel` never reaches the menu.
Adds the zh-CN `apps.showcase_app` block: 49 navigation items, 8 group
headers, and the app's own label. That is the exact set `os lint` reported
missing (57 `i18n/missing-navigation` + 1 `i18n/missing-app`, zh-CN only —
`en` is satisfied by the inline source labels). `nav_sep_reports` is a
separator with no label, so it has no key.
The example's frozen untranslated count drops 451 -> 393, which
`check:i18n-coverage` requires be ratcheted DOWN in the same change.
Wording reuses the vocabulary the bundle already established rather than
minting a second word per idea. The header comment gains one sentence: it
described a columns-only remit, which is exactly why the sidebar gap went
unnoticed — the new sentence states what the file now contains without
widening it into a guarantee about every declared surface.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qYPmkKEsfbWY1yVg83p8F
@os-samos-sam added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 18, 2026 — with Claude
@os-sam
os-sam marked this pull request as ready for review August 18, 2026 13:03
@os-sam
os-sam added this pull request to the merge queueAug 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 32140254326 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (3/3) — 失败步骤: Run this shard's tests

    �[41m�[1m FAIL �[22m�[49m src/error-envelope.conformance.test.ts�[2m > �[22m#8087 — every code the dispatcher door can emit is parsed against ApiErrorSchema�[2m > �[22mevery pending code is still unre
    

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 5 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 在其他 PR 的同类评论里搜同名测试;出现过 ⇒ flaky 实锤,开 issue 修/隔离那条测试。修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Merged via the queue into main with commit e8dba8aAug 18, 2026
27 checks passed
@os-sam
os-sam deleted the claude/issue-9260-showcase-nav-zh-translations branch August 18, 2026 13:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

app-showcase: app navigation labels have no zh-CN translations — sidebar stays English on a zh session while view tabs/columns are translated

2 participants

@os-sam@claude