Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load translations (i18next) with requireJS and precompiled Handlebars templates

I have a requireJS setup with precompiled Handlebars templates and my translations i18next in a database. I need to do a few things in the following order:

  1. Load my translations from a database
  2. Register a helper in Handlebars so my values in my precompiled templates can be translated

My requireJS config file looks like this:

require.config({
  deps: ["main"],   
  paths: {
    'handlebars.runtime': "../assets/js/libs/handlebars.runtime.amd.min",
    i18n: "../assets/js/libs/i18next-1.7.1",

    // Shim Plugin
    use: "../assets/js/plugins/use"
  },

  use: {
    i18n: {
      attach: "i18n"
    }
  } 
});

My main.js file looks like this, which requires namespace.js:

require([
  'namespace',
  'modules/Transport'
], function (
  namespace,
  $,
  Transport
) {
  var Router = Backbone.Router.extend({
    routes: {
      '*any':                'any'
    },

My namespace.js will try to register the Handlebars helper and initialize i18next with the translations:

define([
  "handlebars.runtime",
  "use!i18n"
], function(
  Handlebars,
  i18n
) {   
  var defaultLanguage = 'en';
  var translations;
  $.when(
      $.getJSON('/api/translations', function (result) {
        translations = result;
      })
  ).then(function () {

    i18n.init({ useCookie: false, resStore: translations, lng: defaultLanguage });

    Handlebars.default.registerHelper('t', function(i18n_key) {
      var result = i18n.t(i18n_key);

      return new Handlebars.default.SafeString(result);
    });
  });

My modules/Transport.js module, will depend on namespace.js and will load the precompiled template. When loading the precompiled template, it becomes available in Handlebars.default.templates. SO my module looks like this:

define([
  'namespace',
  'templates/compiled/transport_template'
], function(
  namespace,
) {
  i18n.t('translate something');
  var template = Handlebars.default.templates['transport_template'];

The problem I have is that I can't make requireJS to first load the translations and after that proceed with registering the helper and do some translations in my modules. The modules and templates get loaded before the async call to my database is completed, so I get errors all the time that stuff isn't loaded (or the helper, or i18next module)

I am really confused here, how can I setup requireJS to load Handlebars and i18next first, before loading my modules?

like image 477
Daan Avatar asked Oct 03 '22 06:10

Daan


1 Answers

A couple of notes:

  • RequireJS provides a built-in shim configuration property, you might not need use.js.
  • i18next provides an AMD module version (available on their homepage), so you don't need to shim it. Not sure they follow best practices, but it works.
  • Never use global objects in an AMD module, always require the objects as dependencies explicitly.
  • With all of that in mind, you're already almost where you need to be; you simply require('i18next') whenever you need to use its t function in another module. A remaining issue is the question of how to ensure that i18next is properly initialized, for which I provide an answer below.
  • I'm not familiar with Handlebars and particularly its helpers functionality, so this might just be an answer to half of your problems.

I had to solve essentially the same issue with i18next a while ago; I basically wrapped its module in another module that initializes i18next before returning it.

i18next-wrapper.js

define(function (require) {
    var i18next = require('i18next-actual');
    i18next.init(...);
    return i18next;
});

For added transparency, you can play with paths:

requirejs.config({
    paths: {
        'i18next-actual': 'path/to/i18next.amd-$version',
        'i18next': 'path/to/i18next-wrapper',
    }
});

That way all user modules that require('i18next') as usual will be sure that it's initialized once, and the actual i18next module doesn't have to be modified.

So that hopefully answers your question of how to load and initialize i18next first before the modules that require it. You should be able to do the same with any other module.

If you can use this, somebody else might be able to help with the Handlebars specifics, or maybe you can take it from here.

Edit: I forgot about the async nature of i18next. I remember wrapping the t function to check for an initialized flag to ensure that i18next was indeed fully loaded (including translations). While this worked, I ultimately figured that the added complexity wasn't immediately worth it and decided to use the getAsync: false i18next option instead. You might disagree.

like image 102
tne Avatar answered Oct 10 '22 02:10

tne