Skip to content

test(i18n): pin every shipped string before moving any of them - #590

Merged
mrviduus merged 1 commit into
mainfrom
chore/locale-golden-nets
Sep 9, 2026
Merged

test(i18n): pin every shipped string before moving any of them#590
mrviduus merged 1 commit into
mainfrom
chore/locale-golden-nets

Conversation

@mrviduus

@mrviduus mrviduus commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Slice 1 of five. Nets only — no behaviour change. Both fixtures are generated from today's files, so this is green by construction. That is the point: it makes everything after it reversible.

Why a fixture and not a diff

The two locale files hold 547 of the same keys between them — 523 identical, 24 already quietly diverged — and nothing checks that. Collapsing them onto one source means relocating 72 KB of JSON, and that diff is unreviewable.

Worse, the hazards inside it are invisible to a skim:

  • palette.* vs library.palette.*
  • library.sort.* vs library.sortRecent
  • tts.listen vs home.features.tts

So the fixture becomes the review surface. A pure move leaves it untouched; every line that does change is a shipped string somebody has to defend.

Committed fixture with an explicit compare, deliberately not toMatchFileSnapshot — a snapshot is regenerated with one vitest -u, and the entire value here is that changing a production string costs a hand edit.

The mobile half is the more important one

packages/shared/src/i18n/en.json is about to be edited for the web app's benefit. And mobile-ota.yml triggers on paths: packages/**; i18n is plain JS and does not move the Expo fingerprint, so a merge to main republishes the JS bundle to installed Android phones — no store review, nobody pressing anything.

This fixture is what stands between "we tidied the shared file" and "an OTA changed the copy on someone's phone".

It lives in apps/mobile/src/lib/ because apps/mobile/vitest.config.ts sets include: ['src/lib/**/*.test.ts'] — a test anywhere else in that app silently does not run.

What is asserted

All three directions, with failures naming the key rather than counting them:

a value changed expected [ 'common.loading' ] to deeply equal []
a key vanished listed by name
a key appeared without the fixture being updated listed by name

Verification

Mutation-verified, not assumed. Changing common.loading in each file fails the matching test and names it; reverting restores green.

780 web + 385 mobile tests green; both typechecks clean. Nothing but the four new files is touched.

🤖 Generated with Claude Code

https://claude.ai/code/session_011rgEMvYYi4Egj99dVtvm6E

Slice 1 of the locale de-duplication: nets only, no behaviour change. Both
fixtures are generated from today's files, so this PR is green by construction —
which is the point. It is what makes everything after it reversible.

The two locale files hold 547 of the same keys between them, 523 identical and
24 already quietly diverged, and nothing checks that. Collapsing them onto one
source means relocating 72 KB of JSON, and that diff is unreviewable. Worse, the
hazards in it are invisible to a skim: `palette.*` versus `library.palette.*`,
`library.sort.*` versus `library.sortRecent`, `tts.listen` versus
`home.features.tts`. So the fixture becomes the review surface — a pure move
leaves it untouched, and every line that does change is a shipped string somebody
has to defend.

A committed fixture with an explicit compare, NOT `toMatchFileSnapshot`: a
snapshot is regenerated with one `vitest -u`, and the entire value here is that
changing a production string costs a hand edit.

The mobile half is the more important one. `packages/shared/src/i18n/en.json` is
about to be edited for the WEB app's benefit, and `mobile-ota.yml` triggers on
`paths: packages/**` — i18n is plain JS and does not move the Expo fingerprint,
so a merge republishes the bundle to installed Android phones with no store
review and nobody pressing anything. This fixture is what stands between "we
tidied the shared file" and "an OTA changed the copy on someone's phone".

It lives in `apps/mobile/src/lib/` because `vitest.config.ts` there sets
`include: ['src/lib/**/*.test.ts']` — a test anywhere else in that app silently
does not run.

Both directions are asserted: a value that changed, a key that vanished, and a
key that appeared without the fixture being updated. Failures name the key rather
than counting them.

Mutation-verified rather than assumed: changing `common.loading` in each file
fails the matching test with `expected [ 'common.loading' ] to deeply equal []`,
and reverting restores green.

780 web + 385 mobile tests green; both typechecks clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011rgEMvYYi4Egj99dVtvm6E
@mrviduus
mrviduus force-pushed the chore/locale-golden-nets branch from 29ac69b to e771994 Compare September 9, 2026 03:16
@mrviduus
mrviduus merged commit b52e3af into main Sep 9, 2026
10 checks passed
@mrviduus
mrviduus deleted the chore/locale-golden-nets branch September 9, 2026 03:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant