components/localization/src/localization.js (109 lines of code) (raw):
/*eslint no-magic-numbers: ["error", { "ignore": [1, 10, 100, 0, 2, 4, 11, 20] }]*/
import fecha from 'fecha';
class I18n {
static GET_PLURALS_MAP = {
ru: n => {
if (n % 10 === 1 && n % 100 !== 11) {
return 0;
}
return (n % 10 >= 2) && (n % 10 <= 4) && (n % 100 < 10 || n % 100 >= 20)
? 1
: 2;
},
fr: n => (n > 1 ? 1 : 0),
ja: () => 0,
OTHERS: n => (n !== 1 ? 1 : 0)
};
static DEFAULT_CONTEXT = '$$noContext';
constructor() {
this.dictionary = {};
this.getPlurals = I18n.GET_PLURALS_MAP.OTHERS;
}
setTranslations(lang, dictionary) {
this.dictionary = dictionary || {};
this.getPlurals = I18n.GET_PLURALS_MAP[lang] || I18n.GET_PLURALS_MAP.OTHERS;
}
interpolate(text, interpolationObject) {
if (!interpolationObject || !Object.keys(interpolationObject).length) {
return text;
}
const substringsForReplacing =
getSubstringsForReplacing(text, interpolationObject);
let resultText = text;
Object.keys(substringsForReplacing).forEach(key => {
if (substringsForReplacing[key] !== undefined) {
resultText = resultText.replace(key, substringsForReplacing[key]);
}
});
return resultText;
function getSubstringsForReplacing(str, interpolationValues) {
let currentInterpolatedFragmentStart = -1;
const substringToValueMap = {};
for (let i = 0; i < (str.length - 1); ++i) {
if (str[i] === '{' && str[i + 1] === '{') {
currentInterpolatedFragmentStart = i + 2;
i = currentInterpolatedFragmentStart;
} else if (str[i] === '}' && str[i + 1] === '}' &&
currentInterpolatedFragmentStart > 0) {
const variableName = str.substring(
currentInterpolatedFragmentStart, i
);
substringToValueMap[`{{${variableName}}}`] =
interpolationValues[variableName.trim()];
}
}
return substringToValueMap;
}
}
getTranslationString(text, numberForPlurals, context) {
const contexts = this.dictionary[text] || {};
const currentTranslation = contexts[context || I18n.DEFAULT_CONTEXT] ||
contexts;
if (typeof currentTranslation === 'string') {
return currentTranslation;
}
const pluralFormId = this.getPlurals(
Number.isInteger(numberForPlurals) ? numberForPlurals : 1
);
return currentTranslation[pluralFormId];
}
translate(text, interpolationObject, numberForPlurals, context) {
const currentTranslation = this.getTranslationString(
text, numberForPlurals, context
);
return this.interpolate(
currentTranslation || text, interpolationObject
);
}
translatePlural(
count, textForUnique, textForPlural, interpolationObject, context
) {
const currentTranslation = this.getTranslationString(
textForUnique,
count,
context
);
const stringToInterpolate = currentTranslation ||
(count === 1 ? textForUnique : textForPlural);
return this.interpolate(
stringToInterpolate,
Object.assign({$count: count}, interpolationObject || {})
);
}
}
const I18N_INSTANCE = new I18n();
export const i18n = I18N_INSTANCE.translate.bind(I18N_INSTANCE);
i18n.plural = I18N_INSTANCE.translatePlural.bind(I18N_INSTANCE);
export const i18nTimeIdentifiers = {};
function configureFecha() {
fecha.i18n.dayNamesShort = i18n('Sun|Mon|Tue|Wed|Thu|Fri|Sat').split('|');
fecha.i18n.dayNames = i18n('Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday').split('|');
fecha.i18n.monthNamesShort = i18n('Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec').split('|');
fecha.i18n.monthNames = i18n('January|February|March|April|May|June|July|August|September|October|November|December').split('|');
fecha.i18n.amPm = i18n('am|pm').split('|');
fecha.masks.datePresentation = 'DD MMM YYYY';
fecha.masks.dateAndTimePresentation = 'DD MMM YYYY HH:MM';
}
export function setLocale(lang, translations) {
I18N_INSTANCE.setTranslations(lang, translations[lang]);
configureFecha();
}