Lesson 202: Weblate Export String Table Smoke Before Fest Player Build (2026)
Direct answer: Weblate green proves your PO files—not that String Tables survived an Addressables strip in the player build. This lesson wires string_table_receipt_v1.json, a Localization_Local group contract, and installed-build locale smoke into your live-ops gate chain after Lesson 201 triple-channel labels.

Why this matters now (October 2026 fest localization)
October 2026 Next Fest teams ship CN/JP/KR store capsules from Weblate freeze week while the fest exe still shows UI_START_BUTTON raw keys. Editor play mode lies: tables load from project assets, not from the stripped player payload. The Unity String Tables empty help is the fix playbook; this lesson is the course milestone that connects Weblate export → import → Build Report proof → BUILD_RECEIPT before upload.
VN siblings use language_receipt (Ren'Py selector evening pipeline)—same store vs build lesson, different receipt schema.
Beginner path (40-minute proof)
| Step | Action | Success check |
|---|---|---|
| 1 | Export PO from Weblate with freeze tag | weblate_export_manifest.json lists locales |
| 2 | Import into Unity String Tables | Editor shows translated entries |
| 3 | Move tables to Localization_Local group |
Include in Build, not stripped |
| 4 | Build player for fest lane | Build Report lists String Tables |
| 5 | Launch installed build per locale | First screen translated, no raw keys |
| 6 | Write string_table_receipt_v1.json |
pass: true, raw_keys_visible: false |
| 7 | Attach to BUILD_RECEIPT | string_table_receipt column green |
Time: ~40 minutes smoke; full lesson 68 minutes with CI enumeration + store parity.
Developer path (gates L1–L6)
| Gate | Check | Fail when |
|---|---|---|
| L1 | string_freeze_receipt conflict_count: 0 |
Weblate merge blocked (freeze help) |
| L2 | PO import matches weblate_export_manifest hashes |
Drift between export and import commit |
| L3 | Build Report: Shared Table Data + String Tables | Tables stripped from player |
| L4 | Localization_Local non-remote for fest UI |
Core UI in remote-only group |
| L5 | boot_locale smoke on installed build |
Editor-only pass |
| L6 | BUILD_RECEIPT string_table_receipt |
Any L1–L5 red |
weblate_export_manifest.json (import ticket)
{
"schema": "weblate_export_manifest_v1",
"freeze_tag": "fest-2026-10-rc1",
"exported_at_utc": "2026-05-25T18:00:00Z",
"locales": ["en", "ja", "zh-Hans"],
"po_sha256": {
"ja": "sha256:abc...",
"zh-Hans": "sha256:def..."
}
}
Pin beside PO under localization/imports/.
Localization_Local Addressables contract
| Field | Fest demo rule |
|---|---|
| Group name | Localization_Local |
| Build path | Local (not remote-only) |
| Strip | Never for fest UI tables |
| CJK fonts | Include in same group if glyphs missing |
string_table_receipt_v1.json
{
"schema": "string_table_receipt_v1",
"checked_at_utc": "2026-05-25T22:00:00Z",
"build_label": "fest-demo-2026-10-rc1",
"addressables_group": "Localization_Local",
"locales_in_build": ["en", "ja", "zh-Hans"],
"shared_table_data_sha256": "sha256:abc123...",
"string_table_manifest_sha256": "sha256:def456...",
"boot_locale": "ja",
"first_screen_translated_ok": true,
"raw_keys_visible": false,
"string_freeze_receipt_ref": "string_freeze_receipt_v1.json",
"weblate_export_manifest_ref": "weblate_export_manifest.json",
"pass": true
}
Store under release-evidence/localization/STRING_TABLE_RECEIPT.json.
Boot locale smoke script (installed build)
// Attach to first scene; log for receipt attachment
void Start() {
var locale = UnityEngine.Localization.Settings.LocalizationSettings
.SelectedLocale.Identifier.Code;
var sample = LocalizationSettings.StringDatabase
.GetLocalizedString("UI", "START_BUTTON");
Debug.Log($"boot_locale={locale} sample={sample}");
}
Receipt fails if sample equals the raw key id.
Publish gate
ALTER TABLE release_publish_gate ADD COLUMN IF NOT EXISTS
string_table_smoke_blocked BOOLEAN NOT NULL DEFAULT false;
CI job verify_string_table_receipt_v1 sets blocked false only when:
pass: truestring_freeze_receipt_refresolves- Thursday row review includes
string_table_receipt.tables_ok
Prerequisites
- Lesson 201 — storefront label parity
- Unity String Tables empty help
- 18 Free localization tooling resource
- Wednesday demo smoke ritual
Common mistakes
- Passing Weblate QA without opening Build Report on player.
- Stripping
Localization_Localto save build size mid-fest week. - Screenshots from Editor while exe is English-only.
- Mixing Ren'Py
language_receiptfields into Unity BUILD_RECEIPT rows.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Keys on first screen | Tables not in build | L3–L4 group move |
| English only, no keys | Wrong SelectedLocale |
L5 boot smoke |
| Store CN, game EN | Screenshot / build mismatch | Regen from installed locale |
| Weblate 100%, receipt false | PO never imported | Re-import + rebuild |
Mini exercise (55 minutes)
- Export one locale PO from Weblate manifest.
- Break L3 on purpose (strip group); confirm gate fails.
- Restore
Localization_Local; pass installed smoke. - Add row to BUILD_RECEIPT template.
- Cross-link from Ren'Py blog Unity comparison section.
Continuity
- Previous: Lesson 201 — Triple-channel
channel_label_match - Next: Lesson 203 — FMOD WebGL snapshot boot probe
- Guides (planned): Unity 6 Localization String Tables preflight (Guide queue #15)
FAQ
Does this replace the Unity help article?
No—the help article is the fix; this lesson is the milestone in your RPG live-ops receipt chain.
Ren'Py-only project?
Skip Unity gates; use language_receipt from the Ren'Py blog—still run store parity checklist.
Addressables remote for DLC languages?
Allowed for optional DLC; fest UI must stay in Localization_Local.
Weblate proves PO; your player build proves String Tables—file the receipt before October upload week.