LeJared
LeJared

Reputation: 2052

Good practice for internationalization in RactiveJS (using RequireJS)

I've build a web application based on RactiveJS. It uses RequireJS to handle modules and dependencies. Ractive Templates are loaded with rv.

Now I'd like to support multiple languages (initially only german and english).

So my question is:

What is a good practice to achieve internationalization in RactiveJS?

So far I see two options:

1. Strip all text from templates, put them in a resource file and pass em through a lookup-method.
Downsides:
Either brakes text apart leaving meaningless chunks or breaks data binding in cases like <div>The Item {{name}} is used <b>{{count}}</b> times.</div>

2. Duplicate Templates
Downsides:
• duplicated template-code
• Could not figure out how to build separate language packages for the templates with RequireJS-Optimizer

3. ???
Any other ideas?

BTW: It is ok, to reload the entire app when the language changes. I'd prefer a solution where only resource for currently selected language will be loaded through RequireJS.

Upvotes: 2

Views: 484

Answers (2)

mknecht
mknecht

Reputation: 1255

To address the problem of 1 + 2, namely to keep your templates meaningful while not duplicating all templates, consider providing the text of your primary language (let's say English) as default to the translation function.

Based on the discussion in this Ractive issue, and demonstrated by kurdin in this fiddle, your template might look like so:

<p>{{{ t('Hello <strong>{{name}}</strong> and have a nice day!', 'hello', {name: user.name}) }}}</p>

Then, you'd define a translation function t that handles the default specially.

Translations = []   // load those however you want
Translations["ru"] = {
       localization: 'пример локализации',
       hello: "Привет <strong>{{name}}</strong> хорошего тебе дня!",
       language: 'Язык: {{lang}} ',
       lang: 'Русский'
}

I18n.translations = Translations;
I18n.defaultLocale = "en";

function t(defaultText, name, context) {
    return (I18n.locale === I18n.defaultLocale) ? defaultText : I18n.t(name, context);
}

The downsides are:

  1. Inconsistency. The default language is hard-coded in the templates, i.e. you'd have a different place for the default language than for the language packs.
  2. Overhead: you might load data for two languages, because the default would always be loaded in the templates.

To address the first problem, i.e. consistency, you might want to provide a language pack for English, too, and skip the custom t function.

Upvotes: 2

Joseph
Joseph

Reputation: 119847

There is no canonical way to do this. However, you could get away with it by using decorators. Wrap text in a <span> and add a decorator to the <span>. What the decorator does is fetch the <span>'s contents, get the translation from a prepared mapping, and replace that text.

<div>
  <span decorator="i18n">Some random text to translate</span>
</div>

To generate translation files, you can have a grunt/gulp/whatever tool task that runs through all your component files, parse the HTML part of it and look for elements with the decorator="i18n". You then generate your JSON template off of it, and you manually fill in the translations.

{
  "Some random text to translate": "Alcuni testo casuale da tradurre",
  ...
}

You can then have some global indicator of some sort to signal the decorator module what translation mappings to load in. Since translation can be changed any time, it can't be loaded via RequireJS statically. The mappings will need to be AJAX'ed down or done via a dynamic require call. Either way, translations will not be included by the optimizer.

Additionally, there's a bit of overhead in rendering, as every component that's rendered with that decorator will change the text on the fly.

Upvotes: 2

Related Questions