From 113d06e9d3652b2c94ef6710f716297df237d5c7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 17:02:29 +0000 Subject: [PATCH] skills(i18n): validation messages are a translation group since #14253 `objects.OBJECT._validations.RULE.message` is object-scoped and resolved on the write path by the ObjectQL rule evaluator through the existing i18n service, so the blockquote teaching that validation messages are not a translation group is false on main. Correct it and the two neighbouring inventories it sits in: `_views.VIEW.bulkActions` on the object sub-key table and `datasets` in the top-level group list, plus the validation-rule target `os validate` / `os lint` already report but the list omitted. A correction, not an expansion: the retired-key blockquote's substance moves into the `_validations` row it belongs in, which pays the three additions in the same file. Net +7 tokens (4678 to 4685), 3 lines shorter; ceiling unchanged and the headroom left un-re-locked by #14574 is not spent. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1 --- skills/objectstack-i18n/SKILL.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/skills/objectstack-i18n/SKILL.md b/skills/objectstack-i18n/SKILL.md index 519130b15a..9340036289 100644 --- a/skills/objectstack-i18n/SKILL.md +++ b/skills/objectstack-i18n/SKILL.md @@ -160,18 +160,15 @@ All translatable content for a single object is aggregated under |:--------|:------| | `label` / `pluralLabel` / `description` | Object-level text (every key optional) | | `fields.{field_name}` | `label`, `help`, `placeholder`, `options` (option value → label) per field | -| `_views.{view_name}` | `label`, `description`, `emptyState.title` / `emptyState.message` | +| `_views.{view_name}` | `label`, `description`, `emptyState.title` / `emptyState.message`, `bulkActions.{def_name}` (`label`, `confirmText`, `confirmLabel`, `params`) | | `_actions.{action_name}` | `label`, `description`, `confirmText`, `successMessage`, `params.{param_name}`, `resultDialog` | | `_sections.{section_name}` | Form section `label`, `description` | | `_tabs.{tab_name}` | Filter-preset tab `label` (`ViewTabSchema.name`) | +| `_validations.{rule_name}` | `message` only — the sentence a rejected write returns (replaces the retired `validationMessages`) | Top-level groups alongside `objects`: `apps` (label, description, navigation), -`messages`, `globalActions` (object-less actions), `dashboards`, `pages`, `flows`, -`settings`, `metadataForms`, `settingsCommon`. - -> `validationMessages` is not a translation group — it was removed in spec 17.0.0. -> Author the message on the rule itself (`object.validations[].message`), which the -> engine returns on every rejected write. +`messages`, `globalActions` (object-less actions), `dashboards`, `datasets`, `pages`, +`flows`, `settings`, `metadataForms`, `settingsCommon`. For the exact Zod shape (and any field that may have been added since), read `node_modules/@objectstack/spec/src/system/translation.zod.ts` — @@ -198,9 +195,9 @@ parse, ship, and resolve to nothing. `os validate` / `os lint` / `os compile` check this direction and report it as warnings (`translation-target-unknown`, `translation-option-key-unknown`): a key -naming an object, field, view, tab, action, param, section, app, nav item, -dashboard, widget or flow screen that does not exist is listed alongside the names -that do. A bundle keyed to something since renamed still parses — the label just +naming an object, field, view, tab, action, param, section, validation rule, app, +nav item, dashboard, widget or flow screen that does not exist is listed alongside +the names that do. A bundle keyed to something since renamed still parses — the label just renders silently in its source locale while every neighbouring one resolves. ---