17.2.5. Internationalization and localization

Internationalization and localization of NextGIS Web is built on top of gettext and babel libraries. The workflow for working with messages is standard for projects based on gettext:

  1. Extract messages to translate from sources to .pot file (extract)

  2. Create or update .po files from .pot files (update)

  3. Compile .po files into .mo files (compile)

Each NextGIS Web component becomes an independent domain in terms of the gettext library. As a result, there is no way to internationalize messages that do not belong to any component.

To be able to extract messages for translation, they must be marked up appropriately. Below is described how to do this, as well as how to ensure the display of already translated messages.

All on messages are performed using nextgisweb-i18n command line utility. To update translation in nextgisweb_foo you can do the following:

$ nextgisweb-i18n --package nextgisweb_foo update --extract --locale ru
$ nano package/nextgisweb_foo/nextgisweb_foo/locale/ru.po
$ nextgisweb-i18n --package nextgisweb_foo compile

Server side

Python

Since python code is executed on a server, the same application instance must be able to serve users with different locales, it is necessary to use a two-step work with messages: first, a message is marked as requiring translation, then before displaying it to the user, it’s translated according to the user’s preferences.

from nextgisweb.env import gettext

@view_config(renderer='json')
def view(request):
    tr = request.localizer.translate
    return tr(gettext("Some message for translation"))

Mako

Some of the strings that require translation are also contained in Mako templates. In fact, the work of mako templates is not much different from Python. You don’t need import anything as it’s imported behind the scene. Consider the following example:

<div>
    ${tr(gettext("Another message for translation"))}
</div>

Примечание

Unfortunately, it isn’t possible use this function as a modifier ${expression | tr}. In this case, the result of the standard modifier n, that is markupsafe.Markup gets into the function.

In order to track that all strings requiring translation were translated when outputting in the template in debug mode (setting debug of the component core) a special modifier is added to the standard modifier n, which checks whether the translation was performed using request.localizer and if not, then the corresponding warning is displayed in the log.

Client side

When executing client-side code, user preferences are already known and there is no need for two-step processing. Translation and marking strings for translation are combined into one function.

Modern JavaScript and TypeScript

Here is the minimal example:

import { gettext } from "@nextgisweb/pyramid/i18n";

const msgTranslated = gettext("Some message for translation");
console.log("Localized message: " + msgTranslated);

Legacy JavaScript

define(["@nextgisweb/pyramid/i18n!"], function ({ gettext }) {
    const msgTranslated = gettext("Some message for translation");
    console.log("Localized message: " + msgTranslated);
});

As a result of loading this module, a message will be displayed, translated in exactly the same way as on the server.

Handlebars

Dijit-widgets use template-based construction, which may also require internationalization. To do this, it is possible to first pass the template through the template engine handlebars.

Listing 17.1. SomeWidget.hbs
<div data-dojo-type="${baseClass}">
    <input data-dojo-type="dijit/form/TextBox"
        data-dojo-props="placeHolder: {{gettextString 'Placeholder'}}"/>
    <button data-dojo-type="dijit/form/Button">{{gettext 'Button'}}</button>
</div>
Listing 17.2. SomeWidget.js
define([
    "dojo/_base/declare",
    "dijit/_WidgetBase",
    "dijit/_TemplatedMixin",
    "@nextgisweb/pyramid/i18n!",
    "dojo/text!./SomeWidget.hbs"
], function(declare, _WidgetBase, _TemplatedMixin, i18n, template) {
    return declare([_WidgetBase, _TemplatedMixin], {
        templateString: i18n.renderTemplate(template)
    });
});

Предупреждение

Pay attention to quotes escaping inside attribute values such as data-dojo-props and use gettextString there instead of gettext. It’ll escape quotes keeping javascript code valid.