Skip to content

[finding] check:i18n-coverage counts one untranslated action key more than once — 70 of the 691 baselined units are byte-identical duplicate findings #14728

Description

@os-trump

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions