Skip to content

i18n: an override's own label loses to the base's translation on page, dataset, app and book, and a translation in the .custom pair never wins on any type #30193

Closed

Description

@objectstack-fleet

domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

Carries item 3 of #30062 (label precedence); #30062 keeps items 1, 2, 4 and 5, and item 3's server half (the protocol's getPackaged*Base accessors and the REST table rows) waits on this card.

Filing gate ①: a product defect with a named locus and a measured reach, split from an in-flight p1 card because the fix lives in packages/spec. No domain:* label is set: triage routes it.

Ruling (not open to re-ruling)

On #30062, comment 6631250894, the maintainer: 「…其他全部同意」, which rules acceptance item 3 as the seat proposed: label precedence is a translation in the pair, then the override's own label, then the base's translation. One rule across types, and across runtime and Studio.

Business reason

ADR-0146 D2: an item in the .custom package replaces the base's item. A customer who renames a page or a dataset in their customization sees the old name on a translated locale, so the rename looks like it failed. And a translation they add in the pair is never used, so the documented workaround does not work either.

Defect (read at origin/main b7075d99, not built)

  1. The override's own label loses on four types. In packages/spec/src/system/i18n-resolver.ts, translateView (:1480), translateDashboard, translateReport and translateObject take TranslateDocumentOptions and apply the packagedBase rule (a value that differs from the packaged base is the author's and is kept). translatePage (:3393), translateDataset (:2270), translateApp (:1756) and translateBook (:2441) take only ResolveOptions and apply the catalog translation unconditionally. Matching on the server side, PACKAGED_BASE_ACCESSORS in packages/rest/src/meta-item-read-gate.ts:2954 lists only object, dashboard, view and report. Measured through the API on Declared overrides: a mapping override publishes but is never served, doc and mapping overrides cannot be restored, and an override's label can be hidden by translation #30062 (zh-CN CRM): page crm_welcome label OVpage served as 欢迎页; dataset opportunity_metrics label, description and a measure label served as the base's zh-CN text.
  2. A translation in the pair never wins, on any type. The card thread's reading: the packagedBase rule keeps the original value as soon as it differs from the base, so a translation the pair ships is never applied to an overridden label. Not re-measured by the seat.

Acceptance

  1. All eight translators follow the ruled order: pair translation, then the override's own label, then the base's translation. Pin each type.
  2. A label that is not overridden still takes the base's translation (no regression for uncustomized items).
  3. Studio and runtime read the same answer (the report header case on Declared overrides: a mapping override publishes but is never served, doc and mapping overrides cannot be restored, and an override's label can be hidden by translation #30062: runtime shows the override, Studio showed the base's text).

Reader who acts

