From the translator: This is the ninth article in the Node.js series from the Mozilla Identity team that deals with the Persona project.
All articles of the cycle:

')
Did you know that Mozilla products and services are localized for 90 languages? Localization includes:
- text translated into regional variations and dialects of languages;
- rendering pages, taking into account writing from right to left;
- bulletproof design that can accommodate texts of any length;
- headers, tags and text on buttons, selected for local audiences.
In this series of three localization articles, I’ll talk about the technical aspects of creating a multilingual Node.js application.
We will use the generally accepted terms "internationalization" (i18n) and "localization" (l10n). Internationalization is a set of techniques that allow subsequent localization, that is, the actual translation and adaptation for a particular language.
Mozilla Persona is a Node.js web service localized for many locales. Our team had very specific requests, and therefore we did not use existing libraries for localization.
Our goals
We created several modules to achieve the following goals:
- good integration into the existing community of Mozilla localizers and translators;
- work all the tools on pure javascript.
Here are the modules we wrote:
- i18n-abide
- jsxgettext
- po2json.js
- gobbledygook
i18n-abide is the main module used to include translations in the app. Let's first deal with it. In the examples, we assume that the above application uses Express and EJS templates.
Installation and preparation
i18n-abide installs via npm:
npm install i18n-abide
The following lines should be included in the application code:
var i18n = require('i18n-abide'); app.use(i18n.abide({ supported_languages: ['en-US', 'de', 'es', 'zh-TW'], default_lang: 'en-US', translation_directory: 'static/i18n' }));
We will look at the module configuration parameters in the third article of the localization series. i18n-abide provides several features that are needed for translation. They are available in the
req
object when processing the request and in templates.
The next step is to go through the application code and find all the places where any text is shown to the user. Here is what the template might look like after this:
<title></title>
The main function that abide adds is
gettext()
. In addition, the variables
lang
and
lang_dir
.
lang
is a language code based on browser settings and user preferences;lang_dir
- text direction, from left to right ( ltr
) or right to left ( rtl
);gettext
is a function that takes an English string and returns a translation.
It is worth noting that speaking of strings, we mean
gettext
strings, not JavaScript strings. These are any fragments of text - inscriptions, paragraphs, interface elements that will be visible to the user. If some line in the code is not intended for the user's eyes, then from the point of view of
gettext
it does not exist. In other words,
gettext
strings are any strings that need translation.
Here is an example of using
gettext
in a javascript file:
app.get('/', function(req, res) { res.render('homepage.ejs', { title: req.gettext('Hello, World!') }); });
In order to prepare the application for localization, it is necessary to wrap all the lines in
gettext
calls.
Language definition
How do we know which language the user prefers? Its definition is handled by middleware at runtime.
The i18n-abide module looks at the HTTP request
Accept-language
field. It lists the languages that the user understands, in order of preference.
abide compares this value with the list of supported languages, selects the best match and gives the client a localized version of the page. If no matching translation is found, phrases are directly inserted from
gettext
calls in the code and templates.
Conclusion
In the second article, we will look at exactly how strings are retrieved, processed, and translated.
In the third - we will deal in more detail with technical details and configuration parameters and test the result of localization.
All articles of the cycle: