
Phrase and beyond
Software localization
Pyramid is a lightweight web framework that goes in massively for flexibility and automation. One of its main advantages is that it does not force you to use specific modules. Instead, it bundles with several options for you to choose from.
Although having extra options allows for greater flexibility, maintaining and documenting them is a lot harder now. The documentation can be a nightmare if you are a new user without any prior experience.
As Pyramid's internationalization and localization docs cover only the basics – leaving out, for example, how to implement i18n if you are using a template engine – new users can get lost easily. This Pyramid I18n tutorial seeks to fill that gap.
The internationalization process in Pyramid mainly relies on translation strings (Unicode objects containing extra information related to translation). Instead of creating each of them individually in your Pyramid application, you should use GNU gettext – part of the Pyramid translation services. For your information, GNU gettext makes use of the following files internally:
The translation workflow usually goes as follows:
At the time of writing, Pyramid comes with support for three different template engines:
This tutorial will walk you through the process of internationalizing your Pyramid application easily using Jinja2. Let's get it started!
Make sure you have created a new virtual environment. As soon as you activate it, and install the following modules:
pip install pyramid pip install pyramid_jinja pip install Babel
Next, run the following command to create a new Jinja2-based Pyramid project. It will generate all the required files and folders for you. I am using myproject as the root folder for this tutorial. Modify it accordingly.
pcreate -s pyramid_jinja2_starter myproject
Once you are done with it, change your working directory to the root folder of your new project.
cd myproject
By default, it will include its own scaffold to set up your development files. Continue the setup by running either
python setup.py develop
or
pip install -e .
It will install additional packages for serving your application automatically, based on the configuration in setup.py.
Next, run the following command to start your web application.
pserve development.ini
Head over to the following URL to access your Pyramid application.
http://localhost:6543/
You should see the following user interface.
Find the Jinaj2 template file at the following directory myproject/myproject/templates/mytemplate.jinja2. Open it up and you should notice that 'Welcome to' is marked as translatable based on Jinja2 i18n syntax.
{% trans %}Welcome to{% endtrans %}
Let us add a new paragraph below it. I am going to use the following code:
<p>{% trans %}Phrase is the place where localization teams come together to release translations in the most streamlined way possible.{% endtrans %}</p>
Save the file once you are happy with it, and go back to your terminal. Run the following command to extract translatable strings from your Jinja2 template.
python setup.py extract_messages
It will write PO template file to your project. If your project is named myproject, you can find the file at myproject/myproject/locale/myproject.pot.
The next step is to initialize the message catalog files, based on the language supported by your application. Let us say our application is going to support the following languages:
Run the following command to complete the initialization:
python setup.py init_catalog -l es python setup.py init_catalog -l fr python setup.py init_catalog -l de
It will create new folders using the language code that you have specified inside the locale folder. Each folder contains a subfolder called LC_MESSAGES, containing the following files:
The initialization process is meant to be done only once. Subsequently, you have to run the update catalog command instead after each message extraction as follows:
python setup.py extract_messages python setup.py update_catalog
Let us have a look at the PO files and start editing them. It is highly recommended to use a specialized tool, a translation management tool like Phrase, to streamline the translation process. All the translatable strings in your Jinja2 template will be converted into msgid and msgstr pairs. The latter, by default, is an empty string, where you can fill in your translations. Here is an example for the German language.
#: myproject/templates/mytemplate.jinja2:41 msgid "Welcome to" msgstr "Willkommen zum" #: myproject/templates/mytemplate.jinja2:42 msgid "" "Phrase is the place where localization teams come together to release " "translations in the most streamlined way possible." msgstr "" "Phrase ist der Ort, an dem Lokalisierungsteams zusammenkommen, um ihre " "Übersetzungen einfacher denn je zu releasen."
Once you have completed the translation, run the following command to re-compile the PO files into MO files.
python setup.py compile_catalog
If you happen to see the following output
catalog myproject/locale/de/LC_MESSAGES/myproject.po is marked as fuzzy, skipping
it means that you should review your PO files and check for items or comments marked with the fuzzy keyword. It is simply a call for revision by the translators. A translator may mark a specific translation as 'fuzzy' as a reminder that the translation needs to be revisited later on and before compiling to an MO file. You can simply remove the keyword or force re-compile using an -f flag.
python setup.py compile_catalog -f
It will generate its corresponding MO files in the same folder.
Start your application normally with
pserve development.ini
and open the following URL in your browser:
http://localhost:6543/
For German translation, head over to the following URL
http://localhost:6543/?_LOCALE_=de
Jinja2 i18n has built-in support for pluralization via the following syntax:
{% trans %} {{ day }} day left. {% pluralize %} {{ day}} days left. {% endtrans %}
Add the code above to your mytemplate.jinja2 file. We are going to pass a variable called day to Jinja2, and it will localize it for us based on the value. To do so, open up myproject/myproject/views.py and modify the function inside it as follows:
def my_view(request): return {'project': 'myproject', 'day': 14}
Run the following commands to update your message catalogs:
python setup.py extract_messages python setup.py update_catalog
Next, update your PO files with your translations. The pluralized translatable string will have the following syntax instead:
You should place your singular translation in msgstr[0] while plural translation should be under msgstr[1]. Have a look at the following example for the fr locale.
#: myproject/templates/mytemplate.jinja2:44 #, python-format msgid "" "\n" " %(day)s day left.\n" " " msgid_plural "" "\n" " %(day)s days left.\n" " " msgstr[0] "%(day)s jour restant." msgstr[1] "%(day)s jours restant."
Lastly, all you need to do is to compile your message catalogs.
python setup.py compile_catalog -f
Restart your application and you should see the following user interface for the fr locale.
You can easily obtain the active locale associated with a request by using the built-in pyramid.request.Request.locale_name(). Modify the function inside myproject/myproject/views.py as follows:
def my_view(request): locale_name = request.locale_name return {'project': 'myproject', 'day': 14}
When you print the locale_name variable, you should get the corresponding en, de, or fr locale.
It is by design that any Pyramid application must always know which language should be translatable, regardless of the translation files present in the disk. The reasons behind it are as follows:
The best solution is to pre-define the supported language in your development.ini file. Add a new entry to app:main. We define the key as available_languages and each supported locale is separated by whitespace.
[app:main] use = egg:myproject available_languages = fr de en # ... rest of the settings
Then, head over to myproject/myproject/views.py and add a new import at the top of the file.
from pyramid.settings import aslist
Modify your my_view functions as follows:
def my_view(request): languages = aslist(request.registry.settings['available_languages']) return {'project': 'myproject', 'day': 14}
When you print the language variables, you should get a list.
['fr', 'de', 'en']
By default, the fallback locale is en. You can easily configure it in the development.ini file. Simply modify default_locale_name setting to your desired locale.
[app:main] use = egg:myproject # ... rest of the settings pyramid.default_locale_name = en
The easiest method for number formatting is via the Babel module as Pyramid itself does not come with support for number formatting based on different locales. Add the following import at the top of your views.py file.
from babel.core import Locale from babel.numbers import format_decimal
Modify the content of your my_view function.
def my_view(request): # get request locale locale_name = request.locale_name # construct a new Babel Locale object based on request locale locale = Locale(locale_name) # format number by using format_decimal function result = format_decimal(1.2345, locale=locale) return {'project': 'myproject', 'day': 14}
Likewise, you can use Babel to a specific datetime format, based on the current locale. Add the following import statement:
import datetime from babel.dates import format_date
Call format_date function to format the date.
def my_view(request): # get request locale locale_name = request.locale_name # construct a new Babel Locale object based on request locale locale = Locale(locale_name) # format date by using format_date function result = format_date(datetime.datetime.now(), locale=locale) return {'project': 'myproject', 'day': 14}
To sum things up, you can easily internationalize your Pyramid application using Jinja2 Pyramid i18n. It comes with built-in templates that generate locale files automatically for you.
If you want to speed up your i18n process, give Phrase a try. The leanest, most reliable translation management platform on the market comes with a 14-day free trial and will equip you with everything you need to:
Last but not least, make sure you check out the following articles if you want to know even more about internationalization and localization in Python:
Last updated on October 20, 2022.