Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .changeset/26112-26142-block-inline-title-translations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@objectstack/spec': minor
'@objectstack/cli': minor
---

feat(spec): a joined report's block headings and a master-detail field's inline grid heading are translatable

Clause-②: yes
Metadata-protocol: added

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:

- **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.
- **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.

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.

`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.
2 changes: 1 addition & 1 deletion content/docs/references/api/protocol.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1778,7 +1778,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
| **globalActions** | `Record<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>` | optional | Global action translations keyed by action name |
| **dashboards** | `Record<string, { label?: string; description?: string; actions?: Record<string, object>; widgets?: Record<string, object>; … }>` | optional | Dashboard translations keyed by dashboard name |
| **datasets** | `Record<string, { label?: string; description?: string; dimensions?: Record<string, object>; measures?: Record<string, object> }>` | optional | Analytics dataset translations keyed by dataset name |
| **reports** | `Record<string, { label?: string; description?: string }>` | optional | Report translations keyed by report name |
| **reports** | `Record<string, { label?: string; description?: string; blocks?: Record<string, object> }>` | optional | Report translations keyed by report name |
| **pages** | `Record<string, { label?: string; description?: string; title?: string; subtitle?: string; … }>` | optional | Page translations keyed by page name |
| **flows** | `Record<string, { label?: string; successMessage?: string; errorMessage?: string; screens?: Record<string, object>; … }>` | optional | Screen-flow translations keyed by flow name |
| **positions** | `Record<string, { label?: string; description?: string }>` | optional | Position translations keyed by position name |
Expand Down
24 changes: 15 additions & 9 deletions content/docs/references/system/translation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Translation data for a single field
| **help** | `string` | optional | Translated help text |
| **placeholder** | `string` | optional | Translated placeholder text for form inputs |
| **options** | `Record<string, string>` | optional | Option value to translated label map |
| **inlineTitle** | `string` | optional | Translated inline master-detail grid title — overlays the field's own `inlineTitle` |


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

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

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

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

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

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

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

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

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ hand-patch a number here** — fix the code or the verdict and regenerate.

| Dir | Sites |
|---|---|
| `system/` | 383 |
| `system/` | 384 |
7 changes: 7 additions & 0 deletions examples/app-crm/src/translations/crm.translation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ export const CrmTranslationBundle = defineTranslationBundle({

'zh-CN': {
objects: {
// The inline product grid's heading on the opportunity form. The field
// lives on the line item, so the key is the line item's.
crm_opportunity_line_item: {
fields: {
opportunity: { inlineTitle: '产品明细' },
},
},
crm_account: {
label: '客户',
pluralLabel: '客户列表',
Expand Down
15 changes: 14 additions & 1 deletion examples/app-showcase/src/system/translations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -782,13 +782,18 @@ export const ShowcaseTranslationBundle = {
showcase_expense_line: {
fields: {
incurred_at: { label: '发生时间' },
// The inline grid heading on the expense report's form — a key born
// with its slot, so it is translated at birth like the label above.
expense_report: { inlineTitle: '费用明细' },
},
},
// See the `en` side for why this entry translates exactly ONE field and
// no more (check-i18n-coverage is a two-sided ratchet).
showcase_invoice_line: {
fields: {
service_start: { label: '服务开始时间' },
// The inline grid heading on the invoice form, translated at birth.
invoice: { inlineTitle: '发票明细' },
},
},
showcase_preference: {
Expand Down Expand Up @@ -1349,7 +1354,15 @@ export const ShowcaseTranslationBundle = {
reports: {
showcase_hours_by_status: { label: '按状态统计工时(汇总)', description: '按任务状态分组的预计工时。' },
showcase_status_priority_matrix: { label: '状态 × 优先级(矩阵)', description: '按状态和优先级交叉统计的预计工时。' },
showcase_task_overview: { label: '任务概览(联合)', description: '将多个任务子报表堆叠在一个联合视图中。' },
showcase_task_overview: {
label: '任务概览(联合)',
description: '将多个任务子报表堆叠在一个联合视图中。',
// The joined report's block headings, keyed by block `name`.
blocks: {
open_block: { label: '未完成任务' },
done_block: { label: '已完成任务' },
},
},
showcase_hours_by_status_chart: { label: '按状态统计工时(图表)', description: '以柱状图展示按状态统计的预计工时。' },
},
},
Expand Down
Loading
Loading