Internationalization with Express, Jade and i18next-node
If you run a web application which turns out to be successful, sooner or later you will get to the point where you have to provide more than one language in the user interface.
At the moment modern web application tend to use node.js for the server side. A common stack on node.js is to use the express web application framework together with a template engine of which Jade is a very popular example.
There are many possibilities to get i18n (internationalization/localization) into these kind of projects. Jade provides its own i18n library jade-i18n. Lingua is another example for an i18n express middleware.
I decided to go with i18next and i18next-node, the project of Jan Mühlemann. It is well documented at http://i18next.com/. I chose i18next for several reasons:
support for server side translation (i18next-node) and client side localization(i18next)
support for contexts and nesting
several ways to set language ( url parameter , cookie, via javascript function)
AMD support ( seperate build for AMD usage )
automatic creation of missing resources
dynamic loading of resources from server
resource caching in browser
This post is going to cover server side usage with Jade and Express. Therefore the node-module "i18next" has to be installed:
https://gist.github.com/4594385
Or even better : include i18next into package.json and run npm install:
https://gist.github.com/4594476
https://gist.github.com/4597278
Now i18next-node can be used in the express-based node application. The i18n.init() function has to be called to initialize i18next. Options can be passed to i18n.init(options) to specify the behaviour. For example, saveMissing:true tells i18next to store missing resources in the server resource files. In express.configure i18next.handle has to be added via .use before the view engine and the router. The last step is to call i18n.registerAppHelper(app) which registers express with i18next.
https://gist.github.com/4594536
Afterwards the node application is ready to handle translations within the Jade views. But in order to produce localized output the resources for the different languages have to be specified. With i18next this os done in JSON resource files.
The way how i18next searches for the resources can be configured by the option resGetPath which has to be passed to i18n.init(). The default path for getting resources is "/locales/__lng__/__ns__.json" where "__lng__" and "__ns__" are placeholders for the language and the namespace. The current language is determined either via the URL-parameter "setLng", via Cookie or via a call to i18n.setLng(). The language identifiers are built by combining the language and the country codes, e.g. "en-US". The namespaces can also be configured during initialization as well as the default namespace. The default namespace is called "translation". Thus if no special configuration is done, i18next loads resources from "/locales/__lng__/translation.json".
For example if the current language is "de-DE" then the resources are loaded from "/locales/de-DE/translation.json". For loading the resources i18next uses a fallback mechanism which looks at the mentioned file at first. If a resource is not found then is looks into the directory named after the language but without the country code, e.g. "/locales/de/translation.json". If the resource is not found there too, the last fallback is "/locales/dev/translation.json". This mechanism can be used to specify common resources for a language in the language specific file and completely common resources in the "dev" directory:
/locales
+ /dev
- /translation.json
+ /en
- /translation.json
+ /en-US
- /translation.json
+ /de
- /translation.json
+ /de-DE
- /translation.json
+ /de-CH
- /translation.json
The resource files themselves are JSON formatted files. The resource ids can be structured by specifying properties and sub-properties:
https://gist.github.com/4596022
To access the resource "descriptiontext2" of the above example the complete resource identifier would be "app.descriptiontext2".
After the resource files are places at the right locations the localized resources can be used in the Jade-View templates. If everything is set up correctly the translations can be accessed by the function t(resource_id) within the templates:
https://gist.github.com/4596083
The very last step is to tell the node application to render the index.jade and start the server:
https://gist.github.com/4596109
The sample site has a little navigation on the left to switch between languages. As you can see this can be easily done by extending the URL by "?setLng=" plus the language identifier.
As this post shows the localization via i18next-node can be setup very easily. Server side translation within Jade-Views is usable for applications that navigate between views via loading complete pages from the server.
But most modern web applications are so called "single page" applications which only load a start page. The content, navigation and DOM-manipulation is completely done via Javascript and AJAX-xalls to the server with the help of frameworks such as Backbone.js. For this approach the server side translation within the Jade-templates is not useful. Fortunately i18next is also usable on the client side. It is able to load necessary resources dynamically from the server. I will cover this in a later post.
The source code of the example is available on GitHub.
A running demo can be found HERE.