Repository navigation
Description
Blocked-by: #150
Context
The maintainer, verbatim:
「手机端应该支持多国语言对吧」
「应该根据手机的语言识别」
「看一下 objectstack 官方支持那些语言」
「要能对应上」
「mobile 也需要支持这些语言」
objectstack-ai/objectstack#29349 was ruled F on 2026-11-02. The platform supports one list of 11 languages, complete on every surface: the Console, platform metadata, emails, cloud, the marketing site and this app. The table itself is objectstack-ai/objectstack#29428.
| Language | Tag |
|---|---|
| English | en |
| 简体中文 | zh-Hans |
| 繁體中文 | zh-Hant |
| Español | es |
| العربية | ar (right-to-left) |
| Français | fr |
| Português (Brasil) | pt-BR |
| Русский | ru |
| Deutsch | de |
| 日本語 | ja |
| 한국어 | ko |
How it works today
- Native screens follow the phone through
expo-localization(lib/i18n.tsdetectDeviceLanguage). They ship only en, zh and ar, with 25 keys each. - The Console in the WebView resolves the user's
sys_user.locale, thenAccept-Language, then the deployment default (objectstackui/apps/console/src/languageSeed.ts). The WebView sends the phone's system languages, so a user without a personal locale already gets the phone's language in the Console.
Ask
-
The 11 locales, with the table's tags. Ship the shell's strings in all 11, keyed by the tags above. Rename the current
zhbundle tozh-Hans. Generatezh-Hantfromzh-Hanswith a phrase-aware converter, asobjectstack-ai/www.objectos.aiscripts/gen-zh-hant.mjsdoes, then review it. -
Resolution from the phone. Walk the phone's ordered preferred languages and pick the first supported one:
- Chinese is resolved by script.
zh-Hant,zh-TW,zh-HKandzh-MO→zh-Hant; any otherzh(zh-Hans,zh-CN,zh-SG, barezh) →zh-Hans. pt-*→pt-BR.- Otherwise match on the primary subtag (
es-MX→es,de-AT→de). - Nothing matches →
en. - Keep this rule identical to the platform's alias rule in Platform locale table (ruling F of #29349): one list of 11 languages in spec, complete on every surface objectstack#29428. Until that lands, use the rule above.
- Chinese is resolved by script.
-
Right-to-left for Arabic. Keep
lib/rtl.ts, and make the native screens lay out right-to-left underar, including the switcher sheet, the confirm screen and the sign-in bar. -
Live change. The native screens follow a change of phone language on the next foreground.
-
No in-app language picker on native screens. The phone setting is the source; the Console keeps its own per-user language.
-
Docs.
docs/HYBRID-SHELL.mdstates two things:- once a user is signed in, their Console locale wins over the phone language;
- the shell's locale set is the platform table.
Update
docs/design/connection-flow.md(Languages and layout) from ten to the 11.
Acceptance
- A test pins the shell's locale list to the 11 tags.
- Detection unit tests cover:
- each locale;
zh-Hans-CN,zh-CNandzh-SG→zh-Hans;zh-TW,zh-HKandzh-Hant→zh-Hant;pt-PTandpt-BR→pt-BR;- an unsupported first language falling through to the next supported one;
- empty locales →
en.
- Every native string exists in all 11 bundles, and a test fails on a missing key.
- Native screens render right-to-left under
ar(component test or screenshot).
Activity
[-]Languages: follow the phone's language and match the platform's locale set (en, zh-CN, ja-JP, es-ES)[/-][+]Languages: follow the phone's language and match the Console UI's ten locales one-to-one[/+]objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
First touch. This card was filed behind gate #150, which is now closed. It goes to
pm:queueas typeenhancement.objectos, bundle id / packageai.objectos.mobile#151 and Private servers: probe with the anonymous discovery endpoints and confirm the server's identity before connecting #154 are in flight and both editlocales/{en,zh,ar}.json. This card adds seven bundles and a missing-key test.hotlong commentedon Nov 2, 2026
Heads-up for the seat: objectstack-ai/objectstack#29349 asks the maintainer to rule on the project-wide i18n regime, which is one canonical locale list with tiers.
This card's ask, the shell matching the Console UI's ten locales, holds under options A, B, C and E. E is the recommended option. Only option D would shrink the set to four locales.
Not gating this card on it. If the ruling is D, the ask changes to the four
fulllocales before or during the PR. If the ruling is E, take the locale codes from the new platform table once it lands, and keep the current ten short codes until then.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Claim: PM loop round 1
Session:
session_01QSeKYnW1Xf6HDSWz1eGAqvAccount:
os-justinBranch:
claude/issue-159-ten-localesWorktree:
mobile-issue-159Domain:
n/a — single-lane repo (repo:mobile)Seat:
repo:mobile#1File surface:
lib/i18n.ts,lib/rtl.ts,locales/*.json(seven new bundles), native screens only where RTL layout needs it (app/server-config.tsx,components/shell/ServerConfirm.tsx,components/shell/OfflineFallback.tsx,app/_layout.tsx),__tests__/**,docs/HYBRID-SHELL.md,.changeset/159-ten-locales.md(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: default tier (opus) — dispatch-gates --tier --repo objectstack-ai/mobile: no path-derived mandate,effort: highClause-②: no
Metadata-protocol: none
Responsibility:
n/a — not a defect cardThread-read: 6577353170
Premise: holds.
mainat83d4716shipslocales/{en,zh,ar}.json, andlib/i18n.ts:26hasSUPPORTED_LANGUAGES. objectstackui/packages/i18n/src/locales/lists the ten codes, en zh ja ko de fr es pt ru ar. objectstack-ai/objectstack#29349 is still open and unruled. Per comment 6577353170 it does not gate this card.Serial constraints cleared: #156 is in flight and adds locale keys. Whichever PR lands second carries the other's keys into all ten bundles, and this card's missing-key test enforces it. #163 and #166 are in flight in
ConsoleWebView.tsx, which this card does not touch.objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
os-dev-report
{ "issue": 159, "status": "done", "branch": "claude/issue-159-ten-locales", "pr": "https://github.com/objectstack-ai/mobile/pull/169", "session": "session_01QSeKYnW1Xf6HDSWz1eGAqv", "premise_still_valid": true, "summary": "The native shell now ships the Console UI's ten locales under the same short codes (en zh ja ko de fr es pt ru ar): seven new bundles plus SUPPORTED_LANGUAGES pinned to the Console's order and native names. Detection walks getLocales() in order and applies a faithful port of the Console's matchLanguageTag (objectstack ui/packages/i18n/src/locales/registry.ts:112-117 at origin/main 47718699), so zh-Hans-CN/zh-TW/zh-HK -> zh, pt-BR -> pt, es-MX -> es; unsupported first language falls through; else en. An AppState listener re-detects on next foreground; the dead MMKV language override (read, never written since the 2.0 rebuild) is removed. RTL under ar is applied live via Yoga `direction` on the app root, server-picker root (confirm step inherits) and offline page root; syncRTL still keeps I18nManager in step. objectstack-ai/objectstack#29349 read via REST: open, zero comments, unruled -> ten codes kept and said so in the PR body. ConsoleWebView.tsx untouched.", "tests": "Head 878238e (branch merged with origin/main 29c25df). `npx jest --ci --coverage` EXIT=0: Test Suites 31 passed/31, Tests 433 passed/433. New: __tests__/lib/i18n.test.ts (68 tests: ten-code pin, bundle set, per-bundle key parity, empty/placeholder checks, ported matchLanguageTag pins, detection per locale/region/zh-Hans-CN/5 Traditional Chinese tags/pt-BR/es-MX/fall-through/empty/throwing getLocales, foreground listener) and __tests__/components/RtlLayout.test.tsx (5 tests: picker + offline page rtl under ar, ltr under en, live flip). Cold: `npx jest --ci --no-cache --maxWorkers=2 --verbose __tests__/components/RtlLayout.test.tsx` EXIT=0, 5 passed, slowest 14 ms, Time 7.471 s (beforeAll warm-up pattern). Reverse checks via objectstack scripts/ablation-replace.mjs (anchor hit x1 -> x0 on disk, restored blob == HEAD, git diff HEAD empty each time): lib/i18n.ts base-fallback line removed -> 23 failed/45 passed; locales/ko.json key renamed -> key-parity test 1 failed listing missing+extra; app/server-config.tsx direction forced ltr -> RtlLayout 2 failed. No dist/build involved (jest runs source).", "gates": "Local, on 878238e, exit captured before any pipe: `npx tsc --noEmit` EXIT=0; `npx eslint . --ext .ts,.tsx` EXIT=0; `npx jest --ci --coverage` EXIT=0 (lock VERDICT command-exit 0). dispatch-gates --commands/--ran not applicable: objectstack-only tool, mobile has no scripts/pm; gate list taken from the dispatch order. PR #169 check-runs read once at open: `Code Quality: in_progress`, `Lint & Type Check: in_progress` (not waited on).", "line_budget": "n/a — diff touches no skills/** file", "files_changed": [ ".changeset/159-ten-locales.md", "__tests__/components/RtlLayout.test.tsx", "__tests__/lib/i18n.test.ts", "app/_layout.tsx", "app/server-config.tsx", "components/shell/OfflineFallback.tsx", "docs/HYBRID-SHELL.md", "lib/i18n.ts", "lib/rtl.ts", "locales/de.json", "locales/es.json", "locales/fr.json", "locales/ja.json", "locales/ko.json", "locales/pt.json", "locales/ru.json" ], "deviations": [ "Merge commit 878238e (origin/main 29c25df into the branch) carries git's default message with no Claude-Session/Co-authored-by trailer pair; the three task commits carry the model-free pair per os-dev.md, not the harness's model-named Co-Authored-By form.", "「需要你做的」 is written as ONE action (a device + native-speaker acceptance session) because os-dev.md requires exactly one action, while the dispatch named two items; both items are inside that one line.", "__tests__/lib/i18n.test.ts carries its own AppState module mock with a `default` export: jest.setup.ts (outside the file surface) mocks it without one, so `AppState` from react-native is undefined in every test.", "No local dogfood / test:repo / check:* families: mobile repo, not objectstack." ], "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0 — no MCP GitHub tools called", "api_writes": "2 relay writes — (1) fleet-write dispatch pr_create -> POST /repos/objectstack-ai/mobile/pulls (draft, no labels, no assignees; run 41449254218 success, read-back 9732/9732 bytes identical); (2) post-stamped report comment -> POST /repos/objectstack-ai/mobile/issues/159/comments. Plus `git push` (not REST). Reads: gh api GETs on issue 159 + comments, objectstack issue 29349 + comments, mobile pulls list, #168 files, PR 169, check-runs.", "open_questions": [ { "question": "Should the shell declare its ten localizations to the OS (expo-localization plugin `supportedLocales` in app.config.ts)? NOT MEASURED: on iOS the WebView's Accept-Language may follow the app's declared localizations (none today), so a signed-out Console could stay English while native screens follow the phone.", "options": [ "A: verify on device first (the maintainer device check in the PR covers it), then file a card if the Console stays en", "B: add supportedLocales now in a follow-up card touching app.config.ts" ], "recommendation": "A, because it is an unmeasured inference and app.config.ts is outside this card's surface." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted in PR Acceptance notes, not filed — jest.setup.ts AppState mock lacks `default`, so react-native's AppState getter yields undefined in all tests (test-infra; no user-visible defect).", "carrier: none · noted, not filed — iOS WebView Accept-Language vs undeclared app localizations (unmeasured inference; see open_questions).", "carrier: #156/#168 · noted — #168 adds keys to en/zh/ar only; whichever of #168/#169 lands second must carry the other's keys into all ten bundles; the key-parity test in __tests__/lib/i18n.test.ts reds otherwise." ] }objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
ACCEPT → PR #169 (head
878238e). Ready for review; the maintainer is requested. The maintainer merges.objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Landed. #169 merged as
13d5b01, merged byos-zhuangat 13:11:45Z, from headd01b682(check-runs green).Post-merge review of the merge round. The maintainer merged before the seat reviewed this round. ACCEPT 6577858046 covered
878238e. The seat then read what landed onmainat13d5b01:serverConfig.scan,serverConfig.backTo,scanner.*andpermissions.camera. Each was read per file.app.config.tsdeclares the iOS camera prompt in all ten languages, pluszh-Hant, which maps tozhas the Console does (PERMISSION_LOCALES,app.config.ts:21-33).mainat13d5b01was still running at this reading.Verdict: no follow-up needed.
pm:dispatchedremoved.[-]Languages: follow the phone's language and match the Console UI's ten locales one-to-one[/-][+]Languages: follow the phone's language in the platform's 11 locales (ruling F of objectstack#29349)[/+]