Skip to content

Commit 4f05de4

Browse files
feat(spec): a joined report's block headings and a master-detail field's inline grid heading are translatable (#26757)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} --> _Requested by **Yibo Wang** · [project thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBBHHmbAUS4ebUhc4iQ5UTie)_ Fixes #26112 Fixes #26142 Clause-②: yes Metadata-protocol: added Skills-docs: follow-up owed #26759 — `skills/objectstack-i18n/SKILL.md` (the `fields.{field_name}` row and the `reports` sentence) misses `inlineTitle` and `reports.{report}.blocks.{block}`; `skills/**` is Tier H, so the two-line edit rides that card after this PR lands ## 维护者速读 - 改了什么:翻译包新增两个槽位——联合报表每个区块的标题/说明(`reports.REPORT.blocks.BLOCK.label` / `.description`),以及主从字段的内嵌表格标题(`objects.CHILD.fields.FIELD.inlineTitle`);服务端翻译器按请求语言覆盖,`os i18n extract` / `check` 也开始统计这两个键。 - 为什么改:中文用户打开联合报表时标题是中文、区块标题却是英文;新建表单上的内嵌子表标题(如「Line Items」)也一直是英文,因为翻译包根本没有地方放这些译文,覆盖率检查也看不到缺口。 - 风险与回滚:只新增可选键,已有翻译包与元数据不受影响;组织自行改过的区块标题或内嵌标题仍保留原文(与报表标题同一规则)。回滚即撤销本 PR。 - 先看:`packages/spec/src/system/i18n-resolver.ts`(`translateReportBlocks` 与 `translateObject` 字段处理中的 `inlineTitle`)、`packages/spec/src/system/translation.zod.ts`(两个新槽位)、`packages/cli/src/utils/i18n-extract.ts`(`walkReports` 与字段遍历)。 - 两分钟验证:`pnpm --filter @objectstack/example-showcase exec vitest run test/zh-cn-report-blocks-inline-titles.test.ts`,预期 3 个用例通过:示例应用的联合报表区块显示「未完成任务 / 已完成任务」,发票与费用报销的内嵌表格标题显示「发票明细 / 费用明细」。 - 需要你做的:无,席位落地。 ## What changes - **Spec face** (`translation.zod.ts`): `FieldTranslationSchema` gains `inlineTitle`; the `reports` group gains `blocks`, a record keyed by the joined block's required `name`, closed at `label` / `description` (aliases `name` / `title` / `heading` onto `label`). - **Serving translators** (`i18n-resolver.ts`): `translateReport` overlays each block through a new `translateReportBlocks`; `translateObject`'s field pass overlays `inlineTitle`. Both follow the packaged-base rule (ADR-0029 D9.2a), judged per block (matched by `name`) and per field. A document whose blocks and fields the bundle does not address comes back as the same reference. - **Landing point, measured:** both labels already reach the console as the served document. The joined renderer (`ui/packages/plugin-report/src/DatasetReportRenderer.tsx`) draws `block.label` off `GET /meta/report`, and `attachInlineSubforms` (`ui/packages/app-shell/src/providers/MetadataProvider.tsx`) copies the child field's `inlineTitle` off `GET /meta/object`, both localized at the REST boundary. So the fix lands in the two translators, and neither ui file changes. - **#26142 fallback decision:** no fallback to the child object's translated plural label. An authored `inlineTitle` with no entry keeps its authored text, because a bundle translates an attribute and never swaps another attribute in over authored text (the rule `pages.PAGE.title` follows). An absent `inlineTitle` already renders the child's translated label. - **Extract / check** (`i18n-extract.ts`): `walkReports` walks `blocks[]`, the field walk emits `inlineTitle`, both via `pushOptional` (a block without a `label`, or a field without an `inlineTitle`, is recorded and never demanded). `os i18n check` counts them through the same entries: blocks in the report bucket, `inlineTitle` in the field bucket. - **Examples:** the showcase zh-CN bundle translates both blocks of `showcase_task_overview` and the invoice / expense-report inline headings; the CRM zh-CN bundle translates the opportunity's product grid heading. Each key is translated at birth, so `check:i18n-coverage` stays at its baseline (527, none new). - **Ledgers and generated files:** a `reports.blocks` liveness row with `label` / `description` children (translation 36 → 38 live), plus `gen:api-surface`, `gen:export-origins`, `gen:docs`, `gen:strictness-ledger`, `gen:liveness-counts` output. ## Tests - `packages/spec/src/system/i18n-report-blocks.test.ts` (10): the slot at all three doors, the closed face, the `title` alias, the overlay, the BCP-47 ladder, reference identity, and the packaged-base rule per block. - `packages/spec/src/system/i18n-field-inline-title.test.ts` (7): the slot, a non-string refusal, the overlay through the type dispatch with no `unrecognized_keys`, no plural-label fallback, and the packaged-base rule. - `packages/cli/test/i18n-block-inline-title-walk.test.ts` (5): the extracted keys, unwritten strings recorded without being demanded, the bundle round trip, and coverage reporting both gaps and then clearing them. - `examples/app-showcase/test/zh-cn-report-blocks-inline-titles.test.ts` (3): the acceptance pin for both cards, on the app's own metadata and bundle. ## Acceptance notes - `validate-translation-references` (lint) checks that `reports.REPORT` names a real report, but it does not check that `blocks.BLOCK` names a real block of that report, so a mistyped block key resolves nothing without an orphan finding. This is noted, not filed. - The joined renderer's `resolveText` reads an inline locale map's `default` only. That predates this change and is unrelated to the bundle path. ## Verification All readings were taken at `bcbc83749` (after merging `origin/main` `5cc31c788` through `os-regen-merge.sh`) unless marked as taken at `595f62162` (the pre-merge head). <details><summary>Tests and typecheck</summary> - `pnpm --filter @objectstack/spec test` (595f621): Test Files 733 passed, Tests 21938 passed / 1 todo. - `pnpm --filter @objectstack/spec test:repo` (595f621): Test Files 80 passed, Tests 1295 passed. - `pnpm --filter @objectstack/cli exec vitest run --project unit` (595f621): Test Files 340 passed, Tests 4928 passed. The integration tier is left to CI: no spawn entry or integration file is touched. - `pnpm --filter @objectstack/runtime test` (595f621): Test Files 459 passed, Tests 7443 passed / 19 skipped. - typecheck for spec, cli, example-showcase and example-crm (595f621): all exit 0. - After the merge (bcbc837): `src/system/` + `src/ui/report*` spec tests 64 files / 2150 tests, the two cli i18n walk files 9 tests, and the showcase pin 3 tests, all passing. - The ui packages are untouched, so no ui test run is owed. </details> <details><summary>Ablations (one per behaviour; src-resolved suites, each restored byte-identical by blob hash)</summary> - `translateReport` with the block pass removed: `i18n-report-blocks.test.ts` 6 failed / 4 passed. - `translateObject` with the `inlineTitle` assignment removed: `i18n-field-inline-title.test.ts` 3 failed / 4 passed. - extract with the `inlineTitle` push deleted: the cli walk test 3 failed / 2 passed. - extract with the block `label` push deleted: the cli walk test 4 failed / 1 passed. </details> <details><summary>Gates</summary> - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 109 families (595f621; the derivation at bcbc837 is the same list). 107 exit 0. Two exit 3 (prerequisite: a whole-repo build), `spec check:skill-examples` and `check:dual-build-cjs-loads`, are NOT MEASURED locally and left to CI. `--ran` reconciliation: 109 derived, 107 run, 2 NOT-MEASURED, 0 UNRUN. - Re-run at bcbc837 after the merge: `check:i18n`, `check:i18n-coverage`, `check:i18n-walk-parity`, spec `check:liveness`, `check:empty-state`, `check:strictness-ledger`, `check:api-surface`, `check:authorable-surface`, `check:generated`, `check:nul-bytes`, all exit 0. - No full dogfood run: there is no `!` in the title and no `(narrowing)` arm. </details> --- _Generated by [Claude Code](https://claude.ai/code/session_01WGufziF7p6fS8zz6Gtt1gq)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0f3d068 commit 4f05de4

19 files changed

Lines changed: 645 additions & 27 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/cli': minor
4+
---
5+
6+
feat(spec): a joined report's block headings and a master-detail field's inline grid heading are translatable
7+
8+
Clause-②: yes
9+
Metadata-protocol: added
10+
11+
Two authored, user-visible labels had no translation slot, so a zh-CN user read them in the authoring language and `os i18n check` could not see the gap:
12+
13+
- **A joined report's blocks.** The `reports` translation group declares `blocks`, keyed by the block's `name`: `reports.<report>.blocks.<block>.label` / `.description`. `translateReport` overlays them on each block of the served report (`GET /meta/report`), which is what the joined renderer draws above each block.
14+
- **A field's `inlineTitle`.** `FieldTranslationSchema` declares `inlineTitle`: `objects.<child>.fields.<field>.inlineTitle`, on the child object whose master-detail (or lookup) field declares the heading. `translateObject` overlays it on the served child object (`GET /meta/object`), which is what the console copies onto the parent's form as the inline grid's heading.
15+
16+
Both follow the packaged-base rule (ADR-0029 D9.2a): a string an org re-authored on top of the packaged declaration keeps its text. An authored `inlineTitle` with no entry keeps its authored text; the child object's plural label is never substituted for it.
17+
18+
`os i18n extract` scaffolds both keys and `os i18n check` counts them: a block's `label` / `description` under the report bucket, `inlineTitle` under the field bucket. A block without a `label`, or a field without an `inlineTitle`, is recorded without being demanded.

‎content/docs/references/api/protocol.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1778,7 +1778,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
17781778
| **globalActions** | `Record<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>` | optional | Global action translations keyed by action name |
17791779
| **dashboards** | `Record<string, { label?: string; description?: string; actions?: Record<string, object>; widgets?: Record<string, object>; … }>` | optional | Dashboard translations keyed by dashboard name |
17801780
| **datasets** | `Record<string, { label?: string; description?: string; dimensions?: Record<string, object>; measures?: Record<string, object> }>` | optional | Analytics dataset translations keyed by dataset name |
1781-
| **reports** | `Record<string, { label?: string; description?: string }>` | optional | Report translations keyed by report name |
1781+
| **reports** | `Record<string, { label?: string; description?: string; blocks?: Record<string, object> }>` | optional | Report translations keyed by report name |
17821782
| **pages** | `Record<string, { label?: string; description?: string; title?: string; subtitle?: string; … }>` | optional | Page translations keyed by page name |
17831783
| **flows** | `Record<string, { label?: string; successMessage?: string; errorMessage?: string; screens?: Record<string, object>; … }>` | optional | Screen-flow translations keyed by flow name |
17841784
| **positions** | `Record<string, { label?: string; description?: string }>` | optional | Position translations keyed by position name |

‎content/docs/references/system/translation.mdx‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Translation data for a single field
6666
| **help** | `string` | optional | Translated help text |
6767
| **placeholder** | `string` | optional | Translated placeholder text for form inputs |
6868
| **options** | `Record<string, string>` | optional | Option value to translated label map |
69+
| **inlineTitle** | `string` | optional | Translated inline master-detail grid title — overlays the field's own `inlineTitle` |
6970

7071

7172
---
@@ -90,7 +91,7 @@ Translation data for a single object
9091
| **label** | `string` | optional | Translated singular label |
9192
| **pluralLabel** | `string` | optional | Translated plural label |
9293
| **description** | `string` | optional | Translated object description |
93-
| **fields** | `Record<string, { label?: string; help?: string; placeholder?: string; options?: Record<string, string> }>` | optional | Field-level translations |
94+
| **fields** | `Record<string, { label?: string; help?: string; placeholder?: string; options?: Record<string, string>; … }>` | optional | Field-level translations |
9495
| **_views** | `Record<string, { label?: string; description?: string; title?: string; emptyState?: object; … }>` | optional | View translations keyed by view name |
9596
| **_actions** | `Record<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>` | optional | Action translations keyed by action name |
9697
| **_sections** | `Record<string, { label?: string; description?: string; fields?: Record<string, object> }>` | optional | Section translations keyed by section name |
@@ -107,6 +108,7 @@ Translation data for a single field
107108
| **help** | `string` | optional | Translated help text |
108109
| **placeholder** | `string` | optional | Translated placeholder text for form inputs |
109110
| **options** | `Record<string, string>` | optional | Option value to translated label map |
111+
| **inlineTitle** | `string` | optional | Translated inline master-detail grid title — overlays the field's own `inlineTitle` |
110112

111113
### Nested Shape: `ObjectTranslationData._views[string]`
112114

@@ -177,7 +179,7 @@ Platform translation data — the per-app groups plus the platform-only `setting
177179
| **globalActions** | `Record<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>` | optional | Global action translations keyed by action name |
178180
| **dashboards** | `Record<string, { label?: string; description?: string; actions?: Record<string, object>; widgets?: Record<string, object>; … }>` | optional | Dashboard translations keyed by dashboard name |
179181
| **datasets** | `Record<string, { label?: string; description?: string; dimensions?: Record<string, object>; measures?: Record<string, object> }>` | optional | Analytics dataset translations keyed by dataset name |
180-
| **reports** | `Record<string, { label?: string; description?: string }>` | optional | Report translations keyed by report name |
182+
| **reports** | `Record<string, { label?: string; description?: string; blocks?: Record<string, object> }>` | optional | Report translations keyed by report name |
181183
| **pages** | `Record<string, { label?: string; description?: string; title?: string; subtitle?: string; … }>` | optional | Page translations keyed by page name |
182184
| **flows** | `Record<string, { label?: string; successMessage?: string; errorMessage?: string; screens?: Record<string, object>; … }>` | optional | Screen-flow translations keyed by flow name |
183185
| **positions** | `Record<string, { label?: string; description?: string }>` | optional | Position translations keyed by position name |
@@ -195,7 +197,7 @@ Translation data for a single object
195197
| **label** | `string` | optional | Translated singular label |
196198
| **pluralLabel** | `string` | optional | Translated plural label |
197199
| **description** | `string` | optional | Translated object description |
198-
| **fields** | `Record<string, { label?: string; help?: string; placeholder?: string; options?: Record<string, string> }>` | optional | Field-level translations |
200+
| **fields** | `Record<string, { label?: string; help?: string; placeholder?: string; options?: Record<string, string>; … }>` | optional | Field-level translations |
199201
| **_views** | `Record<string, { label?: string; description?: string; title?: string; emptyState?: object; … }>` | optional | View translations keyed by view name |
200202
| **_actions** | `Record<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>` | optional | Action translations keyed by action name |
201203
| **_sections** | `Record<string, { label?: string; description?: string; fields?: Record<string, object> }>` | optional | Section translations keyed by section name |
@@ -254,6 +256,7 @@ Translation data for a single object
254256
| :--- | :--- | :--- | :--- |
255257
| **label** | `string` | optional | Translated report label — overlays the report's own `label` |
256258
| **description** | `string` | optional | Translated report description — overlays the report's own `description` |
259+
| **blocks** | `Record<string, { label?: string; description?: string }>` | optional | Joined report block translations keyed by block name |
257260

258261
### Nested Shape: `PlatformTranslationData.pages[string]`
259262

@@ -334,7 +337,7 @@ Served translation data — the platform groups the browser reads, without the s
334337
| **globalActions** | `Record<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>` | optional | Global action translations keyed by action name |
335338
| **dashboards** | `Record<string, { label?: string; description?: string; actions?: Record<string, object>; widgets?: Record<string, object>; … }>` | optional | Dashboard translations keyed by dashboard name |
336339
| **datasets** | `Record<string, { label?: string; description?: string; dimensions?: Record<string, object>; measures?: Record<string, object> }>` | optional | Analytics dataset translations keyed by dataset name |
337-
| **reports** | `Record<string, { label?: string; description?: string }>` | optional | Report translations keyed by report name |
340+
| **reports** | `Record<string, { label?: string; description?: string; blocks?: Record<string, object> }>` | optional | Report translations keyed by report name |
338341
| **pages** | `Record<string, { label?: string; description?: string; title?: string; subtitle?: string; … }>` | optional | Page translations keyed by page name |
339342
| **flows** | `Record<string, { label?: string; successMessage?: string; errorMessage?: string; screens?: Record<string, object>; … }>` | optional | Screen-flow translations keyed by flow name |
340343
| **positions** | `Record<string, { label?: string; description?: string }>` | optional | Position translations keyed by position name |
@@ -351,7 +354,7 @@ Translation data for a single object
351354
| **label** | `string` | optional | Translated singular label |
352355
| **pluralLabel** | `string` | optional | Translated plural label |
353356
| **description** | `string` | optional | Translated object description |
354-
| **fields** | `Record<string, { label?: string; help?: string; placeholder?: string; options?: Record<string, string> }>` | optional | Field-level translations |
357+
| **fields** | `Record<string, { label?: string; help?: string; placeholder?: string; options?: Record<string, string>; … }>` | optional | Field-level translations |
355358
| **_views** | `Record<string, { label?: string; description?: string; title?: string; emptyState?: object; … }>` | optional | View translations keyed by view name |
356359
| **_actions** | `Record<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>` | optional | Action translations keyed by action name |
357360
| **_sections** | `Record<string, { label?: string; description?: string; fields?: Record<string, object> }>` | optional | Section translations keyed by section name |
@@ -410,6 +413,7 @@ Translation data for a single object
410413
| :--- | :--- | :--- | :--- |
411414
| **label** | `string` | optional | Translated report label — overlays the report's own `label` |
412415
| **description** | `string` | optional | Translated report description — overlays the report's own `description` |
416+
| **blocks** | `Record<string, { label?: string; description?: string }>` | optional | Joined report block translations keyed by block name |
413417

414418
### Nested Shape: `ServedTranslationData.pages[string]`
415419

@@ -552,7 +556,7 @@ Per-app translation data for objects, apps, and UI messages
552556
| **globalActions** | `Record<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>` | optional | Global action translations keyed by action name |
553557
| **dashboards** | `Record<string, { label?: string; description?: string; actions?: Record<string, object>; widgets?: Record<string, object>; … }>` | optional | Dashboard translations keyed by dashboard name |
554558
| **datasets** | `Record<string, { label?: string; description?: string; dimensions?: Record<string, object>; measures?: Record<string, object> }>` | optional | Analytics dataset translations keyed by dataset name |
555-
| **reports** | `Record<string, { label?: string; description?: string }>` | optional | Report translations keyed by report name |
559+
| **reports** | `Record<string, { label?: string; description?: string; blocks?: Record<string, object> }>` | optional | Report translations keyed by report name |
556560
| **pages** | `Record<string, { label?: string; description?: string; title?: string; subtitle?: string; … }>` | optional | Page translations keyed by page name |
557561
| **flows** | `Record<string, { label?: string; successMessage?: string; errorMessage?: string; screens?: Record<string, object>; … }>` | optional | Screen-flow translations keyed by flow name |
558562
| **positions** | `Record<string, { label?: string; description?: string }>` | optional | Position translations keyed by position name |
@@ -569,7 +573,7 @@ Translation data for a single object
569573
| **label** | `string` | optional | Translated singular label |
570574
| **pluralLabel** | `string` | optional | Translated plural label |
571575
| **description** | `string` | optional | Translated object description |
572-
| **fields** | `Record<string, { label?: string; help?: string; placeholder?: string; options?: Record<string, string> }>` | optional | Field-level translations |
576+
| **fields** | `Record<string, { label?: string; help?: string; placeholder?: string; options?: Record<string, string>; … }>` | optional | Field-level translations |
573577
| **_views** | `Record<string, { label?: string; description?: string; title?: string; emptyState?: object; … }>` | optional | View translations keyed by view name |
574578
| **_actions** | `Record<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>` | optional | Action translations keyed by action name |
575579
| **_sections** | `Record<string, { label?: string; description?: string; fields?: Record<string, object> }>` | optional | Section translations keyed by section name |
@@ -628,6 +632,7 @@ Translation data for a single object
628632
| :--- | :--- | :--- | :--- |
629633
| **label** | `string` | optional | Translated report label — overlays the report's own `label` |
630634
| **description** | `string` | optional | Translated report description — overlays the report's own `description` |
635+
| **blocks** | `Record<string, { label?: string; description?: string }>` | optional | Joined report block translations keyed by block name |
631636

632637
### Nested Shape: `TranslationData.pages[string]`
633638

@@ -729,7 +734,7 @@ One locale of translations — the `translation` metadata type
729734
| **globalActions** | `Record<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>` | optional | Global action translations keyed by action name |
730735
| **dashboards** | `Record<string, { label?: string; description?: string; actions?: Record<string, object>; widgets?: Record<string, object>; … }>` | optional | Dashboard translations keyed by dashboard name |
731736
| **datasets** | `Record<string, { label?: string; description?: string; dimensions?: Record<string, object>; measures?: Record<string, object> }>` | optional | Analytics dataset translations keyed by dataset name |
732-
| **reports** | `Record<string, { label?: string; description?: string }>` | optional | Report translations keyed by report name |
737+
| **reports** | `Record<string, { label?: string; description?: string; blocks?: Record<string, object> }>` | optional | Report translations keyed by report name |
733738
| **pages** | `Record<string, { label?: string; description?: string; title?: string; subtitle?: string; … }>` | optional | Page translations keyed by page name |
734739
| **flows** | `Record<string, { label?: string; successMessage?: string; errorMessage?: string; screens?: Record<string, object>; … }>` | optional | Screen-flow translations keyed by flow name |
735740
| **positions** | `Record<string, { label?: string; description?: string }>` | optional | Position translations keyed by position name |
@@ -756,7 +761,7 @@ Translation data for a single object
756761
| **label** | `string` | optional | Translated singular label |
757762
| **pluralLabel** | `string` | optional | Translated plural label |
758763
| **description** | `string` | optional | Translated object description |
759-
| **fields** | `Record<string, { label?: string; help?: string; placeholder?: string; options?: Record<string, string> }>` | optional | Field-level translations |
764+
| **fields** | `Record<string, { label?: string; help?: string; placeholder?: string; options?: Record<string, string>; … }>` | optional | Field-level translations |
760765
| **_views** | `Record<string, { label?: string; description?: string; title?: string; emptyState?: object; … }>` | optional | View translations keyed by view name |
761766
| **_actions** | `Record<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>` | optional | Action translations keyed by action name |
762767
| **_sections** | `Record<string, { label?: string; description?: string; fields?: Record<string, object> }>` | optional | Section translations keyed by section name |
@@ -815,6 +820,7 @@ Translation data for a single object
815820
| :--- | :--- | :--- | :--- |
816821
| **label** | `string` | optional | Translated report label — overlays the report's own `label` |
817822
| **description** | `string` | optional | Translated report description — overlays the report's own `description` |
823+
| **blocks** | `Record<string, { label?: string; description?: string }>` | optional | Joined report block translations keyed by block name |
818824

819825
### Nested Shape: `TranslationItem.pages[string]`
820826

‎docs/audits/2026-07-unknown-key-strictness-ledger.counts/system.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ hand-patch a number here** — fix the code or the verdict and regenerate.
1919

2020
| Dir | Sites |
2121
|---|---|
22-
| `system/` | 383 |
22+
| `system/` | 384 |

‎examples/app-crm/src/translations/crm.translation.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ export const CrmTranslationBundle = defineTranslationBundle({
9292

9393
'zh-CN': {
9494
objects: {
95+
// The inline product grid's heading on the opportunity form. The field
96+
// lives on the line item, so the key is the line item's.
97+
crm_opportunity_line_item: {
98+
fields: {
99+
opportunity: { inlineTitle: '产品明细' },
100+
},
101+
},
95102
crm_account: {
96103
label: '客户',
97104
pluralLabel: '客户列表',

‎examples/app-showcase/src/system/translations/index.ts‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,13 +782,18 @@ export const ShowcaseTranslationBundle = {
782782
showcase_expense_line: {
783783
fields: {
784784
incurred_at: { label: '发生时间' },
785+
// The inline grid heading on the expense report's form — a key born
786+
// with its slot, so it is translated at birth like the label above.
787+
expense_report: { inlineTitle: '费用明细' },
785788
},
786789
},
787790
// See the `en` side for why this entry translates exactly ONE field and
788791
// no more (check-i18n-coverage is a two-sided ratchet).
789792
showcase_invoice_line: {
790793
fields: {
791794
service_start: { label: '服务开始时间' },
795+
// The inline grid heading on the invoice form, translated at birth.
796+
invoice: { inlineTitle: '发票明细' },
792797
},
793798
},
794799
showcase_preference: {
@@ -1349,7 +1354,15 @@ export const ShowcaseTranslationBundle = {
13491354
reports: {
13501355
showcase_hours_by_status: { label: '按状态统计工时(汇总)', description: '按任务状态分组的预计工时。' },
13511356
showcase_status_priority_matrix: { label: '状态 × 优先级(矩阵)', description: '按状态和优先级交叉统计的预计工时。' },
1352-
showcase_task_overview: { label: '任务概览(联合)', description: '将多个任务子报表堆叠在一个联合视图中。' },
1357+
showcase_task_overview: {
1358+
label: '任务概览(联合)',
1359+
description: '将多个任务子报表堆叠在一个联合视图中。',
1360+
// The joined report's block headings, keyed by block `name`.
1361+
blocks: {
1362+
open_block: { label: '未完成任务' },
1363+
done_block: { label: '已完成任务' },
1364+
},
1365+
},
13531366
showcase_hours_by_status_chart: { label: '按状态统计工时(图表)', description: '以柱状图展示按状态统计的预计工时。' },
13541367
},
13551368
},

0 commit comments

Comments
 (0)