How to translate a game’s .po and .mo files (gettext)
The .po is the editable source; the .mo is the compiled catalog the game actually loads — which is why editing the .po alone changes nothing. RuneTranslate reads both, protects %s and {count} placeholders, refits plural forms to your language, and writes a working .mo back.
If you have opened a game folder and found a locale directory full of .po and .mo files, the game is localized with gettext— and RuneTranslate can now translate it end to end, from project creation through to a playable exported build.
This guide explains what those two file types actually are, why editing the one you can read usually changes nothing in-game, and how to translate them properly.
.po vs .mo — the distinction that trips everyone up
They are two forms of the same catalog:
.po— Portable Object. A plain-text file you can open in any editor. Each entry is amsgid(the original string) and amsgstr(its translation). This is the file translators work in..mo— Machine Object. A compiled binary version of the same data, laid out so the game can look a string up instantly. This is the file the running game reads.
That last point is the one that costs people an afternoon. You can translate every line of a .po, save it, launch the game — and see no change at all, because the game never opens the .po. It loads the .mo sitting next to it, which still holds the old text. Normally you would compile the one into the other with msgfmt from the GNU gettext tools. RuneTranslate writes both, so there is no separate compile step.
What a gettext game folder looks like
The canonical layout is a locale directory keyed by language code:
MyGame/
├─ game.exe
└─ locale/
├─ en/LC_MESSAGES/mygame.mo
├─ fr/LC_MESSAGES/mygame.mo
└─ de/LC_MESSAGES/mygame.moThe LC_MESSAGES folder is part of the convention, not a mistake, and mygame is the text domain— the name the game asks for when it loads its strings. Plenty of games use a flatter shape instead, like lang/fr.po, and that works too.
Which games use gettext?
Gettext originated in the Unix world and became the default way to localize software written in C, Python and anything GTK-based. In games that means:
- Python and pygame games
- Solarus, SDL and other C/C++ engines
- Games localized through Poedit, Weblate or Crowdin, which all speak PO natively
- Some Godot projects, which support PO as a translation source
It is much more common in Western indie games than in Japanese ones — Japanese engines like Kirikiri and RPG Maker keep their text in their own formats instead.
Translating a gettext game with RuneTranslate
- Point RuneTranslate at the game folder. The one with
locale/,lang/ori18n/inside. Detection reads the actual bytes rather than trusting the extension, so a.mothat is really a 3D scene file gets ignored instead of producing a broken project. - Check the summary. Before you create the project you get the catalog count, the split between compiled
.moand source.po, which languages the game already ships, and the total string count. - Choose a language and a provider.Free Google or DeepL, or your own DeepL, OpenAI, Claude or DeepSeek key — see the provider comparison.
- Review, then export. You get a full copy of the game with the translation in place.
The two things that quietly break gettext translations
Placeholders. A msgid is a format string, not finished prose. Hello, %s!has the player's name substituted at runtime. Machine translators are prone to dropping %s, spacing it out, or — with Python's named form %(player)s — translating the word inside it. The consequences are not cosmetic: a dropped %sshifts every later argument along, and most engines crash outright. RuneTranslate masks every placeholder it finds — %s, %d, %(player)s, {count}, {0}— before the text goes to a provider, and restores them after.
Plural forms.English has two (“1 coin” / “5 coins”). Japanese and Turkish have one. Russian and Polish have three. A catalog declares how many it uses in its header, and if that number does not match the language, gettext indexes past the end of the list and silently falls back to the untranslated original. RuneTranslate refits the forms to your target language and rewrites the header to match.
Why the export writes the catalog twice
Exporting produces both a proper new locale — locale/<your language>/LC_MESSAGES/— and an updated copy of the catalog the game was already loading.
The second one is what makes the translation actually appear. A game decides which locale to load in ways nothing in the folder reveals: it may read your OS language, a config file, a launcher flag, or simply hard-code one. Adding a Turkish catalog to a game that only ever asks for English would leave you staring at an apparently unchanged build. Overwriting the catalog it does load removes the guesswork. Both only ever happen in the exported copy — your original game folder is not modified.
Strings you have not translated keep whatever translation the game already shipped, so a partially finished project never blanks out the developer's own localization.
What this cannot do
Only text that is already in a catalog can be translated. If a developer hard-coded a string instead of passing it through gettext, it is not in the .po or .mo, and no PO tool can find it there. That is a limit of the format, not of the tool.
RuneTranslate also treats the msgid as the source text, which is what gettext itself specifies. A few projects put English msgids in place while the real original language sits in msgstr; those will translate from the English.
Finally, if the game runs on an engine RuneTranslate supports directly — Godot, Unity, Ren'Py— that engine handles it instead, so the rest of the game's text is reachable too, not just its catalogs.
Before you share it
Launch the exported build and read a few screens. Menus and buttons are the fastest check, since that is where most catalog text lives. If something is wrong — the game does not detect, the export will not run, or the text is still in the original language — report it from the app's Help menu or in Discord. Download RuneTranslate and open your first catalog.
Ready to try RuneTranslate?
Free tier unlocks every engine + every translation provider. Supporter ($3/mo) unlocks full speed.
Download for Windows