Skip to content

Tutorial Language Packs

johnjohto edited this page Jul 26, 2026 · 2 revisions

Tutorial: Language Packs

Your game can speak more than one language. A language pack is a folder of sparse override tables — translate what you have, and everything you skip falls back to your primary text automatically. Players pick the language on the engine's settings screen; the choice is theirs, stored on their machine, and remembered per project.

The shape

your-project/
  lang/
    fr/
      strings.json    engine UI text (menus, prompts, battle messages)
      text.json       your story text overrides (data/text.json ids)
      names.json      species + move display names
      charmap.json    extra glyphs (optional)
      font.png        a replacement font atlas (optional)

Every file is optional and sparse — a pack with three strings is a valid pack. Resolution for every string is chosen language → your primary text → the key itself.

Translating the engine's words

strings.json overrides the engine string catalog — the keyed table holding every menu label, prompt, and battle message the engine draws. The full en table ships in the engine at defaults/strings.json; any key from it can be overridden:

{
 "common.yes": "OUI",
 "common.no": "NON",
 "battle.critical_hit": "Coup critique!",
 "battle.super_effective": "Super efficace!"
}

Keep %s/%d placeholders — they're filled at runtime ("Un %s\nsauvage apparait!"). Keep \n line breaks and \f page breaks; text boxes show two 18-character lines, so re-break your translation to fit.

Translating names and story text

names.json translates display names without touching your records — ids and save data never change:

{
 "species": {"bramblet": "RONCELET"},
 "moves": {"NIP": "CROC"}
}

Un-nicknamed partners rename themselves when the player switches language; real nicknames are never touched. (Item names can't be translated yet — that's a known limitation, tracked for a later phase.)

text.json overrides your data/text.json entries by id, same shape.

Glyphs your font doesn't have

The engine draws text through a charmap (character → font-atlas tile). If your translation uses characters outside your charmap, they would draw as nothing — so --validate warns you, naming the exact characters (lang.missing_glyph). Fix it by adding entries to the pack's charmap.json (pointing at existing atlas tiles) and, if the glyph art doesn't exist yet, shipping a font.png — a complete replacement atlas for that language. The atlas can be wider or taller than the original; the engine re-derives its grid from the image.

Shipping a partial pack (that's fine)

--validate reports what a pack misses — untranslated engine strings, story text, names — as warnings. A partial pack is a legitimate thing to ship: review each finding once in data/lint_suppressions.json with a reason:

{
 "suppressions": [
  {"rule": "lang.missing_strings", "source": "lang/fr/strings.json",
   "reason": "curated slice; the rest falls back to English by design"}
 ]
}

Unreviewed warnings keep --validate red — so a typo'd key (lang.unknown_key) or a missing glyph can't slip out silently.

Trying it

Bramblewick ships a French pack as the living example — open samples/bramblewick/lang/fr/ to see all of the above in ~30 lines of JSON. Play it: launch Bramblewick, open the settings screen (F9, or the SETTINGS menu row), and cycle the LANGUAGE row — the change applies instantly, and the row only exists when a project actually ships packs. For automated testing, --locale=fr pins a run to a pack (unknown codes refuse at boot); automated runs otherwise always use your primary text, so replays and determinism gates never depend on a player preference.

Clone this wiki locally