Translate third-party mods
This document aims to give a brief explanation on how to set up and operate mod translation workflow for Cataclysm: Bright Nights.
For mod localization the game uses custom localization system that is similar to GNU gettext and is compatible with GNU gettext MO files.
If you desire an in-depth explanation on PO/POT/MO files or how to work with them using GNU gettext utilities, see GNU gettext manual.
To get some generic tips on translating strings for Cataclysm: Bright Nights and its mods, see translation API.
A short glossary
Portable Object Template file (
This is a text file that contains original (English) strings extracted from mod’s JSON and Lua source files. The POT file is a template used for creating empty or updating existing PO files of any language.
Portable Object file (
This is a text file that contains translated strings for one language. The PO files are what translators work with, and what will be compiled into a MO file.
Machine Object file (
This is a binary file that contains translated strings for one language. The MO files are what the game loads, and where it gets translated strings from.
The first translation workflow is as follows:
- Extract strings from mod JSON and Lua source files into a POT file
- Create PO file for target language from this POT
- Fill the PO file with translated strings
- Compile PO into MO
- Put the MO into your mod files
As the mod changes with time, so may change its strings. Updating existing translations is done as follows:
- Extract strings from mod JSON and Lua source files into a new POT file
- Update existing PO file from the new POT file
- Add new or edit existing translated strings in the PO file
- Compile PO into MO
- Replace old MO in the mod files with new version
Step 1 in both workflows requires you to set up environment for string extraction (see below).
Steps 2-4 can be done using translation software either by the mod author/maintainer, or by the translator.
Setting up environment for string extraction
You’ll need Python 3 with
luaparser modules installed (available via
Scripts for string extraction can be found in the
lang subdirectory of the repository:
extract_json_strings.py- main string extraction routines
dedup_pot_file.py- fixes errors in POT file produces by the 1st script
extract_mod_strings.shfor Linux/MacOS) - to automate the other 2 scripts
Copy these 3 scripts into the mod’s folder and:
- on Windows, double-click
- on Linux/MacOS, open terminal and run
If the process completed without errors, you’ll see a new
lang folder with
Creating new PO
Before creating PO file, you need to choose language id.
data/raw/languages.json to see the list of languages supported by the game.
In this list, each entry has its own id in form of
ln determines language and
LN - dialect. You can either use full
ln_LN for exact language+dialect match, or
ln if you
want the game to use your MO regardless of dialect.
- Open the POT file with Poedit
- Press “Create new translation” button (should show up near the bottom)
- In language selection dialog, enter language id you chose
- Save the file as
LANGis the same language id
msginit -i lang/index.pot -o lang/LANG.po -l LANG.UTF-8 --no-translator
LANG is the language id you chose
Updating existing PO
- Open the PO file with Poedit
Catalog->Update from POT file...and select the new POT file
- Save the file
msgmerge lang/LANG.po lang/index.pot
Compiling PO into MO
- Open the PO file with Poedit
- Make sure MO file will be encoded using UTF-8 (it should do so by default, you can double check
Catalog->Properties->"Translation properties" tab->Charset).
- By default, each time PO file is saved Poedit automatically compiles it into MO, but the same can
also be done explicitly via
File->Compile to MO...
msgfmt -o lang/LANG.mo lang/LANG.po
Adding MO file to the mod
lang directory in the mod files directory and put MOs there:
Note: Storing your POT/PO files in the same
lang subdirectory may make it easier to keep track
of them. The game ignores these files, and your mod folder structure will look like this:
Is it possible to use arbitrary location or names for MO files, like with JSONs?
No. The game looks for MO files with specific names that are located in the
lang subdirectory of
path directory specified in
modinfo.json (if not specified,
path matches the mod’s
However, any mod will automatically try to use any other mod’s translation files to translate its strings. This makes it possible to create mods that are purely “translation packs” for other mods (or mod collections).
Reloading translations in a running game
Open debug menu and select
Info...->Reload translations, and the game will reload all MO files
This makes it easy to see how translated string looks in game, provided the translator has a way to compile MO files.
Example workflow with Poedit:
- Translate a string
- Hit Ctrl+S
- Alt+Tab into the game
- Reload translation files via debug menu
- The game now displays translated string
MO load order
MO load order is as follows:
- First and always goes base game’s MO file, which contains translation strings for UI, hardcoded
functionality, base “mod” (
data/json/) and in-repo mods.
- Then MO files of mods are loaded, in same order as the mod load order.
When loading MO files, the game first looks for the file with exact language and dialect match in its name. If such file is absent, then it looks for a file with no dialect.
For example, when using
Español (España) the selection order is
And when using
Español (Argentina) the selection order is
es.mo would be loaded for either dialect of Spanish if the exact translation files are not
What if 2 or more mods provide different translations for same string?
Then the game selects which one to use according to this set of rules:
- If string A’s translation has plural forms, but string B’s translation does not, then translation A is used for both single and plural forms.
- If both translation A and B have (or both don’t have) plural forms, then the first loaded translation is used (see MO load order).
If you want a different translation from the one in the base game, or don’t want it to conflict with a string from some other mod, add a translation context to the string in the corresponding JSON object (see here for which fields support translation context).