Handlebars multi language with i18n

2 months ago

Ref:

install

npm i i18n

i18n_lib.js

const i18n = require("i18n");

i18n.configure({
  locales: ["vi", "en"],
  directory: __dirname + "/locales",
  defaultLocale: 'en',
  cookie: 'lang' // important.
});

module.exports = function(req, res, next) {
  i18n.init(req, res);
  i18n.setLocale('vi');
  return next();
};

app.js

const i18n = require('./lib/i18n_lib');
...
app.use(i18n);

Restart app will auto create directory /locales and 2 json files

Now edit the json file

// en.json
{
  "share": "share"
}
// vn.json
{
  "share": "chia se"
}

In your handlebars template

<span class="app-bar-text">{{i18n "share"}}</span>

Register handlebar helper

compile.js

const Handlebars = require("handlebars");
const i18n = require('i18n');

Handlebars.registerHelper("i18n", function (str) {
  return i18n != undefined ? i18n.__(str) : str;
});

Run: it will render: share will become "chia se"

Debug

"start": "DEBUG=i18n:* nodemon ./src/bin/www",
---- result
 i18n:debug will use /home/cotcac/work/ME/devwalls/src/lib/locales/vi.json +0ms
  i18n:debug read /home/cotcac/work/ME/devwalls/src/lib/locales/vi.json for locale: vi +2ms
  i18n:debug will use /home/cotcac/work/ME/devwalls/src/lib/locales/en.json +0ms
  i18n:debug read /home/cotcac/work/ME/devwalls/src/lib/locales/en.json for locale: en +0ms

Allow user to choose language.

Backend

Example you have 2 users. one user en one use vi. So your system can't use global fix data for locales.

The locales have to be depend on each user request. So it will depend on req object.

We have to pass the locales variable in every res object

example:

res.write(
  compile.loadView(__dirname + "/nav.handlebars", {
    user: req.user || null,
    locales: req.getLocale(), // your magic
    current: req.url,
    menu,
    base_url: req.app.locals.base_url,
  })
);

 

Then In handlebars helpers You can get this locales variable from this object

Handlebars.registerHelper("i18n", function (str) {
  return i18n != undefined ? i18n.__({ phrase: str, locale: this.locales }) : str;
});

this object data

[this] { 
  user: null,
  locales: 'en',
  current: '/',
  menu: false,
  base_url: 'http://localhost:3000' 
}

Now you have a way to deal with multiple users with different languages.

Front-end.

For front-end only give them link to change language:

http://localhost:3000/change-language?lang=en
http://localhost:3000/change-language?lang=vi

change-language.js

module.exports = function (req, res, next) {
  const lang = req.query.lang;
  if (!lang) {
    return next(); // or send 200
  }
  res.cookie("lang", lang, { maxAge: 2592000000 }); // Expires in one month
  req.setLocale(lang);
  return next(); // or send 200
};

Test

app.all("*", (req, res, next) => {
  const lang = req.cookies.lang;
  console.log("get cookie lang: ", lang);
  console.log('[language]', req.headers["accept-language"]);
  console.log('locals i18n: ', i18n.getLocale());
  console.log('loals req:', req.getLocale());
  console.log("login: ", req.__("login"));
  next();
});