As a follow-up to my post from last week on a strategy for i18n and Node.js I’ve published my module for handling internationalization in Node and, specifically, Express.js.
The module is now available on NPM and can be installed by running:
npm install i18n-2
The code and documentation for the module is available on Github here:
I’ve designed the API to work as simply as possible with Express.js – while still supporting a simple Node.js usage or usage with another web framework.
The module includes a number of features built-in:
- Automatic integration with Express as middleware and integration with templating solutions.
- Support for handling the setting of locale based upon query strings and/or subdomain.
- Dynamic reading and updating of translation files during development mode.
- Automatic caching of translation files in production mode.
Usage is simple, in your Express app.js:
// load modules var express = require('express'), I18n = require('i18n-2'); // Express Configuration app.configure(function() { // ... // Attach the i18n property to the express request object // And attach helper methods for use in templates I18n.expressBind(app, { // setup some locales - other locales default to en silently locales: ['en', 'de'] })); // Set up the rest of the Express middleware app.use(app.router); app.use(express.static(__dirname + '/public')); });
Inside your Express view:
module.exports = { index: function(req, res) { req.render("index", { title: req.i18n.__("My Site Title"), desc: req.i18n.__("My Site Description") }); } };
Inside your templates (this example uses the Swig templating system).
{% extends "page.swig" %} {% block content %} <h1>{{ __("Welcome to: %s", title) }}</h1> <p>{{ desc }}</p> {% endblock %}
My module is based off of the i18n-node module by Marcus Spiegel.
I used the original i18n-node module as a basis for further work as it provided the closest solution to full Express.js integration that I needed. After some initial usage though I saw that it was very much lacking. As I described in my previous post there is potential for severe problems to occur in an asynchronous environment like Node.js.
The problem that I was having was rather severe and, unfortunately, required a complete rewrite of the module. The original i18n-node module stored the locale information in a closure. Calling setLocale
changed the locale but did so for every instance that was currently active, resulting in templates being rendered in the wrong language.
Here is the issue that is especially surprising (but not so much once you know how modules work in Node.js): Since the state was stored in a closure repeated calls to require('i18n')
resulted in the current locale state being held the entire time, not reset. This meant that when a new request came in, the locale was set, changing the locale for all other active requests.
The only way to fix this particular problem is to simply encapsulate all state into a single object — I did this by making an object that you instantiate: new I18n(options)
.
I looked at other JavaScript translation solutions, including the excellent Jed and messageformat and while their translation capabilities are top-notch what they provided was overkill for my particular application and did not provide the Express.js integration that I needed.
I hope others will find this module to be useful, especially when used in conjunction with my strategy for i18n and Node.js.
Dan Wolff (January 15, 2013 at 3:29 pm)
I believe that your API is over-simplified wrt to plural. In particular:
i18n.__n('%s cat', '%s cats', 3)
would not work for e.g. Latvian which has 3 plural forms (0; 1 11 21 31…; others). Check out https://developer.mozilla.org/en-US/docs/Localization_and_PluralsFurther more it’s quite inflexible if it only supports pluralization of one term in a sentence (or the same pluralization of all terms).
Gerard Braad (January 16, 2013 at 6:04 am)
@Dan maybe not finished… but much appreciated; Javascript lacks good Internalization options and this is a good start. File an issue with some examples https://github.com/jeresig/i18n-node-2/issues Latvian is not the only language, but examples would help to come up with a good solution.
Gerard Braad (January 16, 2013 at 6:13 am)
Meant “Express.js lacks”. Messageformat.js deals well with gender and pluralization rules. But for express.js integration this is a good start.
Scott Christopher (January 16, 2013 at 6:19 am)
Why not contribute to messageformat.js, which handles plural and gender contexts in translation?
https://github.com/SlexAxton/messageformat.js