Localize Your Node.js Service, part 1 of 3 – A Node.js holiday season, part 9

This is episode 9, out of a total 12, in the A Node.JS Holiday Season series from Mozilla’s Identity team. Now it’s time to delve into localization!

Did you know that Mozilla’s products and services are localized into into as many as 90 languages?

The following are just a few examples of localization:

  • Text translated into a regional language variations
  • A screen rendered right to left for a given language
  • Bulletproof designs that accommodate variable length prose
  • Label, heading, and button text that resonates with a locale audience

In this series of posts, I’m going to cover some technical aspects of how to localize a Node.js service.

Before we start, I’ll be using the acronyms L10n (Localization) and I18n Internationalization. I18n is the technical plumbing needed to make L10n possible.

Mozilla Persona is a Node.js based service localized into X locales. Our team has very specific goals that inhibit us from using existing Node L10n libraries.


We created these modules, to meet the following goals

  • Work well with existing Mozilla L10n community
  • Let developers work with a pure JS toolkit

The resulting toolkit contains several new Node modules:

  • i18n-abide
  • jsxgettext
  • po2json.js
  • gobbledygook

i18n-abide is the main module you’ll use to integrate translations into your own service. Let’s walk through how to add it.

In these examples, we’ll assume your code uses Express and EJS templates.


npm install i18n-abide

Preparing your codebase

In your code

var i18n = require('i18n-abide');

  supported_languages: ['en-US', 'de', 'es', 'zh-TW'],
  default_lang: 'en-US',
  translation_directory: 'static/i18n'

We will look at the configuration values in detail during the third installment of this L10n series.

The i18n abide middleware sets up request processing and injects various functions which We’ll use for translation. Below, we will see these are available in the request object and in the templating context.

Okay, you next step is to work through all of your code where you have user visible prose.

Here is an example template file:

    <%= gettext('Mozilla Persona') %>

The key thing abide does, is it injects into the Node and express framework references to the gettext function.

Abide also provides other variables and functions, such as lang, lang_dir.

  • lang is the language code based on the user’s browser and preferred language settings.
  • lang_dir is for bidirectional text support.
    It will be either ltr or rtl. The English language is rendered ltr or left to right.
  • gettext is a JS function which will take an English string and return a localize string, again based on the user’s preferred region and language.

When doing localization, we refer to strings or Gettext strings: these are pieces of prose, labels, button, etc. Any prose that is visible to the end user is a string.

Technically, we don’t mean JavaScript String, as you can have strings which are part of your program, but never shown to the user. String is overloaded to mean, stuff that must get translated.

Here is an example JavaScript file:

app.get('/', function(req, res) {
    res.render('homepage.ejs', {
        title: req.gettext('Hello, World!')

We can see that these variables and functions (like gettext) are placed in the req object.

So to setup our site for localization, we must look through all of our code and templates and wrap strings in calls to gettext.

Language Detection

How do we know what the user’s preferred language is?

At runtime, the middleware will detect the user’s preferred language.

The i18n-abide module looks at the Accept-Language HTTP header. This is sent by the browser and includes all of the user’s preferred languages with a preference order.

i18n-abide processes this value and compares it with your app’s supported_languages configuration. It will make the best match possible and serve up that language.

If a good match cannot be found, it will use the strings you’ve put into your code and templates, which are typically English strings.

Wrapping Up

In our next post, we’ll look at how strings like “Hello, World!” are extracted, translated, and prepared for use.

In the third post, we’ll look more deeply at the middleware and configuration options. We’ll also test out our localization work.

Previous articles in the series

This was part nine in a series with a total of 12 posts about Node.js. The previous ones are:

About Austin King

Seattle based non-dogmatic Artist / Programmer type human. Rogue web developer with the Apps Engineering team. Spell check is for the week.

More articles by Austin King…

About Robert Nyman [Editor emeritus]

Technical Evangelist & Editor of Mozilla Hacks. Gives talks & blogs about HTML5, JavaScript & the Open Web. Robert is a strong believer in HTML5 and the Open Web and has been working since 1999 with Front End development for the web - in Sweden and in New York City. He regularly also blogs at http://robertnyman.com and loves to travel and meet people.

More articles by Robert Nyman [Editor emeritus]…


  1. Manuel Strehl

    Very interesting post, especially since I was up to write my own jsxgettext with Esprima as parser. Another work saved…

    How is the current state of L20n? Have you thought of using this to localize Persona? If yes, why didn’t you decide to use it in the end?


    April 2nd, 2013 at 10:22

  2. Austin King

    Manuel, I’m glad you can reuse the jsxgettext parser! Please get involved with that project (https://github.com/zaach/jsxgettext).

    Yes, this code is used to run Persona. Here is an example where it is used https://github.com/mozilla/browserid/blob/dev/bin/browserid#L18

    We extracted the Persona code out into the i18n-abide module, after it was working well in production.

    I’m not up to date with L20n. My impression is that it’s early goals are for client code (like Firefox), and that we’re not using it on any web properties. I could be wrong about this.

    April 4th, 2013 at 00:53

Comments are closed for this article.