Skip to content

Instantly share code, notes, and snippets.

@mashpie
Last active June 18, 2021 02:46
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save mashpie/5246334 to your computer and use it in GitHub Desktop.
Save mashpie/5246334 to your computer and use it in GitHub Desktop.
express + i18n-node + handlebars and avoid concurrency issues
// require modules
var express = require('express'),
i18n = require('i18n'),
hbs = require('hbs'),
app = module.exports = express();
i18n.configure({
locales: ['en', 'fr'],
cookie: 'locale',
directory: "" + __dirname + "/locales"
});
app.configure(function () {
// setup hbs
app.set('views', "" + __dirname + "/views");
app.set('view engine', 'hbs');
app.engine('hbs', hbs.__express);
// you'll need cookies
app.use(express.cookieParser());
// init i18n module for this loop
app.use(i18n.init);
});
// register hbs helpers in res.locals' context which provides this.locale
hbs.registerHelper('__', function () {
return i18n.__.apply(this, arguments);
});
hbs.registerHelper('__n', function () {
return i18n.__n.apply(this, arguments);
});
// serving homepage
app.get('/', function (req, res) {
res.render('index');
});
// startup
app.listen(3000);
<span id="text">{{{__ "text to test"}}}</span>
<br>
<span id="onecat">{{{__n "%d cat" "%d cats" 1}}}</span>
<br>
<span id="twocats">{{{__n "%d cat" "%d cats" 2}}}</span>
@ehynds
Copy link

ehynds commented Aug 20, 2015

How are those two Handlebars helpers defined in the res.local context? They're outside of any middleware.

@DJviolin
Copy link

@mashpie Unfortunately my implementation following this tutorial doesn't work. Default Hungarian translation works, but any content from /locales/en.json file never used:

app.js:

var express        = require('express'),
    bodyParser     = require('body-parser'),
    cookieParser   = require('cookie-parser'),
    exphbs         = require('express-handlebars'),
    i18n           = require('i18n');
var app = express();

app.engine('.hbs', exphbs({
  extname: '.hbs',
  defaultLayout: 'main',
  helpers: {
    __: function() { return i18n.__.apply(this, arguments); },
    __n: function() { return i18n.__n.apply(this, arguments); }
  }
}));
app.set('view engine', '.hbs');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());

i18n.configure({
  locales: ['hu', 'en'],
  fallbacks: {'en': 'hu'},
  defaultLocale: 'hu',
  cookie: 'locale',
  queryParameter: 'lang',
  directory: __dirname + '/locales',
  directoryPermissions: '755',
  autoReload: true,
  updateFiles: true,
  api: {
    '__': '__',  //now req.__ becomes req.__
    '__n': '__n' //and req.__n can be called as req.__n
  }
});
app.use(i18n.init);

app.use('/', require('./routes/portfolio'));

views/portfolio.hbs:

<span id="text">{{{__ "text to test"}}}</span>

locales/hu.json:

{
    "text to test": "text to test on hungarian"
}

locales/en.json:

{
    "text to test": "text to test on english"
}

Edit:

Solved with placing i18n's code in front of the routers and I activating the language switch simply with cookies:

// http://127.0.0.1:3000/hu
app.get('/hu', function (req, res) {
  res.cookie('locale', 'hu', { maxAge: 900000, httpOnly: true });
  res.redirect('back');
});
// http://127.0.0.1:3000/en
app.get('/en', function (req, res) {
  res.cookie('locale', 'en', { maxAge: 900000, httpOnly: true });
  res.redirect('back');
});

@DJviolin
Copy link

@mashpie This way my translation called only with cookies, no way to know from the urls which language is used. domain.com/about-me url can be both hungarian and english translated page, depends on the cookie. Is it problem for search engines, like Google?

@eloquence
Copy link

eloquence commented Jun 16, 2016

I had to move the helpers into a middleware, or all messages resolved in the context of {{#each}} loops were output in the default locale, with the debug message, "WARN: No locale found - check the context of the call to __(). Using en as current locale" (the value of this being set to the message key inside the loop). Please let me know if there's a more elegant way to do this:

app.use(function(req, res, next) {
  hbs.registerHelper('__', function() {
    return i18n.__.apply(req, arguments);
  });
  hbs.registerHelper('__n', function() {
    return i18n.__n.apply(req, arguments);
  });
  next();
});

UPDATE: Don't do what I did -- re-registering with hbs on each request creates concurrency issues. Indeed, doing this inside of middleware is not necessary at all, as hbs has access to the current locale through the options parameter that gets passed to each helper. The solution I ultimately went with can be found here (see the __ and __n helpers in particular):
https://github.com/eloquence/lib.reviews/blob/master/util/handlebars-helpers.js

@ekkis
Copy link

ekkis commented Oct 14, 2016

I'm confused. the title of this document reads "express + i18n-node + handlebars", referring to i18n-node, as opposed to i18n which is what is shown as required. which module does this apply to? because both exist. at first I installed i18n-node and then thought that i18n was required so I uninstalled the former and installed the latter. in neither case can I make this work. which is the correct module?

@JailBreakC
Copy link

@eloquence thanks a lot! your method is fucking working! solved my problem.

@bajajDeeksha
Copy link

hello, How is it reading the json files, Mine is going to the views/**.hbs but it is not reading the json file properly. need help

@felquis
Copy link

felquis commented Jun 13, 2020

Thank you, this piece helped me 👍 💯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment