
Python localization (l10n) is easier than you might think. Python’s built-in gettext module provides a standard way to separate your application’s code from its user-facing text.
In this guide, we’ll walk through a simple example of how to make a basic Python application speak more than one language.
Step 1: The Monolingual Application
Don’t want to miss the best from TechLatest ? Set us as a preferred source in Google Search and make sure you never miss our latest.
Let’s start with a simple command-line application that we’ll use as our demo. It’s a very basic program that greets the user and asks for their name. Notice that all the user-facing strings are hard-coded directly in the print() and input() statements.
# app.py
def run_app():
print("Welcome to the multilingual demo!")
name = input("Please enter your name: ")
print(f"Hello, {name}! How are you today?")
if __name__ == "__main__":
run_app()
This code works perfectly fine, but it has one major limitation: it only works for English speakers. Our goal is to make it flexible enough to display these strings in Spanish, French, or any other language without changing the core logic.
Step 2: Marking Strings for Translation
The first step in using gettext is to tell Python which strings in your code need to be translated. We do this by wrapping them in a special function. The gettext module provides a function for this, but it’s a common convention to alias it to a single underscore, _(), for brevity.
Now we’ll walk through a Python gettext example to show you how to prepare your code for translation. This simple change is the most crucial part of the process.
Let’s update our app.py script to use this function.
# app.py
import gettext
# Set up a placeholder for the gettext function.
# This makes it easy to replace with a real translation
# function later, as you'll see.
_ = gettext.gettext
def run_app():
print(_("Welcome to the multilingual demo!"))
name = input(_("Please enter your name: "))
print(_(f"Hello, {name}! How are you today?"))
if __name__ == "__main__":
run_app()
You’ve now successfully marked your strings for translation. We haven’t added any new languages yet; we’ve just prepared our application to handle them.
Step 3: Creating and Managing Translation Files
Next, we need to extract the strings we marked and get them into a format that a human can translate. This is a multi-step process that involves a couple of command-line tools.
The first thing you’ll need to do is create a standard directory structure for our translations, which will look something like this:
└── my_app/
├── app.py
└── locales/
├── en_US/
│ └── LC_MESSAGES/
│ └── base.po
└── es_ES/
└── LC_MESSAGES/
└── base.po
Now, we use a command-line tool like pybabel or xgettext to scan our code and generate a .pot (Portable Object Template) file. This file is a blueprint that contains all of the original strings we marked. We’re naming our primary translation file base.po to signify that it holds the core strings for our application. This is a common practice that helps organize larger projects where you might have separate files for different sections of the app (e.g., forms.po, errors.po, etc.).
# We'll use xgettext, which is part of the gettext-tools package
# Note: You may need to install this system-wide on your machine.
xgettext --language=Python --keyword=_ --output=locale/base.pot app.py
This command scans app.py for strings wrapped in _() and saves them into locale/messages.pot.
Next, we create the actual translation files from this template. Each language gets its own .po (Portable Object) file.
# Create a .po file for Spanish (es_ES)
msginit --locale=es_ES --input=locales/base.pot --output-file=locales/es_ES/LC_MESSAGES/base.po
# You can also create one for English to be explicit
msginit --locale=en_US --input=locales/base.pot --output-file=locales/en_US/LC_MESSAGES/base.po
Step 4: Adding the Translations
The .po file is a plain text file that’s easy for translators to work with. It’s organized into blocks, where msgid is the original string from your code and msgstr is the translation. All a translator needs to do is open the .po file for their target language and fill in the msgstr fields.
Let’s take a look at what our Spanish .po file (locales/es_ES/LC_MESSAGES/base.po) would look like after a translator has worked on it:
# This file is for Spanish translations.
#
msgid ""
msgstr ""
"Project-Id-Version: Multilingual Demo\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: es_ES\n"
msgid "Welcome to the multilingual demo!"
msgstr "¡Bienvenido a la demostración multilingüe!"
msgid "Please enter your name: "
msgstr "Por favor, introduce tu nombre: "
msgid "Hello, {name}! How are you today?"
msgstr "¡Hola, {name}! ¿Cómo estás hoy?"
Notice the format: each msgid is followed by its corresponding msgstr. The translator only edits the msgstr line, leaving the original msgid untouched. This clear separation of source and translation is a core benefit of the gettext system.
Step 5: Compiling and Running the Application
With the translations complete, there’s just one final step: compiling the .po files into a binary format that Python can read efficiently. These are called .mo (Machine Object) files. The msgfmt command takes your human-readable .po file and converts it into the machine-readable .mo format.
# Compile the Spanish .po file into a .mo file
msgfmt -o locales/es_ES/LC_MESSAGES/base.mo locales/es_ES/LC_MESSAGES/base.po
Now our application is ready to be multilingual! We just need to update our Python script to use the correct .mo file based on the user’s locale. This involves a few key steps:
- Define the directory: Tell gettext where to find the .mo files.
- Load the translation: Use gettext.translation() to load the correct file based on the user’s language setting.
- Install the translation: Use translation.install() to “install” the new translation, so our _() function now points to the correct language.
Here’s the final, complete version of our app.py script:
# app.py
import gettext
import os
# Define the root directory for our translation files
LOCALE_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'locales')
def setup_language(lang_code):
try:
# Load the translation from the locale directory
translation = gettext.translation('base', localedir=LOCALE_DIR, languages=[lang_code])
# Install the translation globally so gettext can find it
translation.install()
except FileNotFoundError:
# Fallback to a default language (e.g., English) if translation isn't found
print(f"Warning: Translation for '{lang_code}' not found. Using default language.")
gettext.install('base', LOCALE_DIR, names=['ngettext'])
if __name__ == "__main__":
# Simulate setting the user's language environment.
# On a real system, you might read this from a user setting or system locale.
# Try running the app in Spanish
setup_language('es_ES')
print(_("Welcome to the multilingual demo!"))
# Now, let's switch to English for the rest of the app's flow
setup_language('en_US')
name = input(_("Please enter your name: "))
print(_(f"Hello, {name}! How are you today?"))
If you run this final script, you’ll see the first line of output in Spanish, while the subsequent prompt and output remain in English, demonstrating that the translations are working correctly and can be switched dynamically.
- Fix: Pip is not recognized as an internal or external command
- 6 Malicious PyPi Packages Installing RAT Malware via Cloudflare Tunneling
Final Words
By using gettext , we have cleanly separated our application’s code from its human-readable strings, making the entire project much easier to maintain, translate, and scale.
But this is just the beginning. The gettext module also handles more complex features like pluralization and context-aware translations, allowing you to build advanced multilingual applications. Now that you understand the fundamentals, you are well-equipped to make your own Python projects speak multiple languages.
Enjoyed this article?
If TechLatest has helped you, consider supporting us with a one-time tip on Ko-fi. Every contribution keeps our work free and independent.