feat(terminal): localize settings option chips via display-label catalog#8
Merged
Merged
Conversation
…trix (U1) Token-keyed renderings for font/castMethod/castMode/taijitu/anim chips (zh-Hant + zh-Hans authored explicitly). Theme labels recorded as deferred pending brand-vs-literal ruling. Input-alias policy: none. New surface row term-settings-option-chips + matching policy-matrix entry.
…(U2) OPTION_LABELS Map catalog (ships empty — behavior-neutral) with total optionLabel() fallback to the canonical token; SettingRow now carries canonical values, chip labels derived per render via chipLabel(); getValues() maps field→row by key, killing the positional-index drift (reordering rows can no longer swap persisted values). Language row's endonym badges untouched.
First ratified wave of the option-label catalog. zh-Hant/zh-Hans authored per glossary; en keeps canonical pinyin tokens; persistence and live language flip locked by tests.
銅錢 (coin)/蓍草 (yarrow) carry the canonical token as an in-label hint (no transliteration bridge to the CLI value); 自動/手動, 點陣/密實, and 噪點/點陣/放射/沙化 are literal label-only renderings. Token-keyed per glossary (噪點 = noise, 點陣 = dots, ratified once for both usages). Width budget pinned under the en theme row.
--terminal now asserts the full ratified (settingKey, token) → 繁/简 contract behaviorally via optionLabel(), pins en = canonical token, theme verbatim fallback (deferred), and prototype-chain misses; adds representative label sentinels wired to the inventory row. Deleting a shipped entry reds the verifier (proven by mutation).
Name the role (find the row owning the key, narrow its selected token), not the category.
pro-vi
added a commit
that referenced
this pull request
Jun 10, 2026
The settings option-label localization work shipped in #8; its plan doc (status: completed) no longer needs to live in the tree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pro-vi
added a commit
that referenced
this pull request
Jun 20, 2026
…itle overlap (review #3/#8/#9) Three cast-scene findings from the independent review: - #3 (P2): [s] skip called skipToComplete() with animate defaulting true, so setFocusedHex re-seeded a fresh glyph animator (glyphAnimDone=false) and the central glyph noisily re-played its whole reveal instead of settling. Pass animate:false — [s] is the one caller that wants the static end-state. - #8 (P3): update() derived its delta from the loop's UNCLAMPED `elapsed`. While the terminal is below the size floor the loop skips update() but `elapsed` keeps growing, so the first restored frame credited the entire hidden gap — fast-forwarding past the reveal, ×2/×4 at pace. Advance by the clamped per-frame dt instead (as the yarrow scene does). - #9 (P3): in centered layout (narrow, < MIN_SPLIT_WIDTH, no glyph) the standalone "→ becoming" title renders a few rows below the primary — where the settled reading panel stacks down — garbling on tall-narrow terminals. Suppress it while the reading is showing (the reading carries the becoming). Tests: clamped-clock advance (#8, mutation-verified), [s] settles the glyph (#3, mutation-verified), becoming title suppressed-when-shown / present-when- hidden (#9). The #8 change means a single big update() no longer fast-forwards, so the test settle-helpers now use skipToComplete(false) or clamped-dt loops (the buggy shortcut they relied on is exactly what #8 removes). Gates: 1336 tests pass, typecheck clean, language-surface PASS, smoke 5/5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Localizes the Settings scene's option-value chips (font, cast method, cast mode, taijitu, glyph animation) for zh-Hant/zh-Hans through a token-keyed display-label catalog, while keeping stored config tokens, CLI stdout, and the config parse surface byte-identical. Implements the policy the glossary already sanctioned ("display labels for enum values are a SEPARATE catalog layer that never mutates the stored token") and removes the mixed-register zh Settings screen (CJK row labels above English chips).
Stacked on #5 (
feat/language) — merge after it.Key Changes
Features
packages/terminal/src/i18n/option-labels.ts):optionLabel(lang, settingKey, token)— token-keyedMap(prototype-chain reaches miss by construction), total resolution with canonical-token fallback, zh-Hans authored explicitly. Not routed throughtr()—MessageKeyis a literal union; widening it to dynamic keys would break the exhaustive-catalog contract.enmode unchanged. Labels derive from the live language selection (mid-screen flip re-labels same frame).docs/language-glossary.md,TEXT_SURFACES.md): token-keyed ratification table (噪點=noise / 點陣=dots transposition hazard cross-referenced), theme labels recorded as deferred (brand-vs-literal ruling pending), input-alias policy: none.Refactoring
getValues():SettingRownow carries canonical token arrays (values); field↔row mapping goes through the row key viaselectedValue()instead ofthis.rows[0]…this.rows[6]positional indexing — reordering rows can no longer silently swap persisted values. Pre-existing two-way coupling (display-string→canonical by index AND field→row by position) is fully removed; no casts introduced.Tests / Verifier
scripts/verify-language-surfaces.ts --terminal): full ratified (settingKey, token) → 繁/简 contract checked throughoptionLabel();en= canonical token pinned; theme verbatim-fallback pinned; prototype-chain miss pinned; representative label sentinels wired to the inventory row. Deleting a shipped entry reds the verifier (proven by mutation during development)."[EN] 繁 简"endonym assertion preserved verbatim.Technical Context
LANGUAGE_LABELS(EN/繁/简) are endonym badges, invariant across display languages, heavily pinned by fixture rows + sentinels — untouched.OPTION_LABELSis the per-display-language layer for the other rows.config setand hand-edited files stay canonical-only (recorded in glossary + scope boundary). The TUI↔CLI vocabulary bridge is the in-label canonical hint, not an alias table.docs/plans/2026-06-10-001-feat-settings-option-label-localization-plan.md(origin: PR feat: add language-aware detail view #5 review session + cross-model second opinion).Testing Scenarios
config get castMethodchain pinned by existing CLI testsbun run typecheck, full suite, andverify-language-surfacesrun-all: PASS — 0 issuesGenerated with: Claude Code
QA checklist
All items verified fresh at
ff0ffd8onfeat/settings-option-labels. TUI adaptation note: this is a terminal app — "screenshots" below are renderedCellBufferdumps (80×24, the scene's real render path), captured by a reproducible script (bunone-shot renderingSettingsScene; commands inline). No browser/visual-proof manifests apply; provenance = git HEAD above + the capture commands.R1 — zh modes render localized chips; en unchanged — full-scene dumps per language; only theme chips remain English (deferred by R5):
(zh-Hans block abridged to the wave rows; en block abridged — full dumps reproduce via the capture script.)
R2 — persisted tokens, CLI stdout, and parse surface unchanged; no new aliases — live CLI round-trip on a fresh
--data-dirwithlanguage=zh-Hant:R3 — labels resolve from the live language selection — start in
en, focus Language row,→to 繁: chips re-label in the same rendered frame, values untouched:Locked by test: "flipping Language re-labels font chips immediately, before save".
R4 — missing label falls back to canonical token, never crashes/persists — theme chips render
ink bone cinnabar jade riververbatim in zh dumps above (empty-entry fallback, live); prototype-chain tokens (constructor,__proto__,toString…) pinned to miss byoption-labels.test.tsand the verifier's contract check.R5 — theme chips verbatim pending glossary ruling — visible in the zh-Hant dump (主題 row shows English tokens); deferral + unratified candidates recorded in
docs/language-glossary.md§Settings option-chip display labels andTEXT_SURFACES.mdterm-theme-names.R6 — policy artifacts re-pinned to "tokens canonical; labels via catalog" —
scene-language.test.tscomment re-pinned;"[EN] 繁 简"assertion preserved; verifier--terminalnow enforces the full ratified label contract behaviorally. Mutation red-proof during U5: deleting thenoiseentry →FAIL — option label settings.glyphAnimation.noise: zh-Hant != ratified "噪點".Automated — fresh runs at
ff0ffd8: