diff --git a/.changeset/13478-translation-preview-positions-permissions.md b/.changeset/13478-translation-preview-positions-permissions.md new file mode 100644 index 0000000000..68b15a2e83 --- /dev/null +++ b/.changeset/13478-translation-preview-positions-permissions.md @@ -0,0 +1,11 @@ +--- +"@object-ui/app-shell": patch +"@object-ui/i18n": patch +--- + +Clause-②: yes +Metadata-protocol: none + +The metadata designer's translation preview draws the `positions` and `permissions` groups that `@objectstack/spec` now declares on a translation bundle, each with its own card, count and sample, and counts them in the coverage bar. Before, the preview did not type-check against that spec, and a bundle that translated only position or permission-set labels read as empty. + +`isSpecTranslationData` recognises a served translation document by those two groups as well, so a bundle that translates only positions or permission sets is namespaced under `app` like any other spec bundle instead of being returned untransformed. diff --git a/.objectstack-pin/core/package.json b/.objectstack-pin/core/package.json index 40d33a91e5..3b1ddbd649 100644 --- a/.objectstack-pin/core/package.json +++ b/.objectstack-pin/core/package.json @@ -29,7 +29,7 @@ "dependencies": { "@objectstack/spec": "18.1.0", "@objectstack/types": "18.1.0", - "zod": "^4.6.5" + "zod": "^4.6.6" }, "keywords": [ "objectstack", diff --git a/.objectstack-pin/lint/package.json b/.objectstack-pin/lint/package.json index 618731e343..d72a5b26f0 100644 --- a/.objectstack-pin/lint/package.json +++ b/.objectstack-pin/lint/package.json @@ -50,7 +50,7 @@ "devDependencies": { "@types/node": "^26.6.3", "vitest": "^4.1.11", - "zod": "^4.6.5" + "zod": "^4.6.6" }, "keywords": [ "objectstack", diff --git a/.objectstack-pin/spec/package.json b/.objectstack-pin/spec/package.json index ba0c88a668..4d08a92f7d 100644 --- a/.objectstack-pin/spec/package.json +++ b/.objectstack-pin/spec/package.json @@ -282,7 +282,7 @@ }, "dependencies": { "pg-connection-string": "^2.14.0", - "zod": "^4.6.5" + "zod": "^4.6.6" }, "peerDependencies": { "ai": "^7.0.0" diff --git a/.objectstack-sha b/.objectstack-sha index 406c7555ce..3b0fbc38a4 100644 --- a/.objectstack-sha +++ b/.objectstack-sha @@ -1 +1 @@ -e649c2d91e270e3da26820bcfe54a28445305a75 +47af3dde42bb4e77fd92025605b89b4a473295a4 diff --git a/packages/app-shell/src/views/metadata-admin/i18n.ts b/packages/app-shell/src/views/metadata-admin/i18n.ts index 1a166d2c26..3eb4498491 100644 --- a/packages/app-shell/src/views/metadata-admin/i18n.ts +++ b/packages/app-shell/src/views/metadata-admin/i18n.ts @@ -1785,6 +1785,8 @@ const ENGINE_STRINGS_EN: Record = { 'engine.translationPreview.category.datasets': 'Datasets', 'engine.translationPreview.category.pages': 'Pages', 'engine.translationPreview.category.flows': 'Flows', + 'engine.translationPreview.category.positions': 'Positions', + 'engine.translationPreview.category.permissions': 'Permission Sets', 'engine.translationPreview.category.metadataForms': 'Metadata Forms', 'engine.translationPreview.category.settingsCommon': 'Settings UI', 'engine.translationPreview.coverage': 'Category coverage:', @@ -5407,6 +5409,8 @@ const ENGINE_STRINGS_ZH: Record = { 'engine.translationPreview.category.datasets': '数据集', 'engine.translationPreview.category.pages': '页面', 'engine.translationPreview.category.flows': '流程', + 'engine.translationPreview.category.positions': '岗位', + 'engine.translationPreview.category.permissions': '权限集', 'engine.translationPreview.category.metadataForms': '元数据表单', 'engine.translationPreview.category.settingsCommon': '设置界面', 'engine.translationPreview.coverage': '分类覆盖率:', diff --git a/packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.categories-11765.test.tsx b/packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.categories-11765.test.tsx index c038d5454f..f595f7c9fd 100644 --- a/packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.categories-11765.test.tsx +++ b/packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.categories-11765.test.tsx @@ -58,6 +58,12 @@ const ONE_ENTRY: Record; key: string; ro }, pages: { data: { home: { label: 'Accueil' } }, key: 'home', row: () => keys(1) }, flows: { data: { onboarding: { label: 'Intégration' } }, key: 'onboarding', row: () => keys(1) }, + positions: { data: { legal_counsel: { label: 'Conseiller juridique' } }, key: 'legal_counsel', row: () => keys(1) }, + permissions: { + data: { contract_reviewer: { label: 'Revue des contrats', description: 'Lire et approuver les contrats' } }, + key: 'contract_reviewer', + row: () => keys(2), + }, metadataForms: { data: { object: { label: 'Objet' } }, key: 'object', row: () => keys(1) }, // `settingsCommon` is one strict object, not a record of author-named nodes: // its members (`sourceLabels`) are what the card counts and samples. diff --git a/packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.tsx b/packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.tsx index 06de276d84..f46d43302a 100644 --- a/packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.tsx +++ b/packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.tsx @@ -43,7 +43,9 @@ import { MessageCircle, PanelsTopLeft, Settings2, + ShieldCheck, SquareChevronDown, + Users, Workflow, } from 'lucide-react'; import { EmptyDescription } from '@object-ui/components'; @@ -94,6 +96,8 @@ const CATEGORY_DEFS = { datasets: { label: 'engine.translationPreview.category.datasets', icon: Database, flat: false }, pages: { label: 'engine.translationPreview.category.pages', icon: PanelsTopLeft, flat: false }, flows: { label: 'engine.translationPreview.category.flows', icon: Workflow, flat: false }, + positions: { label: 'engine.translationPreview.category.positions', icon: Users, flat: false }, + permissions: { label: 'engine.translationPreview.category.permissions', icon: ShieldCheck, flat: false }, metadataForms: { label: 'engine.translationPreview.category.metadataForms', icon: FileText, flat: false }, settingsCommon: { label: 'engine.translationPreview.category.settingsCommon', icon: Settings2, flat: false }, } satisfies Record>; diff --git a/packages/app-shell/src/views/metadata-admin/previews/adminPreviews.i18n-10862-s2.test.tsx b/packages/app-shell/src/views/metadata-admin/previews/adminPreviews.i18n-10862-s2.test.tsx index 63520a7da9..0b8aeda0ed 100644 --- a/packages/app-shell/src/views/metadata-admin/previews/adminPreviews.i18n-10862-s2.test.tsx +++ b/packages/app-shell/src/views/metadata-admin/previews/adminPreviews.i18n-10862-s2.test.tsx @@ -461,6 +461,8 @@ describe('TranslationPreview reads the designer locale (objectui#10862)', () => { key: 'engine.translationPreview.category.datasets' }, { key: 'engine.translationPreview.category.pages' }, { key: 'engine.translationPreview.category.flows' }, + { key: 'engine.translationPreview.category.positions' }, + { key: 'engine.translationPreview.category.permissions' }, { key: 'engine.translationPreview.category.metadataForms' }, { key: 'engine.translationPreview.category.settingsCommon' }, { key: 'engine.translationPreview.categoryEmpty' }, @@ -468,7 +470,7 @@ describe('TranslationPreview reads the designer locale (objectui#10862)', () => { key: 'engine.translationPreview.keyCountOne', vars: { count: 1 } }, ]); // The bundle's own locale is author data, not the designer's language. - expectAsWritten(['French', 'fr-FR', 'account', '"Un"', '2/11 (18%)']); + expectAsWritten(['French', 'fr-FR', 'account', '"Un"', '2/13 (15%)']); }); } }); diff --git a/packages/i18n/src/utils/spec-translations.ts b/packages/i18n/src/utils/spec-translations.ts index 960eff7287..98b3e5b291 100644 --- a/packages/i18n/src/utils/spec-translations.ts +++ b/packages/i18n/src/utils/spec-translations.ts @@ -89,6 +89,11 @@ const SPEC_TRANSLATION_GROUPS = [ // `picklist` metadata kind); added at that bump (objectui#11438). The // transform below copies it through onto `app` like any other group. 'picklists', + // objectstack `47af3dde42` serves `positions` and `permissions` (positions + // and permission sets are translatable); added at the pin move to that + // commit (objectui#13478), copied through onto `app` the same way. + 'positions', + 'permissions', ] as const; function isPlainObject(value: unknown): value is Record {