Skip to content

Commit 4ac9319

Browse files
fix(spec): localise the column heads of the view Columns, Sort and Tabs tables (#19968)
Fixes #19955 Clause-②: no This is the localisation follow-up the maintainer asked for, in the shape #17508 ruling A settled. PR #19917 gave the row items of the `view` `columns`, `sort` and `tabs` repeaters English titles. `view.form.ts` enumerated no row children for them, so Studio showed those titles untranslated in `zh-CN` / `ja-JP` / `es-ES`. - `packages/spec/src/ui/view.form.ts`: the three repeaters enumerate their row children. Each `label` equals the item schema's own `.meta({ title })`; the dev compared them on the built dist. - The four metadata-forms catalogs: the generator emitted the `en` leaves; `zh-CN`, `ja-JP` and `es-ES` are AI-authored, reusing the catalog's existing renderings where one exists. The dev report `5813748776` lists every reused and invented rendering. - `repeater-row-properties.test.ts`: `view` moves inside the derived population, so its no-`en`-echo case covers the new leaves. The dev ablated it red. - `object-lifecycle-panel-echo-decisions.test.ts`: the translated-label positive control moves by exactly the rows that landed, as its comment prescribes. The seat extended the claim to this file (`5813775189`). - Changeset: `@objectstack/spec` and `@objectstack/platform-objects` at `patch`. The `view.columns` union renderer question is carried as objectstack-ai/objectui#10239. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent e8f163f commit 4ac9319

8 files changed

Lines changed: 384 additions & 11 deletions
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/spec': patch
3+
'@objectstack/platform-objects': patch
4+
---
5+
6+
Studio's view property panel names the columns of its Columns, Sort and Tabs tables in the author's language, not only in English
7+
8+
Clause-②: no
9+
10+
`view.form.ts` now enumerates the row properties of the `columns`, `sort` and `tabs` repeaters, each with a `label` equal to its item schema's own `.meta({ title })`, so `os i18n extract` emits a `metadataForms.view.fields` key for each row property. The `en`, `zh-CN`, `ja-JP` and `es-ES` platform catalogs carry those keys. The translated catalogs reuse the word they already use for the same concept where they have one (`Label` → 显示名称 / 表示名 / Etiqueta, `Direction` → 排序方向 / 並び方向 / Dirección).
11+
12+
The row children declare no `type`, so each row input's widget is still derived from the schema. The view schema itself is unchanged.

‎packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,48 @@ export const enMetadataForms: NonNullable<TranslationData['metadataForms']> = {
790790
label: "Columns",
791791
helpText: "Columns to display (field names from selected object)"
792792
},
793+
"columns.field": {
794+
label: "Field"
795+
},
796+
"columns.label": {
797+
label: "Label"
798+
},
799+
"columns.width": {
800+
label: "Width (px)"
801+
},
802+
"columns.align": {
803+
label: "Alignment"
804+
},
805+
"columns.hidden": {
806+
label: "Hidden"
807+
},
808+
"columns.sortable": {
809+
label: "Sortable"
810+
},
811+
"columns.resizable": {
812+
label: "Resizable"
813+
},
814+
"columns.wrap": {
815+
label: "Wrap Text"
816+
},
817+
"columns.type": {
818+
label: "Renderer Type"
819+
},
820+
"columns.pinned": {
821+
label: "Pinned"
822+
},
823+
"columns.summary": {
824+
label: "Summary"
825+
},
826+
"columns.prefix": {
827+
label: "Prefix"
828+
},
829+
"columns.link": {
830+
label: "Primary Link"
831+
},
832+
"columns.action": {
833+
label: "Click Action"
834+
},
793835
filter: {
794836
label: "Filter",
795837
helpText: "Filter conditions — same visual builder as the list toolbar, with field-type-aware operators and value inputs"
@@ -798,6 +840,12 @@ export const enMetadataForms: NonNullable<TranslationData['metadataForms']> = {
798840
label: "Sort",
799841
helpText: "Default sort order"
800842
},
843+
"sort.field": {
844+
label: "Field"
845+
},
846+
"sort.order": {
847+
label: "Direction"
848+
},
801849
searchableFields: {
802850
label: "Searchable Fields",
803851
helpText: "Field names available for quick search"
@@ -847,6 +895,33 @@ export const enMetadataForms: NonNullable<TranslationData['metadataForms']> = {
847895
label: "Tabs",
848896
helpText: "In-view filter tabs — each tab applies its own filter rules"
849897
},
898+
"tabs.name": {
899+
label: "Name"
900+
},
901+
"tabs.label": {
902+
label: "Label"
903+
},
904+
"tabs.icon": {
905+
label: "Icon"
906+
},
907+
"tabs.view": {
908+
label: "List View"
909+
},
910+
"tabs.filter": {
911+
label: "Filter"
912+
},
913+
"tabs.order": {
914+
label: "Display Order"
915+
},
916+
"tabs.pinned": {
917+
label: "Pinned"
918+
},
919+
"tabs.isDefault": {
920+
label: "Default Tab"
921+
},
922+
"tabs.visible": {
923+
label: "Visible"
924+
},
850925
appearance: {
851926
label: "Appearance",
852927
helpText: "allowedVisualizations: which renderers users may switch between"

‎packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,48 @@ export const esESMetadataForms: NonNullable<TranslationData['metadataForms']> =
790790
label: "Columnas",
791791
helpText: "Columnas que mostrar (nombres de campo del objeto seleccionado)"
792792
},
793+
"columns.field": {
794+
label: "Campo"
795+
},
796+
"columns.label": {
797+
label: "Etiqueta"
798+
},
799+
"columns.width": {
800+
label: "Ancho (px)"
801+
},
802+
"columns.align": {
803+
label: "Alineación"
804+
},
805+
"columns.hidden": {
806+
label: "Oculta"
807+
},
808+
"columns.sortable": {
809+
label: "Ordenable"
810+
},
811+
"columns.resizable": {
812+
label: "Redimensionable"
813+
},
814+
"columns.wrap": {
815+
label: "Ajustar texto"
816+
},
817+
"columns.type": {
818+
label: "Tipo de renderizador"
819+
},
820+
"columns.pinned": {
821+
label: "Fijada"
822+
},
823+
"columns.summary": {
824+
label: "Resumen"
825+
},
826+
"columns.prefix": {
827+
label: "Prefijo"
828+
},
829+
"columns.link": {
830+
label: "Enlace principal"
831+
},
832+
"columns.action": {
833+
label: "Acción al hacer clic"
834+
},
793835
filter: {
794836
label: "Filtro",
795837
helpText: "Condiciones de filtro — el mismo constructor visual que la barra de herramientas de la lista, con operadores y entradas de valor según el tipo de campo"
@@ -798,6 +840,12 @@ export const esESMetadataForms: NonNullable<TranslationData['metadataForms']> =
798840
label: "Orden",
799841
helpText: "Orden predeterminado"
800842
},
843+
"sort.field": {
844+
label: "Campo"
845+
},
846+
"sort.order": {
847+
label: "Dirección"
848+
},
801849
searchableFields: {
802850
label: "Campos buscables",
803851
helpText: "Nombres de campo disponibles para búsqueda rápida"
@@ -847,6 +895,33 @@ export const esESMetadataForms: NonNullable<TranslationData['metadataForms']> =
847895
label: "Pestañas",
848896
helpText: "Pestañas de filtro en la vista: cada pestaña aplica sus propias reglas de filtro"
849897
},
898+
"tabs.name": {
899+
label: "Nombre"
900+
},
901+
"tabs.label": {
902+
label: "Etiqueta"
903+
},
904+
"tabs.icon": {
905+
label: "Icono"
906+
},
907+
"tabs.view": {
908+
label: "Vista de lista"
909+
},
910+
"tabs.filter": {
911+
label: "Filtro"
912+
},
913+
"tabs.order": {
914+
label: "Orden de visualización"
915+
},
916+
"tabs.pinned": {
917+
label: "Fijada"
918+
},
919+
"tabs.isDefault": {
920+
label: "Pestaña predeterminada"
921+
},
922+
"tabs.visible": {
923+
label: "Visibilidad"
924+
},
850925
appearance: {
851926
label: "Apariencia",
852927
helpText: "allowedVisualizations: qué renderizadores pueden alternar los usuarios"

‎packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,48 @@ export const jaJPMetadataForms: NonNullable<TranslationData['metadataForms']> =
790790
label: "列",
791791
helpText: "表示する列(選択オブジェクトのフィールド名)"
792792
},
793+
"columns.field": {
794+
label: "フィールド"
795+
},
796+
"columns.label": {
797+
label: "表示名"
798+
},
799+
"columns.width": {
800+
label: "幅(ピクセル)"
801+
},
802+
"columns.align": {
803+
label: "配置"
804+
},
805+
"columns.hidden": {
806+
label: "非表示"
807+
},
808+
"columns.sortable": {
809+
label: "並び替え可能"
810+
},
811+
"columns.resizable": {
812+
label: "サイズ変更可"
813+
},
814+
"columns.wrap": {
815+
label: "テキストの折り返し"
816+
},
817+
"columns.type": {
818+
label: "レンダラータイプ"
819+
},
820+
"columns.pinned": {
821+
label: "固定"
822+
},
823+
"columns.summary": {
824+
label: "集計"
825+
},
826+
"columns.prefix": {
827+
label: "プレフィックス"
828+
},
829+
"columns.link": {
830+
label: "主リンク"
831+
},
832+
"columns.action": {
833+
label: "クリック時のアクション"
834+
},
793835
filter: {
794836
label: "フィルター",
795837
helpText: "フィルター条件 — リストツールバーと同じビジュアルビルダー。演算子と値入力はフィールドタイプに応じて変わります"
@@ -798,6 +840,12 @@ export const jaJPMetadataForms: NonNullable<TranslationData['metadataForms']> =
798840
label: "並び替え",
799841
helpText: "既定の並び順"
800842
},
843+
"sort.field": {
844+
label: "フィールド"
845+
},
846+
"sort.order": {
847+
label: "並び方向"
848+
},
801849
searchableFields: {
802850
label: "検索対象フィールド",
803851
helpText: "クイック検索で使用可能なフィールド名"
@@ -847,6 +895,33 @@ export const jaJPMetadataForms: NonNullable<TranslationData['metadataForms']> =
847895
label: "タブ",
848896
helpText: "ビュー内フィルタータブ——各タブが独自のフィルタールールを適用"
849897
},
898+
"tabs.name": {
899+
label: "名前"
900+
},
901+
"tabs.label": {
902+
label: "表示名"
903+
},
904+
"tabs.icon": {
905+
label: "アイコン"
906+
},
907+
"tabs.view": {
908+
label: "リストビュー"
909+
},
910+
"tabs.filter": {
911+
label: "フィルター"
912+
},
913+
"tabs.order": {
914+
label: "表示順"
915+
},
916+
"tabs.pinned": {
917+
label: "固定"
918+
},
919+
"tabs.isDefault": {
920+
label: "既定のタブ"
921+
},
922+
"tabs.visible": {
923+
label: "表示"
924+
},
850925
appearance: {
851926
label: "外観",
852927
helpText: "allowedVisualizations:ユーザーが切り替え可能なレンダラー"

‎packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,8 +1111,10 @@ describe('#19403 round 10 — the verdicts, on the live bundles', () => {
11111111
// control moves by exactly the number of rows that landed, in every
11121112
// locale, which is the reading a per-locale count is for. 584 since
11131113
// #19814: the view form's new `pagination` section, its label authored
1114-
// in all three locales.
1115-
expect(translated.length, `${locale} positive control`).toBe(584);
1114+
// in all three locales. 609 since #19955: the 25 row labels of the view
1115+
// form's `columns` / `sort` / `tabs` repeaters, authored in all three
1116+
// locales.
1117+
expect(translated.length, `${locale} positive control`).toBe(609);
11161118
}
11171119
// ⭐ DARK — the blindness, executable. On a synthetic two-locale catalog the
11181120
// all-three predicate returns 0 while the per-locale one returns 1, so the

‎packages/platform-objects/src/apps/translations/repeater-row-properties.test.ts‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
//
2929
// The population is DERIVED from the forms, not listed here, so a repeater
3030
// enumerated tomorrow is red on the day it lands rather than a month later.
31-
// `view.columns` / `view.sort` / `view.tabs` enumerate no children and are
32-
// therefore outside it — their titles are #17507's.
31+
// `view.columns` / `view.sort` / `view.tabs` joined it with #19955: #17507
32+
// titled their item schemas, and `view.form.ts` now enumerates their children.
3333

3434
import { describe, it, expect } from 'vitest';
3535

@@ -136,17 +136,20 @@ describe('#17508 — the enumerated-repeater survey itself (controls before verd
136136
expect(CARRIERS).toContain('dashboard:header.actions');
137137

138138
const carrying = new Set(ROW_PROPERTIES.map((p) => p.type));
139-
for (const type of ['action', 'app', 'dashboard', 'dataset', 'field', 'flow', 'page', 'report', 'skill']) {
139+
for (const type of ['action', 'app', 'dashboard', 'dataset', 'field', 'flow', 'page', 'report', 'skill', 'view']) {
140140
expect(carrying.has(type), `${type} enumerates a repeater's row properties`).toBe(true);
141141
}
142+
// Lit — the three `view` repeaters are inside the population (#19955).
143+
// `view.columns` is the union case: a string-array arm beside the
144+
// object-array arm whose properties these are.
145+
for (const carrier of ['view:columns', 'view:sort', 'view:tabs']) {
146+
expect(CARRIERS, `${carrier} enumerates its row properties`).toContain(carrier);
147+
}
142148
// Dark — forms that enumerate none contribute none. A walk that matched
143149
// everything, or nothing, cannot pass both halves.
144150
for (const type of ['agent', 'tool', 'hook', 'position']) {
145151
expect(carrying.has(type), `${type} enumerates no repeater row properties`).toBe(false);
146152
}
147-
// Dark — `view.columns` / `view.sort` / `view.tabs` enumerate no children
148-
// (their titles are #17507's), so `view` is outside this population.
149-
expect(carrying.has('view'), 'view enumerates no repeater row properties yet').toBe(false);
150153
});
151154

152155
it('every enumerated row child declares a `label` — an omitted one silently becomes the English source', () => {

0 commit comments

Comments
 (0)