Repository navigation
fix(layout): a present navigation label renders verbatim, with no translate-if-equal-to-name exception, and an inline locale-map label renders its text (objectui#11201) - #11297
Conversation
…ranslate-if-equal-to-name exception, and an inline locale-map label renders its text (objectui#11201, stage 2) resolveNavItemLabel follows the spec's one order: the id-keyed bundle entry (translateApp, upstream at /meta), then the present label (an inline locale map's entry, else its text), then, for an absent label, the target's current localized label. The isCustomized arm, which looked a present plain-string label up through the object / view / dashboard resolvers when its text equalled the target's machine name, is deleted with its comment. The three convention resolvers are still accepted and no longer read. A present map-valued label used to reach the keyed resolver and render empty. It now reads through the spec's resolveI18nLabel, at its no-locale default (the en entry, then default, then any), the floor the area switcher uses. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Inputs: card #11201 (body and all 12 comments; the director's ruling Check-runs on the head: 43 — 40 success, 3 skipped, 0 failure, 0 in progress (polled 00:37Z to 00:45Z; the 8 test shards, Type Check, Lint and Spec Main Shape Gate all completed success). The gate verdicts are green. ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #11201
Clause-②: no
Stage 2 of ruling B: the director's ruling
5921167691, on the ruling record5910404448and its execution note5910537969. Claim5921933038, branchclaude/issue-11201-nav-label-verbatim. Stage 1 landed as PR #11225 (cb2f6fb5b).What changes
resolveNavItemLabelinpackages/layout/src/NavigationRenderer.tsxnow follows the spec's one order and keeps no rule of its own:apps.APP.navigation.ID.label.translateAppapplies it upstream, at/meta. Untouched here.isCustomizedarm and its comment. The arm looked a present plain-string label up through the object, view or dashboard resolver when its text equalled the target's machine name (trimmed, case-insensitive). Nothing onlabelmatches text against a name now.{ en: 'Accounts', 'zh-CN': '客户' }, which the spec accepts) now reads through the spec'sresolveI18nLabel. Areas and the designer canvas already use that resolver. The label used to reach the keyed resolver and render empty.resolveObjectLabel,resolveDashboardLabelandresolveViewLabel(the props, and the matching positional arguments). They are marked@deprecated, so callers compile and get the ruled behaviour. Retiring them is open question 2 in the report.packages/i18n/src/useObjectLabel.ts: the one comment that named the guard now says the guard is gone. Comment only.@object-ui/layoutpatch. It states the visible effect and the one-line fix: clear the entry's label so it inherits, or set the wanted text. No conversion, no notice run.Mechanism finding: which locale a map label reads
The ruling's map pin says "renders the locale's value". This layer is not given a locale.
NavigationRendererhas no locale prop, and@object-ui/layouthas no i18n dependency by design (see the docblock ofresolveAreaLabelinAppSchemaRenderer.tsx). Within the ruled parameters (patch,Clause-②: no, no public prop change), the map resolves at the spec resolver's documented no-locale default: itsenentry, elsedefault, else any entry. The area switcher in this package does the same. So in the console, a zh-CN viewer sees theenentry of a map-valued nav label.Reaching the viewer's locale needs a new optional prop, with the locale threaded from the console (
UnifiedSidebaralready holdslanguage). That widens the public surface, so it is not built here. It is open question 1 in the report.Measured pull: no app navigation in readable sources writes a map-valued entry label. This is the objectstack pointer
5913605784, re-read at objectstack5f6b63a6f: in the files that declarenavigation, the only object-valued labels are in translation bundles.Pins
New file
packages/layout/src/__tests__/NavigationRenderer.labelVerbatim-11201.test.tsx:account, viewboard, dashboardsales_overview) renders verbatim underenand underzh-CN. The console's convention resolvers are wired asUnifiedSidebarwires them, and none is called. Case and padding variants (ACCOUNT,SALES_OVERVIEW) stay as written. Control in the same tree: an absent label inherits the target's localized label in each locale, which shows the wiring is live.[object Object], empty text or the target's label. Search finds them by that text.Restated rows in
NavigationRenderer.navLabelOwnership.test.tsx(they pinned the retired convention):still resolves an un-customized object label through resolveObjectLabelis nowrenders a present object label equal to the machine name verbatim, resolveObjectLabel unasked (objectui#11201).keeps the same guard on the dashboard branchis nowrenders both present dashboard labels verbatim, resolveDashboardLabel unasked (objectui#11201).still lets an authored object label win over resolveObjectLabel: the assertion is unchanged. Its comment no longer names the guard.Unchanged and green:
NavigationRenderer.labelInheritsTarget-9868.test.tsx(absent labels inherit, and an explicit label equal to the machine name stays verbatim with echo resolvers), and the stage-1 pinuseNavigationSync.standardEntryLocalizes-11201.test.tsx.Ablation
Run from committed
54656bd7dwith objectstack'sscripts/ablation-replace.mjs. The layout tests import the renderer by relative path, and the root vitest config aliases@object-ui/layouttosrc, so nodist/is in the path.isCustomizedarm. Overpackages/layout/src/: 6 failed, 287 passed. The 6 are exactly the 4 new VERBATIM rows and the 2 restated rows. The MAP rows stay green.packages/layout/src/: 2 failed, 291 passed. The 2 are exactly the MAP rows.HEAD, andgit diff HEADis empty.Verification
All gates ran from the worktree root, on head
54656bd7d:pnpm exec vitest run --reporter=verbose packages/layout/src/: 30 files, 293 passed.NavigationRendererorresolveNavItemLabel, pluspackages/app-shell/src/layout/,packages/app-shell/src/chrome/, thenav:menurender tests, the Studio and designer nav-label pins,packages/plugin-designer/andpackages/i18n/src/): 173 files, 1966 passed, 13 skipped.turbo run build --filter=@object-ui/layout^... --filter=@object-ui/i18n^... --concurrency=2: 8 of 8 successful. Thentype-checkfor@object-ui/layoutand@object-ui/i18n: exit 0.tsc -p tsconfig.test.json --listFilesOnlylists both touched layout test files.eslinton the 4 changed source and test files: 0 errors and 28 warnings, all pre-existing. Linting the base blobs givesNavigationRenderer.tsx25 warnings against 23 at head (twoas anycasts went with the arm), and the other files the same counts.pnpm exec vitest run scripts/__tests__/(the whole root suite, because a changeset.mdis added): 177 files passed and 2 skipped; 5371 tests passed and 2 skipped; lock VERDICT command-exit 0.check-changeset-presenceexit 0.check:control-bytesexit 0.check:new-line-citationsreports 0 new citations, exit 0.markdown-test-inputs --auditexit 0.check-changeset-no-majorexit 0.check:changeset-claimsis report-only: it names 3 pending changesets that cite a touched file, and I read each paragraph; none is falsified by this diff.Acceptance notes
mainwrites a machine-name label. The console sync effect passes no label. The Studio skeleton and inspector bind and the wizard'sgeneratedEntryLabelwere fixed by stage 1. Remaininglabel: d.name-shaped writes are the Studio rail's object list and a dashboard document seed, not nav entries.@object-ui/typesdeclares a nav entry'slabelasstring(the type and the zod mirror), narrower than the spec'sI18nLabel.safeValidateSchema, whichobjectui validatecalls, refuses a map-valued nav label (navigation[0].label: expected string, received object). The spec'sNavigationItemSchemaaccepts the same entry. The new map pin casts its fixtures for that reason, and says so.packages/app-shell/src/views/nav-menu-renderer.tsxstill says its labels get "the same convention-based object / view / dashboard i18n resolution the sidebar gets". That is no longer true for any label.UnifiedSidebarandnav-menu-rendererstill build the three unread resolvers. Both files are outside this claim's file surface; they belong with open question 2.AppSchemaRenderer.tsx'sresolveAreaLabeldocblock quotes the oldresolveObjectLabelprop doc ("enables convention-based i18n auto-resolution …"), which this PR rewrote. The docblock's point, injection over an i18n dependency, still stands.resolveNavItemLabelstill resolves objectui's keyed{ key, defaultValue }reference throught, unchanged. The spec's nav label refuses that shape and objectui's type does not declare it. The new code tells it apart from a locale map by the two member names a map can never carry.Generated by Claude Code