Modern Python packaging¶
You might be familiar with requirements.txt or setup.py for describing your project dependencies. Since then, a lot has changed in the Python world. At some point, we had multiple tools like poetry, pipenv, dephell, flit, and so on, each with its own file format. Luckily, now everyone is settled on a singe universal approach. There are the standards that set the ground:
PEP 517 introduced
pyproject.tomlas a single configuration file for all Python tools.
PEP 518 intorduced a
pyproject.tomlthat says what tool should be used to buid your project.
PEP 621 intorduced a
pyproject.tomlthat described all project metadata, like name, version, authors, dependencies.
You don’t have to use all this to work with l10n but if you do, l10n will be better at finding your project metadata. In this section, we will use the modern packaging approach for all examples.
Let’s start a new project:
mkdir l10n-playground cd l10n-playground
Now, let’s create a
pyproject.toml with your project metadata and add
l10n into dependencies. In this example, we will use flit for dependency management.
[build-system] requires = ["flit_core >=3.2,<4"] build-backend = "flit_core.buildapi" [project] name = "l10n_playground" dynamic = ["version", "description"] requires-python = ">=3.7" dependencies = ["l10n"] [project.optional-dependencies] dev = ["l10n[cli]"]
"""Experimenting with l10n""" __version__ = "1.0.0"
Now, you can install your project:
python3 -m pip install -U flit flit install --deps=develop --symlink
The environment is ready! It’s time to write some code.
In l10n, your code is the single source of truth, and translation files are generated from it. So you always start from writing code, and translations go later.
Locales provides a runtime catalog of translation for different languages. Create an instance of it (usually, no need to pass any arguments, defaults are good) and use it as mapping where keys are language codes. Sounds vague so far, so let’s get our hands dirty.
from argparse import ArgumentParser from l10n import Locales locales = Locales() def say_hello(lang: str = 'en') -> None: loc = locales[lang] msg = loc.get('Hello, world!') print(msg) def main() -> None: parser = ArgumentParser() parser.add_argument('--lang', default='en') args = parser.parse_args() say_hello(lang=args.lang)
from .example import main main()
If you try to run it, it will fail with
KeyError because there is no
en language in the catalogue. So, it’s time to make some.
Now, let’s generate translation files from our code for English and Russian:
python3 -m l10n extract --lang=en python3 -m l10n extract --lang=ru
These commands will create
locales/ru.po respectively. PO file format is a standard text format for translations introduced in GNU (Linux) and used in many places. it’s kind of a de facto standard for translations, so it’s supported by most of the tools. But since it’s a text format, you can open it in any text editor, it’s very readable. Inside, you’ll see some headers (feel free to edit them) and the text message to be translated extracted from your code. Now, it’s time to translate.
At this point, we have a file to store translations. The file contains the messages from our code (currently, only
"Hello, world!") but no actual translation. Usually, this is where you send the file to a translator and forget about it while they are doing their job. But if you don’t have a translator just yet, don’t worry, we’ve got you covered.
English you can translate yourself, just edi
en.po file in any text editor:
#: ./l10n_playground/example.py:9 msgid "Hello, world!" msgstr "Hello, world!"
And for Russian, we’ll use
l10n translate command which will automatically translate all messages using Google Translate unofficial API:
python3 -m l10n translate
And this is what you now sohuld see in
#: ./l10n_playground/example.py:9 #, fuzzy msgid "Hello, world!" msgstr "Привет, мир!"
Now, it’s time to get it back into your app.
PO file format is a readable text format for humans. For machines, we need mo files which are binary and optimized for machines. The po files are used by you and translators during the app development, mo files are used by your app (or library) to get translations for messages in runtime. So, before we can run the app, we need to compile mo files:
python3 -m l10n compile
The command will create
ru.mo binary files inside of
Running the app¶
Now, it’s time to run the app!
$ python3 -m l10n_playground Hello, world! $ python3 -m l10n_playground --lang=ru Привет, мир!
When you update a message or add a new one:
l10n extractto update po files. No need to specify
--lang, by default it will just update all po files you already have.
l10n translateif you want it to be auto-translated (or translate it manually).
l10n compileto generate mo files.
po file is a text file with translations.
mo file is a binary file compiled out of mo files.
l10n.Localesis a mapping of locales (mo files) with language codes as keys.
l10n extract --lang=nlto generate po file for
l10n extractto update all po files you have.
l10n translateto run Google Translate on all po files you have.
l10n compileto generate mo files from po files.
See also the advanced usage to get more out of l10n.