Skip to content

Languages: follow the phone's language in the platform's 11 locales (ruling F of objectstack#29349) #159

Closed

Description

@hotlong

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.ts detectDeviceLanguage). They ship only en, zh and ar, with 25 keys each.
  • The Console in the WebView resolves the user's sys_user.locale, then Accept-Language, then the deployment default (objectstack ui/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

  1. The 11 locales, with the table's tags. Ship the shell's strings in all 11, keyed by the tags above. Rename the current zh bundle to zh-Hans. Generate zh-Hant from zh-Hans with a phrase-aware converter, as objectstack-ai/www.objectos.ai scripts/gen-zh-hant.mjs does, then review it.

  2. Resolution from the phone. Walk the phone's ordered preferred languages and pick the first supported one:

  3. Right-to-left for Arabic. Keep lib/rtl.ts, and make the native screens lay out right-to-left under ar, including the switcher sheet, the confirm screen and the sign-in bar.

  4. Live change. The native screens follow a change of phone language on the next foreground.

  5. No in-app language picker on native screens. The phone setting is the source; the Console keeps its own per-user language.

  6. Docs. docs/HYBRID-SHELL.md states 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-CN and zh-SG → zh-Hans;
    • zh-TW, zh-HK and zh-Hant → zh-Hant;
    • pt-PT and pt-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

  1. changed the title [-]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[/+] on Nov 2, 2026
  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    Contributor

    repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv

    First touch. This card was filed behind gate #150, which is now closed. It goes to pm:queue as type enhancement.

  3. hotlong commented on Nov 2, 2026

    @hotlong
    ContributorAuthor

    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 full locales 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

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    Contributor

    repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv

    Claim: PM loop round 1
    Session: session_01QSeKYnW1Xf6HDSWz1eGAqv
    Account: os-justin
    Branch: claude/issue-159-ten-locales
    Worktree: mobile-issue-159
    Domain: n/a — single-lane repo (repo:mobile)
    Seat: repo:mobile#1
    File 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: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: 6577353170
    Premise: holds. main at 83d4716 ships locales/{en,zh,ar}.json, and lib/i18n.ts:26 has SUPPORTED_LANGUAGES. objectstack ui/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.

  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    Contributor

    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."
      ]
    }
  7. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    Contributor

    repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv

    ACCEPT → PR #169 (head 878238e). Ready for review; the maintainer is requested. The maintainer merges.

  8. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    Contributor

    repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv

    Landed. #169 merged as 13d5b01, merged by os-zhuang at 13:11:45Z, from head d01b682 (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 on main at 13d5b01:

    • All ten bundles carry the keys feat(connect): objectos://connect links and the QR scanner (screen 6) #168 added: serverConfig.scan, serverConfig.backTo, scanner.* and permissions.camera. Each was read per file.
    • app.config.ts declares the iOS camera prompt in all ten languages, plus zh-Hant, which maps to zh as the Console does (PERMISSION_LOCALES, app.config.ts:21-33).
    • CI on main at 13d5b01 was still running at this reading.

    Verdict: no follow-up needed. pm:dispatched removed.

  9. changed the title [-]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)[/+] on Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions