Repository navigation
Description
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
Filed on the maintainer's word, verbatim: 「立卡」 (2026-11-05), answering their question 「如果元数据app中只有10种语言,系统管理员可以增加新的语言并且处理国际化吗?」 and the proposal below. Measured in a browser on main 04f36b63f8, with a vi-VN and a fr-FR translation item in an app's .custom package.
What a customer sees
An administrator adds Vietnamese for their branch. The result works: labels apply at once on publish, and the language menu gains Tiếng Việt. Getting there does not suit an administrator.
- New form. It asks only for language, label and name.
- Item page. It is about fifteen JSON boxes (Objects, Picklists, Apps, Messages, Global Actions and so on) with English help text. To change one label they must write
asset_repair_ticket→fields→priority→labelby hand, knowing the internal names. - Developer text on screen. Under every box: 「组件 object-fields 尚未注册自定义渲染器,暂时使用 JSON 编辑。」
- No source text. The page does not show what is being translated.
- No gap view. The page I opened shows nothing about what is left.
TranslationPreview.tsxexists, but by its own header its coverage counts groups with at least one entry, not missing strings. - Wrong source. A translation the administrator wrote is listed with the source 「代码包」.
#28569 delivered this editor's place in Studio and the customize path. This card is about what the editor is once you are in it.
Expected
- A table for one language: source text, translation, and a filter for untranslated rows, grouped by object, field, option, app and so on. The rows come from what the package actually declares, using the same walk as
os i18n extractandcomputeI18nCoverage, so a key cannot be misspelled. - A count of what is left, for example 37 of 120 untranslated.
- Saving writes the same
translationitem in the same place,.customon a read-only package (ADR-0146 D2). No new storage. - The internal-only notice no longer appears on this page. JSON stays available as an advanced view for what the table does not cover.
- The list shows a customer-written translation as customer-written.
Measured nearby
A messages entry for common.create and common.new in the vi-VN item did not change the console's 新建 button, which stayed English. I did not find out why. The table should not offer strings that cannot take effect, so the dev should settle this first: either these keys are overridable and this is a defect, or the table leaves console chrome out. Either way the card must say which.
Not verified
How expensive the walk is on a large app when it runs in the browser or on the server. I did not measure that.
Activity
objectstack-fleet commentedon Nov 5, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 ·
domain:ui·pm:queue· 2026-11-05T05:57Z⛔ Not a claim.
Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
One more measured gap for this table, on
main04f36b63f8: the labels of platform objects such as 用户 have bundles for en, zh-CN, ja-JP and es-ES only (packages/platform-objects/src/apps/translations). Under fr-FR or vi-VN they read in English unless the administrator writes each one, and an administrator cannot see which they are. The rows the table shows should include them.objectstack-fleet commentedon Nov 5, 2026
domain:ui#1 · os-support-ai · session_01WGufzfE4V9fzZgJLrLDmMC
Claim: PM loop round 6
Session:
session_01WGufzfqCqBH59yrHmuqXH3Account:
os-support-ai(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/project-thread-eme43kWorktree:
objectstack-issue-30285Domain:
domain:uiSeat:
domain:ui#1File surface: the Studio translation item editor and list (
ui/packages/app-shell/src/views/studio-design/StudioBundleItemEditor.tsx,views/metadata-admin/previews/TranslationPreview.tsxand the translation item form parts), a pure shared row-walk module if the CLI walk (packages/cli/src/utils/i18n-extract.ts/i18n-coverage.ts) must be reused in the browser, their strings and tests, changesets; ⛔ no new storage, no new REST route, no spec key (stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT(p2),effort: highClause-②: no
Metadata-protocol: none
Responsibility: the Studio translation editor shows raw JSON boxes with no source text, no gap view and a wrong source label | the package already declares every translatable key (the
os i18n extractwalk) | every administrator adding a language the console does not ship, today (workaround: hand-written JSON keys)Thread-read: 6650943346
Premise: holds (the translation item page is JSON boxes with the 「尚未注册自定义渲染器」 notice on
origin/maincfb602c4, per the card's browser reading on04f36b63f8)Serial constraints cleared: none — no open PR touches
StudioBundleItemEditor.tsx/TranslationPreview.tsx(read this act).objectstack-fleet commentedon Nov 5, 2026
domain:ui#1 · os-support-ai · session_01WGufzfE4V9fzZgJLrLDmMC
Landed · PR #30375 (PR-A of 2) · 2026-11-05T11:46Z
8293c74df9at 2026-11-05T11:24:11Z via the merge queue (seat ACCEPT on PR #30375;check-governed-merges --pr 30375: not governed, 1561 lines, ordinary landing)origin/mainpackages/lint/src/i18n-walk.tspresent;collectExpectedEntriesexported frompackages/lint/src/index.ts@objectstack/lint, so a browser can run it.Fixes #30285), same card thread, branch merged withmainbefore it opens. The card stays open andpm:dispatched.objectstack-fleet commentedon Nov 5, 2026
domain:ui#1 · os-support-ai · session_01WGufzfqCqBH59yrHmuqXH3
os-dev-report
Stamp: 2026-11-05T12:10Z
{ "issue": 30285, "status": "done", "branch": "claude/project-thread-eme43k", "pr": "https://github.com/objectstack-ai/objectstack/pull/30457", "prs_in_sequence": [ "https://github.com/objectstack-ai/objectstack/pull/30375 (PR-A, Part of #30285, merged as 8293c74d)", "https://github.com/objectstack-ai/objectstack/pull/30457 (PR-B, Fixes #30285, draft, head c488a30b on main f7afab86)" ], "session": "session_01WGufzfqCqBH59yrHmuqXH3", "premise_still_valid": true, "summary": "PR-A moved collectExpectedEntries (the walk os i18n extract and os lint coverage use) from packages/cli/src/utils/i18n-extract.ts to packages/lint/src/i18n-walk.ts as a git rename; the CLI keeps bundle assembly in utils/i18n-bundle-extract.ts and re-exports the walk with its 12 runtime names unchanged. PR-B replaces the translation item's per-group JSON boxes in StudioBundleItemEditor with StudioTranslationTable: one row per declared string grouped by item, source text beside the translation, a count 'N of M untranslated' / 'M 条中 N 条未翻译' and an untranslated-only filter. Rows come from the lint walk over the package's declared documents (layered read: code layer, else effective less system:true injected fields), plus the platform objects the items point at, injected owner lookups included (sys_user, sys_organization, sys_business_unit on CRM). A .custom pair is walked with its base. A row is translated by the item's value, by the language's served translations (only when the server answers that language), or by an inline locale map. Cells write the item's JSON at the key, and saving goes through the same guard to the same package (.custom on a read-only base); no route, key or storage change. JSON stays as an advanced view for messages, settingsCommon and metadataForms. The fallback-JSON notice is gone from the page. The item list labels an item of a .custom pair as customer-written (isCustomizationPackage). The lint walk loads lazily, so the console's lazy-linter build assertion holds. 41 new en/zh strings. Console chrome is left out by design: it comes from the console's language packs, not from a translation item (settlement 1, accepted).", "tests": "PR-B: translationTable.test.ts (10), StudioTranslationTable.test.tsx (5), ResourceListPage.sourceLabel-30285.test.ts (4), all passing. App-shell targeted run (19 files incl. ResourceListPage*, StudioBundleItemEditor*, TranslationPreview*, StudioMetadataArea routes) 195 passed. @object-ui/i18n 95 files, 1443 passed / 13 skipped. Type-check app-shell + i18n exit 0. Eslint on the 7 changed files exit 0. Console VITE_BASE_PATH=/ vite build exit 0. Ablations, each red then restored, tree clean: A1 walk not run (4 failed), A2 source text not declared (2), A3 platform objects not walked (5), A4 served-elsewhere ignored (3), A5 .custom source-label check removed (1), A6 effective layer instead of declared (2). PR-A: lint i18n-walk.test.ts (keys, warned group, browser-default parity pin) and cli i18n-bundle-extract-exports.pin.test.ts (exports unchanged, lint identity, type exports); lint + cli unit and typecheck exit 0. Live, zh-CN console on the CRM example with crm_vi (vi-VN) in com.example.crm.custom: list source 运行时; table 402 条中 401 条未翻译; filter; editing objects.sys_user.label to 'Người dùng' gives 400 untranslated and a draft PUT to the pair; after 发布 → 全部发布, /i18n/translations/vi-VN serves the edit. Shots in /mnt/project-files/ui-orders/30285-shots/.", "gates": "PR-B head c488a30b: dispatch-gates derived 35 commands, all 35 exit 0; --ran 35/35, 0 NOT-MEASURED. check-governed-merges --branch: 0 of 10 paths governed, 1328 ≤ 3000. PR-A head f6ecb065: 87/87 exit 0, --ran clean (check-plugin-teardown-shape --self-test needed its pinned fixture commit fetched in the shallow clone). PR-A CI on 6950aec7: 55 success, 9 skipped. PR-B CI: running at report time, not awaited.", "request_count": "CRM, one .custom translation item: 28 list reads (14 types × base and pair), 28 layered reads, 3 platform-object reads, 1 served-translations read = 60, at most 4 in flight, none per row.", "dogfood_battery": "n/a — no ! in either PR title and no narrowing", "mcp_calls": "6 — GitHub MCP reads only (get_job_logs on PR-A's two red CI jobs); no GitHub MCP write. Plus 2 subscribe_pr_activity (session tooling, not GitHub writes).", "api_writes": "4 — relay pr_create #30375, one stamped CI note on #30375 (main's liveness red, #30367/#30368), relay pr_create #30457, and this report; plus git pushes", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: PR-B body · noted, not filed: check:i18n and check:i18n-coverage derive from packages/cli/src/** only, so an edit to packages/lint/src/i18n-walk.ts alone does not derive them locally (CI still runs them); fix is to add that path to their derivation roster. Dedupe words: i18n walk derivation lint i18n-walk check:i18n-coverage", "carrier: PR-B body · noted, not filed: the CRM boot serves locales en and zh-CN only; a published vi-VN translation is served by /i18n/translations/vi-VN but metadata reads under vi-VN fall back to en, so the console shows English. Deployment locale setting. Dedupe words: vi-VN locale fallback served translation not applied", "carrier: PR-B body · noted, not filed: a pair-only translation opened through the base package URL by hand loads an empty draft (the editor reads it at the base); the list links to the pair. Dedupe words: pair-only item base route empty draft StudioBundleItemEditor", "carrier: none · noted: a console vite dev server cannot evaluate @objectstack/lint's dist (node:module browser-external throws), so live checks of the table need a built console; recorded in project memory" ] }Deviations: the card was delivered as two sequential PRs (the walk move first), per the seat's ruling. PR-B was rebased onto main after PR-A's squash merge, so it is a fresh branch with no stacked history. Its two commits were never pushed before that, so no force-push was needed.
objectstack-fleet commentedon Nov 5, 2026
domain:ui#1 · os-support-ai · session_01WGufzfE4V9fzZgJLrLDmMC
Landed · PR #30457 (PR-B of 2) · 2026-11-05T13:02Z
648089da49at 2026-11-05T12:52:10Z via the merge queue (seat ACCEPT on headc488a30b; not governed, 1328 lines; no contract review owed: ui only,Clause-②: no)origin/mainui/packages/app-shell/src/views/studio-design/StudioTranslationTable.tsxpresentcompletedbyFixes #30285;pm:dispatchedremoved in the same stroke. Both halves delivered: the walk in@objectstack/lint(PR refactor(lint): move the i18n translatable-key walk from the CLI into @objectstack/lint #30375,8293c74df9) and Studio's source-to-translation table with its gap count (this PR).en/zh-CNonly, so a metadata read under vi-VN falls back toeneven after a vi-VN translation item is published (/i18n/translations/vi-VNserves it): a deployment locale setting; (2) a pair-only translation item opened by hand through the base package URL loads an empty draft (pre-existing; the list links to the pair); (3)check:i18n/check:i18n-coveragedo not derive frompackages/lint/src/i18n-walk.tssince refactor(lint): move the i18n translatable-key walk from the CLI into @objectstack/lint #30375 (CI still runs them). (2) and (3) are carried in the ui#1 hand-off for the next ui / devx seat.