fix(mobile): make i18n formatting Hermes-safe - #5478
Conversation
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryIncremental commit Files Reviewed (89 files)
Previous Review Summaries (10 snapshots, latest commit f782bcc)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit f782bcc)Status: No Issues Found | Recommendation: Merge Executive SummaryIncremental commits Files Reviewed (104 files)
Previous review (commit ed73052)Status: 6 Issues Found | Recommendation: Address before merge Executive SummarySecurity Agent and Code Reviewer still render catalog keys for analysis mode, gate/style option labels, dismissal reasons, and remediation status badges. Overview
Issue Details (click to expand)WARNING
Files Reviewed (93 files)
Fix these issues in Kilo Cloud Previous review (commit f7bfff1)Status: 6 Issues Found | Recommendation: Address before merge Executive SummarySecurity Agent and Code Reviewer still render catalog keys for analysis mode, gate/style option labels, dismissal reasons, and remediation status badges. Overview
Issue Details (click to expand)WARNING
Files Reviewed (115 files)
Fix these issues in Kilo Cloud Previous review (commit ad371ba)Status: 3 Issues Found | Recommendation: Address before merge Executive SummarySecurity Agent still renders catalog keys for dismissal reasons and remediation status badges instead of translated copy. Overview
Issue Details (click to expand)WARNING
Files Reviewed (1 file)
Fix these issues in Kilo Cloud Previous review (commit a84ae71)Status: 3 Issues Found | Recommendation: Address before merge Executive SummarySecurity Agent now maps status and dismissal codes to catalog keys but forgets to translate them, so badges and dismissal copy show raw i18n keys. Overview
Issue Details (click to expand)WARNING
Files Reviewed (98 files)
Fix these issues in Kilo Cloud Previous review (commit 0f9b478)Status: 1 Issue Found | Recommendation: Address before merge Executive SummaryKilo Pass remaps the App Store not-linked error by comparing against Overview
Issue Details (click to expand)WARNING
Files Reviewed (129 files)
Fix these issues in Kilo Cloud Previous review (commit 17f711a)Status: No Issues Found | Recommendation: Merge Executive SummaryIncremental commit Files Reviewed (116 files)
Previous review (commit b07a6e8)Status: No Issues Found | Recommendation: Merge Executive SummaryIncremental commit Files Reviewed (107 files)
Previous review (commit 7801cc0)Status: No Issues Found | Recommendation: Merge Executive SummaryIncremental commit Files Reviewed (136 files)
Previous review (commit 122aac9)Status: 2 Issues Found | Recommendation: Address before merge Executive Summary
Overview
Issue Details (click to expand)WARNING
Files Reviewed (176 files)
[Snapshot truncated.] Additional previous summary content was truncated to keep this comment within platform limits. Reviewed by grok-4.6 · Input: 240.6K · Output: 7.9K · Cached: 463.7K Review guidance: REVIEW.md from base branch |
Move every remaining English UI string into the catalog, and fix the formatting bugs the i18n audit found. Copy: GitHub install alerts, merge-blocked reasons, merge method labels, screen-reader announcements, reaction labels, thread anchor labels, attachment validation copy, mutation error fallbacks, Security Agent command toasts, and device sign-in errors. 72 new keys, translated into all 87 catalogs. Formatting: - The audit report labels its dates UTC, so format them in UTC. - Format gigabytes with the unit formatter, and vCPU from the catalog. - Read the Security Agent severity from the SLA keys, not capitalize(). - Treat zh-TW, zh-HK, and zh-MO as Traditional Chinese for voice input. - Ensure NumberFormat before the RelativeTimeFormat polyfill, which constructs one internally. - Never let a prewarm failure throw; it is only a cache fill, and a throw keeps the splash screen up forever. Guard: no-literal-copy now flags announceForA11y literals and the `error.message || 'English'` fallback shape, so neither can come back. Also delete resolveVoiceInputLanguageTag, which has no caller.
Move the rest of the mobile app's English strings into the catalog, and fix the four bugs the audit round confirmed. Copy: attachment upload failures, voice-input feedback, pull request mutation errors, external-link and invoice and remote-spawn toasts, Kilo Pass purchase errors, Code Reviewer errors, and Security Agent mutation copy. 43 new keys, translated into all 87 catalogs. Bugs: - The audit report labelled its event times UTC but rendered them in the device time zone. One call site was missed last round. - poll-response.ts held two English sign-in errors. - The merge announcement spelled English although its catalog keys existed. - Voice input picked the wrong script for a region-only tag: zh-MY, sr-RS, pa-PK, and az-IR all resolved wrongly. Chinese now follows the same rule resolve-language.ts already states, and a small region map covers sr, pa, az, and uz. - Six catalog values had come back in English (fil, mg, ms, so). Guard: no-literal-copy now reads a conditional expression, so a ternary can no longer hide copy. That change alone found three more English leaks, all fixed here. KiloClaw is allowlisted as a brand name. Copy that lived in a module-level constant now resolves per call, so an in-place language change no longer leaves stale words on screen.
The largest remaining English surface came from `packages/app-shared`, which web reads directly. Web has no translations, so the shared modules keep every English string exactly as it is. Mobile now maps the stable code each module already carries — an analysis state, a status, a severity, a reason — onto a catalog key, and renders the translated word. Surfaces: every finding row, analysis panel, remediation panel, details panel, dashboard metric, and Code Reviewer status badge. 120 new keys, translated into all 87 catalogs. The deadline copy is computed from dates, so it carries no code to map. `getSecurityDeadlinePresentation` now also returns a `state` union naming the branch and the values the copy needs. The field is additive: web keeps reading `label` and `detail`, and app-shared's own tests still pass. Mobile renders from `state` and formats each date in the active language. Day counts use plural keys, with the categories each language needs rather than English's two. Also localize the pull request submit block reason, and bump the app to 1.0.6.
The backend sends its purchase errors in English whatever the app's language is, so the mobile match must compare English, not a translated catalog value. The last commit translated one of the two matched constants, which made the remapping silently skip in every other language and showed the raw English server text instead. Both matched constants are English again, and a comment names them as the backend contract. Only the copy the user reads is translated. The owned-by-another-account sentence is now the key `kiloPass.purchaseOwnedByAnotherAccount`, and the contract sentence is no longer a catalog key at all. Reported by Kilo Code review on PR 5478.
The finding details panel mapped a severity and a lifecycle status onto a catalog key, and fell back to the word "Unknown" when the backend sent a value the app does not know yet. packages/app-shared renders the raw code there, so web shows the new value and mobile hid it. Fall back to the raw code, the same way the shared module does.
`capitalize()` on a database code reads as English in all 87 languages. It was the app's largest systemic i18n gap: severity, review style, gate threshold, focus area, analysis mode, thinking effort, subscription plan, and invoice status all rendered an English word this way. Each of those now maps its code to a catalog key, and falls back to the raw code when the backend sends a value the app does not know yet, the same way packages/app-shared renders it for web. Also localized, all reaching the user as English before: - the four pull request retry toasts and the merge-result default - the Code Reviewer needs-attention banner - the Security Agent command failure toasts - the Kilo Pass App Store errors - the Added / Deleted / Context words a screen reader speaks per diff line - a set of smaller toasts and fallback labels 82 new keys, translated into all 87 catalogs. packages/app-shared is unchanged, so web keeps every English string it reads today.
A final sweep found six English strings hiding in shapes the lint rule cannot see. All six reach a user. - The pull request ledger re-wrapped its ambiguous and persistence markers for display. The marker is also a server contract the sheets classify against, so the kind now travels on the error object and the message is translated. Comparing the message alone would have broken pr-merge-sheet.tsx:220 and reply-input.tsx:184. - A deleted GitHub account showed as the literal `deleted user`. - A screen reader spoke `cost` before every session row's price. - A screen reader spoke `line N` and `(empty)` on every pull request diff line. - The force-update screen named `app store page` in its failure toast. - A non-Error FORBIDDEN throw fell back to the word `Forbidden`. 10 new keys, translated into all 87 catalogs.
A chat 401 or 403 toasted the English words "Not allowed" in every language, and the message-length and empty-message phrases read English too. A reply preview to an attachment with no filename showed "Image" or "Attachment". packages/kilo-chat is shared with the web app, which has no translations, so its English is untouched: `formatKiloChatError` and `contentBlocksPreviewText` return byte-for-byte what web reads today. Both now build that English from a new exported classifier, and mobile maps the classifier's stable kind onto a catalog key. A server-phrased error still shows the server's own message rather than the caller's generic fallback, so nothing is lost when the backend explains what went wrong. Also translate the pull request ledger markers in the inline error boxes. The toast already read the translated message, but the composer, submit sheet, and reply box each spelled the English constant. Six new keys, translated into all 87 catalogs.
Six places passed a catalog key where the copy belonged, so the user read `securityAgent.analysisMode.automatic` instead of `Automatic`. The Code Reviewer option lists render `labels[option]` verbatim, and the Security Agent badges and rows rendered the lookup result directly. Fixed the analysis mode row, the gate and style option lists, the dismissal reason, and both remediation status badges. Reported by Kilo Code review on PR 5478.
|
Final i18n sweep verdict, with one finding rejected. Fixed — Fixed — the three pinned remote-session exit messages ( Rejected — the StoreKit refund toasts in |
The agent-chat working indicator and the child-session cards showed "Initializing snapshot…" in English while a CLI snapshot started. The literal stays pinned so it still matches the CLI's own typography; only the text the user reads comes from the catalog now. The three remote-session exit errors were toasted verbatim. They are a pinned producer/consumer contract with the SDK, so they are still matched in English and only the shown copy is translated. The dev-only StoreKit refund toasts stay English on purpose: the button is gated behind __DEV__, so no production reader can reach them. Four new keys, translated into all 87 catalogs.
Summary
Intlformatters.Intl.Localeprerequisite that Hermes omits.$.Root cause
The latest build loaded FormatJS locale data without
Intl.Locale. Hermes does not provide that constructor.Verification
pnpm test: 5,907 tests passed.pnpm format: passed.pnpm typecheck: passed.pnpm lint: passed with zero warnings.pnpm check:unused: passed.pnpm check:i18n: all 87 catalogs passed.git diff --check: passed.pnpx expo-doctor: 20 of 21 checks passed.Visual Changes: The app now shows localized copy and values. The component design did not change.