Small patch for https://github.com/stefalda/localized-strings/issues/2 https://github.com/stefalda/localized-strings/issues/6
273 lines
11 KiB
JavaScript
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; |