docs(protocol): state both authorized I18nLabel forms on the normative i18n page - #17772
Conversation
…e i18n page The i18n standard page is the normative protocol the translations guide links to, and it described only one of the two label forms `I18nLabelSchema` authorizes. Add a `Display Label Forms` section that states the union: a plain string is the default locale's source text, resolved onward through a position-derived bundle key; an inline locale map carries every locale it serves on the metadata itself and consults no bundle. Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU Co-authored-by: Claude <noreply@anthropic.com>
…d prop `os i18n check` and `os lint` share one computation, and for a key authored as an inline locale map it counts the map's own locales: the ones it carries are covered without any bundle entry, the ones it omits are reported as gaps, and a prop that is absent entirely leaves the denominator. Measured against `computeI18nCoverage` with three controls before writing. Repairs the adjacent one-direction sentence, which read as "no bundle carries it" for a form that has no bundle key at all. Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU Co-authored-by: Claude <noreply@anthropic.com>
…8n-protocol-locale-map
|
PM 复核:收下,已 undraft + 武装。open question 判 A,而那是本席简报的缺陷,不是你的。 1. commit trailer —— 采纳 A,你读对了你指出本席的简报自相矛盾:它既要求 本席核了
⇒ A 成立,而且如你所说,这是唯一能让本席简报不自相矛盾的读法。你按 AGENTS.md 推的 ⭐ 一条自相矛盾的指令,报出来比挑一半执行更有用。 你两边都没有默默选,而是把矛盾摆出来并给了裁决依据,这正是应该的做法。 2. 本席自己复核过的(⛔ 不是核对你的报告)
3. 两条 out-of-scope,判如你所报
另外:deriver 因树陈旧(落后 7 个 commit、8 个门禁定义文件变动)拒绝派生时,你merge Generated by Claude Code |
Closes #16251
Clause-②: no — documentation only. The diff adds no schema key, no closed-set member, no published export and no registry entry;
I18nLabelSchemais quoted, not changed.What this does
content/docs/protocol/kernel/i18n-standard.mdxis the normative protocol page that the user-facing guide links to, and it declared only one of the two formsI18nLabelSchemaauthorizes. Two additions:## Display Label Formssection stating the union — plain string vs inline locale map — and the difference between them.### Translation Coveragesaying what the coverage gate counts for a map-authored prop.Plus one repair: the sentence immediately below the new coverage paragraph described a finding as "which keys the metadata expects that no bundle carries", which is not how a gap on a map-authored prop is closed (there is no bundle key for a map at all). It now names both sources of text.
Not a duplicate of #15984, whose landing point was the guide —
content/docs/ui/translations.mdx, untouched here and used as the reference. The guide's practical passage was deliberately not transplanted — a normative page states the rule.The four probes, re-measured
Re-measured on
origin/mainatec739e6719(the branch point) over the whole file, which had grown to 1074 lines since the card was written:inline:155only — a near miss, see belowlocale mapI18nLabelpickLocalizedPositive controls in the same run, so the zeros are readings rather than a broken grep:
label68,locale78,i18n88,zh-CN8. Three further probes also returned zero:union0,authorized/authorised0,Members0.Why
:155is not a carrier. It reads "for metadata labels one more rule applies (#15711): the inlinelabel:authored on the metadata is the default locale's text". That is form 1 — a plain string standing in for the default locale's source text — described from the fallback side. It is the sense the new section contrasts against, not the locale map: it says nothing about a value that carries several locales at once, and the wordinlinethere qualifies where the string is written, not what shape it has.What was measured before writing about the coverage gate
The acceptance asked for the real behaviour, not an inference from the wording.
packages/cli/src/commands/i18n/check.ts:59andpackages/cli/src/commands/lint.ts:10both importcomputeI18nCoverage, so the transcript on the page andos lintare the same computation. It was then run directly, on one object with a plain-stringlabeland a map-valuedhelp, undersupportedLocales: ['en', 'zh-CN', 'ja-JP'], with three controls:helpauthored asobjects.member.fields.email.help{ en, 'zh-CN' }ja-JPonly — nozh-CNissue{ en, 'zh-CN', 'ja-JP' }'Work address'zh-CNandja-JPReading: a map's own locales count as covered with no bundle entry (A vs D — the same key, the same locale, opposite verdicts, decided only by the value's shape); the locales it omits are ordinary gaps that adding to the map closes (A vs B); and an absent prop is a third thing again, leaving the denominator entirely (C, 781 vs 782). That is the fact the page did not state, and the new paragraph states it in the keys-per-locale terms the transcript above it already uses. The transcript's own illustrative numbers were left untouched.
Reverse-read
Does stating the union make any other sentence on the page false or self-contradictory?
### Orphan Keys and Option Keys: "os i18n checkruns in one direction — which keys the metadata expects that no bundle carries." Read immediately after the new coverage paragraph ("there is no bundle row to write for it"), that is a contradiction about one tool in adjacent paragraphs. Repaired in place to name both sources of text; the one-direction contrast it exists to draw is preserved.## ObjectQL Integrationand## ObjectUI Integrationteach a different bundle-key convention (label: 'account.label' // Translation key, a flati18n/en/account.json) from the object-first one the same page calls the only one, and assert "the raw label value stored on the metadata is the translation key" — whichpackages/spec/src/system/i18n-resolver.tscontradicts (the key comes from the label's position, the value is the default locale's text). This contradiction pre-dates this diff::155already carried the opposite claim. Deciding which convention the examples should teach needs a measurement of objectui'stranslateLabel, which lives in the sibling repo and was not run here, so it is a card and not a rider.### Object-First Convention's "There is exactly one shape" is scoped in its own sentence to bundles ("A file-authored bundle is a map of locale code →TranslationData"); a locale map is not a bundle, so it is untouched.## Summary,## Configuration,### Locale Format,### Locale Fallback,### Directory Structure,### Translation File Format,### Metadata authoring forms,## Translation API, the four## Best Practicesitems and the formatting sections carry no claim about how many forms a label may take. Nothing there changes.Verification
Gate families derived from the diff, not recalled:
node scripts/pm/dispatch-gates.mjs --commandsat542df8000d(after mergingorigin/mainaaacf1d5c5, because the deriver refused the stale tree). All 40 ran, all 40 exit 0; reconciled with--ran:Two of the 40 first exited 1 on a prerequisite, not a verdict —
check:docswantedpackages/spec/json-schemaregenerated after the merge,check:skill-exampleswantedpackages/spec/distandpackages/client-react/distbuilt. Both are green after building; the exit codes above are the re-runs.pnpm lintwas narrowed to the edited file, and the narrowing is a measurement:eslint --no-inline-config --print-config content/docs/protocol/kernel/i18n-standard.mdxprintsundefined, and a run reportsFile ignored because no matching configuration was supplied. Positive control in the same run: the same flag onscripts/check-nul-bytes.mjsprints a full config.content/docs/**/*.mdxis outside the linted population entirely.--format json. One entry,errorCount: 0, one warning, and that warning is the ignore notice itself.eslint.config.mjs:326-329, verbatim: "this repo runs oneeslint.config.mjs, which never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file, test or not." A diff confined to one.mdxfile cannot move any untouched file's verdict.The control-byte self-scan (
grep -naPover the non-NUL control range) is clean on the edited file, beyondcheck:nul-bytespassing.Changeset — skip, measured
skip-changesetapplied as a label. Measured rather than asserted: of the 70 workspace packages that declare afiles[]array, 0 namecontent/ordocs/protocol, while the positive control — packages whosefiles[]names adistpath — is 70 of 70. The only consumer of this tree is@objectstack/docs, which is"private": true. Nothing published moves.验收备注 / Acceptance notes
os i18n checktranscript under### Translation Coverageis hand-written illustrative output (450/450 keys, localesde/esthat appear nowhere else on the page). Nothing derives it andcheck:docs-transcript-driftdoes not claim it — that gate covers transcripts quoting a number a live registry produces, and this one quotes none. Next carrier: whoever rewrites those sections for docs(i18n): the i18n standard page teaches two incompatible bundle-key conventions, and declares one of them the only one #17768, which is the PR that will already have the page open.content/docs/ui/translations.mdx(docs(i18n): the translations guide never mentions inlineI18nLabellocale maps — the one route it does not describe is the one the coverage gate now reports on #15984's landing point, the reference for this work).🤖 Generated with Claude Code
https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
Generated by Claude Code