Lesson 256: Construct Weblate CSV UTF-8 Handoff Receipt on BUILD_RECEIPT (2026)

Direct answer: Before October string-freeze week ingests Discord playtest CSV into Weblate, promote weblate_csv_utf8_handoff_receipt_v1.json proving UTF-8 export (not Excel UTF-16), BOM strip on facilitator files, Poedit re-export hashes match weblate_import_manifest_v1.json, and a README row documents the facilitator who approved the handoff—distinct from Lesson 202 (Unity String Tables in player build) and Lesson 241 (GA4 allowlist). Pair the Construct CSV UTF-8 Weblate preflight (Guide #19) and Weblate freeze tools.

Lesson hero for Construct Weblate CSV UTF-8 handoff receipt

Why this matters now (October string-freeze CSV handoff)

October 2026 facilitators export Discord thread tables as CSV from Excel defaults (UTF-16 LE + BOM). Weblate import shows mojibake on CJK feedback strings, Poedit re-export drifts from the facilitator sheet, and producers blame translators when the root cause was encoding on Tuesday ingest. Lesson 202 proves Unity player tables; 256 proves CSV → Weblate → Poedit bytes before Construct HTML5 string keys update.

The Construct CSV UTF-8 preflight is the ninety-second gate (W1–W6 shipped May 27, 2026)—256 is the BUILD_RECEIPT milestone. When the Tuesday Discord CSV ingest ritual publishes, run it the same week—this lesson owns the receipt column, that blog owns the facilitator rhythm.

Beginner path (Discord CSV → UTF-8 → Weblate)

Step Action Success check
1 Export Discord sheet UTF-8 (no Excel double-click save) W1 pass
2 Strip BOM / validate with file -bi or PowerShell W2 pass
3 Import to Weblate component No import errors
4 Poedit re-export PO/CSV per locale W4 hash match
5 Add facilitator README row W5 pass
6 File receipt + BUILD_RECEIPT weblate_csv_utf8_ok: true

Time: ~44 minutes first string-freeze week; ~12 minutes when UTF-8 export template exists.

Developer path (gates W1–W6)

Gate Check Fail when
W1 Source CSV encoding UTF-16 LE from Excel default
W2 BOM stripped / absent EF BB BF breaks Weblate
W3 weblate_import_manifest_v1.json Locale list mismatch
W4 Poedit re-export sha256 Drift vs manifest
W5 Facilitator README row Missing approver email
W6 Receipt + BUILD_RECEIPT Promote before W4 GREEN

W1 — cousin receipt crosswalk

Field Cousin (202 Unity) Cousin (241 Construct) This lesson (256)
Schema string_table_receipt_v1 construct_playtest_analytics_receipt_v1 weblate_csv_utf8_handoff_receipt_v1
Scope Player build tables GA4 allowlist Discord CSV → Weblate bytes
Engine Unity Addressables Construct HTML5 analytics Construct + Weblate TMS

W2 — BOM strip (PowerShell example)

$bytes = [System.IO.File]::ReadAllBytes("playtest-feedback.csv")
if ($bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) {
  [System.IO.File]::WriteAllBytes("playtest-feedback-utf8.csv", $bytes[3..($bytes.Length-1)])
}

Log bom_stripped: true in receipt.

weblate_import_manifest_v1.json

{
  "schema": "weblate_import_manifest_v1",
  "freeze_tag": "october-fest-2026-rc2",
  "source_csv_path": "localization/ingest/playtest-feedback-utf8.csv",
  "source_csv_sha256": "aa11…",
  "encoding_detected": "utf-8",
  "locales": ["en", "ja", "zh-Hans"],
  "poedit_exports": {
    "ja": { "path": "localization/po/ja.po", "sha256": "bb22…" },
    "zh-Hans": { "path": "localization/po/zh_Hans.po", "sha256": "cc33…" }
  }
}

facilitator_readme_row (W5)

Column Example
handoff_id UUID
facilitator_email [email protected]
discord_thread_url https://discord.com/channels/…
approved_at_utc ISO-8601
encoding_fix_applied bom_strip_v1

weblate_csv_utf8_handoff_receipt_v1.json

{
  "schema": "weblate_csv_utf8_handoff_receipt_v1",
  "build_label": "october-fest-2026-rc2",
  "manifest_path": "localization/ingest/weblate_import_manifest_v1.json",
  "bom_stripped": true,
  "encoding_final": "utf-8",
  "facilitator_readme_row": "localization/ingest/FACILITATOR_README.csv",
  "cousin_receipts": {
    "string_table_smoke": "release-evidence/localization/STRING_TABLE_RECEIPT.json",
    "construct_playtest_analytics": "release-evidence/html5/CONSTRUCT_PLAYTEST_ANALYTICS_RECEIPT.json"
  },
  "gates": {
    "W1_source_utf8": "pass",
    "W2_bom_strip": "pass",
    "W3_manifest": "pass",
    "W4_poedit_hash": "pass",
    "W5_facilitator_row": "pass",
    "W6_build_receipt": "pass"
  },
  "weblate_csv_utf8_ok": true,
  "string_freeze_promotion_allowed": true
}

Pin under release-evidence/localization/WEBLATE_CSV_UTF8_HANDOFF_RECEIPT.json.

BUILD_RECEIPT row (W6)

Column Pass when
weblate_csv_utf8_handoff weblate_csv_utf8_ok: true
string_table_receipt Cousin Lesson 202 independent
construct_playtest_analytics Cousin Lesson 241 independent
ALTER TABLE release_publish_gate ADD COLUMN IF NOT EXISTS
  weblate_csv_utf8_handoff_blocked BOOLEAN NOT NULL DEFAULT false;

Thursday row reviewWeblate CSV UTF-8 line: encoding + manifest hash Y/N.

Key takeaways

  1. Excel “Save As CSV” is UTF-16 by default on Windows—W1 fails without conversion.
  2. BOM strip is not optional for Weblate CSV components.
  3. Poedit re-export hash must match manifest—W4 catches silent drift.
  4. Facilitator README row is audit evidence, not bureaucracy.
  5. Weblate merge freeze help — TMS conflicts after import.
  6. Lesson 255 Deck geometry is parallel—string freeze is separate.
  7. Cousin: Lesson 257 Addressables jq inventory audit—parallel Unity strip column.
  8. 14 Weblate freeze tools — PO export lane.
  9. OBS concat help — Tuesday ingest rhythm cousin.
  10. October capstone 265 wires 254–264 including this row.

Common mistakes

  • Importing UTF-16 CSV directly into Weblate—mojibake on CJK (W1).
  • Skipping BOM strip because “it looked fine in Notepad” (W2).
  • Filing 202 while CSV handoff still UTF-16—player build ≠ ingest bytes.
  • Missing facilitator row—cannot trace who approved strings (W5).
  • Merging weblate_csv_utf8_handoff_receipt into string_table_receipt.

Troubleshooting

Symptom Lane
Mojibake on import W1/W2 encoding
Weblate rejects CSV Delimiter / quoting + encoding
Poedit hash mismatch W4 re-export from wrong source file
Strings OK in editor, wrong in HTML5 Lesson 202
Merge conflicts post-import Weblate freeze help

Mini exercise (35 minutes)

  1. Save sample sheet as UTF-16—confirm W1 fail.
  2. Re-export UTF-8 + strip BOM.
  3. Write weblate_import_manifest_v1.json for two locales.
  4. Add facilitator README row.
  5. File receipt; BUILD_RECEIPT GREEN.

Continuity — October–Q4 2026 fest ops truth (254–265)

Lesson Receipt focus
255 Godot Deck letterbox reset
256 (this) Construct Weblate CSV UTF-8 handoff
257 Unity Addressables jq audit (queued)
265 October ops capstone (queued)

Previous: Lesson 255 — Godot Deck letterbox Gamescope reset
Next: Lesson 258 — OBS replay buffer uniform fragments (queued)

FAQ

Same as Lesson 202?
202 = Unity String Tables in installed player build; 256 = Discord CSV encoding into Weblate.

Same as the Construct guide chapter?
Guide = W1–W6 checklist; 256 = BUILD_RECEIPT promotion.

Need PO files instead of CSV?
Team policy—receipt documents source encoding; PO hashes still required in W4.

Construct-only studio?
Yes—Unity 202 is cousin reference only; do not file Unity receipts for HTML5-only SKUs.


String-freeze week fails when Discord CSV stays UTF-16—strip BOM, hash Poedit exports, log facilitator approval, then weblate_csv_utf8_ok before Weblate merge.