fix(i18n): emit settings expected-result codes from producers - #4551
fix(i18n): emit settings expected-result codes from producers#4551orangeCatDeveloper wants to merge 3 commits into
Conversation
ad2b11d to
89c58cd
Compare
89c58cd to
3ffb1a2
Compare
4b6d344 to
b526451
Compare
b526451 to
c9390a5
Compare
236c0da to
daeb9ba
Compare
2a2c114 to
bc9228e
Compare
bc9228e to
c315e20
Compare
Adding a UI locale compiled cleanly and still rendered the wrong language wherever code compared `locale` to a literal, defaulted a `locale: UiLocale` parameter, sniffed CJK in a payload, or translated by looking up one locale's string. The type system cannot see any of the four, and Biome cannot express "no growth against base" for the two unformatted trees. `scripts/check-locale-hygiene.mjs` counts those four patterns per file in `apps/desktop/src`, `packages/core/src` and `packages/ui/src`, both quote styles, per match, and fails CI when any (file, rule) count grows against the merge base or `BASE_SHA`. There is no ledger to maintain; only files in the diff are scanned. With no resolvable base the step fails instead of skipping. The step runs with the install-free gates before `setup-node`. `locale-literal-compare` is deliberately narrower than `check-tui-copy`'s AST rule so it stays dependency-free. No runtime or migration impact. Baseline on this commit: 67 / 4 / 12 / 32; the four `silent-locale-default` hits retire with #4524 and #4551, after which that rule can become a hard zero. Refs #2672 Generated-by: Claude Code
Settings-area producers (memory, data, permission center, connection test, computer-use health, dev dialogs) emitted zh prose that reached en users verbatim, and presenters sniffed CJK to decide whether to show it. Producers now return stable codes and redact probe detail at the source; each settings catalog maps its codes per locale with complete message templates and an explicit unknown fallback. The remaining CJK passthrough is gated to zh. Generated-by: Claude Code Claude-Session: https://claude.ai/code/session_01AqdSkg56F2x55wEGRWvzcB
Keep capability details localized with structured health signals and reuse the own-key lookup for unknown memory result codes. Generated-by: OpenCode
c315e20 to
54ed20b
Compare
The Copilot and local-memory producers emitted untyped code strings while the renderer catalogs kept their own unions, so a new producer code could miss the catalog without a type error. Generated-by: Claude Code
Astro-Han
left a comment
There was a problem hiding this comment.
Reviewed at af07077 against 03b5a2cc5a, two independent passes. This is the CapabilityReasonCode follow-up #4524 asked for, and the producer side is right: CAPABILITY_REASON_CODES is one closed list in @maka/core, the memory and subscription codes are closed unions, the native dialog titles read the existing main-process locale authority rather than a new one, the interim CJK sniff in settings-health-copy.ts is gone, and zh-TW is real traditional copy.
One P1, three P2s, then smaller items.
P1: the 23 capability reason codes get two full three-locale tables, and they already say different things. settings-health-copy.ts:257-333 and permission-center-copy.ts:153-348 each map every code. For missing platform credentials the Health center renders 等待填写平台凭据。 and the Permission Center 未配置平台凭据; macOS TCC only is 仅 macOS 系统权限可探测。 in one and 仅 macOS TCC 权限适用 in the other. That is 138 strings maintained twice and two pages describing one state in two ways, which is the exact defect this family of PRs exists to remove. The body defers the reconciliation to #4524, but #4524 is merged and did not do it; it is this PR's job. One Record<CapabilityReasonCode, string> per locale in a shared module, both pages read it, and the Permission Center keeps only its cuBackendStatus composition. (Path ①: both pages show the same capability.)
P2: the Permission Center loses every bot capability's reason. capability-snapshot.ts:247-251 passes the bot's readinessReason into runtimeProbe.reason; on main localizedSnapshotText shows it when it is not CJK, so English users see "Telegram requires a Bot Token." The new resolver at permission-center-page.tsx:723-729 sends anything that is not a capability code to reasonFallback, and the comment says bot reasons are machine codes owned by the bot page. That is #4639's world, not main's, and even after #4639 the right answer is the bot copy, not a generic line. Land after #4639 and resolve bot:* reasons through its botStatusReasonMessage; until then keep the non-CJK pass-through.
P2: three of the new "codes" are English prose. capabilities.ts:115-117 lists 'missing platform credentials', 'macOS TCC only' and 'no Electron API for per-target Apple Events TCC status', and the producer now emits those where it used to emit Chinese. The body says the legacy tokens "keep their existing prose form"; they changed language. Any consumer that misses the map shows English to a Chinese user, which is the old failure with the locales swapped. Make them snake_case like the other twenty, or correct the body and add the missing-map test.
P2: subscriptionResultMessage drops an unclassified Host reason. The old subscription-result-message.ts:52 passed a non-CJK message through when nothing matched; the new function (settings-provider-copy.ts:681) returns the generic fallback when the classifier has no match, which can contradict what the Host said. The order (code → typed reason → three legacy matches → classifier) is right; keep the raw-text pass-through as the last step, and note at the three legacy matches when they can go (once every Host sends code).
Smaller:
test-connection.ts:508deletes the 429 message rather than coding it, so a rate-limited OAuth account is now indistinguishable fromprovider_unavailableand the "retry later or switch model" guidance is gone; the body lists it under covered producers.settings-error-copy.ts:33replaces the zh-CN user-visible main-process message withunknownErrorplusconsole.error; andscripts/storybook-visual-smoke.mjs:188-193now matches that console line by full text, so a story that changes the sentence fails the smoke. Match on the[settings]prefix.settingsTestResultMessage'sdefaultbranch loses the en pass-through (settings-test-result-copy.ts:163-166); this is the line #4639 also changes.runtime-host-memory-ipc-main.ts:626-627writesreasonandcodewith the same value, the Copilot IPC writesmessage: code, andbridge-contract.d.ts:1511-1517still carriesmessagethat nothing reads. One field.localizedCapabilityGuidancemapscapability.guidance, which no producer fills;health.ts:398-404prefersruntimeProbe.reason, which Computer Use always has, so the Healthcu_backend_statusentry is unreachable;apps/desktop/src/main/permission-snapshot-e2e-fixture.ts:94still emits Chinese prose and now rendersreasonFallback.- "W2b" appears only in this body; if it is a roadmap item, link it.
- Separable scope: the dev-singleton dialog, the message-box close label and the native dialog titles are a different producer family with no code union; fine to keep, but they are why the diff is 42 files.
Ordering: #4639 first, then this rebases and picks up its bot copy; with #4641 and #4526 the shared files differ only by key, any order.
Manual acceptance, three locales: Permission Center's Computer Use row against main word for word, a bot row with a failed test, the Health center row for the same capability (should now match the Permission Center), a memory save with counts and a backup, an OAuth subscription failure without a code.
Evidence boundary: static read against main; memory string fidelity was not re-checked line by line; no build, no suites, no Desktop.
AI-assisted review: drafted with Maka in two passes; I verified the two reason tables, the Permission Center resolver, the three prose codes and the sniff removal myself.
Summary
Settings-area producers emitted zh-only result strings, so English users either saw Chinese or — where consumers guarded with
/[㐀-鿿]/content sniffing — silently lost the information (permission guidance was dropped entirely). Producers now emit stable machine codes (IPC payloads stay locale-free), and each settings catalog maps its union exhaustively per locale with an explicit unknown-code fallback. CJK sniffing was removed where this PR code-ified the producer; one pre-existing zh-only action-error passthrough remains pending locale roadmap W2b. zh copy moved verbatim, en copy is new.Covered producers:
capability-snapshotreasons, memory ops,test-connectionOAuth rate-limit,computer-use-hosthealth reasons, the GitHub Copilot import path (github-copilot-local-credential+ its IPC handler, re-based onto main's device-grant rewrite), plus the dev-singleton dialog, browser message-box close label, and artifact/config native dialog titles (wired through the main-process locale authority).subscriptionResultMessagenow takes the result envelope and resolvescode, then the typedreason(experimental_disabled), through the catalog before falling back to the keyword classifier; its trailing CJK passthrough is gone. It moved out of theuseOAuthLoginFlowhook module into the catalog module beside the copy it renders, which also makes it testable from node.The bot domain moved to its own PR so each PR is one reviewer context.
Memory messages now use complete per-locale templates instead of a shared sentence skeleton with translated fragments. The typed catalog owns counts, summaries, redaction notices, backup failures, and preview/action descriptions; existing wording and pluralization are preserved.
Refs #2672
Verification
Boundaries: the four legacy capability reason tokens keep their existing prose form, and
health.tsandpermission-center-copy.tsretain parallel zh code-to-copy maps; #4524 coordinates that vocabulary and presenter cleanup.provider-panel-shared.tsretains one zh-only CJK passthrough until its remaining main-process producers are code-ified in locale roadmap W2b.subscriptionResultMessagestill matches three English Host messages (enrollment is disabled,already in progress,did not present OAuth) as a fallback for Hosts that predate the codes; those matches go once every Host sendscode.Follow-up verification: all 13 memory-copy and label tests pass, with exact formatted-output coverage across all three locales, draft states, singular/plural counts, archived entries, backup kinds, and preview messages. Desktop build and all four typechecks, repository format/lint, and the renderer architecture check pass.
AI use
Select exactly one:
Tool(s) and scope: Claude Code, Muse Spark, and OpenCode — analysis, implementation, review fixes, tests, and this description, under the contributor's direction; each affected commit carries the corresponding
Generated-bytrailer.OpenCode also implemented the complete memory-message templates and their output tests, and updated this description.
Checklist