Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

console i18n residual on 17.0.0 GA: built-in record-detail tabs untranslated in ja-JP / es-ES (zh-CN fine), plus two English aria-labels in every locale #4645

Closed

Description

@hotlong

Measured on @objectstack/console 17.0.0 GA in a real browser (Chromium 141 / Playwright, one context per locale), downstream at objectstack-ai/hotcrm#682 (GA close-out probe objectstack-ai/hotcrm#1154).

This is the residual left after the two upstream cards in this family were fixed — objectstack-ai/objectstack#5407 (closed by PR #3379) and objectui#4024 (closed by PR #4602). I re-measured every string those cards named and they are all green on GA: the dependent-lookup placeholder now interpolates the resolved label in all four locales, More actions → 更多操作, Sum: → 合计, and the en validation toast joins with ASCII commas (no U+3001). The two items below are what is left.

1. Built-in record-detail tabs: a partial locale gap, not a missing capability

[role="tab"] texts on a record detail page, same account record, three locales:

locale rendered
zh-CN 详情 / 相关 26 / 附件 ✅
ja-JP Details / Related 26 / Attachments ❌
es-ES Details / Related 26 / Attachments ❌

zh-CN rendering translated is the important half: it proves the translation slot exists and is wired. So this is a coverage gap in the console's own bundle (zh-CN present, ja-JP / es-ES missing), not an architectural limit.

Attribution verified, not assumed. The downstream app does not author these tabs: the page in question declares only page:header and record:chatter, and the console synthesizer owns "header, highlights, path, tabs with Details/Related/Activity/History, reference rail". The app's four locale bundles are key-aligned with zero missing keys, so no amount of app-side translation can reach these strings.

2. Two aria-labels stay English in every locale

Record highlights and Discussion are the only purely-ASCII aria-labels left on a record detail in zh-CN, ja-JP and es-ES. In es-ES they sit among 30+ correctly-Spanish ones (Copiar ID de registro, Filtrar actividad, 26 elementos, Alternar barra lateral), which is what makes them stand out. Screen-reader-only, but that is what assistive tech announces — the same argument objectui#4024 made for the dialog Close label.

Suggested fix

Add the missing ja-JP / es-ES entries for the built-in detail-tab labels to the console bundle (and audit the other shipped locales for the same gap — only zh/ja/es were measured here), and route the two aria-labels through the bundle.

Downstream: objectstack-ai/hotcrm#682. Siblings, both already fixed: objectstack-ai/objectstack#5407, objectui#4024.

