Restore lost interpolation placeholders in 141 translated values across 16 languages - #1086
Restore lost interpolation placeholders in 141 translated values across 16 languages#1086emooreatx wants to merge 1 commit into
Conversation
16 languages carry translations whose `{named}` / printf placeholders no
longer match their en.json source, so those strings render with the
substitution missing or unsubstituted at runtime. The English sources are
unchanged — only the translations drifted.
Detected by comparing, for every (language, key), the multiset of runtime
interpolation tokens in the translated value against the en.json value:
120 values had lost a placeholder, 13 carried one en.json does not have,
8 had a renamed/mismatched set.
Each corrupt value is replaced with CIRISServer's vendored value for the
same key, and only where the two repos' en.json values for that key are
byte-identical — an unchanged source means the replacement is a pure
repair, not an overwrite of a retranslation. Zero keys were skipped.
Applied to all five committed runtime bundle copies, which remain
byte-identical. Key sets, key order and file formatting are untouched:
705 changed lines = 141 values x 5 bundles, no en.json or manifest change.
Refs #1085
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tDGM9WfkcScEvHRRYfc4i
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2468c8b204
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "settings_integrity_unverified": "Tính toàn vẹn chưa được xác minh", | ||
| "settings_integrity_verified": "Tính toàn vẹn được xác minh", | ||
| "settings_integrity_verified_count": "Số lần xác minh tính toàn vẹn", | ||
| "settings_integrity_verified_count": "Đã xác minh {count} lần kiểm tra", |
There was a problem hiding this comment.
Supply the placeholder used by the integrity message
When Vietnamese is selected and Level 4 reports nonzero checked and total file counts, this string renders the literal {count}. SettingsScreen.kt:1266 supplies only passed, checked, and mode, while LocalizationManager.interpolate leaves parameters absent from that map unchanged. Update the caller to supply count (the same regression is introduced in the Chinese bundle) before adding this placeholder to these translations.
Useful? React with 👍 / 👎.
Restores the
{named}/ printf interpolation placeholders that 141 translated values lost, across 16 languages. Their English sources never changed, so these strings render today with the substitution missing ("Showing entries"where the UI meant"Showing 42 entries") or with a token no formatter will fill.Closes #1085.
What was wrong
vitemyzharfaptswamdeesfrtrurhajaBy kind: 120 values had lost a placeholder outright, 13 carried a placeholder
en.jsondoes not have, 8 carried a renamed or otherwise mismatched set. Representative cases:de audit_showingshows the shape of it: the translation is a faithful rendering of an older English source (Showing {current} of {total}), left behind whenen.jsonmoved toShowing {count} entries.How it was detected
For every (language, key) pair, the multiset of runtime interpolation tokens in the translated value is compared against the
en.jsonvalue's. Tokens are matched withcovering named braces (
{count}), indexed ({0}), Kotlin template (${...}) and printf (%s,%d,%1$s). Missing and empty translations are excluded — that is translation lag, a different problem. This is the same rule that found the drift in the first place (CIRISServer'sclient/tools/check_localization_sync.py,check_placeholder_parity), ported unchanged so the count here is directly comparable: 103,552 value comparisons over 3,724 English strings in 29 languages, 141 findings — exactly the 141 reported in #1085, same per-language distribution.How it was repaired
Each corrupt value is replaced with CIRISServer's vendored value for the same key — and only where the two repos'
en.jsonvalues for that key are byte-identical. An unchanged English source means the replacement is a pure repair; had the source been retranslated on your side, overwriting it would be corruption in the other direction. 0 keys were skipped: all 141 sources matched byte-for-byte.Provenance: the values come from CIRISServer's vendored copy of this client, where they were restored from the pre-regression translations and verified placeholder-clean.
Surgical application: each file is loaded preserving its own key insertion order and re-serialized with this repo's exact formatting (2-space indent,
ensure_ascii=False, trailing newline) — a round-trip that is byte-identical on an untouched file, verified against all 30 bundle files before any edit. Only the 141 value lines move.Verification
Key-address list and key order are preserved in all 80 files; every one of the 705 changed lines is a string value. The five committed runtime bundle copies —
— were byte-identical before and are byte-identical after (
diff -rqclean against the canonical bundle).Recommendation: gate this class
There was no check on this side that could have caught it. The
FOREIGN_ALPHABETratchet catches a value written in the wrong script; a value in the right language that quietly dropped its{count}passes every existing check — right key, right script, plausible sentence, and the key-set comparison sees nothing missing. That is why 141 of them accumulated between 2.9.24 and 2.9.28 without a red build.The parity rule above is cheap (one regex, one multiset compare, ~100k comparisons in well under a second) and has no false-positive mode: a translated value legitimately carrying a different set of interpolation tokens than its source is always a bug. Worth adding next to the alphabet ratchet as an error-severity check, along with a mirror check that the five bundle copies stay byte-identical — a stale copy ships raw keys at runtime for whichever platform loads it.
🤖 Generated with Claude Code
https://claude.ai/code/session_016tDGM9WfkcScEvHRRYfc4i