PO translation catalogs and gettext
PO headers, contexts, plurals, fuzzy entries and catalog updates from POT.
Use one PO catalog per locale when translators need separate files, message context and plural forms. Reckfell loads .po directly as a Translation resource; compiling it to .mo is not required for this workflow.
A working Russian catalog
Save this as Locale/ru.po in UTF-8. It is a complete catalog, not a template:
msgid ""
msgstr ""
"Language: ru\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
msgid "PO_PLAY"
msgstr "Играть"
msgctxt "verb"
msgid "Open"
msgstr "Открыть"
msgctxt "state"
msgid "Open"
msgstr "Открыто"
msgid "PO_ITEM"
msgid_plural "PO_ITEMS"
msgstr[0] "{0} предмет"
msgstr[1] "{0} предмета"
msgstr[2] "{0} предметов"- Add Locale/ru.po in Project Settings → Localization → Translations. Unlike a CSV table, this PO file is itself the loadable translation resource.
- Set the game locale with TranslationServer.SetLocale("ru").
- TranslationServer.Translate("PO_PLAY").ToString() returns Играть.
- TranslationServer.Translate("Open", "state").ToString() returns Открыто.
- TranslationServer.TranslatePlural("PO_ITEM", "PO_ITEMS", 5).ToString() returns {0} предметов. Pass that result to string.Format with the count to display 5 предметов.
These calls belong inside a method. For a complete Label script that refreshes on locale changes, use the CSV example, replacing ITEM/ITEMS with PO_ITEM/PO_ITEMS and registering this PO catalog.
The Language header determines the resource locale: the filename alone does not select it. Keep every indexed plural form, from 0 through nplurals−1, and preserve format placeholders. Do not register this catalog and another catalog defining the same key/context/locale pair; merge the messages or assign distinct keys.
Entries that do not produce translated text
An entry marked with the special comment #, fuzzy is skipped by the loader. The flag means the translation needs review. Correct and approve the text before removing it; do not strip flags merely to make strings appear.
An empty msgstr does not request an empty UI label. It supplies no usable text for lookup, so the fallback language or source key is returned. Contextual messages require the same context in the call. A generic Open lookup cannot choose between verb and state.
PO quoted fragments are concatenated. Write \n inside the quoted string for a newline; represent a quote as " and a literal backslash as \. Keep these escapes intact when translating.
Maintain a POT template
A .pot file records source messages; its msgstr values stay empty. A .po file contains the translations. A .mo file is a compiled catalog. Register PO or MO catalogs, not the POT template.
In Project Settings → Localization → Template Generation, add the source files and generate a .pot file. The source parser for PackedEntity accepts its recognized resource extensions, including levels and prefabs; use actual .rscene/.rprefab files. The C# parser recognizes translation calls such as Tr, TrN, TranslationServer.Translate and TranslatePlural when their message arguments can be resolved as constant strings. A key assembled at runtime needs an explicit catalog entry or a deliberate extraction strategy; template generation cannot infer every possible value.
After adding messages, regenerate the template, update each language catalog and review changed translations. Do not replace a translated catalog with an empty template.
With GNU gettext installed, run these commands from the project directory:
msgmerge --update Locale/ru.po Locale/messages.pot
msgfmt --check --output-file=Locale/ru.mo Locale/ru.poThe first command updates the existing PO from the current POT. The second checks the catalog and writes MO output. Inspect diagnostics; header checks can report missing metadata, and a successful command does not establish the accuracy of the translation. See the official msgmerge options and msgfmt checks.
Keep the editable PO files under version control even if shipping compiled catalogs. Do not register both PO and MO versions of the same catalog. The current runtime loader recognizes both extensions; the native acceptance run for this guide exercises PO, not MO compilation/loading or external gettext tools.
Diagnose a catalog
| Symptom | Check |
|---|---|
| Header/configuration error | Initial empty msgid/msgstr and quoted header lines are present and correctly escaped |
| Wrong locale | Language header, active locale and registered resource |
| A translated entry still shows its key | fuzzy flag, empty msgstr, exact key/context, registration and fallback |
| Plural parse error | Valid Plural-Forms header before plural entries; matching count of indexed msgstr entries |
| New source messages are absent | Regenerate POT, merge catalogs, translate and reload the resources |
| The count is still {0} | Format the selected plural string; lookup does not insert numbers |
Native tests cover loading this catalog structure, Russian plural rules, contexts, multiline escapes, skipping fuzzy entries, empty translations and runtime registration. Editor extraction instructions were checked in source. Automatic POT generation, external tool execution, MO files, font rendering and platform exports remain outside this run's evidence.