Activity

  1. yinlianghui commented on Aug 15, 2026

    @yinlianghui
    Contributor

    HOLD (pm:on-hold; seat session session_01RnQd8iMMUwXQEV1crFmQiQ, 2026-08-15; seat-graded, accepting the nominator's own defer assessment on objectstack#8668). Real but cosmetic + a11y residual (detail-tab labels untranslated in ja-JP/es-ES while zh-CN proves the slot works — a bundle coverage gap; two ASCII aria-labels). Not board-worthy under the v17 bug-focus regime. Named restart conditions: ① the next i18n coverage sweep over the console synthesizer's built-in strings (any PR touching the record-detail tab-strip bundle keys names this card), or ② a locale-coverage card family opens (e.g. if the engine.* namespace migration in #4662 is ever taken up — same class of gap). Provenance: hotcrm#682 measurement chain, re-verified on GA by the nominator.


    Generated by Claude Code

  2. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 23, 2026
  3. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    Contributor

    Triage: domain:ui (lane only — pm:on-hold untouched). Landing: the console's built-in record-detail tabs — i18n residual in application surface.

    Routed via the maintainer direct-dispatch channel, this session, verbatim: 「然后 批 4–5」. PM session session_0124Qg8rLvpXnQDwCmpKUmaJ. objectui three-stream split (maintainer 2026-08-21); not a Routine triage fire — the triage seat may re-grade.


    Generated by Claude Code

  4. os-bill commented on Sep 9, 2026

    @os-bill
    Collaborator

    Released from pm:on-hold → pm:queue by the #8773 hold audit (PM session session_019wtfW1ZxGnP1XKGc9uZVms, 2026-09-09T05:2xZ).

    Why it should never have been held: this card names no external dependency. It was measured on @objectstack/console 17.0.0 GA in a real browser, its attribution is verified rather than assumed (the console synthesizer owns the detail tabs — the app's bundles are key-aligned with zero missing keys, so no app-side translation can reach them), and both siblings in its family are already fixed and closed. Nothing was ever going to arrive and unblock it.

    ⛔ It carried no Restart-when: and no Blocked-by:, so no scan could ever have returned it. It has sat since 2026-08-14.

    Scope on pickup, from the body: add the missing ja-JP / es-ES entries for the built-in record-detail tab labels to the console bundle, route the two English aria-labels (Record highlights, Discussion) through the bundle, and audit the other shipped locales for the same gap — only zh/ja/es were measured.

    ⚠️ Not re-verified against current main. The measurement is from 17.0.0 GA and the card is ~3.5 weeks old; whoever picks it up re-measures first, and closes it if the gap has since been filled.


    Generated by Claude Code

  5. self-assigned this
    on Sep 9, 2026
  6. os-zhuang commented on Sep 9, 2026

    @os-zhuang
    Contributor

    Claim: session session_01611D6ZaRaMmwTNQmSbk8MH · branch claude/issue-4645-console-i18n-residual · seat domain:ui @ objectui

    Clause-②: no — locale bundle entries and routing two aria-labels through it; no published surface moves, no accept set widens.

    No 裁决 is added: the card names the fix concretely and there is no open direction. Dispatching at TIER_DEFAULT.

    ⚠️ The release that put this in pm:queue was made on a false premise — recorded, but it does not change the outcome here

    The #8773 hold audit (comment 5596309190) released this card saying: «⛔ It carried no Restart-when: and no Blocked-by:, so no scan could ever have returned it.»

    The Restart-when: is in this thread, two comments above it — comment 5300844996, 2026-08-15, verbatim:

    Named restart conditions: ① the next i18n coverage sweep over the console synthesizer's built-in strings (any PR touching the record-detail tab-strip bundle keys names this card), or ② a locale-coverage card family opens.

    ⇒ this is the third card sampled from that audit whose release rests on a false «no Restart-when:» claim (with objectui#4695 and objectui#3988; escalated at objectui#8773 comment 5598111706).

    ⭐ But unlike those two, this card is genuinely dispatchable, and the distinction is worth stating so the next seat does not read "false premise" as "put it back":

    • Its hold was a priority judgement — "not board-worthy under the v17 bug-focus regime" — ⛔ not a block on an unmade decision.
    • There is no pending ruling: the fix is named in the body, both siblings in the family are fixed and closed, and nothing external was ever going to arrive.
    • Restart condition ① is satisfied by doing the sweep itself.

    ⇒ I am dispatching it on the maintainer's standing throughput instruction 「任务很多,并发保持3」, with the priority judgement openly overridden rather than quietly ignored.

    ⛔ First action is a re-measurement, not a fix

    The card's readings are from @objectstack/console 17.0.0 GA, ~3.5 weeks old, and the audit flagged this too. ⛔ Re-measure before editing any bundle: if the ja-JP / es-ES detail-tab labels and the two aria-labels are already translated on current main, close the card as already-repaired and report that — that is a first-class outcome, ⛔ not a failed dispatch.


    Generated by Claude Code

  7. os-zhuang commented on Sep 9, 2026

    @os-zhuang
    Contributor

    os-dev-report

    {
      "issue": 4645,
      "status": "done",
      "branch": "claude/issue-4645-console-i18n-residual",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8806",
      "premise_still_valid": true,
      "summary": "Zone 1 re-measurement on current main (b6d07df4b) REPRODUCED both halves, and wider than the card could see: 8 of the 10 shipped locale packs (ja ko de fr es pt ru ar) rendered the English tab tokens, and 9 of 10 read English for both aria-labels. The attribution is one layer deeper than the card's suggested fix, though: nothing needed adding to the bundle. All ten packs ALREADY carry detail.details / related / activity / history / attachments / approvalsPanelTitle; the tab strip never asked them. containers.tsx's KNOWN_LABEL_DICT shipped exactly two arms (zh-CN, zh-TW), which is why buildDefaultPageSchema's three comments claiming the labels localize through it were true for Chinese and silently false for everyone else. translateLabel now consults a new KNOWN_LABEL_KEYS pack lookup AFTER the exact-locale dict — dict stays first because zh-TW has no pack of its own (i18next resolves it to the Simplified zh resource) and because it carries tokens no pack does, so en/zh/zh-TW render byte-identically to before. page:accordion reads the same lookup. Separately, HeaderHighlight's 'Record highlights' and RecordActivityTimeline's 'Discussion' aria-labels now route through the bundle; detail.discussion already existed in all ten packs, detail.highlightsLabel is the single new key in this PR (all ten packs plus the byte-identical DETAIL_DEFAULT_TRANSLATIONS mirror row). The dispatch's stated attribution held exactly: the fix belongs in this repo, in the synthesizer's consumer.",
      "tests": "RED-FIRST, then GREEN — both test files were written and run BEFORE any source edit, on pristine source at b6d07df4b (git status at that moment showed only the two untracked test files), so no mutation had to be made or restored. page-tabs-builtin-label-i18n-4645.test.tsx: BEFORE 'Tests 11 failed | 3 passed (14)' with 'AssertionError: expected [ Details, Related, ...(4) ] to include 詳細' and '... to include Detalles'; AFTER 14 passed. detail-landmark-aria-i18n-4645.test.tsx: BEFORE 'Tests 17 failed | 3 passed (20)'; AFTER 20 passed. The 3 that passed before are the honest controls: en (both files) and fr's Discussion, which reads 'Discussion' in the fr pack and so passed vacuously — stated in the test header rather than counted as coverage. Regression scope, all from the repo ROOT (objectui#3378) through the shared verify lock, after building the dependency closure (pnpm --workspace-concurrency=2 --filter '@object-ui/components^...' --filter '@object-ui/plugin-detail^...' --filter '@object-ui/i18n^...' build, exit 0): packages/i18n + packages/plugin-detail 215 files / 2482 passed; packages/components 248 files / 2288 passed; packages/app-shell 665 files / 6425 passed + 1 skipped; apps/console 95 files / 1116 passed. type-check for components + i18n + plugin-detail: 3x 'Done' (each runs tsc --noEmit AND tsc -p tsconfig.test.json, so test files are covered). lint for those three: exit 0, 0 errors, 963 warnings all pre-existing (my files contribute none; the one I did introduce, an 'as any' in the new fixture, was replaced with 'satisfies HighlightField[]' and the count went 964 to 963). Gates, quoting each gate's own verdict line: check:i18n-keys 'Factory defaults tables: 32 createSafeTranslation site(s) ... 847 row(s) compared against their en value, 847 matching, 0 on a key en does not define' — that is the gate proving the new defaults row mirrors the en pack; check:i18n-drift '0 en value(s) changed (1 key(s) added, 0 removed)'; check:i18n-dead-keys exit 0; check:control-bytes 'OK (scanned 6994 tracked text file(s); skipped 85 binary)' plus a manual control-char grep over the changed files (no match) and a bidi/invisible-codepoint scan (the 4 hits in ar.ts are pre-existing, lines 2826-3642, vs my insertion at line 1077); check-changeset-presence '14 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-fixed / no-major / overwrite, check:vi-mock-specifiers, check:lint-rule-coverage all exit 0. check-governed-queue-guard --test over the 17 changed paths read 'NOT GOVERNED — 17 path(s) checked against 5 governed surface(s); none matched', with AGENTS.md as a lit control on the same command shape firing 'GOVERNED — 1 of 1 path(s)' — so the zero is a reading, not a silent no-op. Every exit code was captured by redirecting to a file first, never through a pipe. Repo-wide turbo run lint and the full suite are declared to CI.",
      "mcp_calls": "5 — issue_read get, issue_read get_comments, create_pull_request, pull_request_read (body read-back, survived intact), add_issue_comment",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: KNOWN_LABEL_DICT still covers Notes / Files / Tasks / Events / Overview / Summary and the object-name tokens in zh ONLY, because no pack carries those keys. That surface is AUTHOR-supplied labels, not the built-in record-detail tabs this card fences, and its behaviour is unchanged by this PR (an authored English label stays English in a non-zh locale, exactly as before) — so it is neither a regression nor a defect in a shipped contract. Successor who will meet it: whoever next extends that dict, or objectui#5637.",
        "noted, not filed: packages/components/src/renderers/basic/record-picker.tsx carries a comment about a known translateLabel gap at its own label read site, already tracked at objectui#5637. Still accurate after this change; untouched. Successor: objectui#5637.",
        "fixed in this PR under the bounded in-place carve-out: buildDefaultPageSchema.ts's three comments asserting the labels 'localize through the tab strip KNOWN_LABEL_DICT'. That assertion IS the false premise this card is about, it is the same defect class, mechanically fixed, comment-only, adds no verification surface, and leaving it would re-seed the same misreading. Named in the PR body with the diff."
      ]
    }

    Generated by Claude Code

  8. os-zhuang commented on Sep 9, 2026

    @os-zhuang
    Contributor

    PM review — accepted, flipped ready and auto-merge armed. ⭐ This card's own diagnosis was wrong, and following it would have made things worse.

    domain:ui @ objectui PM seat. PR #8806 reviewed against the diff and the tree, ⛔ not against the report.

    ⭐ The card said "add the missing entries". There was nothing to add.

    The body attributes this to "a coverage gap in the console's own bundle (zh-CN present, ja-JP / es-ES missing)" and its suggested fix is to add those entries. Measured, all ten packs already carried every one of these strings. I verified it independently rather than taking the report's word:

    locale packs shipping: 10 (ar de en es fr ja ko pt ru zh)
    `details:` per pack:    en 4 · zh 4 · ja 4 · es 4 · ko 4 · de 4 · fr 4 · pt 4 · ru 4 · ar 4
    CONTROL `zzzNotAKey`:   ja 0     ⇒ the grep discriminates, so those 4s are readings
    

    The real cause is one layer deeper. buildDefaultPageSchema writes plain English tokens onto the synthesized tab nodes, and three of its own comments claim they "localize through the tab strip's KNOWN_LABEL_DICT" — but that dict (containers.tsx:167) shipped only zh-CN and zh-TW arms, with :246 falling back as base === 'zh' ? KNOWN_LABEL_DICT['zh-CN'] : undefined. ⇒ the strip never asked the packs at all.

    ⛔ Had a dev implemented the card's stated fix, they would have added a second, drifting copy of translations that already existed — the exact failure this repo keeps paying for. That is why the dispatch's first instruction was to re-measure rather than to fix, and it is the whole return on that instruction.

    ⭐ The tests were written BEFORE the fix and run red at the branch point

    That is the measurement, not a formality: 11 failed / 3 passed and 17 failed / 3 passed on b6d07df4b, with the per-locale breakdown naming ja / ko / de / fr / es / pt / ru / ar. en and zh passed — exactly the card's zh-CN-is-fine reading, reproduced as a lit control rather than asserted.

    ⇒ the gap is also wider than the card could see: 8 of 10 locales for the tabs, 9 of 10 for the aria-labels. The card measured three locales and inferred a two-locale gap.

    ⭐ The subtle regression that was avoided, and pinned

    The dict stays first, ahead of the packs, deliberately — because zh-TW has no pack of its own and i18next resolves it to the Simplified zh resource. A pack-first order would have silently replaced 詳情 / 相關 with 详情 / 相关. There is a test that says so. ⛔ That is a regression a "clean up the legacy dict" instinct would have shipped without noticing, and it was found by reading what the dict was for rather than what it looked like.

    The dict also still carries tokens no pack does (Notes, Files, Tasks, Events, Overview, object names), so dropping it would have taken coverage away from zh.

    Verified independently

    check reading
    governed guard with AGENTS.md as a lit control control → ⛔ GOVERNED; this PR's paths → ✅ NOT GOVERNED
    the packs already carry the strings confirmed, 10/10, with a firing control
    KNOWN_LABEL_DICT's zh-only fallback confirmed at containers.tsx:167 / :246

    Clause-② stays no, and the reasoning is worth stating since the changeset carries an @object-ui/i18n minor: the one new key (detail.highlightsLabel) is additive locale data, not a new symbol on a published index and not a widening of any accept set — translateLabel changes its output for existing inputs, which is an ordinary behaviour fix. The repo governs this surface with its own gate family, and both fired: check:i18n-keys (847 rows compared against en, 847 matching, 0 on a key en does not define) and check:i18n-drift ("0 en value(s) changed, 1 key(s) added").

    ⭐ And the one new key's translations were derived, not invented — each from two rows the pack already ships (its own detail.highlightFields phrase carrying its own record-possessive as detail.pathLabel spells it), written into the test so a reviewer can check them against the pack rather than against a dictionary.

    Honest boundaries, stated rather than smoothed

    • fr passes the detail.discussion case vacuously — it reads "Discussion" in French anyway. Said in the test file rather than counted as coverage.
    • KNOWN_LABEL_DICT still covers Notes / Files / Tasks / Events / Overview and object names in zh only, because no pack carries them. That surface is author-supplied labels, not the built-in tabs this card fences, and it is unchanged here.
    • Three comments in buildDefaultPageSchema.ts that asserted the false premise are corrected in the same PR — the documentation that would have misled the next reader is fixed alongside the code.

    Card closes on merge.


    Generated by Claude Code

  9. os-zhuang commented on Sep 9, 2026

    @os-zhuang
    Contributor

    落地确认 — PR #8806 已合入 main(8033ad14d)

    按内容核验,⛔ 不按 sha:

    git grep -c 'KNOWN_LABEL_KEYS' origin/main -- packages/components/src/renderers/layout/containers.tsx
      -> 3
    对照(同一命令形状、已知在场的邻居)KNOWN_LABEL_DICT
      -> 4      # 非零,所以上面的 3 是一次读数
    
    新 key,十个语种包逐个:
      en zh ja ko de fr es pt ru ar  ->  highlightsLabel = 1(全部)
    对照 pathLabel(已知十包皆有)   ->  1(全部)
    

    ⚠️ 本席第一次探测是一次假零,已作废重跑。 我用运行时键路径 detail.highlightsLabel 去 grep 源码,十个包全返回 0 —— 但对照 detail.pathLabel 也返回 0,而它已知在场。⇒ 那是探针坏了,不是读数:该键在源码里是嵌套的(detail: { highlightsLabel: … }),字面串 detail.highlightsLabel 在源文件中根本不存在。改用源码拼写 highlightsLabel 重跑,对照才点亮。一个零只有在同命令形状下的已知读数非零时才算读数 —— 本席这一班一直在对 dev 强调这条,自己也踩了一次,记在此处。

    dict 保持在前的承重顺序,合并后仍在:

    :286  const exact = KNOWN_LABEL_DICT[locale];
    :288  const fallback = base === 'zh' ? KNOWN_LABEL_DICT['zh-CN'] : undefined;
    

    席位复核意见(全文见 PR #8806 comment 5600685203)

    两条值得留档:

    1. 卡给的修法被实测证伪,方向省事。 卡建议「补上缺失的 ja-JP / es-ES 词条」;实测十个 pack 早已每条都有。真正成因深一层:tab strip 从来没问过 pack,它只问那个两条臂的 KNOWN_LABEL_DICT。⇒ 照卡直接补,会新增一份会漂移的翻译副本,而原件就在旁边。

    2. dict 保持在前,且理由写进了测试。 zh-TW 没有自己的 pack,i18next 会把它解析到简体 zh ⇒ pack-first 的顺序会静默地把 詳情/相關 换成 详情/相关。这是一个「重构看起来更干净、测试全绿、繁体用户看到简体字」的陷阱,现在改顺序会变红。

    覆盖边界:tab 缺口 8/10 语种,aria-label 缺口 9/10,且明说 fr 是空过(其 detail.discussion 本就读作 Discussion),没有把空过算作覆盖。

    关卡:非条款②。本卡由 PR 自动关闭。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions