Skip to content

Web: the isZh migration is losing ground — add a one-way ceiling so it can converge #5519

Description

@Lstarsky0

Problem

The #5337 migration is not converging. Counting files outside web/lib/i18n/ that
branch on locale === "zh", on main:

  • 90 days ago: 12
  • 60 days ago: 15
  • 30 days ago: 27
  • today: 31

Over the last 30 days, 10 files gained a branch and 6 lost one. All 6 are
migration work — four came out with the #4934 chrome and homepage move
([locale]/layout, [locale]/page, nav, footer), two with the docs shell
in #5488. The 10 are almost all new docs/* pages, written with the ternary
from the first commit.

Two of those 10 are docs/constitution and docs/runtime-api, which I migrated
in #5517 — written and migrated inside the same month. That round trip is the
whole problem in one line: nothing tells the author of a new page that the
ternary is not the pattern any more, so the copy goes in the page, and then a
later PR moves it out again.

check-locales.mjs cannot see this. It holds dictionaries to parity with the
English reference, so a page whose copy never reaches a dictionary is invisible
to it — exactly the case that needs catching.

Proposal

A one-way ceiling on the set of files allowed to branch, in the shape
scripts/check-persistence-backlog-budget.py already uses here: a committed
document that CI enforces and that can only move in one direction.

web/scripts/check-i18n-ratchet.mjs plus web/scripts/i18n-ratchet-baseline.json
holding today's 31 paths. Two failures:

  • a file that branches and is not on the list — a new leak, the message names
    the file and points at lib/i18n/dictionaries/
  • a file on the list that no longer branches — a stale entry, the message says
    to delete the line

The second one is what makes it a ratchet rather than a snapshot: migrating a
page means deleting its line, and the ceiling drops by one. It also means the
list is #5337's remaining checklist, maintained by CI instead of by hand.

Detection is the literal comparison, [!=]== "zh". That is deliberately narrow.
A boolean passed down as a prop is not matched directly — admin/admin-client.tsx
takes isZh from its parent — but the site that computes it always is, so a
branch cannot enter the tree without failing somewhere. Matching the bare
identifier instead would be worse: components/nav.tsx mentions isZh in a
comment saying it has none, and lib/public-copy.test.ts asserts on isZh
strings to prove copy is not hardcoded. Both would be false failures on a
required check.

I have the script written and exercised: it passes on the 31-file baseline,
fails on a probe file added to components/ naming that file, and fails when a
migrated path is left on the list telling you which line to delete.

Offer

I'll send this as one PR — script, baseline, a step in the Lint job next to the
existing check-locales.mjs line, and a note in docs/LOCALIZATION.md. Two
questions first:

  1. Required check, or run it advisory for a release first? The PR intake gate is
    in dry-run mode right now and this could follow the same path.
  2. Scope. Today's scan catches app/api/admin/post/route.ts and lib/page-meta.ts
    alongside the pages. Is Web: finish the #4934 dictionary spine — retire every isZh branch and inline { en, zh } module #5337 meant to cover those, or should the ceiling be
    page copy only?

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

    Labels

    No labels
    No labels

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions