localized-strings/lib/LocalizedStrings.js

273 lines
11 KiB
JavaScript

'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _utils = require('./utils');
var utils = _interopRequireWildcard(_utils);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Simple module to localize the React interface using the same syntax
* used in the ReactNativeLocalization module
* (https://github.com/stefalda/ReactNativeLocalization)
*
* Originally developed by Stefano Falda (stefano.falda@gmail.com)
*
* It uses a call to the Navigator/Browser object to get the current interface language,
* then display the correct language strings or the default language (the first
* one if a match is not found).
*
* How to use:
* Check the instructions at:
* https://github.com/stefalda/localized-strings
*/
var placeholderReplaceRegex = /(\{[\d|\w]+\})/;
var placeholderReferenceRegex = /(\$ref\{[\w|.]+\})/;
var LocalizedStrings = function () {
/**
* Constructor used to provide the strings objects in various language and the optional callback to get
* the interface language
* @param {*} props - the strings object
* @param {*} getInterfaceLanguageCallback - the optional method to use to get the InterfaceLanguage
*/
function LocalizedStrings(props, getInterfaceLanguageCallback) {
_classCallCheck(this, LocalizedStrings);
if (getInterfaceLanguageCallback) {
this._interfaceLanguage = getInterfaceLanguageCallback();
} else {
this._interfaceLanguage = utils.getInterfaceLanguage();
}
this._language = this._interfaceLanguage;
this.setContent(props);
}
/**
* Set the strings objects based on the parameter passed in the constructor
* @param {*} props
*/
_createClass(LocalizedStrings, [{
key: 'setContent',
value: function setContent(props) {
var _this = this;
var _Object$keys = Object.keys(props),
_Object$keys2 = _slicedToArray(_Object$keys, 1),
defaultLang = _Object$keys2[0];
this._defaultLanguage = defaultLang;
this._defaultLanguageFirstLevelKeys = [];
// Store locally the passed strings
this._props = props;
utils.validateTranslationKeys(Object.keys(props[this._defaultLanguage]));
// Store first level keys (for identifying missing translations)
Object.keys(this._props[this._defaultLanguage]).forEach(function (key) {
if (typeof _this._props[_this._defaultLanguage][key] === 'string') {
_this._defaultLanguageFirstLevelKeys.push(key);
}
});
// Set language to its default value (the interface)
this.setLanguage(this._interfaceLanguage);
}
/**
* Can be used from ouside the class to force a particular language
* indipendently from the interface one
* @param {*} language
*/
}, {
key: 'setLanguage',
value: function setLanguage(language) {
var _this2 = this;
// Check if exists a translation for the current language or if the default
// should be used
var bestLanguage = utils.getBestMatchingLanguage(language, this._props);
var defaultLanguage = Object.keys(this._props)[0];
this._language = bestLanguage;
// Associate the language object to the this object
if (this._props[bestLanguage]) {
// delete default propery values to identify missing translations
for (var i = 0; i < this._defaultLanguageFirstLevelKeys.length; i += 1) {
delete this[this._defaultLanguageFirstLevelKeys[i]];
}
var localizedStrings = _extends({}, this._props[this._language]);
Object.keys(localizedStrings).forEach(function (key) {
_this2[key] = localizedStrings[key];
});
// Now add any string missing from the translation but existing in the default language
if (defaultLanguage !== this._language) {
localizedStrings = this._props[defaultLanguage];
this._fallbackValues(localizedStrings, this);
}
}
}
/**
* Load fallback values for missing translations
* @param {*} defaultStrings
* @param {*} strings
*/
}, {
key: '_fallbackValues',
value: function _fallbackValues(defaultStrings, strings) {
var _this3 = this;
Object.keys(defaultStrings).forEach(function (key) {
if (Object.prototype.hasOwnProperty.call(defaultStrings, key) && !strings[key] && strings[key] !== '') {
strings[key] = defaultStrings[key]; // eslint-disable-line no-param-reassign
console.log('\uD83D\uDEA7 \uD83D\uDC77 key \'' + key + '\' not found in localizedStrings for language ' + _this3._language + ' \uD83D\uDEA7');
} else if (typeof strings[key] !== 'string') {
// It's an object
_this3._fallbackValues(defaultStrings[key], strings[key]);
}
});
}
/**
* The current language displayed (could differ from the interface language
* if it has been forced manually and a matching translation has been found)
*/
}, {
key: 'getLanguage',
value: function getLanguage() {
return this._language;
}
/**
* The current interface language (could differ from the language displayed)
*/
}, {
key: 'getInterfaceLanguage',
value: function getInterfaceLanguage() {
return this._interfaceLanguage;
}
/**
* Return an array containing the available languages passed as props in the constructor
*/
}, {
key: 'getAvailableLanguages',
value: function getAvailableLanguages() {
var _this4 = this;
if (!this._availableLanguages) {
this._availableLanguages = [];
Object.keys(this._props).forEach(function (key) {
_this4._availableLanguages.push(key);
});
}
return this._availableLanguages;
}
// Format the passed string replacing the numbered or tokenized placeholders
// eg. 1: I'd like some {0} and {1}, or just {0}
// eg. 2: I'd like some {bread} and {butter}, or just {bread}
// Use example:
// eg. 1: strings.formatString(strings.question, strings.bread, strings.butter)
// eg. 2: strings.formatString(strings.question, { bread: strings.bread, butter: strings.butter })
}, {
key: 'formatString',
value: function formatString(str) {
var _this5 = this;
for (var _len = arguments.length, valuesForPlaceholders = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
valuesForPlaceholders[_key - 1] = arguments[_key];
}
var ref = (str || '').split(placeholderReferenceRegex).filter(function (textPart) {
return !!textPart;
}).map(function (textPart) {
if (textPart.match(placeholderReferenceRegex)) {
var matchedKey = textPart.slice(5, -1);
var referenceValue = _this5.getString(matchedKey);
if (referenceValue) return referenceValue;
console.log('No Localization ref found for \'' + textPart + '\' in string \'' + str + '\'');
// lets print it another way so next replacer doesn't find it
return '$ref(id:' + matchedKey + ')';
}
return textPart;
}).join('');
return ref.split(placeholderReplaceRegex).filter(function (textPart) {
return !!textPart;
}).map(function (textPart) {
if (textPart.match(placeholderReplaceRegex)) {
var matchedKey = textPart.slice(1, -1);
var valueForPlaceholder = valuesForPlaceholders[matchedKey];
// If no value found, check if working with an object instead
if (valueForPlaceholder === undefined) {
var valueFromObjectPlaceholder = valuesForPlaceholders[0][matchedKey];
if (valueFromObjectPlaceholder !== undefined) {
valueForPlaceholder = valueFromObjectPlaceholder;
} else {
// If value still isn't found, then it must have been undefined/null
return valueForPlaceholder;
}
}
return valueForPlaceholder;
}
return textPart;
}).join('');
}
// Return a string with the passed key in a different language or defalt if not set
// We allow deep . notation for finding strings
}, {
key: 'getString',
value: function getString(key, language) {
try {
var current = this._props[language || this._language];
var paths = key.split('.');
for (var i = 0; i < paths.length; i += 1) {
if (current[paths[i]] === undefined) {
throw Error(paths[i]);
}
current = current[paths[i]];
}
return current;
} catch (ex) {
console.log('No localization found for key \'' + key + '\' and language \'' + language + '\', failed on ' + ex.message);
}
return null;
}
/**
* The current props (locale object)
*/
}, {
key: 'getContent',
value: function getContent() {
return this._props;
}
}]);
return LocalizedStrings;
}();
exports.default = LocalizedStrings;