Translate third-party mods
Intro
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.
While it’s possible to use Transifex or any other platform or software that supports gettext, this document only gives examples on how to work with Poedit and command-line GNU gettext utilities.
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
POT file
Portable Object Template file (.pot
).
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.
PO file
Portable Object file (.po
).
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.
MO file
Machine Object file (.mo
).
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.
Workflow overview
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 polib
and luaparser
modules installed (available via pip
).
Scripts for string extraction can be found in the lang
subdirectory of the repository:
extract_json_strings.py
- main string extraction routinesdedup_pot_file.py
- fixes errors in POT file produces by the 1st scriptextract_mod_strings.bat
(extract_mod_strings.sh
for Linux/MacOS) - to automate the other 2 scripts
Extracting strings
Copy these 3 scripts into the mod’s folder and:
- on Windows, double-click
extract_mod_strings.bat
- on Linux/MacOS, open terminal and run
./extract_mod_strings.sh
If the process completed without errors, you’ll see a new lang
folder with extracted_strings.pot
file inside.
Creating new PO
Before creating PO file, you need to choose language id.
Open 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_LN
, where 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.
Poedit
- 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
path/to/mod/lang/LANG.po
whereLANG
is the same language id
msginit
msginit -i lang/index.pot -o lang/LANG.po -l LANG.UTF-8 --no-translator
Where LANG
is the language id you chose
Updating existing PO
Poedit
- Open the PO file with Poedit
- Choose
Catalog->Update from POT file...
and select the new POT file - Save the file
msgmerge
msgmerge lang/LANG.po lang/index.pot
Compiling PO into MO
Poedit
- 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
in
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
msgfmt -o lang/LANG.mo lang/LANG.po
Adding MO file to the mod
Create lang
directory in the mod files directory and put MOs there:
mods/
YourMod/
modinfo.json
lang/
es.mo
pt_BR.mo
zh_CN.mo
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:
mods/
YourMod/
modinfo.json
lang/
extracted_strings.pot
es.po
es.mo
pt_BR.po
pt_BR.mo
zh_CN.po
zh_CN.mo
Miscellaneous notes
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
the mod’s path
directory specified in modinfo.json
(if not specified, path
matches the mod’s
directory).
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
from disk.
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.
Dialects
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
es_ES.mo
es.mo
And when using Español (Argentina)
the selection order is
es_AR.mo
es.mo
Thus, es.mo
would be loaded for either dialect of Spanish if the exact translation files are not
present.
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).
PR that implements mod translations, for reference
https://github.com/cataclysmbnteam/Cataclysm-BN/pull/505