Adding multiple languages might sound complex, but the standard Python ecosystem offers powerful, built-in tools. These tools rely on the Command-Line Interface (CLI) and can automate the entire process.
This post shows how to localize your Python scripts into 3 easy steps using standard CLI utilities.
Contents
Is CLI Usage Necessary for Simple Python Script Localization?
The short answer is: No, but it’s essential for a maintainable process.
For a simple script with just a few lines, you could manually manage translations in dictionaries or JSON files. However, this quickly becomes messy and full of errors if:
- The number of translatable strings grows large.
- The strings are updated frequently.
- You need to work with professional translators.
- You need to handle plural forms correctly.
CLI usage is necessary when:
- Your script grows, or you start a new, larger project. Manually finding and updating hundreds of strings is impossible.
- You want to use the industry-standard tooling. The gettext system is the standard solution. It automatically scans your code for translatable strings and manages the compilation of those files.
- You want to integrate translation updates into your development process (e.g., via a pre-commit hook or CI/CD pipeline).
The CLI tools automate localization tasks, letting you focus on writing code.
3-Step Workflow for Python CLI Localization
Your Python script localization can be broken down into three simple stages:
Step 1: Prepare Your Script by Externalizing Strings
The core idea is simple: separate your text from your code. Every piece of human-readable text (a message, an error, a prompt) must be marked so that extraction tools know it needs translation.
You can do this by wrapping the string in a special function, conventionally named an underscore _()
|
Before: Hardcoded String |
After: Externalized String |
|
print(“Error: File not found.”) |
print(_(“Error: File not found.”)) |
The _() function is a conventional shorthand for “get text”. When your script runs, it looks up the string within the parentheses and replaces it with the translated version for the active language.
This is the basic preparation. However, to handle all real-world language complexities, you need two additional techniques:
Handling Plural Forms
Different languages have complex rules for plurals (e.g., singular, dual, few, many). You can’t just wrap the singular phrase in _(). You must use the plural-aware function, usually aliased as _n(), to provide both the singular and plural forms:
-
Syntax: _n(singular_string, plural_string, count)
|
Example |
Code |
|
Singular: “1 error found.” |
count = 1 |
|
Plural: “3 errors found.” |
print(_n(“%d error found.”, “%d errors found.”, count) % count) |
The extraction tools recognize this function and create placeholders in the translation file for all necessary plural forms required by that specific language. The %d is the placeholder for the number.
Contextual Disambiguation
Sometimes, the same word or short phrase needs different translations based on its meaning. For instance, the English word “Close” could be a verb (“Close the window”) or an adjective (“Close proximity”).
If you only use _(“Close”), the translator can only provide one translation. Use the function _p(), which adds a context marker to distinguish the entries:
-
Syntax: _p(context, string_to_translate)
|
Example |
Code |
|
Context 1 (Verb): |
print(_p(“action”, “Close”)) |
|
Context 2 (Adjective): |
print(_p(“adjective”, “Close”)) |
This creates two separate entries in the translation file and allows translators to provide the correct word for each distinct meaning. Now we are ready to move to the second step: text extraction and the creation of translation files.
Step 2: Extract Text and Create Translation Files
In this step, we will use CLI. The gettext toolchain is the built-in way to handle this in Python.
The gettext suite includes command-line utilities (like pygettext.py or xgettext) to automatically scan your Python files and generate a template for your translators. This template is a .po (Portable Object) file, which is a human-readable text file that pairs the original English string with a placeholder for the translation in another language.
For a deep dive into the technical commands and file management, read this guide to Python gettext.
Step 3: Load the Translations in Your Script
The final step is to tell your script where to find the translation files and which language to use. You only need to do this setup once, typically at the beginning of your script.
Here is a minimal code snippet to initialize gettext:
import gettext
import locale
# 1. Set up the locale based on the user’s system environment ($LANG)
locale.setlocale(locale.LC_ALL, ”)
# 2. Tell gettext where your translation files are stored
LOCALE_DIR = ‘locale’ # The folder containing your language files
PACKAGE_NAME = ‘my_tool’ # Your script’s domain/package name
# 3. Install the _() function globally
gettext.install(PACKAGE_NAME, LOCALE_DIR)
# Now you can use _() anywhere in your script!
print(_(“Welcome to the tool!”))
This initialization automatically checks the user’s system environment variables (like $LANG or $LC_MESSAGES) to determine the correct language to load. For official reference on the module, check the Python gettext standard library documentation.
Best Practices for Maintainable CLI Translations
Keep these tips in mind to make your localized scripts easy to update and maintain.
Provide Context
Translators often need more information than just the string itself. You can add comments in your code that the extraction tools will copy into the .po file:
# Translators: This message appears when the user tries to save an empty file.
print(_(“Cannot save. File is empty.”))
Define a Clear Naming Convention for Files
Consistency is key, especially when dealing with CLI tools that expect files in specific locations. Your project structure and file names must strictly follow the gettext standard.
Translations must be placed in a directory structure like locale/[language_code]/LC_MESSAGES/[domain_name].po and compiled to a .mo file (Machine Object) for your script to load.
Example: For Spanish translations of a tool named my_app, the compiled file should be located at: locale/es/LC_MESSAGES/my_app.mo
This structure is what the gettext library expects and eliminates runtime errors related to file loading.
Avoid Using Technical Jargon in Translatable Strings
CLI tools often use technical terms, abbreviations, or internal variables that developers understand but translators might not.
Keep user-facing messages simple, descriptive, and polite. Move technical specifics (like path names or internal codes) outside the translatable string whenever possible.
Example:
- Bad Way: print(_(“Err 404: Can’t access remote DB $DB_HOST.”))
- Correct Way: print(_(“Connection failed. Please check the network settings.”))
It reduces translation errors and ensures the user receives clear, actionable feedback, regardless of the language.
Don’t Concatenate
Never build a translated sentence by stringing together multiple translated parts. This breaks grammar rules in many languages.
- Bad Way: print(_(“Copied “) + filename + _(” to folder.”))
- Correct Way (using placeholders): print(_(“Copied {filename} to the destination folder.”).format(filename=filename))
Using f-strings or .format() keeps the entire sentence together for the translator.
Automate Your Workflow
Manually running the string extraction command every time you update a string is inefficient. Mention that the string extraction process can be added to a pre-commit hook (which runs before you commit code) or a CI/CD pipeline (which runs during deployment) to ensure your translation files are always up-to-date.
Wrapping Up
Localizing a Python script using CLI tools like gettext is an accessible way to improve your tool’s quality and reach. By externalizing your strings and automating the file creation process, you lower the barrier for non-English speakers.
Go ahead and try it on your next project – that will be a massive upgrade for your users!
