Lesson 225: Ren'Py PO-Hash Language Receipt on BUILD_RECEIPT (2026)
Direct answer: When Weblate shows 100% but the Ren'Py selector stays English, file po_hash_language_receipt_v1.json proving export PO sha256 matches game/tl/ on the installed build, then set BUILD_RECEIPT column po_hash_language to verified—after Lesson 209 matrix wiring and the PO-hash preflight (H-gates). Distinct from language_receipt_v1 (L-gates UI) and .rpyc rebuild receipt (bytecode lane).

Why this matters now (January 2027 storefront truth)
January 2027 Q1 recovery continues after Lesson 224 VOD rows. Visual-novel SKUs promote CN/JP/KR store capsules from Weblate freeze tooling while the fest build still boots English—producers blame translators when the installer packaged last week’s PO tree. Lesson 202 covers Unity; language_receipt covers selector UI; this lesson owns hash parity on BUILD_RECEIPT. Planned help #8 (selector stuck English) pairs copy-paste fixes.
Beginner path (hash audit evening)
| Step | Action | Success check |
|---|---|---|
| 1 | Confirm language_map_v1.json |
Weblate preflight done |
| 2 | Build po_hash_manifest_v1.json |
sha256 per PO under game/tl/ |
| 3 | Diff vs Weblate export manifest | hash_mismatch_count: 0 |
| 4 | Installed exe boot smoke | One non-English line |
| 5 | File po_hash_language_receipt_v1.json |
selector_stuck_resolved: true |
| 6 | Update BUILD_RECEIPT + matrix | po_hash_language GREEN |
Time: ~25 min audit + 20 min rebuild if mismatch—65 minutes first fest SKU.
Developer path (gates H1–H6)
| Gate | Check | Fail when |
|---|---|---|
| H1 | language_map_v1.json linked |
Weblate preflight skipped |
| H2 | po_hash_manifest_v1.json |
Missing sha256 for shipped locale |
| H3 | Export ↔ installed parity | Any hash_mismatch |
| H4 | Preferences selector | Missing mapped locale button |
| H5 | Boot smoke (installed) | English-only on CN-primary store |
| H6 | BUILD_RECEIPT po_hash_language |
Column empty while store claims 3+ langs |
Extend localization_receipt_matrix_v1.json
Add optional extension on the Ren'Py engine row (do not merge into Unity JSON):
{
"renpy_vn_demo": {
"receipt_schema": "language_receipt_v1",
"receipt_path": "release-evidence/localization/renpy/LANGUAGE_RECEIPT.json",
"extensions": {
"po_hash_language_receipt": "release-evidence/localization/PO_HASH_LANGUAGE_RECEIPT.json"
},
"pass": true
}
}
po_hash_language_receipt_v1.json
{
"schema": "po_hash_language_receipt_v1",
"build_label": "vn-fest-2027-01-12-rc1",
"language_map_path": "release-evidence/localization/language_map_v1.json",
"po_hash_manifest_path": "release-evidence/localization/po_hash_manifest_v1.json",
"locales_shipped": ["english", "chinese", "japanese", "korean"],
"hash_mismatch_count": 0,
"selector_stuck_resolved": true,
"paired_receipts": {
"language_receipt": "release-evidence/localization/renpy/LANGUAGE_RECEIPT.json",
"localization_matrix": "release-evidence/localization/LOCALIZATION_RECEIPT_MATRIX.json"
},
"gates": {
"H1_language_map": "pass",
"H2_po_manifest": "pass",
"H3_hash_parity": "pass",
"H4_preferences": "pass",
"H5_boot_smoke": "pass",
"H6_build_receipt": "pass"
},
"fest_promotion_allowed": true
}
Pin under release-evidence/localization/PO_HASH_LANGUAGE_RECEIPT.json.
BUILD_RECEIPT row (H6)
| Column | Pass when |
|---|---|
language_receipt |
L-gates GREEN (Weblate preflight) |
po_hash_language |
This receipt fest_promotion_allowed: true |
localization_matrix |
Lesson 209 matrix path |
fest_locales |
Matches store capsule languages |
ALTER TABLE release_publish_gate ADD COLUMN IF NOT EXISTS
po_hash_language_blocked BOOLEAN NOT NULL DEFAULT false;
CI verify_po_hash_language_receipt_v1 fails when:
hash_mismatch_count > 0andfest_promotion_allowed: trueselector_stuck_resolved: falsewhilepo_hash_languageisverifiedlocales_shippedomits a locale advertised on the store page
Hash mismatch recovery order
- Re-copy PO into
game/tl/from Weblate export artifact. - Rerun PO-hash preflight H2–H3.
- If hashes match but boot still English → R-gates
.rpycrebuild. - Refresh matrix + BUILD_RECEIPT; Thursday review.
Proof table (Thursday review)
| Locale | Export sha256 | Build sha256 | Selector | Boot |
|---|---|---|---|---|
| chinese | yes | non-EN | ||
| japanese | yes | |||
| korean | yes |
Key takeaways
- Weblate 100% ≠ shipped PO—hash before debate.
po_hash_languageis optional until store claims multi-locale—then it is mandatory.- Keep
language_receiptandpo_hash_language_receiptas separate files. - Unity SKUs use
string_table_receipt—never merge schemas. - Evening beginner pipeline is onboarding; this lesson is BUILD_RECEIPT promotion.
- Q1 capstone 229 will wire 225 with 224, 226–228.
- Help #8 should link here for
hash_mismatchrows. - 18-free store localization QA catches capsule lies; this catches installer lies.
Common mistakes
- Verifying PO in git but shipping old
archive.rpa. - Setting
po_hash_language: verifiedwithhash_mismatch_count: 1. - Skipping H5 on “English primary” SKUs that still show JP screenshots on Steam.
- Pasting hash gates into Unity
string_table_receiptJSON. - Running only editor F5 smoke—depot build still English.
Troubleshooting
| Symptom | Lane |
|---|---|
hash_mismatch |
Re-import PO; rebuild distribution |
| Hash match, English boot | R-gates rebuild |
| Missing buttons | L-gates Weblate preflight |
| Unity + Ren'Py portfolio | Lesson 209 matrix |
| Fest language wrong after playtest branch | Ren'Py save/language isolation preflight — separate from PO hash |
Mini exercise (65 minutes)
- Export PO from Weblate; record export sha256 manifest.
- Copy into
game/tl/; build H2 manifest; fail H3 deliberately (wrong file). - Fix copy; pass H3–H5 on installed exe.
- File
po_hash_language_receipt_v1.json; extend Lesson 209 matrix row. - Set BUILD_RECEIPT
po_hash_languageGREEN on Thursday template.
Continuity — Q1 2027 post-fest recovery (224–229)
| Lesson | Receipt focus |
|---|---|
| 224 | MKV gap reencode |
| 225 (this) | Ren'Py PO-hash language |
| 226 | Wwise DSP packaged Windows |
| 227–229 | Review keys, FMOD WebGL, Q1 capstone (queued) |
Previous: Lesson 224 — OBS MKV gap reencode
Next: Lesson 226 — Wwise DSP packaged Windows receipt.
FAQ
Same as Lesson 209?
209 wires matrix + columns; 225 files the po_hash_language proof for Ren'Py SKUs.
Same as the PO-hash guide chapter?
Guide = H-gates preflight; lesson = BUILD_RECEIPT + CI + matrix extension.
Required for English-only VN?
Set po_hash_language: n/a when store and fest_locales are EN-only—do not fake hashes.
Localized store copy with English-only installed PO is a packaging bug—po_hash_language_receipt makes it visible before fest traffic.