* perf(i18n): load built-in catalogs once, without copies or a runtime plural fill
Each catalog load parsed the JSON, rebuilt it to fill CLDR plural forms
from _other, then deep-copied and deep-merged it into the store (about
26 ms after parsing per 1.3 MB catalog), and concurrent loads of one
locale fetched it twice.
- sync:localization-catalog now commits the plural forms a locale's
CLDR rules need beside each _other (fr/es/pt many, tr one, a few
de/ru), and verify:localization-catalog fails when one is missing.
Plural forms may include {{count}} where English omits it.
- Renderer and main load built-in catalogs as-is; plugin packs keep the
runtime fill.
- Renderer catalog requests are shared per locale; a timed-out request
is dropped so a retry fetches again. Bundles are added with skipCopy.
* fix(i18n): require {{count}} in plural forms when English shows it
* chore(i18n): sync plural forms for catalogs merged from main
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* feat(mobile): add UI localization infrastructure
Mobile follows the phone's language (or an in-app choice under Settings > Appearance)
across the same 11 languages as desktop, switching live like the theme does.
- i18next with desktop's translate(key, fallback, options) call shape; catalogs in
mobile/src/i18n/locales load lazily, English fallbacks stay inline
- Hermes lacks Intl.PluralRules: load the FormatJS polyfill with the 11 languages' rules
- OTA pages get the shell's language through an optional bridge init field
- iOS permission prompts localized; iOS and Android 13+ per-app language pickers
- verify:mobile-localization and a coverage ratchet run in lint, check:changed and CI
* fix(mobile): mount pages after the newest language request; tighten localization gates
- Wait for the latest page language activation, not a superseded one
- Keep literal fallbacks when a key also has a dynamic one
- Count headerTitle as visible copy; select the mobile check when the runtime catalog generator changes
* feat(mobile): repaint themed components on language changes
Components that read the theme already subscribe for repaint; a language switch
repaints them too, so labels built by helpers during render follow it. RPC
recordings load the real i18next and JSON catalogs.
* fix(mobile): scope the translate() gate to render bodies and memos; prune stale keys on sync
Handlers and effects may call translate() when they run. sync:mobile-localization now
removes keys no code references from every catalog.
* fix(mobile): use node: imports in the locale config plugin; pass uiLocale in bridge hook test
* chore(mobile): refresh the localization coverage baseline
* fix(mobile): follow render callbacks in the translate() gate; keep used plural siblings on prune
Also let anonymous default-exported screens count as components, flag any plural
family a locale started, and mark JSON modules as ES modules in the recording loader.
* fix(mobile): treat async and named local callbacks as handlers in the translate() gate
* chore(mobile): refresh the localization coverage baseline
* chore(mobile): refresh the localization coverage baseline
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>