Skip to content

Commit 9620486

Browse files
committed
docs(spec): stop the translation @example blocks teaching an unresolvable messages id
`messages` is a flat `Record<string, string>` while `t()` resolves a key by walking its dot path, so an id that merely contains a dot is unreachable. Both docblock `@example` blocks on `TranslationDataSchema` and `TranslationItemSchema` demonstrated exactly such an id (`'common.save'`); they now author `commonSave` and state the rule. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 79a046f commit 9620486

2 files changed

Lines changed: 32 additions & 2 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
docs(translation): the two `TranslationData` / `TranslationItem` `@example` blocks stop teaching a `messages` id that cannot resolve (#18190)
6+
7+
`messages` is declared a flat `Record<string, string>` (`translation.zod.ts` — `messages: z.record(z.string(), z.string())`), while `t()` resolves a key by walking its dot path segment by segment. Both implementations do this, identically:
8+
9+
- `packages/core/src/fallbacks/memory-i18n.ts` — `resolveKey()`, `key.split('.')`, walked by `t()`;
10+
- `packages/services/service-i18n/src/file-i18n-adapter.ts` — a second `resolveKey()` with the same body, walked by `t()` through `resolveFromLocale()`.
11+
12+
So an id that merely *contains* a dot is one flat key named `common.save`, and `t('messages.common.save', …)` looks for a nested `common` object, finds a string or nothing at the first hop, and returns the key itself. Both docblock `@example` blocks on this schema demonstrated exactly that id — the doorway an author (or an authoring agent) copies from.
13+
14+
- The JSON example on `TranslationDataSchema` and the TypeScript example on `TranslationItemSchema` now author `commonSave`, the single-segment spelling `content/docs/protocol/kernel/i18n-standard.mdx` already prescribes and `packages/plugins/plugin-audit/src/translations/messages.ts` already applies to its own bundle.
15+
- Both docblocks now state the rule, so the counter-example is named as one rather than demonstrated.
16+
17+
⚠️ **The schema still accepts a dotted id** — nothing is narrowed here and no key is retired. Whether the door should refuse a dotted `messages` key narrows a published accept set and rides its own card; this change is the doorway half only.
18+
19+
For authors: a `messages` id containing a dot never resolved, so re-spelling one single-segment (`'common.save'` → `commonSave`, looked up as `messages.commonSave`) turns a key that was returning itself into one that translates. No key that resolved before stops resolving.

‎packages/spec/src/system/translation.zod.ts‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -574,9 +574,16 @@ const TRANSLATION_KEY_GUIDANCE: Record<LegacyObjectFirstKey | 'validationMessage
574574
* {
575575
* "objects": { "account": { "label": "Account" } },
576576
* "apps": { "crm": { "label": "CRM" } },
577-
* "messages": { "common.save": "Save" }
577+
* "messages": { "commonSave": "Save" }
578578
* }
579579
* ```
580+
*
581+
* `messages` ids are single-segment. `t()` resolves a key by walking its dot
582+
* path into this structure, and `messages` is a flat `Record<string, string>`,
583+
* so an id that itself contains a dot (`'common.save'`) is looked up as a
584+
* nested `common` object and resolves to nothing; `messages.commonSave`
585+
* resolves. Both implementations walk identically — see
586+
* `content/docs/protocol/kernel/i18n-standard.mdx`.
580587
*/
581588
/**
582589
* The translation groups, as a shape rather than a schema.
@@ -1414,6 +1421,10 @@ export type TranslationConfig = z.input<typeof TranslationConfigSchema>;
14141421
* sync skips an item whose locale it cannot resolve, and a skip is invisible
14151422
* to whoever — or whatever — authored it.
14161423
*
1424+
* `messages` ids are single-segment, for the reason spelled out on
1425+
* {@link TranslationDataSchema}: `t()` walks the dot path, so an id containing
1426+
* a dot resolves to nothing.
1427+
*
14171428
* @example
14181429
* ```typescript
14191430
* const zhCN = defineTranslation({
@@ -1427,7 +1438,7 @@ export type TranslationConfig = z.input<typeof TranslationConfigSchema>;
14271438
* },
14281439
* },
14291440
* apps: { crm: { label: '客户关系管理' } },
1430-
* messages: { 'common.save': '保存' },
1441+
* messages: { commonSave: '保存' },
14311442
* });
14321443
* ```
14331444
*/

0 commit comments

Comments
 (0)