Localization String Freeze PO Import Receipt Fails Selector Stuck on English - How to Fix
Problem: During August–September 2026 October Next Fest string freeze, the team files or reopens a Weblate PO drop into Ren'Py game/tl/, but Preferences still boots English, verify_localization_freeze.sh exits 3 or localization_freeze_ok stays false, and Steam Supported languages already claim CN/JP/KR.
Who is affected now: VN / Ren'Py teams following localization freeze evening and Ren'Py freeze preflight (Guide #49). This is recovery after freeze latch / PO reopen failed the player selector, not Weblate merge conflicts blocking export, not the ninety-second prevention gate alone, and not store short-description CJK overflow.
Fastest safe fix: Stop depot promotion → confirm freeze label + locale_manifest_v1.json paths → re-copy PO into tl_path folders → delete game/cache/ and stale .rpyc under tl/ → regenerate translations → Preferences probe on installed build → rewrite localization_freeze_receipt_v1.json → promote localization_freeze_ok: true.
Direct answer
English-only menus after a “frozen” PO import usually means path drift, stale bytecode, or a freeze receipt that was flipped without L3–L5. Fix on-disk game/tl/ + cache, then re-prove the freeze receipt—do not keep importing fuzzy PO while Weblate writes stay open.
Why this issue spikes in August–September 2026
- Verbal freeze + late PO — writers reopen Weblate after L6 without a new freeze label.
tl_pathdrift — manifest points atgame/tl/japanesewhile files landed underja.- Stale
.rpyc/ cache — old English bytecode wins after PO overwrite. - Editor-only smoke — Launcher Language() works; installed exe still English.
- Weblate keys as Ren'Py ids — Preferences buttons use
zh_CNinstead of map values (chinese).
Symptoms and search phrases
- “localization_freeze_ok false after PO import”
- Ren'Py language selector stuck English after Weblate
verify_localization_freeze.shMISSING tl:- Steam lists Japanese; in-game Preferences English-only
- Weblate 100% but fest demo English menus
po_import_reopen_allowedtrue without exception list
Root causes (check in order)
- Freeze latch skipped — BUILD_RECEIPT flipped without Guide #49 L1–L6.
locale_manifest_v1path drift —tl_pathfolders missing or wrong locale id.- Incomplete / fuzzy PO export — merge conflicts half-resolved (Weblate merge help).
- Stale compiled translations — cache /
.rpycnot cleared after import. - Preferences / language_map mismatch — buttons not wired to map values (Weblate map Guide).
- Hash mismatch packaging — installer embeds old
tl/(PO-hash Guide).
Beginner path (first 25 minutes)
Prerequisites: Ren'Py project with game/tl/, jq, write access to release-evidence/localization/, current Weblate export ZIP, Guide #49 open.
- Prove the failure:
jq -e '.localization_freeze_ok == true' \
release-evidence/localization/*/LOCALIZATION_FREEZE_RECEIPT.json
# expect fail or false
Boot the installed build → Preferences → confirm non-English buttons missing or strings stay English.
- Open
locale_manifest_v1.jsonand walk paths:
MAN="release-evidence/localization/october-next-fest-2026/locale_manifest_v1.json"
jq -r '.locales[] | select(.in_game_required==true) | .tl_path' "$MAN" \
| while IFS= read -r p; do test -d "$p" || echo "MISSING: $p"; done
-
Re-copy PO from the freeze commit export into each
tl_path(not a newer unfrozen Weblate tip unless you start a new freeze label). -
Clear compiled state:
- Delete
game/cache/ - Delete stale
game/tl/**/*.rpycif your workflow compiles beside sources - Ren'Py Launcher → Generate Translations / lint clean
-
Preferences probe on installed distribution: switch to Japanese (or claimed fest locale) → main menu strings change → screenshot into
smoke/. -
Rewrite freeze receipt + BUILD_RECEIPT (below). Do not promote while Preferences stay English.
Developer path (recovery gates)
| Step | Action | Pass when |
|---|---|---|
| R1 | Confirm freeze vs merge vs map lane | Not merge-blocked; not crop/marketing |
| R2 | Fix locale_manifest + tl_path walk |
Every required folder exists |
| R3 | Re-import freeze-commit PO | PO files under correct tl_path |
| R4 | Cache / .rpyc rebuild |
Rebuild Guide GREEN |
| R5 | Preferences + installed smoke | Non-English line visible |
| R6 | Rewrite freeze receipt + BUILD_RECEIPT | localization_freeze_ok: true |
R1 — Pick the correct cousin
| Symptom | Lane |
|---|---|
| Weblate cannot export | Merge conflicts help |
| Paths OK, hashes wrong in installer | PO-hash Guide |
| Buttons missing | Language map Guide |
| Freeze GREEN claimed, player English | This article (R2–R6) |
R2 — Manifest repair sketch
Ensure engine_code matches Preferences Language() ids and tl_path is a real directory:
{
"id": "ja",
"steam_language": "japanese",
"engine_code": "japanese",
"tl_path": "game/tl/japanese",
"store_claim": true,
"in_game_required": true
}
R4 — Rebuild after import
Follow default language + .rpyc rebuild before another PO drop. Hash audit with PO-hash Guide if the installer still embeds old bytes.
R6 — Receipt rewrite
{
"schema": "localization_freeze_receipt_v1",
"freeze_label": "october-next-fest-2026",
"locked_utc": "2026-09-12T20:15:00Z",
"locale_manifest_path": "release-evidence/localization/october-next-fest-2026/locale_manifest_v1.json",
"weblate_freeze_commit": "abc123def",
"writes_closed": true,
"recovery": {
"reason": "selector_stuck_english_after_po_import",
"fix": "reimport_freeze_po_clear_cache_preferences_probe"
},
"gates": {
"L1_manifest": "pass",
"L2_tms_pin": "pass",
"L3_tl_paths": "pass",
"L4_store_parity": "pass",
"L5_installed_smoke": "pass",
"L6_receipt": "pass"
},
"localization_freeze_ok": true,
"po_import_reopen_allowed": false
}
Promote:
{
"localization_freeze_ok": true,
"localization_freeze_receipt": "release-evidence/localization/october-next-fest-2026/LOCALIZATION_FREEZE_RECEIPT.json",
"po_import_reopen_allowed": false
}
./scripts/verify_localization_freeze.sh \
release-evidence/localization/october-next-fest-2026/LOCALIZATION_FREEZE_RECEIPT.json
jq -e '.localization_freeze_ok == true' release-evidence/BUILD_RECEIPT.json
Verification checklist
| Check | Pass |
|---|---|
tl_path walk |
No MISSING lines |
| Preferences | Fest locales selectable |
| Installed smoke | Non-English UI strings on main menu |
| Weblate writes | Still closed (or new freeze label documented) |
| Receipt | localization_freeze_ok: true |
| BUILD_RECEIPT | Same boolean; verify exit 0 |
Prevention
- Run Guide #49 before October store language freeze.
- Never reopen PO without a new
freeze_label+ exception list. - Keep evening freeze tutorial L1–L6 as the ritual; Guide is the Ren'Py gate; this page is recovery.
- Bookmark 14 Weblate freeze tools for export stacks.
- Pair store parity with store-page QA localization resources.
Common mistakes
- Flipping
localization_freeze_okwhile Preferences stay English. - Importing tip-of-Weblate after freeze without closing writes again.
- Clearing only Editor cache and shipping the old distribution.
- Treating merge-conflict RED as selector recovery (wrong help).
- Using Steam language codes as Ren'Py
Language()ids.
Related GamineAI reads
- Localization string freeze receipt evening
- Ren'Py freeze PO import preflight (Guide #49)
- Weblate PO + Preferences map
- PO-hash selector stuck English preflight
- Default language +
.rpycrebuild - Weblate merge conflicts block freeze
- 14 free Weblate string-freeze PO export tools
- Official: Ren'Py Translation · Weblate docs · Steamworks languages
Freeze receipts lie when Preferences stay English—re-import freeze-commit PO, clear cache, probe an installed build, rewrite localization_freeze_receipt_v1.json, then latch localization_freeze_ok.