Files
orca/mobile/package.json
T
OrcaWinandOrcaWin 12a7d4c13a feat(mobile): localization infrastructure and language setting (#35399)
* 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>
2026-10-28 17:14:05 -07:00

104 lines
3.9 KiB
JSON

{
"name": "orca-mobile",
"version": "0.0.1",
"private": true,
"main": "index.js",
"scripts": {
"start": "node scripts/start-expo.mjs",
"android": "expo run:android",
"ios": "expo run:ios",
"native": "node scripts/native-client.mjs",
"postinstall": "node scripts/build-terminal-webview-engine.mjs && node scripts/build-mermaid-page-engine.mjs && node scripts/build-terminal-document-script.mjs",
"test": "vitest run",
"typecheck": "node ../config/scripts/run-typecheck-projects-in-parallel.mjs --project=mobile/tsconfig.json",
"typecheck:tests": "node ../config/scripts/run-typecheck-projects-in-parallel.mjs --project=mobile/tsconfig.test.json",
"check:tests-typecheck": "node scripts/check-tests-typecheck-ratchet.mjs",
"rpc:record": "tsx --conditions=orca-source scripts/rpc-recording.mts",
"rpc:diff": "tsx --conditions=orca-source scripts/rpc-diff.mts",
"lint": "oxlint",
"format": "oxfmt --write .",
"format:check": "oxfmt --check .",
"mock-server": "npx tsx scripts/mock-server.ts",
"repro:workspace-picker-lag": "npx tsx scripts/repro-workspace-picker-lag.ts",
"start:emulator": "node scripts/start-emulator.mjs"
},
"dependencies": {
"@formatjs/intl-pluralrules": "6.3.16",
"@noble/hashes": "1.8.0",
"@orca/expo-two-way-audio": "file:./packages/expo-two-way-audio",
"@react-native-async-storage/async-storage": "^2.2.0",
"@xterm/addon-unicode11": "0.10.0-beta.301",
"@xterm/addon-webgl": "0.20.0-beta.300",
"@xterm/xterm": "6.1.0-beta.304",
"buffer": "^6.0.3",
"expo": "^55.0.31",
"expo-build-properties": "^55.0.18",
"expo-camera": "^55.0.23",
"expo-clipboard": "^55.0.17",
"expo-constants": "^55.0.17",
"expo-crypto": "^55.0.19",
"expo-dev-client": "~55.0.40",
"expo-document-picker": "^55.0.17",
"expo-file-system": "55.0.26",
"expo-haptics": "^55.0.18",
"expo-image-picker": "^55.0.24",
"expo-keep-awake": "~55.0.8",
"expo-linking": "^55.0.17",
"expo-localization": "~55.0.19",
"expo-modules-core": "~55.0.26",
"expo-network": "~55.0.18",
"expo-notifications": "^55.0.27",
"expo-router": "^55.0.18",
"expo-secure-store": "^55.0.18",
"expo-splash-screen": "^55.0.25",
"expo-status-bar": "^55.0.6",
"expo-task-manager": "~55.0.20",
"fflate": "0.8.3",
"i18next": "26.5.11",
"lowlight": "^3.3.0",
"lucide-react-native": "^1.14.0",
"markdown-it": "^14.3.2",
"markdown-it-cjk-friendly": "^2.0.3",
"markdown-it-footnote": "^4.0.0",
"mermaid": "11.17.2",
"react": "19.2.8",
"react-dom": "19.2.8",
"react-native": "^0.83.10",
"react-native-gesture-handler": "^2.31.2",
"react-native-libsodium": "1.7.0",
"react-native-reanimated": "4.3.4",
"react-native-safe-area-context": "^5.7.0",
"react-native-screens": "^4.24.0",
"react-native-svg": "^15.15.5",
"react-native-web": "^0.21.3",
"react-native-webview": "13.16.2",
"react-native-worklets": "^0.8.3",
"remend": "1.4.0",
"tweetnacl": "^1.0.3",
"zod": "~4.6.7"
},
"devDependencies": {
"@orca/process-host": "link:../src/packages/process-host",
"@types/markdown-it": "14.1.2",
"@types/markdown-it-footnote": "^3.0.4",
"@types/mdast": "^4.0.4",
"@types/react": "~19.2.18",
"@types/react-test-renderer": "19.1.0",
"@types/ws": "^8.18.2",
"acorn": "8.18.0",
"esbuild": "0.28.2",
"expo-module-scripts": "^55.0.2",
"happy-dom": "^21.0.4",
"oxfmt": "^0.74.0",
"oxlint": "^1.88.0",
"react-test-renderer": "19.2.8",
"tsx": "^4.23.15",
"typescript": "^7.0.2",
"typescript-api": "npm:typescript@6.0.3",
"vite": "^8.3.4",
"vitest": "5.0.5",
"ws": "^8.22.0"
},
"packageManager": "pnpm@12.14.0+sha512.c18ba6d87993d3d164cb5e8948f32b2c06ee854487e06cd8d86781342fb3e5bb4634367034711e86802baa9c37bea906ce1929ad08d70884c97d801df13389a6"
}