Python (PO)
gettext PO localization for Python applications.
Translate the .po catalog of your application, reuse earlier translations, check placeholders, and write a translated .po file for each language. Your Python project keeps extraction, msgfmt, and runtime loading.
- Input
- An English
.pofile:msgidis the key,msgstrthe text - Output
- Translated
.pofiles, such asde\messages.po - Runtime
.mofiles built by msgfmt, loaded bygettext- Preview
- None: strings are translated in the table
On this page
Tutorial
Localize PO files with Lingobit, step by step.
Four steps from the English .po file to translated files for msgfmt.
Your tools extract, Lingobit translates, msgfmt compiles.
xgettext or pybabel extract collects the marked strings into messages.pot, and msgen turns it into an English messages.po, in which every msgstr holds the English text. Lingobit translates those texts and writes one .po file for each language. msgfmt or your framework compiles them into .mo files, which gettext loads at runtime.
Lingobit does
- Translates the
msgstrtext of every message;msgidstays the key. - Reuses earlier translations and the translation memory.
- Checks placeholders such as
%sand%d, line breaks, empty and too long translations, and more. - Writes a translated
.pofile for each language and keeps the rest of the file as it was.
Your Python project keeps
- Marking strings with
_(), extracting them, and making the English.pofile. - Compiling
.pointo.mowith msgfmt,pybabel compile, orcompilemessages. - Loading the catalog at runtime and choosing the language.
- The final test in the running application.
Reference
At startup, gettext finds the catalog by its folder.
msgfmt turns de\messages.po into messages.mo in locale\de\LC_MESSAGES, and gettext.translation looks for it there. For German (Germany) it tries de_DE first, then de, so one German catalog serves every German-speaking country.
- A missing message shows the source text. A string that is new or still untranslated appears in English; it does not break the application.
- A missing catalog is an error unless you pass
fallback=True; then the whole application stays in English.
import gettext
t = gettext.translation("messages", localedir="locale",
languages=["de_DE"], fallback=True)
t.install() # _() now returns the German text
Write the files into the locale folder.
By default Lingobit writes de\messages.po into the output folder of the project. To get the full layout gettext expects, change the output path in the project settings for PO, on the Encoding / Output page, so the files go into locale\de\LC_MESSAGES.
- Language folders follow gettext. German writes
de, German (Germany) writesde_DE; gettext finds both. - Compile next to the .po file with
msgfmt -o messages.mo messages.poin each folder, or withpybabel compile -d locale.
Choose the language in your app.
On Windows, gettext does not read the Windows language: it looks at the LANGUAGE, LC_ALL, LC_MESSAGES, and LANG variables, which are usually not set there. Pass the language yourself:
- List the languages you ship from the folders in
locale. - Read the saved choice or a command-line switch; otherwise use the language you detect for the user.
- Install the catalog first before you import modules that call
_()at import time. - Switch while running by installing another catalog and rebuilding the windows, or restart.
import gettext, sys
lang = sys.argv[1] if len(sys.argv) > 1 else saved_language()
gettext.translation("messages", localedir="locale",
languages=[lang], fallback=True).install()
from myapp.main_window import MainWindow # after install()
What Lingobit reads in a PO file.
Lingobit translates the msgstr text and writes the rest back as it was, apart from the fuzzy flag of translated messages. A .pot template, or a .po file with empty msgstr, has nothing to translate: make the English file with msgen first.
| In the PO file | In Lingobit | In the translated file |
|---|---|---|
msgid | The message key in the tree, also when it is wrapped over several lines; translations are matched by it | Unchanged |
msgstr | The text that is translated, also when it spans several lines; \" and \n are handled | The translation |
Plural forms: msgstr[0], msgstr[1] | Each form is translated | The translated forms, with the forms and the Plural-Forms header of the English file: right for German, Spanish, or Italian. Russian, Polish, or French keep the English rule |
msgctxt | Part of the key: the same msgid in two contexts gets two translations | Unchanged |
Comments #. | Shown to translators in Translation Hints | Unchanged |
Flags #, | Not shown | fuzzy removed from translated messages; other flags unchanged |
| Header | Not translated; its charset is used to read the file | Written in UTF-8, with charset=UTF-8 |
msgen messages.pot -o messages.po
Do and don't.
Do
- Refresh the English
messages.powith msgen before every handoff, and run Scan for Changes. - Use
%s,%d, or named%(count)dplaceholders, and ask translators to keep the names. - Keep the domain and folder names in line with your loading code.
Don't
- Add the
.pottemplate or a.pofile with emptymsgstr. - Edit the translated
.pofiles by hand: the next Create overwrites them. - Build sentences in code from translated pieces.
- Send Python code or templates to translators.
Troubleshooting.
| Symptom | Likely cause | What to do |
|---|---|---|
| Messages have no text to translate in Lingobit | The file is a .pot template, or a .po file whose msgstr is empty | Make the English file with msgen and add it instead |
| The application stays in English | No .mo file was built, it is not in locale\de\LC_MESSAGES, the domain differs, or no language was passed on Windows | Compile the translated .po files, check the folder and the domain, and pass the language to gettext.translation |
| The application fails with one language | A translation breaks a placeholder: a lost %s, or a translated name inside %(count)d or {name} | Run Validation: it reports %s and %d that do not match the source. Check named placeholders in the running application |
| German text is cut off in the application | .po files have no preview, so long text shows only in the running application | Set a maximum length for the strings in Lingobit; Validation reports translations that exceed it |
More about Python gettext localization.
PO, POT, and MO files
.pot templates are written by extraction tools from Python code, templates, or framework sources; their msgstr entries are empty. .po files are the translation catalogs: Lingobit reads the English one, made with msgen, and writes a translated one for each language. .mo files are the compiled runtime catalogs that developer or framework tools build from the translated .po files.
messages.pot
messages.po (English, made with msgen)
locale/de/LC_MESSAGES/messages.po
locale/de/LC_MESSAGES/messages.mo
Babel, Django, and framework workflows
Python projects reach gettext catalogs through different toolchains, and all of them lead to the same English .po handoff:
- Plain gettext: generate the
.pottemplate, make the English.powithmsgen, compile the translated catalogs into.mo. - Babel /
pybabel: runpybabel extract, make the English.powithmsgen, compile withpybabel compile. - Django and similar frameworks: make the English catalog with
makemessagesand fill it withmsgen, build the runtime catalogs withcompilemessages.
Clear boundaries
- Lingobit does not replace
xgettext,pybabel, Djangomakemessagesandcompilemessages, or the framework's own i18n setup. - It does not compile or load runtime catalogs, and Python source files or templates are not the translator's handoff.
- Packaging the locale folders and loading the translations at runtime stay with the application.
Related paths
- Localization QA — Validation, placeholder checks, and Crash Finder.
- Release workflow — Scan for Changes and translated files for every release.
- Qt TS — The same translation stage for Qt applications.
- Migration — Bring translations from earlier tools.