You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit c61ae6d
Browse filesBrowse the repository at this point in the historyBrowse files
docs(i18n): restate the orphan-key table from the emitter, sibling packages included
The "Must name" table under Orphan Keys and Option Keys still stated the
pre-fix rule: `objects.{object}` resolved only against "an object this stack
defines", which is false on the per-package pass, where a sibling package of
the same artifact is resolution context. Rewrite the table as one unit from
`validate-translation-references.ts`: define "declared" once (this stack or a
sibling package), add the rungs the emitter judges and the table omitted
(`_tabs`, `_validations`, `flows.*`, contributed-into apps, objectExtensions
targets), and extend the per-package sentence in validating-metadata.mdx to
the collections that resolve the same way.
Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: content/docs/protocol/kernel/i18n-standard.mdx
+32-12Lines changed: 32 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1017,20 +1017,40 @@ write for it: no bundle key exists for a map.
1017
1017
nothing supplies text for, whether that text would come from a bundle or from
1018
1018
an inline locale map. The reverse direction (keys a bundle carries that no
1019
1019
metadata claims) is checked by `os validate`, `os lint`, and `os compile`, which walk
1020
-
every bundle against the stack it ships with:
1021
-
1022
-
| Key | Must name |
1020
+
every bundle against the stack it ships with.
1021
+
1022
+
"Declared" below has one meaning in every row: declared by the stack being
1023
+
checked **or by a sibling package of the same artifact**. On a single
1024
+
`defineStack` project, and on the union pass over an artifact, those are the same
1025
+
thing. On the per-package pass over an artifact's `packages[]` (see
1026
+
[Validating metadata](/docs/deployment/validating-metadata)) each package is
1027
+
checked alone, but with the artifact's `packages[]` as resolution context — so a
1028
+
package may translate an object, view, page, action, app, dashboard or flow a
1029
+
sibling package declares. The keys beneath it are then judged against the
1030
+
sibling's own declaration, not waved through: a typo under a sibling's object is
1031
+
still an error. Where the package being checked and a sibling declare the same
1032
+
name, the package's own record is the one a field's options, an action's params,
1033
+
a dashboard's widgets and header actions, or a flow's screens are judged against;
1034
+
every other name the two declare adds up.
1035
+
1036
+
| Key | Resolves when it names |
1023
1037
|:---|:---|
1024
-
|`objects.{object}`| an object this stack defines, or a platform object |
1025
-
|`objects.{object}.fields.{field}`| a field that object declares |
1038
+
|`objects.{object}`| an object declared by this stack **or a sibling package of the same artifact** — its keys are then judged row by row below; **or** a platform object, or an object an `objectExtensions[]` entry extends that no package of the artifact declares — both skipped whole, because the owner's fields are not visible from here|
1039
+
|`objects.{object}.fields.{field}`| a field that object declares, a field an `objectExtensions[]` entry merges into it, or a system column the registry injects (`id`, `owner_id`, the audit and tenant columns; `_id`, `name` and `space` are accepted too)|
1026
1040
|`objects.{object}.fields.{field}.options.{key}`| an option's stored **`value`**|
1027
-
|`objects.{object}._views.{view}`| a view's `name`|
1028
-
|`objects.{object}._actions.{action}`| an action bound to that object |
1029
-
|`objects.{object}._actions.{action}.params.{param}`| a param's `name`|
1030
-
|`objects.{object}._sections.{section}`| a `fieldGroups[].key`, or a section with a `name`|
1031
-
|`apps.{app}` / `apps.{app}.navigation.{id}`| an app's `name` / a navigation item's `id`|
1032
-
|`dashboards.{dash}` / `.widgets.{id}` / `.actions.{actionUrl}`| the dashboard's `name` / a widget `id` / a header `actionUrl`|
1033
-
|`globalActions.{action}`| an action with **no**`objectName`|
1041
+
|`objects.{object}._views.{view}`| the key the view registry assigns a view over that object: a `listViews` / `formViews` map key, or the default `list`'s `name` — `default` when it has none (see [ObjectUI Integration](#objectui-integration)) |
1042
+
|`objects.{object}._sections.{section}`| a `fieldGroups[].key`, a named section of a form view over that object (a `formViews` entry or the default `form`), or a named section of a `record:details` component on a page bound to it |
1043
+
|`objects.{object}._tabs.{tab}`| a filter-preset tab's `name` in `interfaceConfig.userFilters.tabs` on a page over that object (`interfaceConfig.source`, else the page's `object`) — a list view's own `tabs` has no renderer and is not resolvable |
1044
+
|`objects.{object}._validations.{rule}`| a validation rule's `name` in that object's `validations[]` or in an `objectExtensions[]` entry aimed at it — a `conditional` rule's `then` / `otherwise` branch included |
1045
+
|`objects.{object}._actions.{action}`| an action bound to that object: one in its `actions`, or a stack-level action whose `objectName` is that object |
1046
+
|`objects.{object}._actions.{action}.params.{param}`| a param's `name` on that action |
1047
+
|`globalActions.{action}`| an action with **no**`objectName` — a key naming a bound action is reported with the `objects.{owner}._actions` key the resolver reads instead |
1048
+
|`apps.{app}`| an app declared, or one a `navigationContributions` entry anywhere in the artifact contributes into — including an app owned outside the artifact |
1049
+
|`apps.{app}.navigation.{id}`| a navigation item `id` the app declares (nested `children` and `areas[]` included) or one contributed into it; under an app that is only contributed into, the contributed ids alone |
1050
+
|`dashboards.{dash}` / `.widgets.{id}` / `.actions.{actionUrl}`| a dashboard declared / a widget `id` on it / a header action's `actionUrl` on it |
1051
+
|`flows.{flow}`| a flow's machine `name`, declared |
1052
+
|`flows.{flow}.screens.{node}`| the `id` of a `type: 'screen'` node in that flow, nodes nested in loop, parallel and try/catch regions included |
1053
+
|`flows.{flow}.screens.{node}.fields.{field}`| a `config.fields[].name` on that screen — an object-form screen (`config.objectName`) declares none, and its input labels live under `objects.{objectName}.fields`|
1034
1054
1035
1055
An unresolvable key is an **error**, not a warning: `os validate`, `os lint` and
1036
1056
`os compile` each report it as `translation-target-unknown` and exit non-zero, so
0 commit comments