Python gettext i18n — Core Concepts
Internationalization (i18n) means preparing your code so it can be translated. Localization (l10n) means doing the actual translation for a specific language. Python’s gettext module handles both sides.
The Core Workflow
The gettext process has four steps that repeat every time you add or change user-facing text:
- Mark translatable strings in your code with
_() - Extract those strings into a
.pot(Portable Object Template) file - Translate — create
.pofiles for each language - Compile — convert
.pofiles to binary.mofiles Python reads at runtime
Step 1: Marking Strings
import gettext
_ = gettext.gettext
print(_("Welcome to the application"))
print(_("You have {} new messages").format(count))
The convention of naming the function _ is universal across gettext-based systems. It keeps code readable without cluttering every string call.
For plural forms, use ngettext:
ngettext = gettext.ngettext
print(ngettext("One file deleted", "{} files deleted", count).format(count))
Step 2: Extracting Strings
The xgettext tool (from GNU gettext) or Python’s pygettext.py scans your source and produces a .pot file:
xgettext -d myapp -o locales/myapp.pot *.py
The .pot file is a catalog of every marked string with blank translation slots.
Step 3: Translation Files
For each language, copy the template into a .po file:
locales/
fr/LC_MESSAGES/myapp.po
de/LC_MESSAGES/myapp.po
ja/LC_MESSAGES/myapp.po
A .po entry looks like:
msgid "Welcome to the application"
msgstr "Bienvenue dans l'application"
Translators edit these files directly or use tools like Poedit, Lokalize, or Weblate.
Step 4: Compiling
msgfmt -o locales/fr/LC_MESSAGES/myapp.mo locales/fr/LC_MESSAGES/myapp.po
The .mo file is a binary lookup table that Python loads efficiently at runtime.
Activating Translations at Runtime
import gettext
lang = gettext.translation("myapp", localedir="locales", languages=["fr"])
lang.install() # Binds _() to the French catalog globally
print(_("Welcome to the application"))
# Output: Bienvenue dans l'application
For class-based usage without global state:
lang = gettext.translation("myapp", localedir="locales", languages=["de"])
_ = lang.gettext
Fallback Behavior
If a translation file is missing, gettext.translation() raises FileNotFoundError by default. Pass fallback=True to gracefully fall back to the original strings:
lang = gettext.translation("myapp", localedir="locales",
languages=["pt"], fallback=True)
Common Misconception
“gettext translates strings automatically.” It doesn’t. gettext is a lookup system — it matches msgid to msgstr in pre-written catalogs. Every translation must be written by a human (or a translation service) and compiled before your app can use it.
Directory Layout Convention
project/
├── locales/
│ ├── myapp.pot
│ ├── fr/LC_MESSAGES/
│ │ ├── myapp.po
│ │ └── myapp.mo
│ └── de/LC_MESSAGES/
│ ├── myapp.po
│ └── myapp.mo
├── app.py
The LC_MESSAGES directory name is required by the gettext standard.
The one thing to remember: gettext is a four-step cycle — mark, extract, translate, compile — and Python’s standard library handles every step except the human translation work.
See Also
- Python Babel Localization Babel teaches your Python app how dates, numbers, and currencies look in every country — not just yours.
- Python Locale Module Python's locale module reads your computer's regional settings so numbers, dates, and sorting feel right for where you live.
- Ci Cd Why big apps can ship updates every day without turning your phone into a glitchy mess — CI/CD is the behind-the-scenes quality gate and delivery truck.
- Containerization Why does software that works on your computer break on everyone else's? Containers fix that — and they're why Netflix can deploy 100 updates a day without the site going down.
- Python 310 New Features Python 3.10 gave programmers a shape-sorting machine, friendlier error messages, and cleaner ways to say 'this or that' in type hints.