Internationalization with ES2015 template literals

When ED2015 came out with the template strings (as that was its name there) i cheered the old Great Lisp Macros would be on the way to go back but it was overestimating. Nevertheless i quickly found a good real use of the literals besides just being handy.

The idea is to have in React:

<div>{T`Hello World,`}</div>

and to see in a browser:

Привет, Мир

The dirty, quick, but still long living implementation is as easy as to have a JSON object with the translations like this:

src/l10n/translations.json

{
“en”:{
“No balance”: “Balance is not updated yet”,
“No web3”: “Not connected to Ethereum”
},
“ru”:{
“No balance”: “Баланс неизвестен”,
“No web3”: “Нет подключения к Ethereum”
}
}

and a module implementing T``

src/l10n/index.js

// default en
let locale = ‘en’;
// this might be useful, so let's export
export const currentLocale = () => locale;
// messages in the language of the current locale
let messages = {};
let dictionary = {};
// Lookup helper to simplify the monster function be
const translate = key => messages[key] ? messages[key] : key;
// The work horse. It does all we work with the literal 
// template translating and will be exported as a default
const l10n = (strings, …values) => {
const r = strings.reduce((prevString, nextString, index) => prevString + (index > 0 ? values[index — 1] : ‘’) + nextString, ‘’);
return translate(r);
};
export default l10n;
// Let's try to be clever an build `messages` lookup
// table combining the available languages with the fallbacks
// to default
const init = (lc, json) => {
// Uniform the locale abbreveation
locale = lc.split(‘-’);
const [mainlocale] = locale;
locale = locale[1] ? `${locale[0]}-${locale[1].toUpperCase()}` : lc;
// Create `messages` dictionary from the corresponding
// langauge
const locales = Object.keys(json);
if (locales.length > 0) {
messages = Object.assign({}, json[locales[0]]);
if (mainlocale !== locale) {
if (json[mainlocale]) {
messages = Object.assign(messages, json[mainlocale]);
}
}
if (json[locale]) {
messages = Object.assign(messages, json[locale]);
}
}
 // also save json object in the dictionary
// just it case we would nee other be current localization

dictionary = json;
dictionary[lc] = messages;
};
 // export `just in case` function to get message
// a message in non-default language
export const byLang = (lc, key) => dictionary[lc][key] ? dictionary[lc][key] : `L10N:${key}[${lc}]:L10N`;
 // Finally read the translation from json and init
import translations from ‘./translations.json’;
init(navigator.language, translations);

What’s the reason to invent a wheel instead of picking up one of the miriads others l18n modules? The main reason is “miriads”. Really it is much faster to get this small module done than to waste hours if not days trying to choose “the best” from the npm pile. Another reasons are “flexibility” and “risk free” — once we in S4Y needed to have few versions of wordings in the same language and it cost us nothing to change the getting “locale” from http header to mobx observable and we are sure we will adopt this homebrew solution in countable minutes/hours in case of the unexpected requirements.

And besides all it is pretty.