Skip to content

i18n-extract scaffolds none of the three key families #14253 adds, so check:i18n-coverage is blind to them #14376

Description

@os-warren

Found while implementing #14253. Filed rather than fixed in that PR because closing it moves nine generated bundles and scripts/i18n-coverage-baseline.json, and that ratchet movement should be readable on its own rather than buried inside a contract change.

What is missing

#14253 gives three authored display surfaces their first bundle keys:

  • objects.OBJECT._views.VIEW.bulkActions.NAME.{label,confirmText,confirmLabel,params.PARAM.{label,help,placeholder}}
  • objects.OBJECT._validations.RULE.message
  • datasets.NAME.{label,description,dimensions.DIM.label,measures.MEASURE.label}

All three are resolved at runtime by that PR. None of them is emitted by the extractor (packages/cli/src/utils/i18n-extract.ts), whose module header enumerates the families it walks — _sections, _views (label / description / emptyState), _actions (incl. params and resultDialog), _tabs, dashboards, pages.components, flows.screens, metadataForms.

Why that matters, beyond convenience

Two consequences, and the second is the one that decays quietly:

  1. os i18n extract will not scaffold the new keys, so a translator has to know they exist and hand-write them. That is the ordinary cost of an un-extracted family and it is survivable.
  2. pnpm check:i18n-coverage cannot see them. That ratchet measures untranslated declared labels against what the extract walk produces, so a family the walk does not visit contributes nothing to coverage — the number stays green while the surface it is supposed to describe grows. Same shape as the gate families this repo keeps finding dormant: not wrong, just silently narrower than it reads.

Precedent, both directions

This is the third time this exact follow-up has been needed, which is itself worth noticing: #11485 added the flow/screen bucket after #11287 landed the keys, and #13109 found the per-component pass stuck at region level after translatePage learned nested children. Both were filed the same way and closed on their own. The recurring shape is resolver first, extractor second, and nothing today makes the second half visible when the first lands.

Worth considering whether the fix is a fourth manual walk or a structural one — the resolvers and the extractor are two hand-maintained enumerations of the same key set, which is the same "second vocabulary" that TRANSLATABLE_METADATA_TYPES was made derived to avoid (#3786).

Measured today, so the baseline movement is predictable

No package that owns an i18n-extract.config.ts (platform-objects, five plugins, three services) currently authors a bulkActionDefs, a validations[].message, or a dataset — so wiring the emitters moves zero platform bundles. scripts/i18n-coverage-baseline.json may still move: check:i18n-coverage reads example apps, and examples/app-showcase, examples/app-crm and examples/app-todo do ship *.dataset.ts files.

Acceptance

  • The three families are emitted by the extract walk, gated the way the existing ones are.
  • pnpm check:i18n and pnpm check:i18n-coverage green, with any baseline movement explained in the PR body rather than regenerated silently.
  • A test that fails without the new emitters, per family.

Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions