From 36404e7a55c420210d51e9cf3f1601ea887187aa Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 18:16:11 +0000 Subject: [PATCH] fix(metadata-admin): page block inspector curated labels follow the locale (#3913) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The inspector's chrome went through `t('engine.inspector.pageBlock.*')` from the start while the panel's CONTENTS did not: every `label` in `previews/block-config.ts` was an English literal handed straight to the field components. A zh-CN admin opening any page block read 「属性」 over a stack of English field names. All 157 label literals (152 field/option labels + 5 addLabel) are now translation keys resolved through `t(key, locale)` at render, with 154 distinct keys added to both the en-US and zh-CN sides of `metadata-admin/i18n.ts`. The English text is unchanged: substituting every key back to its en value reproduces the pre-change data region byte-for-byte. Key shape is derived from the label's POSITION in BLOCK_CONFIG (`.field..`, `.add..`, `.option..`) and a test re-derives it, because these keys differ by one segment: copy-pasting a neighbouring block's key yields one that EXISTS in both locales and renders a plausible label for the wrong property, so an existence-only check is green for it. Positional keys also let one English word take different Chinese per block (`element:button.label` is 「按钮文字」, `page:tabs.items.label` is 「标签」). `addLabel` becomes REQUIRED on the array variant: it was optional and the inspector fell back to a bare English 'Add' that no locale table could reach. Requiring it deletes the fallback rather than translating it. Option labels are translated too, including the ones ColorVariantPicker renders only as aria-label/title. --- ...block-inspector-curated-label-i18n-3913.md | 41 ++ .../src/views/metadata-admin/i18n.ts | 317 ++++++++++++++++ .../PageBlockInspector.i18n.test.tsx | 172 +++++++++ .../inspectors/PageBlockInspector.tsx | 38 +- .../__tests__/block-config-i18n.test.ts | 255 +++++++++++++ .../previews/__tests__/block-config.test.ts | 10 +- .../metadata-admin/previews/block-config.ts | 357 ++++++++++-------- 7 files changed, 1017 insertions(+), 173 deletions(-) create mode 100644 .changeset/page-block-inspector-curated-label-i18n-3913.md create mode 100644 packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.i18n.test.tsx create mode 100644 packages/app-shell/src/views/metadata-admin/previews/__tests__/block-config-i18n.test.ts diff --git a/.changeset/page-block-inspector-curated-label-i18n-3913.md b/.changeset/page-block-inspector-curated-label-i18n-3913.md new file mode 100644 index 0000000000..5ef3d668e1 --- /dev/null +++ b/.changeset/page-block-inspector-curated-label-i18n-3913.md @@ -0,0 +1,41 @@ +--- +"@object-ui/app-shell": patch +--- + +Page block inspector: the PROPERTIES panel's curated field labels now follow the session locale instead of always rendering English + +`PageBlockInspector`'s chrome went through the translation table from the start — +`t('engine.inspector.pageBlock.properties')` and its siblings — while the panel's +CONTENTS did not. Every `label` in `previews/block-config.ts` was an English +literal handed straight to the field components, so a zh-CN admin opening any +page block read 「属性」 over a stack of English field names: two languages in one +panel, reproducible on any block with no special data. + +All 157 label literals (152 field/option labels + 5 `addLabel`s) are now +translation keys resolved through `t(key, locale)` at render, with 154 distinct +keys added to both the `en-US` and `zh-CN` sides of +`views/metadata-admin/i18n.ts`. The English text is unchanged: every key's `en` +value is the literal it replaced, verified by substituting all 157 keys back and +diffing the result byte-for-byte against the pre-change file. + +The key is a function of the label's POSITION in `BLOCK_CONFIG` +(`engine.inspector.pageBlock.field..`, `.add..`, +`.option..`), and a test re-derives all three shapes from the +table's own structure. That is what makes the realistic mistake visible: these +keys differ by one segment, so adding a block by copy-pasting a neighbour's key +yields a key that EXISTS in both locales and renders a plausible label belonging +to a different property — an existence-only check is green for it. Positional +keys also let one English word take different Chinese per block, which the panel +needs: `element:button.label` is a button caption 「按钮文字」 while +`page:tabs.items.label` is a tab title 「标签」, and `Add section` is 「添加分区」 +here but stays 「添加分组」 in the form-layout canvas. + +`addLabel` is now REQUIRED on the `array` field variant. It was optional and the +inspector fell back to a bare English `'Add'` — an untranslatable literal no +locale table could reach. Requiring it deletes the fallback instead of +translating it, so a new array field cannot compile without naming its +add-button key. + +Option labels are translated too, including the ones `ColorVariantPicker` renders +only as `aria-label`/`title`, where an untranslated string is invisible to a text +query. diff --git a/packages/app-shell/src/views/metadata-admin/i18n.ts b/packages/app-shell/src/views/metadata-admin/i18n.ts index a35116697c..257e0566dd 100644 --- a/packages/app-shell/src/views/metadata-admin/i18n.ts +++ b/packages/app-shell/src/views/metadata-admin/i18n.ts @@ -478,6 +478,166 @@ const ENGINE_STRINGS_EN: Record = { 'engine.inspector.pageBlock.advanced': 'Advanced', 'engine.inspector.pageBlock.remove': 'Remove block', 'engine.inspector.pageBlock.outlineLabel': 'Blocks', + // Page block inspector — curated property labels (#3913). + // These are the `label` / `addLabel` / option-label values of + // `previews/block-config.ts`; that file stores the KEY and the inspector + // resolves it through `t()` at render. Key shape is derived from the label's + // position in BLOCK_CONFIG and pinned by + // `previews/__tests__/block-config-i18n.test.ts` — see that file's header. + 'engine.inspector.pageBlock.field.object-grid.objectName': 'Object', + 'engine.inspector.pageBlock.field.object-grid.columns': 'Columns', + 'engine.inspector.pageBlock.field.object-grid.pageSize': 'Page size', + 'engine.inspector.pageBlock.field.object-grid.striped': 'Striped rows', + 'engine.inspector.pageBlock.field.object-grid.bordered': 'Bordered', + 'engine.inspector.pageBlock.field.object-form.objectName': 'Object', + 'engine.inspector.pageBlock.field.object-form.mode': 'Mode', + 'engine.inspector.pageBlock.option.mode.create': 'Create', + 'engine.inspector.pageBlock.option.mode.edit': 'Edit', + 'engine.inspector.pageBlock.option.mode.view': 'View', + 'engine.inspector.pageBlock.field.object-form.formType': 'Form type', + 'engine.inspector.pageBlock.option.formType.simple': 'Simple', + 'engine.inspector.pageBlock.option.formType.tabbed': 'Tabbed', + 'engine.inspector.pageBlock.option.formType.wizard': 'Wizard', + 'engine.inspector.pageBlock.option.formType.split': 'Split', + 'engine.inspector.pageBlock.option.formType.drawer': 'Drawer', + 'engine.inspector.pageBlock.option.formType.modal': 'Modal', + 'engine.inspector.pageBlock.field.object-form.layout': 'Layout', + 'engine.inspector.pageBlock.option.layout.vertical': 'Vertical', + 'engine.inspector.pageBlock.option.layout.horizontal': 'Horizontal', + 'engine.inspector.pageBlock.option.layout.inline': 'Inline', + 'engine.inspector.pageBlock.option.layout.grid': 'Grid', + 'engine.inspector.pageBlock.field.object-form.columns': 'Columns (grid layout)', + 'engine.inspector.pageBlock.field.object-form.fields': 'Fields', + 'engine.inspector.pageBlock.field.object-form.title': 'Title', + 'engine.inspector.pageBlock.field.object-form.description': 'Description', + 'engine.inspector.pageBlock.field.object-metric.objectName': 'Object', + 'engine.inspector.pageBlock.field.object-metric.label': 'Label', + 'engine.inspector.pageBlock.field.object-metric.description': 'Description', + 'engine.inspector.pageBlock.field.object-metric.icon': 'Icon', + 'engine.inspector.pageBlock.field.object-metric.colorVariant': 'Color', + 'engine.inspector.pageBlock.option.colorVariant.default': 'Default', + 'engine.inspector.pageBlock.option.colorVariant.blue': 'Blue', + 'engine.inspector.pageBlock.option.colorVariant.teal': 'Teal', + 'engine.inspector.pageBlock.option.colorVariant.orange': 'Orange', + 'engine.inspector.pageBlock.option.colorVariant.purple': 'Purple', + 'engine.inspector.pageBlock.option.colorVariant.success': 'Success', + 'engine.inspector.pageBlock.option.colorVariant.warning': 'Warning', + 'engine.inspector.pageBlock.option.colorVariant.danger': 'Danger', + 'engine.inspector.pageBlock.field.object-metric.format': 'Format', + 'engine.inspector.pageBlock.option.format.number': 'Number', + 'engine.inspector.pageBlock.option.format.currency': 'Currency', + 'engine.inspector.pageBlock.option.format.percent': 'Percent', + 'engine.inspector.pageBlock.field.object-metric.prefix': 'Prefix', + 'engine.inspector.pageBlock.field.object-metric.suffix': 'Suffix', + 'engine.inspector.pageBlock.field.object-kanban.objectName': 'Object', + 'engine.inspector.pageBlock.field.object-kanban.groupField': 'Group by field', + 'engine.inspector.pageBlock.field.object-kanban.titleField': 'Title field', + 'engine.inspector.pageBlock.field.object-kanban.cardFields': 'Card fields', + 'engine.inspector.pageBlock.field.grid.columns': 'Columns', + 'engine.inspector.pageBlock.field.grid.gap': 'Gap', + 'engine.inspector.pageBlock.field.element:text.content': 'Content', + 'engine.inspector.pageBlock.field.element:text.variant': 'Variant', + 'engine.inspector.pageBlock.option.variant.heading': 'Heading', + 'engine.inspector.pageBlock.option.variant.subheading': 'Subheading', + 'engine.inspector.pageBlock.option.variant.body': 'Body', + 'engine.inspector.pageBlock.option.variant.caption': 'Caption', + 'engine.inspector.pageBlock.field.element:text.align': 'Align', + 'engine.inspector.pageBlock.option.align.left': 'Left', + 'engine.inspector.pageBlock.option.align.center': 'Center', + 'engine.inspector.pageBlock.option.align.right': 'Right', + 'engine.inspector.pageBlock.field.element:image.src': 'Source URL', + 'engine.inspector.pageBlock.field.element:image.alt': 'Alt text', + 'engine.inspector.pageBlock.field.element:image.fit': 'Fit', + 'engine.inspector.pageBlock.option.fit.cover': 'Cover', + 'engine.inspector.pageBlock.option.fit.contain': 'Contain', + 'engine.inspector.pageBlock.option.fit.fill': 'Fill', + 'engine.inspector.pageBlock.field.element:definition-list.items': 'Items', + 'engine.inspector.pageBlock.add.element:definition-list.items': 'Add item', + 'engine.inspector.pageBlock.field.element:definition-list.items.label': 'Label', + 'engine.inspector.pageBlock.field.element:definition-list.items.value': 'Value', + 'engine.inspector.pageBlock.field.element:definition-list.columns': 'Columns (1 or 2)', + 'engine.inspector.pageBlock.field.element:definition-list.inline': 'Inline (label · value)', + 'engine.inspector.pageBlock.field.element:repeater.object': 'Object', + 'engine.inspector.pageBlock.field.element:repeater.titleField': 'Title field', + 'engine.inspector.pageBlock.field.element:repeater.fields': 'Fields', + 'engine.inspector.pageBlock.field.element:repeater.limit': 'Limit', + 'engine.inspector.pageBlock.field.element:repeater.emptyText': 'Empty text', + 'engine.inspector.pageBlock.field.element:repeater.divided': 'Dividers between rows', + 'engine.inspector.pageBlock.field.element:number.object': 'Object', + 'engine.inspector.pageBlock.field.element:number.field': 'Field', + 'engine.inspector.pageBlock.field.element:number.aggregate': 'Aggregate', + 'engine.inspector.pageBlock.option.aggregate.count': 'Count', + 'engine.inspector.pageBlock.option.aggregate.sum': 'Sum', + 'engine.inspector.pageBlock.option.aggregate.avg': 'Average', + 'engine.inspector.pageBlock.option.aggregate.min': 'Min', + 'engine.inspector.pageBlock.option.aggregate.max': 'Max', + 'engine.inspector.pageBlock.field.element:number.format': 'Format', + 'engine.inspector.pageBlock.field.element:number.prefix': 'Prefix', + 'engine.inspector.pageBlock.field.element:number.suffix': 'Suffix', + 'engine.inspector.pageBlock.field.element:button.label': 'Label', + 'engine.inspector.pageBlock.field.element:button.variant': 'Variant', + 'engine.inspector.pageBlock.option.variant.primary': 'Primary', + 'engine.inspector.pageBlock.option.variant.secondary': 'Secondary', + 'engine.inspector.pageBlock.option.variant.danger': 'Danger', + 'engine.inspector.pageBlock.option.variant.ghost': 'Ghost', + 'engine.inspector.pageBlock.option.variant.link': 'Link', + 'engine.inspector.pageBlock.field.element:button.size': 'Size', + 'engine.inspector.pageBlock.option.size.small': 'Small', + 'engine.inspector.pageBlock.option.size.medium': 'Medium', + 'engine.inspector.pageBlock.option.size.large': 'Large', + 'engine.inspector.pageBlock.field.element:button.icon': 'Icon', + 'engine.inspector.pageBlock.field.element:button.action': 'Action', + 'engine.inspector.pageBlock.field.page:header.title': 'Title', + 'engine.inspector.pageBlock.field.page:header.subtitle': 'Subtitle', + 'engine.inspector.pageBlock.field.page:header.icon': 'Icon', + 'engine.inspector.pageBlock.field.page:header.breadcrumb': 'Show breadcrumb', + 'engine.inspector.pageBlock.field.page:card.title': 'Title', + 'engine.inspector.pageBlock.field.page:card.bordered': 'Bordered', + 'engine.inspector.pageBlock.field.page:tabs.items': 'Tabs', + 'engine.inspector.pageBlock.add.page:tabs.items': 'Add tab', + 'engine.inspector.pageBlock.field.page:tabs.items.key': 'Key', + 'engine.inspector.pageBlock.field.page:tabs.items.label': 'Label', + 'engine.inspector.pageBlock.field.page:accordion.title': 'Title', + 'engine.inspector.pageBlock.field.page:accordion.items': 'Sections', + 'engine.inspector.pageBlock.add.page:accordion.items': 'Add section', + 'engine.inspector.pageBlock.field.page:accordion.items.value': 'Key', + 'engine.inspector.pageBlock.field.page:accordion.items.label': 'Label', + 'engine.inspector.pageBlock.field.record:related_list.objectName': 'Object', + 'engine.inspector.pageBlock.field.record:related_list.relationshipField': 'Relationship field', + 'engine.inspector.pageBlock.field.record:related_list.title': 'Title', + 'engine.inspector.pageBlock.field.record:related_list.limit': 'Limit', + 'engine.inspector.pageBlock.field.record:highlights.fields': 'Fields', + 'engine.inspector.pageBlock.field.record:details.sections': 'Sections', + 'engine.inspector.pageBlock.add.record:details.sections': 'Add section', + 'engine.inspector.pageBlock.field.record:details.sections.name': 'Name (i18n key)', + 'engine.inspector.pageBlock.field.record:details.sections.label': 'Label', + 'engine.inspector.pageBlock.field.record:details.sections.columns': 'Columns', + 'engine.inspector.pageBlock.field.record:details.sections.fields': 'Fields', + 'engine.inspector.pageBlock.field.record:alert.severity': 'Severity', + 'engine.inspector.pageBlock.option.severity.info': 'Info', + 'engine.inspector.pageBlock.option.severity.warning': 'Warning', + 'engine.inspector.pageBlock.option.severity.error': 'Error', + 'engine.inspector.pageBlock.option.severity.success': 'Success', + 'engine.inspector.pageBlock.field.record:alert.title': 'Title', + 'engine.inspector.pageBlock.field.record:alert.body': 'Body', + 'engine.inspector.pageBlock.field.record:alert.icon': 'Icon', + 'engine.inspector.pageBlock.field.record:alert.dismissible': 'Dismissible', + 'engine.inspector.pageBlock.field.record:path.statusField': 'Status field', + 'engine.inspector.pageBlock.field.record:path.stages': 'Stages', + 'engine.inspector.pageBlock.add.record:path.stages': 'Add stage', + 'engine.inspector.pageBlock.field.record:path.stages.value': 'Value', + 'engine.inspector.pageBlock.field.record:path.stages.label': 'Label', + 'engine.inspector.pageBlock.field.record:quick_actions.actionNames': 'Action names', + 'engine.inspector.pageBlock.field.record:quick_actions.location': 'Location', + 'engine.inspector.pageBlock.option.location.record_header': 'Record header', + 'engine.inspector.pageBlock.option.location.record_more': 'Record more menu', + 'engine.inspector.pageBlock.option.location.record_section': 'Record section', + 'engine.inspector.pageBlock.option.location.record_related': 'Record related', + 'engine.inspector.pageBlock.option.location.list_toolbar': 'List toolbar', + 'engine.inspector.pageBlock.option.location.list_item': 'List item', + 'engine.inspector.pageBlock.option.location.global_nav': 'Global nav', + 'engine.inspector.pageBlock.field.ai:input.agentName': 'Agent', + 'engine.inspector.pageBlock.field.ai:input.placeholder': 'Input placeholder', // Report default ("home") inspector 'engine.inspector.report.kind': 'Report', 'engine.inspector.report.close': 'Close report', @@ -2037,6 +2197,163 @@ const ENGINE_STRINGS_ZH: Record = { 'engine.inspector.pageBlock.advanced': '高级属性', 'engine.inspector.pageBlock.remove': '删除区块', 'engine.inspector.pageBlock.outlineLabel': '区块', + // 页面区块检查器 —— curated 属性 label(#3913)。 + // 键形状与 en 侧一一对应,由 `previews/__tests__/block-config-i18n.test.ts` + // 按 BLOCK_CONFIG 的结构重新推导并断言两语齐备。 + 'engine.inspector.pageBlock.field.object-grid.objectName': '对象', + 'engine.inspector.pageBlock.field.object-grid.columns': '列', + 'engine.inspector.pageBlock.field.object-grid.pageSize': '每页条数', + 'engine.inspector.pageBlock.field.object-grid.striped': '斑马纹行', + 'engine.inspector.pageBlock.field.object-grid.bordered': '显示边框', + 'engine.inspector.pageBlock.field.object-form.objectName': '对象', + 'engine.inspector.pageBlock.field.object-form.mode': '模式', + 'engine.inspector.pageBlock.option.mode.create': '新建', + 'engine.inspector.pageBlock.option.mode.edit': '编辑', + 'engine.inspector.pageBlock.option.mode.view': '查看', + 'engine.inspector.pageBlock.field.object-form.formType': '表单类型', + 'engine.inspector.pageBlock.option.formType.simple': '简单', + 'engine.inspector.pageBlock.option.formType.tabbed': '标签页', + 'engine.inspector.pageBlock.option.formType.wizard': '向导', + 'engine.inspector.pageBlock.option.formType.split': '分栏', + 'engine.inspector.pageBlock.option.formType.drawer': '抽屉', + 'engine.inspector.pageBlock.option.formType.modal': '弹窗', + 'engine.inspector.pageBlock.field.object-form.layout': '布局', + 'engine.inspector.pageBlock.option.layout.vertical': '纵向', + 'engine.inspector.pageBlock.option.layout.horizontal': '横向', + 'engine.inspector.pageBlock.option.layout.inline': '行内', + 'engine.inspector.pageBlock.option.layout.grid': '网格', + 'engine.inspector.pageBlock.field.object-form.columns': '列数(网格布局)', + 'engine.inspector.pageBlock.field.object-form.fields': '字段', + 'engine.inspector.pageBlock.field.object-form.title': '标题', + 'engine.inspector.pageBlock.field.object-form.description': '描述', + 'engine.inspector.pageBlock.field.object-metric.objectName': '对象', + 'engine.inspector.pageBlock.field.object-metric.label': '标签', + 'engine.inspector.pageBlock.field.object-metric.description': '描述', + 'engine.inspector.pageBlock.field.object-metric.icon': '图标', + 'engine.inspector.pageBlock.field.object-metric.colorVariant': '颜色', + 'engine.inspector.pageBlock.option.colorVariant.default': '默认', + 'engine.inspector.pageBlock.option.colorVariant.blue': '蓝色', + 'engine.inspector.pageBlock.option.colorVariant.teal': '青色', + 'engine.inspector.pageBlock.option.colorVariant.orange': '橙色', + 'engine.inspector.pageBlock.option.colorVariant.purple': '紫色', + 'engine.inspector.pageBlock.option.colorVariant.success': '成功', + 'engine.inspector.pageBlock.option.colorVariant.warning': '警告', + 'engine.inspector.pageBlock.option.colorVariant.danger': '危险', + 'engine.inspector.pageBlock.field.object-metric.format': '格式', + 'engine.inspector.pageBlock.option.format.number': '数字', + 'engine.inspector.pageBlock.option.format.currency': '货币', + 'engine.inspector.pageBlock.option.format.percent': '百分比', + 'engine.inspector.pageBlock.field.object-metric.prefix': '前缀', + 'engine.inspector.pageBlock.field.object-metric.suffix': '后缀', + 'engine.inspector.pageBlock.field.object-kanban.objectName': '对象', + 'engine.inspector.pageBlock.field.object-kanban.groupField': '分组字段', + 'engine.inspector.pageBlock.field.object-kanban.titleField': '标题字段', + 'engine.inspector.pageBlock.field.object-kanban.cardFields': '卡片字段', + 'engine.inspector.pageBlock.field.grid.columns': '列数', + 'engine.inspector.pageBlock.field.grid.gap': '间距', + 'engine.inspector.pageBlock.field.element:text.content': '内容', + 'engine.inspector.pageBlock.field.element:text.variant': '样式', + 'engine.inspector.pageBlock.option.variant.heading': '标题', + 'engine.inspector.pageBlock.option.variant.subheading': '副标题', + 'engine.inspector.pageBlock.option.variant.body': '正文', + 'engine.inspector.pageBlock.option.variant.caption': '说明文字', + 'engine.inspector.pageBlock.field.element:text.align': '对齐', + 'engine.inspector.pageBlock.option.align.left': '左对齐', + 'engine.inspector.pageBlock.option.align.center': '居中', + 'engine.inspector.pageBlock.option.align.right': '右对齐', + 'engine.inspector.pageBlock.field.element:image.src': '图片地址', + 'engine.inspector.pageBlock.field.element:image.alt': '替代文本', + 'engine.inspector.pageBlock.field.element:image.fit': '填充方式', + 'engine.inspector.pageBlock.option.fit.cover': '覆盖', + 'engine.inspector.pageBlock.option.fit.contain': '包含', + 'engine.inspector.pageBlock.option.fit.fill': '拉伸', + 'engine.inspector.pageBlock.field.element:definition-list.items': '条目', + 'engine.inspector.pageBlock.add.element:definition-list.items': '添加条目', + 'engine.inspector.pageBlock.field.element:definition-list.items.label': '标签', + 'engine.inspector.pageBlock.field.element:definition-list.items.value': '值', + 'engine.inspector.pageBlock.field.element:definition-list.columns': '列数(1 或 2)', + 'engine.inspector.pageBlock.field.element:definition-list.inline': '行内(标签 · 值)', + 'engine.inspector.pageBlock.field.element:repeater.object': '对象', + 'engine.inspector.pageBlock.field.element:repeater.titleField': '标题字段', + 'engine.inspector.pageBlock.field.element:repeater.fields': '字段', + 'engine.inspector.pageBlock.field.element:repeater.limit': '条数上限', + 'engine.inspector.pageBlock.field.element:repeater.emptyText': '空数据提示', + 'engine.inspector.pageBlock.field.element:repeater.divided': '行间分隔线', + 'engine.inspector.pageBlock.field.element:number.object': '对象', + 'engine.inspector.pageBlock.field.element:number.field': '字段', + 'engine.inspector.pageBlock.field.element:number.aggregate': '聚合方式', + 'engine.inspector.pageBlock.option.aggregate.count': '计数', + 'engine.inspector.pageBlock.option.aggregate.sum': '求和', + 'engine.inspector.pageBlock.option.aggregate.avg': '平均值', + 'engine.inspector.pageBlock.option.aggregate.min': '最小值', + 'engine.inspector.pageBlock.option.aggregate.max': '最大值', + 'engine.inspector.pageBlock.field.element:number.format': '格式', + 'engine.inspector.pageBlock.field.element:number.prefix': '前缀', + 'engine.inspector.pageBlock.field.element:number.suffix': '后缀', + 'engine.inspector.pageBlock.field.element:button.label': '按钮文字', + 'engine.inspector.pageBlock.field.element:button.variant': '样式', + 'engine.inspector.pageBlock.option.variant.primary': '主要', + 'engine.inspector.pageBlock.option.variant.secondary': '次要', + 'engine.inspector.pageBlock.option.variant.danger': '危险', + 'engine.inspector.pageBlock.option.variant.ghost': '幽灵', + 'engine.inspector.pageBlock.option.variant.link': '链接', + 'engine.inspector.pageBlock.field.element:button.size': '尺寸', + 'engine.inspector.pageBlock.option.size.small': '小', + 'engine.inspector.pageBlock.option.size.medium': '中', + 'engine.inspector.pageBlock.option.size.large': '大', + 'engine.inspector.pageBlock.field.element:button.icon': '图标', + 'engine.inspector.pageBlock.field.element:button.action': '动作', + 'engine.inspector.pageBlock.field.page:header.title': '标题', + 'engine.inspector.pageBlock.field.page:header.subtitle': '副标题', + 'engine.inspector.pageBlock.field.page:header.icon': '图标', + 'engine.inspector.pageBlock.field.page:header.breadcrumb': '显示面包屑', + 'engine.inspector.pageBlock.field.page:card.title': '标题', + 'engine.inspector.pageBlock.field.page:card.bordered': '显示边框', + 'engine.inspector.pageBlock.field.page:tabs.items': '标签页', + 'engine.inspector.pageBlock.add.page:tabs.items': '添加标签页', + 'engine.inspector.pageBlock.field.page:tabs.items.key': '键', + 'engine.inspector.pageBlock.field.page:tabs.items.label': '标签', + 'engine.inspector.pageBlock.field.page:accordion.title': '标题', + 'engine.inspector.pageBlock.field.page:accordion.items': '分区', + 'engine.inspector.pageBlock.add.page:accordion.items': '添加分区', + 'engine.inspector.pageBlock.field.page:accordion.items.value': '键', + 'engine.inspector.pageBlock.field.page:accordion.items.label': '标签', + 'engine.inspector.pageBlock.field.record:related_list.objectName': '对象', + 'engine.inspector.pageBlock.field.record:related_list.relationshipField': '关联字段', + 'engine.inspector.pageBlock.field.record:related_list.title': '标题', + 'engine.inspector.pageBlock.field.record:related_list.limit': '条数上限', + 'engine.inspector.pageBlock.field.record:highlights.fields': '字段', + 'engine.inspector.pageBlock.field.record:details.sections': '分区', + 'engine.inspector.pageBlock.add.record:details.sections': '添加分区', + 'engine.inspector.pageBlock.field.record:details.sections.name': '名称(i18n 键)', + 'engine.inspector.pageBlock.field.record:details.sections.label': '标签', + 'engine.inspector.pageBlock.field.record:details.sections.columns': '列数', + 'engine.inspector.pageBlock.field.record:details.sections.fields': '字段', + 'engine.inspector.pageBlock.field.record:alert.severity': '严重度', + 'engine.inspector.pageBlock.option.severity.info': '信息', + 'engine.inspector.pageBlock.option.severity.warning': '警告', + 'engine.inspector.pageBlock.option.severity.error': '错误', + 'engine.inspector.pageBlock.option.severity.success': '成功', + 'engine.inspector.pageBlock.field.record:alert.title': '标题', + 'engine.inspector.pageBlock.field.record:alert.body': '正文', + 'engine.inspector.pageBlock.field.record:alert.icon': '图标', + 'engine.inspector.pageBlock.field.record:alert.dismissible': '可关闭', + 'engine.inspector.pageBlock.field.record:path.statusField': '状态字段', + 'engine.inspector.pageBlock.field.record:path.stages': '阶段', + 'engine.inspector.pageBlock.add.record:path.stages': '添加阶段', + 'engine.inspector.pageBlock.field.record:path.stages.value': '值', + 'engine.inspector.pageBlock.field.record:path.stages.label': '标签', + 'engine.inspector.pageBlock.field.record:quick_actions.actionNames': '动作名称', + 'engine.inspector.pageBlock.field.record:quick_actions.location': '位置', + 'engine.inspector.pageBlock.option.location.record_header': '记录头部', + 'engine.inspector.pageBlock.option.location.record_more': '记录更多菜单', + 'engine.inspector.pageBlock.option.location.record_section': '记录分区', + 'engine.inspector.pageBlock.option.location.record_related': '记录关联列表', + 'engine.inspector.pageBlock.option.location.list_toolbar': '列表工具栏', + 'engine.inspector.pageBlock.option.location.list_item': '列表行', + 'engine.inspector.pageBlock.option.location.global_nav': '全局导航', + 'engine.inspector.pageBlock.field.ai:input.agentName': '智能体', + 'engine.inspector.pageBlock.field.ai:input.placeholder': '输入框占位提示', // Report default ("home") inspector 'engine.inspector.report.kind': '报表', 'engine.inspector.report.close': '关闭报表', diff --git a/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.i18n.test.tsx b/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.i18n.test.tsx new file mode 100644 index 0000000000..3d1b48acae --- /dev/null +++ b/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.i18n.test.tsx @@ -0,0 +1,172 @@ +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * objectui#3913 — the PROPERTIES panel renders in the session's language. + * + * The sibling `previews/__tests__/block-config-i18n.test.ts` pins the TABLE + * (every label is a key its position implies, and both locales define it). That + * is a fact about data. This file pins the fact about the screen, and they are + * different: `renderField` reaches `f.label` from eleven places — eight as a + * `label=` prop, three as `