Skip to content

docs(localization): describe the TUI packs and gate locale drift in CI#4839

Merged
Hmbown merged 1 commit into
mainfrom
l10n-cluster
Jul 26, 2026
Merged

docs(localization): describe the TUI packs and gate locale drift in CI#4839
Hmbown merged 1 commit into
mainfrom
l10n-cluster

Conversation

@Hmbown

@Hmbown Hmbown commented Jul 26, 2026

Copy link
Copy Markdown
Owner

What was broken

docs/LOCALIZATION.md is the stated single source of truth for every locale the project ships, plans, or has deferred — but it only had tables for the website and README surfaces. The TUI locale packs under crates/tui/locales/ are the largest translation surface in the repo (8 packs, 1128 keys each) and were absent entirely.

Consequences, all verified against the tree:

  • Complete ko, es-419, and pt-BR TUI packs ship, while the website table marks es and pt-BR deferred — the doc reads as though no Latin-American work exists.
  • zh-Hant ships as a deliberate 478/1128 partial with English fallback (v0.8.67: bring shipped UI locale packs to parity or explicitly scope zh-Hant as partial #4057) and appeared nowhere in the matrix.
  • Four drift checks were listed Planned and unbuilt, including scripts/check-readme-locales.sh, which already existed and simply was not wired into CI.

The v0.9.2 locale wave (#4788, #4789, #4790, #4791, #4749) adds six more locales. Each one added to a matrix that already misdescribes reality compounds the drift.

Why the fix is shaped this way

The parity gate is file-based, not a Rust test. crates/tui/src/localization.rs already has shipped_complete_packs_have_raw_key_parity_with_english, but it can only see packs that have a Locale variant. During a locale wave the pack JSON lands before (or in a different PR from) the Rust wiring, and that window is exactly when a pack silently drifts. scripts/check-tui-locale-parity.py reads the directory, so it covers packs whose wiring has not landed — and a failure names a file instead of being buried in a test binary.

Placeholder parity is checked, not just keys. Call sites substitute {named} placeholders with .replace(), so a translator who drops or renames one produces literal braces at runtime with no test failure. The gate compares placeholder sets per key.

Partials are a declared status, not an escape hatch. PARTIAL_PACKS mirrors Locale::is_partial_pack() and requires a tracking issue per entry. A partial is exempt from completeness but still may not define keys English lacks, because the English fallback can never surface those.

Galician/Basque (#4749) is recorded as an assessment, not a pack. #4749 asked whether gl/eu are "similar-value European additions" worth shipping with Catalan. The answer is no, and the reasoning is written down in the matrix rather than left in a PR comment: the case #4788 makes for Catalan is about review capacity ("an unusually strong software-localization tradition and an active volunteer community"), not market size, and that argument does not transfer to either language.

Gates I actually ran

$ python3 scripts/check-tui-locale-parity.py
[tui-locale-parity] reference en.json: 1128 keys
[tui-locale-parity] es-419: 1128/1128 keys — complete
[tui-locale-parity] ja: 1128/1128 keys — complete
[tui-locale-parity] ko: 1128/1128 keys — complete
[tui-locale-parity] pt-BR: 1128/1128 keys — complete
[tui-locale-parity] vi: 1128/1128 keys — complete
[tui-locale-parity] zh-Hans: 1128/1128 keys — complete
[tui-locale-parity] zh-Hant: 478/1128 keys (declared partial, #4057)
[tui-locale-parity] PASS

$ bash scripts/check-readme-locales.sh
[check-readme-locales] OK — all linked locale READMEs exist
[check-readme-locales] OK — no orphaned locale READMEs
[check-readme-locales] PASS

$ git diff --check
(no output)

$ python3 -c "import yaml; ..."   # ci.yml parses; both new steps appear in jobs.lint.steps
Check README locale link symmetry
Check TUI locale pack parity

cargo fmt / clippy / cargo test were not run for this PR and are not applicable: the diff touches only docs/LOCALIZATION.md, .github/workflows/ci.yml, and a new Python script. No Rust source is modified.

What I could NOT verify

Closes #4787
Refs #4749

docs/LOCALIZATION.md is the stated single source of truth for what the
project ships, plans, or has deferred — but it only described the website
and README surfaces. The TUI locale packs are the largest translation
surface in the repo (eight packs, 1128 keys each), and they were absent
entirely. So complete ko/es-419/pt-BR packs shipped while the matrix read
as though no Latin-American work existed, and zh-Hant's deliberate
478/1128 partial (#4057) appeared nowhere at all. Every check that would
have caught the drift was listed as Planned and unbuilt.

That matters more now than it did: the v0.9.2 wave adds six locales
(#4788, #4789, #4790, #4791, #4749). Adding them to a matrix that already
misdescribes reality compounds the problem once per locale.

- Add a TUI locale packs table with measured key counts against en.json,
  and represent zh-Hant as partial with a pointer to its English-fallback
  behaviour rather than omitting it.
- Add scripts/check-tui-locale-parity.py, which fails when a pack that
  claims completeness diverges from en.json's key set in either direction
  or drops a {named} placeholder. Declared partials are exempt from
  completeness but still may not define keys English lacks, since the
  English fallback can never surface those. PARTIAL_PACKS mirrors
  Locale::is_partial_pack(). The check is file-based on purpose: it also
  covers packs whose Locale wiring has not landed yet, and it attributes
  a failure to a file instead of burying it in a test binary.
- Wire that gate and the already-existing scripts/check-readme-locales.sh
  into the CI Lint job, and flip both Drift-checks rows off Planned. The
  three rows that remain Planned now name their tracking issue (#3091)
  instead of sitting bare.
- Extend "How to add a locale" into a three-surface checklist (TUI pack,
  README, website) so the v0.9.2 wave follows one procedure rather than
  six improvisations.
- Record the #4749 assessment of Galician and Basque: defer both. The
  argument #4788 makes for Catalan is about review capacity, not market
  size, and it does not transfer — neither language has a localization
  community that could review a pack, Galician already has a workable
  fallback in the shipped es-419 pack, and Basque is an isolate with none.

The website-side items from #4787 (npm run check:locales, and using the
existing "partial" LocaleStatus for zh-Hant in web/lib/i18n/config.ts)
depend on the #3091 dictionary layer and stay Planned under that issue.

Closes #4787
Refs #4749
Copilot AI review requested due to automatic review settings July 26, 2026 00:13
@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@claude

claude Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 21s —— View job


Reviewing PR #4839

  • Gather context (PR body, diff, custom review instructions)
  • Read docs/LOCALIZATION.md diff
  • Read scripts/check-tui-locale-parity.py
  • Read .github/workflows/ci.yml diff
  • Cross-check claims against tree (pack files, localization.rs, PARTIAL_PACKS)
  • Post review feedback

@Hmbown
Hmbown merged commit 0fd5fdc into main Jul 26, 2026
22 of 23 checks passed
@Hmbown
Hmbown deleted the l10n-cluster branch July 26, 2026 02:10
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.

v0.9.2: Make the localization matrix describe the TUI packs and gate locale drift in CI

2 participants