What was measured
While re-deriving the coverage baseline on the branch for #14376, the per-config
os lint --json reports were classified by key shape. The i18n/ population the
ratchet counts contains repeated paths: the same key reported more than once,
with an identical rule, an identical message and an identical path.
Measured on main merged at 4d0d9445a8 (these duplicates are pre-existing —
they live entirely in the long-standing _actions walk, not in any family that
branch adds):
| config | duplicate findings beyond the first occurrence |
|---|
examples/app-todo/objectstack.config.ts | 40 |
examples/app-showcase/objectstack.config.ts | 29 |
examples/app-crm/objectstack.config.ts | 1 |
70 of the baselined units are the same string counted twice. Every one is in
the objects.OBJECT._actions.ACTION.* family; the objects carrying them in
showcase are showcase_field_zoo, showcase_invoice and showcase_task.
Two of the records, verbatim and byte-identical to each other:
{"severity":"warning","rule":"i18n/missing-action","message":"Action \"showcase_field_zoo\" _actions.showcase_action_param_gallery.label missing translation for locale \"zh-CN\"","path":"translations.zh-CN.objects.showcase_field_zoo._actions.showcase_action_param_gallery.label"}
{"severity":"warning","rule":"i18n/missing-action","message":"Action \"showcase_field_zoo\" _actions.showcase_action_param_gallery.label missing translation for locale \"zh-CN\"","path":"translations.zh-CN.objects.showcase_field_zoo._actions.showcase_action_param_gallery.label"}showcase_action_param_gallery is declared once, at
examples/app-showcase/src/ui/actions/index.ts:296. So this is not two authored
strings; it is one expected entry emitted twice, presumably because the action is
reachable through two carriers the walk visits.
Why it is worth recording
scripts/check-i18n-coverage.mjs counts issues, not distinct keys
(countI18nRuleIssues filters on the i18n/ prefix and takes .length), and
its own report calls the number "untranslated declared strings". With duplicates
in the population that sentence is not true of the number: translating one
key can move the ratchet by two, and the frozen debt is ~11% larger than the
set of strings a translator would actually have to write.
What this is not: it is not a hole in the ratchet. The gate stays monotone
and still reds on growth, and the DOWN direction still forces a re-derive. The
defect is in what the number means, and in the fact that "how many strings are
untranslated" cannot be read off it.
Shape of a fix (not attempted here)
Either de-duplicate the expected entries where they are produced (collectExpectedEntries
in packages/cli/src/utils/i18n-extract.ts — the same path for the same locale
is the same demand, whichever carrier reached it), or de-duplicate at the
reporting seam in packages/cli/src/utils/i18n-coverage.ts. Either way the
baseline moves DOWN by 70 across three configs in the same change, and a pin
should assert that no report contains two findings with the same path for the
same locale, so the property cannot regress silently.
⚠️ Note for whoever takes it: the counts above are on the merged tree that
also carries the three new families of #14376. If that branch has landed by then,
re-measure rather than reusing these numbers — the totals will differ even though
the duplicate set (all _actions) should not.
How to reproduce
pnpm --workspace-concurrency=2 build
node packages/cli/bin/run.js lint examples/app-todo/objectstack.config.ts --json > /tmp/todo.json
node -e "const L=require('/tmp/todo.json').issues.filter(i=>String(i.rule).startsWith('i18n/'));const m=new Map();for(const i of L)m.set(i.path,(m.get(i.path)||0)+1);const d=[...m].filter(([,n])=>n>1);console.log('findings',L.length,'distinct',m.size,'extra',d.reduce((s,[,n])=>s+n-1,0))"
Seen while re-deriving the baseline for #14376; out of scope for that card and
deliberately not touched there.
Generated by Claude Code
What was measured
While re-deriving the coverage baseline on the branch for #14376, the per-config
os lint --jsonreports were classified by key shape. Thei18n/population theratchet counts contains repeated paths: the same key reported more than once,
with an identical
rule, an identicalmessageand an identicalpath.Measured on
mainmerged at4d0d9445a8(these duplicates are pre-existing —they live entirely in the long-standing
_actionswalk, not in any family thatbranch adds):
examples/app-todo/objectstack.config.tsexamples/app-showcase/objectstack.config.tsexamples/app-crm/objectstack.config.ts70 of the baselined units are the same string counted twice. Every one is in
the
objects.OBJECT._actions.ACTION.*family; the objects carrying them inshowcase are
showcase_field_zoo,showcase_invoiceandshowcase_task.Two of the records, verbatim and byte-identical to each other:
{"severity":"warning","rule":"i18n/missing-action","message":"Action \"showcase_field_zoo\" _actions.showcase_action_param_gallery.label missing translation for locale \"zh-CN\"","path":"translations.zh-CN.objects.showcase_field_zoo._actions.showcase_action_param_gallery.label"} {"severity":"warning","rule":"i18n/missing-action","message":"Action \"showcase_field_zoo\" _actions.showcase_action_param_gallery.label missing translation for locale \"zh-CN\"","path":"translations.zh-CN.objects.showcase_field_zoo._actions.showcase_action_param_gallery.label"}showcase_action_param_galleryis declared once, atexamples/app-showcase/src/ui/actions/index.ts:296. So this is not two authoredstrings; it is one expected entry emitted twice, presumably because the action is
reachable through two carriers the walk visits.
Why it is worth recording
scripts/check-i18n-coverage.mjscounts issues, not distinct keys(
countI18nRuleIssuesfilters on thei18n/prefix and takes.length), andits own report calls the number "untranslated declared strings". With duplicates
in the population that sentence is not true of the number: translating one
key can move the ratchet by two, and the frozen debt is ~11% larger than the
set of strings a translator would actually have to write.
What this is not: it is not a hole in the ratchet. The gate stays monotone
and still reds on growth, and the DOWN direction still forces a re-derive. The
defect is in what the number means, and in the fact that "how many strings are
untranslated" cannot be read off it.
Shape of a fix (not attempted here)
Either de-duplicate the expected entries where they are produced (
collectExpectedEntriesin
packages/cli/src/utils/i18n-extract.ts— the samepathfor the same localeis the same demand, whichever carrier reached it), or de-duplicate at the
reporting seam in
packages/cli/src/utils/i18n-coverage.ts. Either way thebaseline moves DOWN by 70 across three configs in the same change, and a pin
should assert that no report contains two findings with the same
pathfor thesame locale, so the property cannot regress silently.
also carries the three new families of #14376. If that branch has landed by then,
re-measure rather than reusing these numbers — the totals will differ even though
the duplicate set (all
_actions) should not.How to reproduce
Seen while re-deriving the baseline for #14376; out of scope for that card and
deliberately not touched there.
Generated by Claude Code