353 lines
14 KiB
JavaScript
353 lines
14 KiB
JavaScript
import _objectSpread from "@babel/runtime/helpers/objectSpread";
|
|
import _typeof from "@babel/runtime/helpers/typeof";
|
|
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
import _createClass from "@babel/runtime/helpers/createClass";
|
|
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
|
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
|
import _inherits from "@babel/runtime/helpers/inherits";
|
|
import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
|
|
import baseLogger from './logger.js';
|
|
import EventEmitter from './EventEmitter.js';
|
|
import postProcessor from './postProcessor.js';
|
|
import * as utils from './utils.js';
|
|
|
|
var Translator =
|
|
/*#__PURE__*/
|
|
function (_EventEmitter) {
|
|
_inherits(Translator, _EventEmitter);
|
|
|
|
function Translator(services) {
|
|
var _this;
|
|
|
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
|
|
_classCallCheck(this, Translator);
|
|
|
|
_this = _possibleConstructorReturn(this, _getPrototypeOf(Translator).call(this));
|
|
EventEmitter.call(_assertThisInitialized(_assertThisInitialized(_this))); // <=IE10 fix (unable to call parent constructor)
|
|
|
|
utils.copy(['resourceStore', 'languageUtils', 'pluralResolver', 'interpolator', 'backendConnector', 'i18nFormat'], services, _assertThisInitialized(_assertThisInitialized(_this)));
|
|
_this.options = options;
|
|
|
|
if (_this.options.keySeparator === undefined) {
|
|
_this.options.keySeparator = '.';
|
|
}
|
|
|
|
_this.logger = baseLogger.create('translator');
|
|
return _this;
|
|
}
|
|
|
|
_createClass(Translator, [{
|
|
key: "changeLanguage",
|
|
value: function changeLanguage(lng) {
|
|
if (lng) this.language = lng;
|
|
}
|
|
}, {
|
|
key: "exists",
|
|
value: function exists(key) {
|
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
|
|
interpolation: {}
|
|
};
|
|
var resolved = this.resolve(key, options);
|
|
return resolved && resolved.res !== undefined;
|
|
}
|
|
}, {
|
|
key: "extractFromKey",
|
|
value: function extractFromKey(key, options) {
|
|
var nsSeparator = options.nsSeparator || this.options.nsSeparator;
|
|
if (nsSeparator === undefined) nsSeparator = ':';
|
|
var keySeparator = options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator;
|
|
var namespaces = options.ns || this.options.defaultNS;
|
|
|
|
if (nsSeparator && key.indexOf(nsSeparator) > -1) {
|
|
var parts = key.split(nsSeparator);
|
|
if (nsSeparator !== keySeparator || nsSeparator === keySeparator && this.options.ns.indexOf(parts[0]) > -1) namespaces = parts.shift();
|
|
key = parts.join(keySeparator);
|
|
}
|
|
|
|
if (typeof namespaces === 'string') namespaces = [namespaces];
|
|
return {
|
|
key: key,
|
|
namespaces: namespaces
|
|
};
|
|
}
|
|
}, {
|
|
key: "translate",
|
|
value: function translate(keys, options) {
|
|
var _this2 = this;
|
|
|
|
if (_typeof(options) !== 'object' && this.options.overloadTranslationOptionHandler) {
|
|
/* eslint prefer-rest-params: 0 */
|
|
options = this.options.overloadTranslationOptionHandler(arguments);
|
|
}
|
|
|
|
if (!options) options = {}; // non valid keys handling
|
|
|
|
if (keys === undefined || keys === null) return '';
|
|
if (!Array.isArray(keys)) keys = [String(keys)]; // separators
|
|
|
|
var keySeparator = options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator; // get namespace(s)
|
|
|
|
var _this$extractFromKey = this.extractFromKey(keys[keys.length - 1], options),
|
|
key = _this$extractFromKey.key,
|
|
namespaces = _this$extractFromKey.namespaces;
|
|
|
|
var namespace = namespaces[namespaces.length - 1]; // return key on CIMode
|
|
|
|
var lng = options.lng || this.language;
|
|
var appendNamespaceToCIMode = options.appendNamespaceToCIMode || this.options.appendNamespaceToCIMode;
|
|
|
|
if (lng && lng.toLowerCase() === 'cimode') {
|
|
if (appendNamespaceToCIMode) {
|
|
var nsSeparator = options.nsSeparator || this.options.nsSeparator;
|
|
return namespace + nsSeparator + key;
|
|
}
|
|
|
|
return key;
|
|
} // resolve from store
|
|
|
|
|
|
var resolved = this.resolve(keys, options);
|
|
var res = resolved && resolved.res;
|
|
var resUsedKey = resolved && resolved.usedKey || key;
|
|
var resExactUsedKey = resolved && resolved.exactUsedKey || key;
|
|
var resType = Object.prototype.toString.apply(res);
|
|
var noObject = ['[object Number]', '[object Function]', '[object RegExp]'];
|
|
var joinArrays = options.joinArrays !== undefined ? options.joinArrays : this.options.joinArrays; // object
|
|
|
|
var handleAsObjectInI18nFormat = !this.i18nFormat || this.i18nFormat.handleAsObject;
|
|
var handleAsObject = typeof res !== 'string' && typeof res !== 'boolean' && typeof res !== 'number';
|
|
|
|
if (handleAsObjectInI18nFormat && res && handleAsObject && noObject.indexOf(resType) < 0 && !(typeof joinArrays === 'string' && resType === '[object Array]')) {
|
|
if (!options.returnObjects && !this.options.returnObjects) {
|
|
this.logger.warn('accessing an object - but returnObjects options is not enabled!');
|
|
return this.options.returnedObjectHandler ? this.options.returnedObjectHandler(resUsedKey, res, options) : "key '".concat(key, " (").concat(this.language, ")' returned an object instead of string.");
|
|
} // if we got a separator we loop over children - else we just return object as is
|
|
// as having it set to false means no hierarchy so no lookup for nested values
|
|
|
|
|
|
if (keySeparator) {
|
|
var resTypeIsArray = resType === '[object Array]';
|
|
var copy = resTypeIsArray ? [] : {}; // apply child translation on a copy
|
|
|
|
/* eslint no-restricted-syntax: 0 */
|
|
|
|
var newKeyToUse = resTypeIsArray ? resExactUsedKey : resUsedKey;
|
|
|
|
for (var m in res) {
|
|
if (Object.prototype.hasOwnProperty.call(res, m)) {
|
|
var deepKey = "".concat(newKeyToUse).concat(keySeparator).concat(m);
|
|
copy[m] = this.translate(deepKey, _objectSpread({}, options, {
|
|
joinArrays: false,
|
|
ns: namespaces
|
|
}));
|
|
if (copy[m] === deepKey) copy[m] = res[m]; // if nothing found use orginal value as fallback
|
|
}
|
|
}
|
|
|
|
res = copy;
|
|
}
|
|
} else if (handleAsObjectInI18nFormat && typeof joinArrays === 'string' && resType === '[object Array]') {
|
|
// array special treatment
|
|
res = res.join(joinArrays);
|
|
if (res) res = this.extendTranslation(res, keys, options);
|
|
} else {
|
|
// string, empty or null
|
|
var usedDefault = false;
|
|
var usedKey = false; // fallback value
|
|
|
|
if (!this.isValidLookup(res) && options.defaultValue !== undefined) {
|
|
usedDefault = true;
|
|
|
|
if (options.count !== undefined) {
|
|
var suffix = this.pluralResolver.getSuffix(lng, options.count);
|
|
res = options["defaultValue".concat(suffix)];
|
|
}
|
|
|
|
if (!res) res = options.defaultValue;
|
|
}
|
|
|
|
if (!this.isValidLookup(res)) {
|
|
usedKey = true;
|
|
res = key;
|
|
} // save missing
|
|
|
|
|
|
var updateMissing = options.defaultValue && options.defaultValue !== res && this.options.updateMissing;
|
|
|
|
if (usedKey || usedDefault || updateMissing) {
|
|
this.logger.log(updateMissing ? 'updateKey' : 'missingKey', lng, namespace, key, updateMissing ? options.defaultValue : res);
|
|
var lngs = [];
|
|
var fallbackLngs = this.languageUtils.getFallbackCodes(this.options.fallbackLng, options.lng || this.language);
|
|
|
|
if (this.options.saveMissingTo === 'fallback' && fallbackLngs && fallbackLngs[0]) {
|
|
for (var i = 0; i < fallbackLngs.length; i++) {
|
|
lngs.push(fallbackLngs[i]);
|
|
}
|
|
} else if (this.options.saveMissingTo === 'all') {
|
|
lngs = this.languageUtils.toResolveHierarchy(options.lng || this.language);
|
|
} else {
|
|
lngs.push(options.lng || this.language);
|
|
}
|
|
|
|
var send = function send(l, k) {
|
|
if (_this2.options.missingKeyHandler) {
|
|
_this2.options.missingKeyHandler(l, namespace, k, updateMissing ? options.defaultValue : res, updateMissing, options);
|
|
} else if (_this2.backendConnector && _this2.backendConnector.saveMissing) {
|
|
_this2.backendConnector.saveMissing(l, namespace, k, updateMissing ? options.defaultValue : res, updateMissing, options);
|
|
}
|
|
|
|
_this2.emit('missingKey', l, namespace, k, res);
|
|
};
|
|
|
|
if (this.options.saveMissing) {
|
|
var needsPluralHandling = options.count !== undefined && typeof options.count !== 'string';
|
|
|
|
if (this.options.saveMissingPlurals && needsPluralHandling) {
|
|
lngs.forEach(function (l) {
|
|
var plurals = _this2.pluralResolver.getPluralFormsOfKey(l, key);
|
|
|
|
plurals.forEach(function (p) {
|
|
return send([l], p);
|
|
});
|
|
});
|
|
} else {
|
|
send(lngs, key);
|
|
}
|
|
}
|
|
} // extend
|
|
|
|
|
|
res = this.extendTranslation(res, keys, options, resolved); // append namespace if still key
|
|
|
|
if (usedKey && res === key && this.options.appendNamespaceToMissingKey) res = "".concat(namespace, ":").concat(key); // parseMissingKeyHandler
|
|
|
|
if (usedKey && this.options.parseMissingKeyHandler) res = this.options.parseMissingKeyHandler(res);
|
|
} // return
|
|
|
|
|
|
return res;
|
|
}
|
|
}, {
|
|
key: "extendTranslation",
|
|
value: function extendTranslation(res, key, options, resolved) {
|
|
var _this3 = this;
|
|
|
|
if (this.i18nFormat && this.i18nFormat.parse) {
|
|
res = this.i18nFormat.parse(res, options, resolved.usedLng, resolved.usedNS, resolved.usedKey, {
|
|
resolved: resolved
|
|
});
|
|
} else if (!options.skipInterpolation) {
|
|
// i18next.parsing
|
|
if (options.interpolation) this.interpolator.init(_objectSpread({}, options, {
|
|
interpolation: _objectSpread({}, this.options.interpolation, options.interpolation)
|
|
})); // interpolate
|
|
|
|
var data = options.replace && typeof options.replace !== 'string' ? options.replace : options;
|
|
if (this.options.interpolation.defaultVariables) data = _objectSpread({}, this.options.interpolation.defaultVariables, data);
|
|
res = this.interpolator.interpolate(res, data, options.lng || this.language, options); // nesting
|
|
|
|
if (options.nest !== false) res = this.interpolator.nest(res, function () {
|
|
return _this3.translate.apply(_this3, arguments);
|
|
}, options);
|
|
if (options.interpolation) this.interpolator.reset();
|
|
} // post process
|
|
|
|
|
|
var postProcess = options.postProcess || this.options.postProcess;
|
|
var postProcessorNames = typeof postProcess === 'string' ? [postProcess] : postProcess;
|
|
|
|
if (res !== undefined && res !== null && postProcessorNames && postProcessorNames.length && options.applyPostProcessor !== false) {
|
|
res = postProcessor.handle(postProcessorNames, res, key, options, this);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
}, {
|
|
key: "resolve",
|
|
value: function resolve(keys) {
|
|
var _this4 = this;
|
|
|
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
var found;
|
|
var usedKey; // plain key
|
|
|
|
var exactUsedKey; // key with context / plural
|
|
|
|
var usedLng;
|
|
var usedNS;
|
|
if (typeof keys === 'string') keys = [keys]; // forEach possible key
|
|
|
|
keys.forEach(function (k) {
|
|
if (_this4.isValidLookup(found)) return;
|
|
|
|
var extracted = _this4.extractFromKey(k, options);
|
|
|
|
var key = extracted.key;
|
|
usedKey = key;
|
|
var namespaces = extracted.namespaces;
|
|
if (_this4.options.fallbackNS) namespaces = namespaces.concat(_this4.options.fallbackNS);
|
|
var needsPluralHandling = options.count !== undefined && typeof options.count !== 'string';
|
|
var needsContextHandling = options.context !== undefined && typeof options.context === 'string' && options.context !== '';
|
|
var codes = options.lngs ? options.lngs : _this4.languageUtils.toResolveHierarchy(options.lng || _this4.language, options.fallbackLng);
|
|
namespaces.forEach(function (ns) {
|
|
if (_this4.isValidLookup(found)) return;
|
|
usedNS = ns;
|
|
codes.forEach(function (code) {
|
|
if (_this4.isValidLookup(found)) return;
|
|
usedLng = code;
|
|
var finalKey = key;
|
|
var finalKeys = [finalKey];
|
|
|
|
if (_this4.i18nFormat && _this4.i18nFormat.addLookupKeys) {
|
|
_this4.i18nFormat.addLookupKeys(finalKeys, key, code, ns, options);
|
|
} else {
|
|
var pluralSuffix;
|
|
if (needsPluralHandling) pluralSuffix = _this4.pluralResolver.getSuffix(code, options.count); // fallback for plural if context not found
|
|
|
|
if (needsPluralHandling && needsContextHandling) finalKeys.push(finalKey + pluralSuffix); // get key for context if needed
|
|
|
|
if (needsContextHandling) finalKeys.push(finalKey += "".concat(_this4.options.contextSeparator).concat(options.context)); // get key for plural if needed
|
|
|
|
if (needsPluralHandling) finalKeys.push(finalKey += pluralSuffix);
|
|
} // iterate over finalKeys starting with most specific pluralkey (-> contextkey only) -> singularkey only
|
|
|
|
|
|
var possibleKey;
|
|
/* eslint no-cond-assign: 0 */
|
|
|
|
while (possibleKey = finalKeys.pop()) {
|
|
if (!_this4.isValidLookup(found)) {
|
|
exactUsedKey = possibleKey;
|
|
found = _this4.getResource(code, ns, possibleKey, options);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
return {
|
|
res: found,
|
|
usedKey: usedKey,
|
|
exactUsedKey: exactUsedKey,
|
|
usedLng: usedLng,
|
|
usedNS: usedNS
|
|
};
|
|
}
|
|
}, {
|
|
key: "isValidLookup",
|
|
value: function isValidLookup(res) {
|
|
return res !== undefined && !(!this.options.returnNull && res === null) && !(!this.options.returnEmptyString && res === '');
|
|
}
|
|
}, {
|
|
key: "getResource",
|
|
value: function getResource(code, ns, key) {
|
|
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
if (this.i18nFormat && this.i18nFormat.getResource) return this.i18nFormat.getResource(code, ns, key, options);
|
|
return this.resourceStore.getResource(code, ns, key, options);
|
|
}
|
|
}]);
|
|
|
|
return Translator;
|
|
}(EventEmitter);
|
|
|
|
export default Translator; |