Spec lane, the seat that takes it from pm:queue. Then domain:server seat 5 (#28677) dispatches #30062's server half: the protocol accessors for page, dataset, app and book, and the PACKAGED_BASE_ACCESSORS rows.

Dedup

Searched issues, open and closed: "translatePage packagedBase" (1 hit, #28214, a closed thank-you-panel key card, unrelated); "override label translation precedence" (59 hits) and "packaged base translation page dataset label" (45 hits): beyond #30062 itself, only QA runs and closed cards on other keys; none carries this rule.

Re-check

git grep -n "export function translatePage" origin/main -- packages/spec/src/system/i18n-resolver.ts (the signature's options type), and git grep -n -A6 "const PACKAGED_BASE_ACCESSORS" origin/main -- packages/rest/src/meta-item-read-gate.ts.

Activity

  1. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p1 · domain:spec · pm:queue · 2026-11-05T01:05Z

    ⛔ Not a claim. This answers domain:server#5's Request: triage · #30193.


    Generated by Claude Code

  2. added
    area:i18nThe customer's own language, across UI, metadata and notifications
    bugSomething isn't working
    domain:specDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    priority:p1High: required for production / M2
    on Nov 5, 2026
  3. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K

    Claim: PM loop round 14 (p1)
    Session: session_01WGufzfgvGexKn9vLHsy89K
    Account: os-support-ai
    Branch: the card thread's own claude/project-thread-* branch, named in the dev's hand-back and restated in a later claim (a measured platform exception to claude/issue-30193-…)
    Worktree: objectstack-issue-30193
    Domain: domain:spec
    Seat: domain:spec#3
    File surface: packages/spec/src/system/i18n-resolver.ts (the eight translate* functions and TranslateDocumentOptions), its tests, and the spec ledgers an options-type change moves. ⛔ Not packages/rest/src/meta-item-read-gate.ts (PACKAGED_BASE_ACCESSORS) and not the protocol's getPackaged*Base accessors: those are #30062's server half, which domain:server dispatches after this lands. Not the locale-keyed maps' contents.
    Container & model: M, mode:thread, model: TIER_DEFAULT, effort: xhigh
    Clause-②: yes (widening: translatePage, translateDataset, translateApp and translateBook gain the packagedBase option; the dev measures whether a pair-provenance input is needed)
    Metadata-protocol: none (a resolver behaviour and options-type change, no authorable key; the dev confirms with check-changeset-no-major)
    Responsibility: the spec resolver applies the catalog translation over an override's own label on page, dataset, app and book, and never prefers the pair's translation on any type | the packagedBase rule covers view, dashboard, report and object only | every customer who renames a page, dataset, app or book in a .custom package on a translated locale; measured on #30062 (zh-CN CRM)
    Thread-read: 6646733715
    Premise: holds. At origin/main dfafae81, translateView (:1480), translateDashboard (:2101), translateReport (:2590) and translateObject (:4487) read opts?.packagedBase. translatePage (:3393), translateDataset (:2270), translateApp (:1756) and translateBook (:2441) do not.
    Human floor: ruling 6631250894 on #30062 (「…其他全部同意」), item 3: the pair's translation first, then the override's own label, then the base's translation. One rule across types, and across runtime and Studio.
    Serial constraints cleared: no open PR touches i18n-resolver.ts.

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

    @objectstack-fleet
    ContributorAuthor

    domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K

    Claim: PM loop round 14 (p1), restated with the branch
    Session: session_01WGufzfgvGexKn9vLHsy89K
    Account: os-support-ai
    Branch: claude/project-thread-yfsy7x (the card thread's own branch, a measured platform exception to claude/issue-30193-…), off origin/main dfafae81
    Worktree: objectstack-issue-30193
    Domain: domain:spec
    Seat: domain:spec#3
    File surface: packages/spec/src/system/i18n-resolver.ts (the eight translate* functions and TranslateDocumentOptions), its tests, and the spec ledgers an options-type change moves. ⛔ Not packages/rest/src/meta-item-read-gate.ts (PACKAGED_BASE_ACCESSORS) and not the protocol's getPackaged*Base accessors: those are #30062's server half, which domain:server dispatches after this lands. Not the locale-keyed maps' contents.
    Container & model: M, mode:thread, model: TIER_DEFAULT, effort: xhigh
    Clause-②: yes (widening: translatePage, translateDataset, translateApp and translateBook gain the packagedBase option; the dev measures whether a pair-provenance input is needed)
    Metadata-protocol: none (a resolver behaviour and options-type change, no authorable key; the dev confirms with check-changeset-no-major)
    Responsibility: the spec resolver applies the catalog translation over an override's own label on page, dataset, app and book, and never prefers the pair's translation on any type | the packagedBase rule covers view, dashboard, report and object only | every customer who renames a page, dataset, app or book in a .custom package on a translated locale; measured on #30062 (zh-CN CRM)
    Thread-read: 6647321790
    Premise: holds. At origin/main dfafae81, translateView (:1480), translateDashboard (:2101), translateReport (:2590) and translateObject (:4487) read opts?.packagedBase. translatePage (:3393), translateDataset (:2270), translateApp (:1756) and translateBook (:2441) do not.
    Human floor: ruling 6631250894 on #30062 (「…其他全部同意」), item 3: the pair's translation first, then the override's own label, then the base's translation. One rule across types, and across runtime and Studio.
    Serial constraints cleared: no open PR touches i18n-resolver.ts.

  6. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#3 · os-support-ai · session_01WGufzhytXa3Yi3uzE5pvuv
    os-dev-report

    {
      "issue": 30193,
      "status": "done",
      "branch": "claude/project-thread-yfsy7x",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30236",
      "head": "8476878165579f66320292abdddcdbaa156d6149",
      "session": "session_01WGufzhytXa3Yi3uzE5pvuv",
      "premise_still_valid": true,
      "summary": "New TranslateDocumentOptions.pairBundle plus one shared rule (labelCatalogs / catalogFor in packages/spec/src/system/i18n-resolver.ts): a string the override changed reads the pair's translations only, else keeps its own text; a string it left alone reads, per chain locale, the pair then the deployment catalog. All eight document translators and their nested helpers go through it; translatePage, translateDataset, translateApp and translateBook now read packagedBase. No pairBundle (or one answering no chain locale) gives the answer of today, so no caller's output moves until the serving half passes both (#30062).",
      "tests": "os-verify-lock: pnpm exec vitest run --project local --maxWorkers=2 over the 25 spec test files importing the resolver: Test Files 25 passed (25), Tests 1099 passed (1099), VERDICT command-exit 0 (75 new). Byte-identical run: in-chain empty pair forced on every call, 1023/1024, the 1 red a no-bundle reference-identity assert, closed by the short-circuit. Ablations on disk, each restored: pair never read 41/75 red; interleave dropped 8 red; changed strings reading the merged catalog 16 red. pnpm --filter @objectstack/spec typecheck exit 0.",
      "gates": "dispatch-gates --commands: 78 derived at 84768781, 78 run, all exit 0 (4 first exit 3 PREREQUISITE NOT MET, re-run green after fetching the teardown fixture commit and turbo build: check-plugin-teardown-shape --self-test, check:doc-formula-expressions, check:dual-build-cjs-loads, check:lean-entry-closure); --ran: 78 derived, 78 run, 0 NOT-MEASURED, 0 UNRUN. check-changeset-no-major --event (PR body): exit 0, clause-2 yes (widening), metadata-protocol none, patch. spec build exit 0; check:generated 14/14 up to date. PR check-runs: in_progress at report time.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "5 — mcp__github__issue_read x4, mcp__github__pull_request_read x1; no write tool",
      "api_writes": "2 — fleet-write relay POST /repos/objectstack-ai/objectstack/dispatches (pr_create, #30236, read back identical); this comment via post-stamped.mjs (POST /repos/objectstack-ai/objectstack/issues/30193/comments)",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: b · reach: Console header report crumb, ui/packages/i18n/src/useObjectLabel.ts:489 reportLabel (and :482 pageLabel) reads the catalog key before the served label, re-applying the base's translation over the server's answer · evidence: PR #30236 body, Carried · dedupe words: reportLabel, served label, catalog first, override header",
        "carrier: PR #30236 body (Carried) · noted, not filed — the seat files the ui card"
      ],
      "deviations": [
        "PR body: the _Requested by_ line sits on line 3, after Fixes #30193, because os-dev puts Fixes right after the harness attribution comment",
        "the harness's model-named co-author trailer is not used; commits carry the model-free pair",
        "the harness's PR subscription and assignee steps are skipped: pr_create takes no assignees or reviewers and the dispatch contract owns the PR after it opens",
        "first push refused by check:commit-card-trailers (a Refs line in an unpublished commit body); the tip was amended before any push of it",
        "accepted per the seat's design ruling: object validations[].message and inline actions stay on the merged bundle"
      ]
    }

    Stamp: 2026-11-05T02:54Z


    Generated by Claude Code

  7. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K

    Accepted: PR #30236, head 84768781. The ACCEPT is ; the contract review is 6648581045 (PASS), both on the PR. It lands through the merge queue. Follow-ups filed: #30244 (Console header re-translates served labels, ui p2) and #30245 (validation messages and the pair, spec p3); the server half stays on #30062.

  8. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K

    Landed: PR #30236 merged at bb86ff96. The spec resolver now orders every translatable label on an overridden item the same way for all eight types: the .custom pair's own translation, then the override's own text, then the base's translation. A label the override did not change keeps the base's translation. The pair's translations come in through the new optional TranslateDocumentOptions.pairBundle; page, dataset, app and book now read packagedBase. Without those inputs the output is unchanged, so users see nothing new until the server passes them. The card closed by Fixes, and the seat removes pm:dispatched.

    Carried:

    Posted 2026-11-05T03:39Z.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:i18nThe customer's own language, across UI, metadata and notificationsbugSomething isn't workingdomain:specDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions