rink.attendant.6
rink.attendant.6

Reputation: 46257

Internationalization with Handlebars

I'm trying to internationalize my application that uses Express and Handlebars. Is it possible to get Handlebars partials (fragments) to load and render the localization resource file?

Noting that I've already read this question: express3-handlebars and 18next-node - internationalisation based on page?.

Here is my directory structure:

views/
   index.html
   login.html
   fragments/
       frag1.html
       frag2.html
       frag3.html
locales/
   index.json
   login.json
   fragments/
       frag1.json
       frag2.json
       frag3.json

If necessary, I can separate the JSON files in the locales/ directory to be something like this:

locales/
   en-CA/
       index.json
       ...other files
   fr-CA/
       index.json
       ...other files

Here is the relevant code in my server.js file:

// ...
hbs = exphbs.create({
    extname: '.html',
    layoutsDir: [
        __dirname + '/views'
    ],
    partialsDir: [
        __dirname + '/views/fragments'
    ],
    helpers: {
        'json': function(context) {
            return JSON.stringify(context);
        },
        't': function(k) {
            // ?
        }
    }
});
app.engine('.html', hbs.engine);
app.set('view engine', 'html');

The t helper is what I need help with. In my templates/template fragments, I have these:

<h1>{{ t 'pageTitle' }}</h1>
<p>{{ t 'foo' }}</p>
<p>{{ t 'moreThings' }}</p>

And my JSON file could look like this:

{
    "pageTitle": "Hello world",
    "foo": "Paragraph contents here",
    "moreThings": "There are %d things"
}

Also how do I deal with the printf parameters?

Upvotes: 1

Views: 2686

Answers (1)

harco gijsbers
harco gijsbers

Reputation: 461

Doing internationalization in your application means doing two things:

1) Determine which locale should be used

Depending on how you determine the used locale it can be difficult to do this inside a helper. Helpers do not have access to the request object for instance. To be honest i cannot think of a good way to do this inside a helper.

Personally i use the i18n-abide middle-ware to do internationalization. They have several options to determine the locale for a given request. Once locale is determined it is added as a property to the request object. So you only need to determine the locale once for each request. An other advantage is that you have also access to the locale outside the handlebars helper.

2) Access the resource files

To access the resource files from within a helper means that you should read and parse the resource files outside the helper. Parsing resource files every time you need to translate a string really hurts performance.

Here you also should use middle-ware. You can do something like the pseudo code below.

function setup() {
  // Load resource files from disk and parse them.
  var resources = { /* parsed resources*/ }

  return function(req, res, next) {
    var locale = determineLocalFunction(req);
    req.getText = function(label) {
      return resources[local][label];
    }
  }
}

Now you can use the req.getText function every where in your code. Personally i never use language labels inside a partial. Instead i pass all the language strings needed in a partial using a data object. The reason behind this is that i think partials should be as re-usable as possible. Using hardcoded language labels inside them makes them less re-useable.

When you do want to use the getText function in your partials you can pass to getText function to your partial.

Something like this:

var objectPassedToPartial = {
  getText: req.getText
}

Use it like:

{{getText 'label'}}

Read more about Mozilla's i18n-abide solution, i really love it.

Upvotes: 1

Related Questions