Data files in your Python package
The cleanest way I’ve found has 3 steps.
MANIFEST.in must include the files you want in the package.
For example, to include all .mo files:
recursive-include multisense_processes *.py *.mo recursive-include tests * include *.md
All the directories containing data files must be Python modules, even if they
don’t actually contain any actual code. In other words, they must all have files
__init__.py. For instance:
your_root/ |-- pyproject.toml | |-- your_python_package/ | |-- __init__.py | | | |-- img/ | | |-- __init__.py | | |-- some_image.jpeg
When installed, your Python module may or may not be decompressed. It may
be installed decompressed, or as an egg file (pretty much a .zip file). So
you cannot access the files directly with some hack like
Instead, in your Python module, you must use
pkg_resources. If you need
the file to be extracted (for instance to use it with some C library),
pkg_resources.resource_filename() is your friend.
You can use
pkg_resources.resource_filename with directories too. They
will be extracted recursively.
Translations in your Python package
Some frameworks provide their own translation mechanism
(Django for instance). If you’re not using one of those frameworks, then the most
common way to add support for translations in your Python module
is to use gettext (
sudo apt install gettext). It’s a GNU tool that works
with many other programming languages.
Gettext provides tools to:
- Extract strings to translate from your code
- Compile translation files
- Use those translations
Flag the strings to translate
In your code, the strings to translate must be wrapped by a specific function
call. Usually, this function is called
It’s common to map
Doing so will allow to find the strings to translate in the code, but also
will return their translations (if available) at runtime. If no translation
is available, by default, the original string is returned.
One way to do this is:
You must follow the same 3 steps as for any data files, as described previously. In the end, we aim at the following file structure:
your_root/ |-- pyproject.toml | |-- your_python_package/ | |-- __init__.py | | | |-- i18n/ | | |-- __init__.py | | | | | |-- locales/ | | | |-- __init__.py | | | |-- messages.pot | | | | | | | |-- fr/ | | | | |-- __init__.py | | | | | | | | | |-- LC_MESSAGES/ | | | | | |-- __init__.py | | | | | |-- some_project_name.mo | | | | | |-- some_project_name.po
Extraction of strings to translate
Here is the black magic command:
It will generate a file
messages.pot that contains all the strings to
translate. This file can then be used to update your translation files.
Note: you can store the file messages.pot anywhere else if you want. Putting it there just makes writing a Makefile easier (see below).
Updating the strings to translate
Assuming you’re working on the french translations, your translation
file would be called
Once compiled, it will be
If the translation file doesn’t exist yet, you must create it with:
If it does exist, you must update its strings with:
You’re strongly advised to have a quick look at the man pages of
to make sure it matches the behaviour you want.
Updating the strings
You can simply update the
.po files with any editor. A more classy way is
to install a system like Weblate.
Compiling the translations
To keep track of all those commands, I advise you add a Makefile at the root of your project. For instance:
This Makefile has 2 targets.
One target to extract the strings to translate and update the translation file accordingly:
Then you can edit the translations in the files
And one target to compile the translations:
You’re advised to look at the Python|gettext documentation. You should have something akin to the following in your Python code: