Skip to content

Commit 51d74ad

Browse files
fix(platform-objects,cli): 补齐 Setup 运行时贡献导航的四语翻译,并把覆盖判定移到合并后的 app 元数据上 (#5750) (#6661)
zh 界面下 Setup 侧边栏 51 条里有 4 条仍是英文,服务端发出的合并后 app 元数据 本身就是英文。两处成因:`nav_packages` 的翻译写在了 `apps.studio.navigation` 名下(Setup 查的是 `apps.setup.navigation`,落空回退到作者的英文字面量),另外 三条 —— `nav_approval_delegations` / `nav_webhooks` / `nav_http_deliveries` —— 在任何 locale 都不存在。 真正值得记一笔的是两道闸门之间的交接:Setup 的导航由 `SETUP_NAV_CONTRIBUTIONS` 和各能力插件在运行时贡献(ADR-0029 D7),静态走查看不到;而 parity test 注释说 「交给 coverage ratchet」,extract config 注释也说「交给 coverage ratchet」, 那道 ratchet 走的却是静态配置。它记的 0 不是「查过了,干净」,而是「没查到这里」, 整个过程报绿。 新增 `pnpm check:app-nav-i18n`(`packages/cli/scripts/check-app-nav-i18n.mjs`, 已接入 lint.yml):启动真实组合,按 `/api/v1/meta/app` 同一条 `applyNavContributions` 路径合并导航,断言每个合并后的 nav id 在每个 locale 都有 label。它同时在任一声明 的贡献方一条 nav id 都没落地时报红 —— 合并出的 id 少了就是被检查的 id 少了, 这个方向会让闸门变绿而不是变红。 顺带被新闸门查出、单 locale 复现看不到的另外四条:`nav_capabilities` / `nav_settings_localization` / `nav_settings_company` / `nav_datasources` 只在 zh-CN 有翻译,ja-JP 与 es-ES 同样显示英文。八条现已在四个 locale 全部补齐。 两处把责任交给 ratchet 的注释已改写为指向真正的所有者。插件 nav 的 `label` 仍 保持裸英文字面量,本 PR 不动这个授权契约。 Claude-Session: https://claude.ai/code/session_01F8q5J1MQyocgtNspb15fSn Co-authored-by: Claude <noreply@anthropic.com>
1 parent f123670 commit 51d74ad

12 files changed

Lines changed: 680 additions & 5 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
"@objectstack/platform-objects": patch
3+
---
4+
5+
fix(platform-objects): translate the Setup app's runtime-contributed navigation, and gate it on the merged app instead of a static walk (#5750)
6+
7+
Under `zh-CN`, four of the Setup app's ~50 sidebar entries rendered in English —
8+
`Packages`, `Delegations (OOO)`, `Webhooks`, `HTTP Deliveries` — and it was not a
9+
client-side fallback: the server's own merged `app` metadata carried the English
10+
literals. Sitting in a screen of Chinese menu items, they read like words that
11+
were simply never meant to be translated.
12+
13+
Two different causes, both now fixed:
14+
15+
- **`nav_packages` was translated in the wrong app's namespace.** A
16+
`nav_packages: { label: '软件包' }` existed under `apps.studio.navigation`.
17+
Setup contributes an entry with the same id (package administration is an
18+
operator concern, ADR-0084) and looks it up under
19+
`apps.setup.navigation.nav_packages` — a different subtree, so the lookup
20+
missed and the author's `'Packages'` literal won. Both entries are legitimate;
21+
the Setup one has been added and the Studio one left alone.
22+
- **The other three had no translation anywhere.** `nav_approval_delegations`
23+
(`@objectstack/plugin-approvals`), `nav_webhooks` and `nav_http_deliveries`
24+
(`@objectstack/plugin-webhooks`) are contributed at runtime by the capability
25+
plugins that own the objects, and no locale file carried a label for them.
26+
27+
Four more were found by the new gate below, invisible to the one-locale browser
28+
session that reported this: `nav_capabilities`, `nav_settings_localization`,
29+
`nav_settings_company` and `nav_datasources` were translated in `zh-CN` **only**,
30+
so `ja-JP` and `es-ES` menus showed English there too. All eight ids are now
31+
labelled in all four locales (`en`, `zh-CN`, `ja-JP`, `es-ES`).
32+
33+
**Why nothing caught it, which is the part worth keeping.** The Setup app is a
34+
shell of empty group anchors whose entries arrive at runtime (ADR-0029 D7), so a
35+
static walk sees none of them. Both existing gates knew this and each named the
36+
*other* as the owner: `app-nav-translation-parity.test.ts` excluded Setup and
37+
deferred to "the coverage ratchet", while `platform-objects`' extract config
38+
deferred the same labels to that ratchet "baselined at 0 for this package". The
39+
ratchet runs `os lint` over **static** stack configs, so its 0 meant "not looked
40+
at here", not "checked, clean" — and it reported OK the whole time.
41+
42+
A new gate closes the handoff — `pnpm check:app-nav-i18n`
43+
(`packages/cli/scripts/check-app-nav-i18n.mjs`, wired into `lint.yml`). It boots
44+
the real composition, merges the navigation contributions through the same
45+
`applyNavContributions` path the `/api/v1/meta/app` read uses, and asserts every
46+
merged nav id carries a label in every locale the platform bundle declares — so
47+
the next plugin-contributed entry cannot leak the same way. It also fails when a
48+
declared contributor lands no nav id at all, because fewer merged ids means
49+
fewer ids checked: a contributor that silently stops contributing would
50+
otherwise make the gate greener rather than redder. The two comments that
51+
delegated to the ratchet now say what actually owns these labels.
52+
53+
No authoring change: plugin nav `label` values stay plain English literals, and
54+
translations continue to live in `apps.setup.navigation` in this package.

.github/workflows/lint.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,24 @@ jobs:
10031003
- name: Check no new untranslated declared labels
10041004
run: pnpm check:i18n-coverage
10051005

1006+
# The THIRD i18n question, and the one neither step above can answer
1007+
# (#5750). Both of them read STATIC declarations; the Setup app declares a
1008+
# shell of empty group anchors and gets its ~50 menu entries at RUNTIME
1009+
# from SETUP_NAV_CONTRIBUTIONS and from the capability plugins that own the
1010+
# underlying objects (ADR-0029 D7). So those labels were covered by
1011+
# nothing: the extract config deferred them to the ratchet, the parity test
1012+
# deferred them to the ratchet, and the ratchet walks static configs. Four
1013+
# of them were untranslated in `zh-CN` while every gate reported green.
1014+
#
1015+
# This one boots the real composition, merges the contributions the same
1016+
# way the `/api/v1/meta/app` read path does, and asserts every merged nav
1017+
# id carries a label in every locale the platform bundle declares.
1018+
#
1019+
# Imports the BUILT output of ten workspace packages, so it belongs here
1020+
# with the other post-build consumer gates.
1021+
- name: Check runtime-merged app navigation is translated in every locale
1022+
run: pnpm check:app-nav-i18n
1023+
10061024
# Seed the shared Turbo cache from main only (see the restore step above).
10071025
- name: Save Turbo cache (main only)
10081026
if: always() && github.event_name == 'push'

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"i18n:extract": "tsx packages/cli/bin/run-dev.js i18n extract packages/platform-objects/scripts/i18n-extract.config.ts --locales=zh-CN,ja-JP,es-ES --fill=default --out=packages/platform-objects/src/apps/translations",
3333
"check:i18n": "node scripts/check-i18n-bundles.mjs --self-test && node scripts/check-i18n-bundles.mjs",
3434
"check:i18n-coverage": "node scripts/check-i18n-coverage.mjs --self-test && node scripts/check-i18n-coverage.mjs",
35+
"check:app-nav-i18n": "pnpm --filter @objectstack/cli run check:app-nav-i18n",
3536
"check:nul-bytes": "node scripts/check-nul-bytes.mjs --self-test && node scripts/check-nul-bytes.mjs",
3637
"check:doc-authoring": "node scripts/check-doc-authoring.mjs --self-test && node scripts/check-doc-authoring.mjs",
3738
"check:docs-audit-scope": "node scripts/docs-audit/affected-docs.mjs --self-test && node scripts/docs-audit/check-audit-scope.mjs --self-test && node scripts/docs-audit/check-audit-scope.mjs",

packages/cli/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"build": "if [ -n \"$OS_SKIP_DTS\" ]; then tsc -p tsconfig.build.json --noCheck --declaration false --declarationMap false; else tsc -p tsconfig.build.json; fi",
1313
"dev": "tsc -p tsconfig.build.json --watch",
1414
"test": "vitest run",
15-
"typecheck": "tsc --noEmit"
15+
"typecheck": "tsc --noEmit",
16+
"check:app-nav-i18n": "node scripts/check-app-nav-i18n.mjs --self-test && node scripts/check-app-nav-i18n.mjs"
1617
},
1718
"keywords": [
1819
"objectstack",

0 commit comments

Comments
 (0)