Skip to content

Commit

Permalink
Merge pull request #505 from olanti-p/mod-translate-final
Browse files Browse the repository at this point in the history
Support translations for 3rd party mods
  • Loading branch information
Coolthulhu authored May 7, 2021
2 parents e9e3f56 + df47fa2 commit fbf53c7
Show file tree
Hide file tree
Showing 42 changed files with 2,632 additions and 42 deletions.
14 changes: 7 additions & 7 deletions data/raw/languages.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[
{
"//": "See language.h for documentation",
"id": "en",
"id": "en_US",
"name": "English",
"locale": "en_US.UTF-8",
"genders": [ "n" ],
"lcids": [ 1033, 2057, 3081, 4105, 5129, 6153, 7177, 8201, 9225, 10249, 11273 ]
},
{
"id": "de",
"id": "de_DE",
"name": "Deutsch",
"locale": "de_DE.UTF-8",
"genders": [ ],
Expand Down Expand Up @@ -49,35 +49,35 @@
]
},
{
"id": "fr",
"id": "fr_FR",
"name": "Français",
"locale": "fr_FR.UTF-8",
"genders": [ ],
"lcids": [ 1036, 2060, 3084, 4108, 5132 ]
},
{
"id": "hu",
"id": "hu_HU",
"name": "Magyar",
"locale": "hu_HU.UTF-8",
"genders": [ ],
"lcids": [ 1038 ]
},
{
"id": "ja",
"id": "ja_JP",
"name": "日本語",
"locale": "ja_JP.UTF-8",
"genders": [ ],
"lcids": [ 1041 ]
},
{
"id": "ko",
"id": "ko_KR",
"name": "한국어",
"locale": "ko_KR.UTF-8",
"genders": [ ],
"lcids": [ 1042 ]
},
{
"id": "pl",
"id": "pl_PL",
"name": "Polski",
"locale": "pl_PL.UTF-8",
"genders": [ ],
Expand Down
3 changes: 3 additions & 0 deletions doc/TRANSLATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
The official location for translating Cataclysm: BN is the
[Transifex translations project][1].

If you're looking for a way to translate mods not included
in game repository, see [TRANSLATING_MODS.md](TRANSLATING_MODS.md).

Some of the currently supported languages are:

* Arabic
Expand Down
234 changes: 234 additions & 0 deletions doc/TRANSLATING_MODS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
# Translating mods for Cataclysm: BN

- [Intro](#intro)
- [A short glossary](#a-short-glossary)
- [Workflow overview](#workflow-overview)
- [Setting up environment for string extraction](#setting-up-environment-for-string-extraction)
- [Extracting strings](#extracting-strings)
- [Creating new PO](#creating-new-po)
- [Updating existing PO](#updating-existing-po)
- [Compiling PO into MO](#compiling-po-into-mo)
- [Adding MO file to the mod](#adding-mo-file-to-the-mod)
- [Miscellaneous notes](#miscellaneous-notes)

## 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](https://www.gnu.org/software/gettext/) and is compatible with GNU gettext MO files.
The system is currently in experimental state and must be enabled in Debug settings tab
(`Modular translations testing` option). Please report any bugs or suggestions
on our discord or by submitting an issue on our GitHub page.

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](https://poedit.net/) and
command-line [GNU gettext utilities](https://www.gnu.org/software/gettext/).

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](https://www.gnu.org/software/gettext/manual/gettext.html).

To get some generic tips on translating strings for Cataclysm: Bright Nights and its mods,
see [TRANSLATING.md](TRANSLATING.md).

## 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 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:

1. Extract strings from mod JSON files into a POT file
2. Create PO file for target language from this POT
3. Fill the PO file with translated strings
4. Compile PO into MO
5. 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:

1. Extract strings from mod JSON files into a new POT file
2. Update existing PO file from the new POT file
3. Add new or edit existing translated strings in the PO file
4. Compile PO into MO
5. 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` library 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 routines
* `dedup_pot_file.py` - fixes errors in POT file produces by the 1st script

## Extracting strings
To extract strings, you'll need to run these python scripts in the following order:
```bash
python extract_json_strings.py -i path/to/mod/ -o path/to/resulting/index.pot --project YourModName
python dedup_pot_file.py path/to/resulting/index.pot
```

Replace `YourModName` with the mod's name.
This is what translation software would use as project name for PO/POT,
so it doesn't have to match actual mod name or id.

Replace `path/to/resulting/index.pot` with output POT file name.

Replace `path/to/mod/` with path to the mod's folder.
Resulting POT will remember which file each string comes from (including `path/to/mod/` part),
and as such it's best to keep this path as short as possible
(or even use `./` and run the scripts from the mod's directory)
to keep the resulting references short, clean and easy for translators to understand.

If the mod is under development and you plan on keeping translations up-to-date in the future,
it may be worthwhile to create _another_ script (batch or bash, depending on your OS)
that would take care of string extraction process for you.

Extraction script itself (`extract_json_strings.py`) has a number of extra features
not documented here; call it with `-h` flag (or see the code) for more info.

## 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
1. Open the POT file with Poedit
2. Press "Create new translation" button (should show up near the bottom)
3. In language selection dialog, enter language id you chose
4. Save the file as `path/to/mod/lang/LANG.po` where `LANG` is the same language id

### msginit
```bash
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
1. Open the PO file with Poedit
2. Choose `Catalog->Update from POT file...` and select the new POT file
3. Save the file

### msgmerge
```bash
msgmerge lang/LANG.po lang/index.pot
```

## Compiling PO into MO
### Poedit
1. Open the PO file with Poedit
2. 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`).
3. 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 optional extraction script and 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/
extract.bat (extract.sh on MacOS/Linux)
index.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:
1. Translate a string
2. Hit Ctrl+S
3. Alt+Tab into the game
4. Reload translation files via debug menu
5. The game now displays translated string

### Dialects and MO load order
When loading MO files, the game first looks for the file with
exact language and dialect match.
If such file is absent, then it looks for a file with no dialect.

For example, when using `Español (España)` the load order is
1. `es_ES.mo`
2. `es.mo`

And when using `Español (Argentina)` the load order is
1. `es_AR.mo`
2. `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 uses translation from the first such mod in the mod loading order.

The in-repo mods (including the core content "mod") are an exception:
all of them use single MO file, which is loaded at all times and always
takes priority over 3-rd party translations.

If you want a different translation from the one in the base game,
add a translation context to the string.
2 changes: 1 addition & 1 deletion msvc-full-features/Cataclysm-test-vcpkg-static.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
<DisableSpecificWarnings>4819;4146;26495;26444;26451;4068</DisableSpecificWarnings>
<ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>
<AdditionalOptions>/bigobj /utf-8 %(AdditionalOptions)</AdditionalOptions>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_CONSOLE;SDL_SOUND;TILES;SDL_MAIN_HANDLED;LOCALIZE;USE_VCPKG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_CONSOLE;SDL_SOUND;TILES;SDL_MAIN_HANDLED;LOCALIZE;USE_VCPKG;CATCH_CONFIG_ENABLE_BENCHMARKING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard>stdcpp14</LanguageStandard>
<AdditionalIncludeDirectories>..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
Expand Down
Loading

0 comments on commit fbf53c7

Please sign in to comment.