Skip to content

i18n: a source-string edit still leaves zh-CN / ja-JP / es-ES silently stale — and pinning en to the source makes the asymmetry sharper, not smaller (needs a maintainer decision) #8765

Description

@hotlong

The half of #8721 its triage comment deliberately excluded, filed separately as that comment instructed ("If the work convinces you it is needed, file it separately and link back"). This one needs a product decision before any code, which is why it was kept out of that PR. Filed unassigned.

The gap

For the hand-authored apps / dashboards / pages bundles in packages/platform-objects/src/apps/translations/, nothing anywhere compares a translated locale against the source string it was translated from. Every gate over that half judges presence or ownership:

  • app-nav-translation-parity.test.ts — a translation exists for every declared id, and none outlives its declaration. Key-set claims; a stale value satisfies both.
  • pnpm check:i18n-coverage — ratchets untranslated labels. A translated-but-wrong label counts as covered.
  • pnpm check:app-nav-i18n — judges the merged nav tree, again for presence of a label per locale.

So when a source string changes, three bundles keep serving the previous translation under a fully green build. That is exactly how widget_recent_events shipped its pre-conversion title in all four locales.

Why implementing #8721 makes this more pressing, not less

#8721 pins en.ts to the declared source verbatim, because en is a copy rather than a translation. That is strictly good on its own, and it changes the shape of the remaining exposure in a way worth stating plainly:

  • Before: a source edit left all four bundles stale together. Wrong everywhere, but uniformly, and an English-speaking reviewer looking at the running app could see it.
  • After: the same edit fails the build until en.ts is corrected, so en is right and zh-CN / ja-JP / es-ES are wrong. The drift becomes locale-specific and invisible to every reviewer who reads the product in English — which is every reviewer this repo has.

The gate does not create the drift; it removes the one accidental symptom that used to make it visible.

The decision this needs

What should a translated locale do when its source string changes underneath it? The three options are real and they are not interchangeable:

  • A. Serve the stale translation (today's behavior, made explicit). Zero build friction; ships wrong text indefinitely; the failure is silent and locale-specific.
  • B. Fall back to the source string when the translation is known-stale. Never wrong, sometimes English in a Chinese UI — the same degradation an untranslated key already produces, so it introduces no new failure mode. Needs a staleness signal to exist: a recorded source-hash per translated leaf, or equivalent.
  • C. Fail the build until every locale is re-translated. Strongest guarantee, and it puts a four-locale translation task in front of every single-word source edit — including edits by agents who cannot judge the three target languages.

B and C both need the same missing machinery (a per-leaf record of which source string a translation was made from). A needs nothing and is what happens if this card is closed unfixed.

My reading, offered as input rather than a recommendation to adopt: B, gated by a recorded source hash, because it is the only option whose failure mode is a degradation the product already has a shape for, and because C's cost lands on exactly the contributors least able to pay it. But this is a product call about what users in non-English locales should see, and it should be the maintainer's.

Related: #8543 (the analogous hole on the generated side, which cannot occur there because en is rewritten every extract). Backlink: #8721.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions