UNPKG

@mongez/localization

Version:

A simple i18n localization handler for browsers and nodejs

230 lines (229 loc) 8.96 kB
'use strict';var reinforcements=require('@mongez/reinforcements'),config=require('./config.js'),converters=require('./converters.js'),countRules=require('./count-rules.js'),events=require('./events.js'),placeholderPatternConfig=require('./placeholder-pattern-config.js');/** * Current locale code */ let currentLocaleCode = "en"; /** * Current converter */ let currentConverter = converters.plainConverter; /** * all keywords for all locale codes */ let translationsList = {}; /** * Current fall back locale code */ let fallbackLocaleCode = "en"; /** * Get current locale code */ function getCurrentLocaleCode() { return currentLocaleCode; } /** * Get fallback locale code */ function getFallbackLocaleCode() { return fallbackLocaleCode; } /** * Set current converter */ function setConverter(converter) { currentConverter = converter; } /** * Get the locale code used in translation, this allows to get the locale code on the fly */ function getTranslationLocaleCode() { return (config.getLocalizationConfigurations().translationLocalCode || currentLocaleCode); } /** * Set current locale code */ function setCurrentLocaleCode(localeCode) { const oldLocaleCode = currentLocaleCode; currentLocaleCode = localeCode; events.localizationEvents.triggerChange("localeCode", localeCode, oldLocaleCode); } /** * Add keywords */ function extend(localeCode, keywords) { translationsList[localeCode] = reinforcements.merge(translationsList[localeCode] || {}, keywords); } /** * Create a grouped translations based on keyword, each keyword contains list of locale codes and beside it its corresponding translation * * @example * { * home: { * en: "Home", * ar: "الرئيسية" * } * } * * Also it could have nested grouped translations * * @example * { * general: { * home: { * en: "Home", * ar: "الرئيسية" * } * } */ function groupedTranslations(groupKey, groupedTranslations) { if (typeof groupKey !== "string" && !groupedTranslations) { groupedTranslations = groupKey; groupKey = undefined; } // now we need to loop over the grouped translations // we have two cases here // first one we have a group key // second one we don't have a group key // as values are always objects until the last level // we need to create a recursive function to loop over the object // output of the flatten object will be something like: // general.home.en: "Home" // general.home.ar: "الرئيسية" const object = reinforcements.flatten(groupKey && typeof groupKey === "string" ? { [groupKey]: groupedTranslations } : groupedTranslations); // now the locale codes are the last dot in each key // now we will loop over the object and add each key to the translations list for (const key in object) { const keyword = key.split("."); const localeCode = keyword.pop(); reinforcements.set(translationsList, localeCode + "." + keyword.join("."), object[key]); } } /** * Override the entire translations list */ function setTranslationsList(translations) { translationsList = translations; } /** * Get the entire translations list */ function getTranslationsList() { return translationsList; } /** * Get the keywords list of the given locale code */ function getKeywordsListOf(localeCode) { return translationsList[localeCode] || null; } /** * Set fallback locale code, if the keyword does not exist on current locale code, * then check it in the faLLBACK locale code instead */ function setFallbackLocaleCode(fallbackLocale) { const oldFallback = fallbackLocaleCode; fallbackLocaleCode = fallbackLocale; events.localizationEvents.triggerChange("fallback", fallbackLocale, oldFallback); } /** * Translate the given keyword in current locale code */ function trans(keyword, placeholders, converter = currentConverter) { return transFrom(getTranslationLocaleCode(), keyword, placeholders, converter); } /** * Translate using the default converter */ function plainTrans(keyword, placeholders) { return transFrom(getTranslationLocaleCode(), keyword, placeholders, converters.plainConverter); } /** * Translate the given keyword for the given locale code * Please note this method accepts dot notation syntax */ function transFrom(localeCode, keyword, placeholders, converter = currentConverter) { let translation; if (typeof keyword === "object") { translation = keyword[localeCode] || keyword[fallbackLocaleCode]; } else { // Check if we have a count in placeholders if (placeholders?.count !== undefined) { const count = Number(placeholders.count); // Try current locale with its count rules const currentCountKey = countRules.getCountKey(count, localeCode); translation = reinforcements.get(translationsList, `${localeCode}.${keyword}${currentCountKey}`); // If not found and we have a fallback, try the fallback locale with its own count rules if (!translation && fallbackLocaleCode) { const fallbackCountKey = countRules.getCountKey(count, fallbackLocaleCode); translation = reinforcements.get(translationsList, `${fallbackLocaleCode}.${keyword}${fallbackCountKey}`); } // If still not found, try other variants in order: // 1. Current locale _other // 2. Fallback locale _other // 3. Current locale base // 4. Fallback locale base if (!translation) { translation = reinforcements.get(translationsList, `${localeCode}.${keyword}_other`) || (fallbackLocaleCode && reinforcements.get(translationsList, `${fallbackLocaleCode}.${keyword}_other`)) || reinforcements.get(translationsList, `${localeCode}.${keyword}`) || (fallbackLocaleCode && reinforcements.get(translationsList, `${fallbackLocaleCode}.${keyword}`)); } // Format the count value according to configuration if (translation && placeholders.count !== undefined) { placeholders = { ...placeholders, count: countRules.formatCount(count) }; } } else { // No count, just get regular translation translation = reinforcements.get(translationsList, `${localeCode}.${keyword}`) || (fallbackLocaleCode ? reinforcements.get(translationsList, `${fallbackLocaleCode}.${keyword}`) : null); } } if (!translation) return keyword; return placeholders ? converter(translation, placeholders, placeholderPatternConfig.getPlaceholderPattern()) : translation; } /** * Get a translation object with automatic translation using object syntax * Please note this does not support nested objects, only keywords and their translations * i.e * const translations = transObject({ * name: { * en: 'name', * ar: 'الاسم' * } * }); * * Usage: translations.name // returns the name in current locale code * If the keyword does not exist on current locale code, then it will check it in the fallback locale code * * If keyword is "p", then it will return a function that accepts keyword and its placeholders * If keyword is "plain", then the converter used in translation will be the plain converter */ function transObject(translations) { // use proxy return new Proxy(translations, { get(target, key) { if (key === "p") { return function (keyword, placeholders) { return transFrom(currentLocaleCode, target[keyword], placeholders, currentConverter); }; } if (key === "plain") { return function (keyword, placeholders) { return transFrom(currentLocaleCode, target[keyword], placeholders, converters.plainConverter); }; } if (!target[key]) return transFrom(fallbackLocaleCode, key); return transFrom(currentLocaleCode, target[key]); }, }); }exports.extend=extend;exports.getCurrentLocaleCode=getCurrentLocaleCode;exports.getFallbackLocaleCode=getFallbackLocaleCode;exports.getKeywordsListOf=getKeywordsListOf;exports.getTranslationLocaleCode=getTranslationLocaleCode;exports.getTranslationsList=getTranslationsList;exports.groupedTranslations=groupedTranslations;exports.plainTrans=plainTrans;exports.setConverter=setConverter;exports.setCurrentLocaleCode=setCurrentLocaleCode;exports.setFallbackLocaleCode=setFallbackLocaleCode;exports.setTranslationsList=setTranslationsList;exports.trans=trans;exports.transFrom=transFrom;exports.transObject=transObject;//# sourceMappingURL=translator.js.map