UNPKG

93.6 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 typeof define === 'function' && define.amd ? define(factory) :
4 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.i18next = factory());
5})(this, (function () { 'use strict';
6
7 const isString = obj => typeof obj === 'string';
8 const defer = () => {
9 let res;
10 let rej;
11 const promise = new Promise((resolve, reject) => {
12 res = resolve;
13 rej = reject;
14 });
15 promise.resolve = res;
16 promise.reject = rej;
17 return promise;
18 };
19 const makeString = object => {
20 if (object == null) return '';
21 return '' + object;
22 };
23 const copy = (a, s, t) => {
24 a.forEach(m => {
25 if (s[m]) t[m] = s[m];
26 });
27 };
28 const lastOfPathSeparatorRegExp = /###/g;
29 const cleanKey = key => key && key.indexOf('###') > -1 ? key.replace(lastOfPathSeparatorRegExp, '.') : key;
30 const canNotTraverseDeeper = object => !object || isString(object);
31 const getLastOfPath = (object, path, Empty) => {
32 const stack = !isString(path) ? path : path.split('.');
33 let stackIndex = 0;
34 while (stackIndex < stack.length - 1) {
35 if (canNotTraverseDeeper(object)) return {};
36 const key = cleanKey(stack[stackIndex]);
37 if (!object[key] && Empty) object[key] = new Empty();
38 if (Object.prototype.hasOwnProperty.call(object, key)) {
39 object = object[key];
40 } else {
41 object = {};
42 }
43 ++stackIndex;
44 }
45 if (canNotTraverseDeeper(object)) return {};
46 return {
47 obj: object,
48 k: cleanKey(stack[stackIndex])
49 };
50 };
51 const setPath = (object, path, newValue) => {
52 const {
53 obj,
54 k
55 } = getLastOfPath(object, path, Object);
56 if (obj !== undefined || path.length === 1) {
57 obj[k] = newValue;
58 return;
59 }
60 let e = path[path.length - 1];
61 let p = path.slice(0, path.length - 1);
62 let last = getLastOfPath(object, p, Object);
63 while (last.obj === undefined && p.length) {
64 e = `${p[p.length - 1]}.${e}`;
65 p = p.slice(0, p.length - 1);
66 last = getLastOfPath(object, p, Object);
67 if (last && last.obj && typeof last.obj[`${last.k}.${e}`] !== 'undefined') {
68 last.obj = undefined;
69 }
70 }
71 last.obj[`${last.k}.${e}`] = newValue;
72 };
73 const pushPath = (object, path, newValue, concat) => {
74 const {
75 obj,
76 k
77 } = getLastOfPath(object, path, Object);
78 obj[k] = obj[k] || [];
79 obj[k].push(newValue);
80 };
81 const getPath = (object, path) => {
82 const {
83 obj,
84 k
85 } = getLastOfPath(object, path);
86 if (!obj) return undefined;
87 return obj[k];
88 };
89 const getPathWithDefaults = (data, defaultData, key) => {
90 const value = getPath(data, key);
91 if (value !== undefined) {
92 return value;
93 }
94 return getPath(defaultData, key);
95 };
96 const deepExtend = (target, source, overwrite) => {
97 for (const prop in source) {
98 if (prop !== '__proto__' && prop !== 'constructor') {
99 if (prop in target) {
100 if (isString(target[prop]) || target[prop] instanceof String || isString(source[prop]) || source[prop] instanceof String) {
101 if (overwrite) target[prop] = source[prop];
102 } else {
103 deepExtend(target[prop], source[prop], overwrite);
104 }
105 } else {
106 target[prop] = source[prop];
107 }
108 }
109 }
110 return target;
111 };
112 const regexEscape = str => str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
113 var _entityMap = {
114 '&': '&amp;',
115 '<': '&lt;',
116 '>': '&gt;',
117 '"': '&quot;',
118 "'": '&#39;',
119 '/': '&#x2F;'
120 };
121 const escape = data => {
122 if (isString(data)) {
123 return data.replace(/[&<>"'\/]/g, s => _entityMap[s]);
124 }
125 return data;
126 };
127 class RegExpCache {
128 constructor(capacity) {
129 this.capacity = capacity;
130 this.regExpMap = new Map();
131 this.regExpQueue = [];
132 }
133 getRegExp(pattern) {
134 const regExpFromCache = this.regExpMap.get(pattern);
135 if (regExpFromCache !== undefined) {
136 return regExpFromCache;
137 }
138 const regExpNew = new RegExp(pattern);
139 if (this.regExpQueue.length === this.capacity) {
140 this.regExpMap.delete(this.regExpQueue.shift());
141 }
142 this.regExpMap.set(pattern, regExpNew);
143 this.regExpQueue.push(pattern);
144 return regExpNew;
145 }
146 }
147 const chars = [' ', ',', '?', '!', ';'];
148 const looksLikeObjectPathRegExpCache = new RegExpCache(20);
149 const looksLikeObjectPath = (key, nsSeparator, keySeparator) => {
150 nsSeparator = nsSeparator || '';
151 keySeparator = keySeparator || '';
152 const possibleChars = chars.filter(c => nsSeparator.indexOf(c) < 0 && keySeparator.indexOf(c) < 0);
153 if (possibleChars.length === 0) return true;
154 const r = looksLikeObjectPathRegExpCache.getRegExp(`(${possibleChars.map(c => c === '?' ? '\\?' : c).join('|')})`);
155 let matched = !r.test(key);
156 if (!matched) {
157 const ki = key.indexOf(keySeparator);
158 if (ki > 0 && !r.test(key.substring(0, ki))) {
159 matched = true;
160 }
161 }
162 return matched;
163 };
164 const deepFind = function (obj, path) {
165 let keySeparator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '.';
166 if (!obj) return undefined;
167 if (obj[path]) return obj[path];
168 const tokens = path.split(keySeparator);
169 let current = obj;
170 for (let i = 0; i < tokens.length;) {
171 if (!current || typeof current !== 'object') {
172 return undefined;
173 }
174 let next;
175 let nextPath = '';
176 for (let j = i; j < tokens.length; ++j) {
177 if (j !== i) {
178 nextPath += keySeparator;
179 }
180 nextPath += tokens[j];
181 next = current[nextPath];
182 if (next !== undefined) {
183 if (['string', 'number', 'boolean'].indexOf(typeof next) > -1 && j < tokens.length - 1) {
184 continue;
185 }
186 i += j - i + 1;
187 break;
188 }
189 }
190 current = next;
191 }
192 return current;
193 };
194 const getCleanedCode = code => code && code.replace('_', '-');
195
196 const consoleLogger = {
197 type: 'logger',
198 log(args) {
199 this.output('log', args);
200 },
201 warn(args) {
202 this.output('warn', args);
203 },
204 error(args) {
205 this.output('error', args);
206 },
207 output(type, args) {
208 if (console && console[type]) console[type].apply(console, args);
209 }
210 };
211 class Logger {
212 constructor(concreteLogger) {
213 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
214 this.init(concreteLogger, options);
215 }
216 init(concreteLogger) {
217 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
218 this.prefix = options.prefix || 'i18next:';
219 this.logger = concreteLogger || consoleLogger;
220 this.options = options;
221 this.debug = options.debug;
222 }
223 log() {
224 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
225 args[_key] = arguments[_key];
226 }
227 return this.forward(args, 'log', '', true);
228 }
229 warn() {
230 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
231 args[_key2] = arguments[_key2];
232 }
233 return this.forward(args, 'warn', '', true);
234 }
235 error() {
236 for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
237 args[_key3] = arguments[_key3];
238 }
239 return this.forward(args, 'error', '');
240 }
241 deprecate() {
242 for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
243 args[_key4] = arguments[_key4];
244 }
245 return this.forward(args, 'warn', 'WARNING DEPRECATED: ', true);
246 }
247 forward(args, lvl, prefix, debugOnly) {
248 if (debugOnly && !this.debug) return null;
249 if (isString(args[0])) args[0] = `${prefix}${this.prefix} ${args[0]}`;
250 return this.logger[lvl](args);
251 }
252 create(moduleName) {
253 return new Logger(this.logger, {
254 ...{
255 prefix: `${this.prefix}:${moduleName}:`
256 },
257 ...this.options
258 });
259 }
260 clone(options) {
261 options = options || this.options;
262 options.prefix = options.prefix || this.prefix;
263 return new Logger(this.logger, options);
264 }
265 }
266 var baseLogger = new Logger();
267
268 class EventEmitter {
269 constructor() {
270 this.observers = {};
271 }
272 on(events, listener) {
273 events.split(' ').forEach(event => {
274 if (!this.observers[event]) this.observers[event] = new Map();
275 const numListeners = this.observers[event].get(listener) || 0;
276 this.observers[event].set(listener, numListeners + 1);
277 });
278 return this;
279 }
280 off(event, listener) {
281 if (!this.observers[event]) return;
282 if (!listener) {
283 delete this.observers[event];
284 return;
285 }
286 this.observers[event].delete(listener);
287 }
288 emit(event) {
289 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
290 args[_key - 1] = arguments[_key];
291 }
292 if (this.observers[event]) {
293 const cloned = Array.from(this.observers[event].entries());
294 cloned.forEach(_ref => {
295 let [observer, numTimesAdded] = _ref;
296 for (let i = 0; i < numTimesAdded; i++) {
297 observer(...args);
298 }
299 });
300 }
301 if (this.observers['*']) {
302 const cloned = Array.from(this.observers['*'].entries());
303 cloned.forEach(_ref2 => {
304 let [observer, numTimesAdded] = _ref2;
305 for (let i = 0; i < numTimesAdded; i++) {
306 observer.apply(observer, [event, ...args]);
307 }
308 });
309 }
310 }
311 }
312
313 class ResourceStore extends EventEmitter {
314 constructor(data) {
315 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
316 ns: ['translation'],
317 defaultNS: 'translation'
318 };
319 super();
320 this.data = data || {};
321 this.options = options;
322 if (this.options.keySeparator === undefined) {
323 this.options.keySeparator = '.';
324 }
325 if (this.options.ignoreJSONStructure === undefined) {
326 this.options.ignoreJSONStructure = true;
327 }
328 }
329 addNamespaces(ns) {
330 if (this.options.ns.indexOf(ns) < 0) {
331 this.options.ns.push(ns);
332 }
333 }
334 removeNamespaces(ns) {
335 const index = this.options.ns.indexOf(ns);
336 if (index > -1) {
337 this.options.ns.splice(index, 1);
338 }
339 }
340 getResource(lng, ns, key) {
341 let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
342 const keySeparator = options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator;
343 const ignoreJSONStructure = options.ignoreJSONStructure !== undefined ? options.ignoreJSONStructure : this.options.ignoreJSONStructure;
344 let path;
345 if (lng.indexOf('.') > -1) {
346 path = lng.split('.');
347 } else {
348 path = [lng, ns];
349 if (key) {
350 if (Array.isArray(key)) {
351 path.push(...key);
352 } else if (isString(key) && keySeparator) {
353 path.push(...key.split(keySeparator));
354 } else {
355 path.push(key);
356 }
357 }
358 }
359 const result = getPath(this.data, path);
360 if (!result && !ns && !key && lng.indexOf('.') > -1) {
361 lng = path[0];
362 ns = path[1];
363 key = path.slice(2).join('.');
364 }
365 if (result || !ignoreJSONStructure || !isString(key)) return result;
366 return deepFind(this.data && this.data[lng] && this.data[lng][ns], key, keySeparator);
367 }
368 addResource(lng, ns, key, value) {
369 let options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {
370 silent: false
371 };
372 const keySeparator = options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator;
373 let path = [lng, ns];
374 if (key) path = path.concat(keySeparator ? key.split(keySeparator) : key);
375 if (lng.indexOf('.') > -1) {
376 path = lng.split('.');
377 value = ns;
378 ns = path[1];
379 }
380 this.addNamespaces(ns);
381 setPath(this.data, path, value);
382 if (!options.silent) this.emit('added', lng, ns, key, value);
383 }
384 addResources(lng, ns, resources) {
385 let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {
386 silent: false
387 };
388 for (const m in resources) {
389 if (isString(resources[m]) || Array.isArray(resources[m])) this.addResource(lng, ns, m, resources[m], {
390 silent: true
391 });
392 }
393 if (!options.silent) this.emit('added', lng, ns, resources);
394 }
395 addResourceBundle(lng, ns, resources, deep, overwrite) {
396 let options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {
397 silent: false,
398 skipCopy: false
399 };
400 let path = [lng, ns];
401 if (lng.indexOf('.') > -1) {
402 path = lng.split('.');
403 deep = resources;
404 resources = ns;
405 ns = path[1];
406 }
407 this.addNamespaces(ns);
408 let pack = getPath(this.data, path) || {};
409 if (!options.skipCopy) resources = JSON.parse(JSON.stringify(resources));
410 if (deep) {
411 deepExtend(pack, resources, overwrite);
412 } else {
413 pack = {
414 ...pack,
415 ...resources
416 };
417 }
418 setPath(this.data, path, pack);
419 if (!options.silent) this.emit('added', lng, ns, resources);
420 }
421 removeResourceBundle(lng, ns) {
422 if (this.hasResourceBundle(lng, ns)) {
423 delete this.data[lng][ns];
424 }
425 this.removeNamespaces(ns);
426 this.emit('removed', lng, ns);
427 }
428 hasResourceBundle(lng, ns) {
429 return this.getResource(lng, ns) !== undefined;
430 }
431 getResourceBundle(lng, ns) {
432 if (!ns) ns = this.options.defaultNS;
433 if (this.options.compatibilityAPI === 'v1') return {
434 ...{},
435 ...this.getResource(lng, ns)
436 };
437 return this.getResource(lng, ns);
438 }
439 getDataByLanguage(lng) {
440 return this.data[lng];
441 }
442 hasLanguageSomeTranslations(lng) {
443 const data = this.getDataByLanguage(lng);
444 const n = data && Object.keys(data) || [];
445 return !!n.find(v => data[v] && Object.keys(data[v]).length > 0);
446 }
447 toJSON() {
448 return this.data;
449 }
450 }
451
452 var postProcessor = {
453 processors: {},
454 addPostProcessor(module) {
455 this.processors[module.name] = module;
456 },
457 handle(processors, value, key, options, translator) {
458 processors.forEach(processor => {
459 if (this.processors[processor]) value = this.processors[processor].process(value, key, options, translator);
460 });
461 return value;
462 }
463 };
464
465 const checkedLoadedFor = {};
466 class Translator extends EventEmitter {
467 constructor(services) {
468 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
469 super();
470 copy(['resourceStore', 'languageUtils', 'pluralResolver', 'interpolator', 'backendConnector', 'i18nFormat', 'utils'], services, this);
471 this.options = options;
472 if (this.options.keySeparator === undefined) {
473 this.options.keySeparator = '.';
474 }
475 this.logger = baseLogger.create('translator');
476 }
477 changeLanguage(lng) {
478 if (lng) this.language = lng;
479 }
480 exists(key) {
481 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
482 interpolation: {}
483 };
484 if (key === undefined || key === null) {
485 return false;
486 }
487 const resolved = this.resolve(key, options);
488 return resolved && resolved.res !== undefined;
489 }
490 extractFromKey(key, options) {
491 let nsSeparator = options.nsSeparator !== undefined ? options.nsSeparator : this.options.nsSeparator;
492 if (nsSeparator === undefined) nsSeparator = ':';
493 const keySeparator = options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator;
494 let namespaces = options.ns || this.options.defaultNS || [];
495 const wouldCheckForNsInKey = nsSeparator && key.indexOf(nsSeparator) > -1;
496 const seemsNaturalLanguage = !this.options.userDefinedKeySeparator && !options.keySeparator && !this.options.userDefinedNsSeparator && !options.nsSeparator && !looksLikeObjectPath(key, nsSeparator, keySeparator);
497 if (wouldCheckForNsInKey && !seemsNaturalLanguage) {
498 const m = key.match(this.interpolator.nestingRegexp);
499 if (m && m.length > 0) {
500 return {
501 key,
502 namespaces
503 };
504 }
505 const parts = key.split(nsSeparator);
506 if (nsSeparator !== keySeparator || nsSeparator === keySeparator && this.options.ns.indexOf(parts[0]) > -1) namespaces = parts.shift();
507 key = parts.join(keySeparator);
508 }
509 if (isString(namespaces)) namespaces = [namespaces];
510 return {
511 key,
512 namespaces
513 };
514 }
515 translate(keys, options, lastKey) {
516 if (typeof options !== 'object' && this.options.overloadTranslationOptionHandler) {
517 options = this.options.overloadTranslationOptionHandler(arguments);
518 }
519 if (typeof options === 'object') options = {
520 ...options
521 };
522 if (!options) options = {};
523 if (keys === undefined || keys === null) return '';
524 if (!Array.isArray(keys)) keys = [String(keys)];
525 const returnDetails = options.returnDetails !== undefined ? options.returnDetails : this.options.returnDetails;
526 const keySeparator = options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator;
527 const {
528 key,
529 namespaces
530 } = this.extractFromKey(keys[keys.length - 1], options);
531 const namespace = namespaces[namespaces.length - 1];
532 const lng = options.lng || this.language;
533 const appendNamespaceToCIMode = options.appendNamespaceToCIMode || this.options.appendNamespaceToCIMode;
534 if (lng && lng.toLowerCase() === 'cimode') {
535 if (appendNamespaceToCIMode) {
536 const nsSeparator = options.nsSeparator || this.options.nsSeparator;
537 if (returnDetails) {
538 return {
539 res: `${namespace}${nsSeparator}${key}`,
540 usedKey: key,
541 exactUsedKey: key,
542 usedLng: lng,
543 usedNS: namespace,
544 usedParams: this.getUsedParamsDetails(options)
545 };
546 }
547 return `${namespace}${nsSeparator}${key}`;
548 }
549 if (returnDetails) {
550 return {
551 res: key,
552 usedKey: key,
553 exactUsedKey: key,
554 usedLng: lng,
555 usedNS: namespace,
556 usedParams: this.getUsedParamsDetails(options)
557 };
558 }
559 return key;
560 }
561 const resolved = this.resolve(keys, options);
562 let res = resolved && resolved.res;
563 const resUsedKey = resolved && resolved.usedKey || key;
564 const resExactUsedKey = resolved && resolved.exactUsedKey || key;
565 const resType = Object.prototype.toString.apply(res);
566 const noObject = ['[object Number]', '[object Function]', '[object RegExp]'];
567 const joinArrays = options.joinArrays !== undefined ? options.joinArrays : this.options.joinArrays;
568 const handleAsObjectInI18nFormat = !this.i18nFormat || this.i18nFormat.handleAsObject;
569 const handleAsObject = !isString(res) && typeof res !== 'boolean' && typeof res !== 'number';
570 if (handleAsObjectInI18nFormat && res && handleAsObject && noObject.indexOf(resType) < 0 && !(isString(joinArrays) && Array.isArray(res))) {
571 if (!options.returnObjects && !this.options.returnObjects) {
572 if (!this.options.returnedObjectHandler) {
573 this.logger.warn('accessing an object - but returnObjects options is not enabled!');
574 }
575 const r = this.options.returnedObjectHandler ? this.options.returnedObjectHandler(resUsedKey, res, {
576 ...options,
577 ns: namespaces
578 }) : `key '${key} (${this.language})' returned an object instead of string.`;
579 if (returnDetails) {
580 resolved.res = r;
581 resolved.usedParams = this.getUsedParamsDetails(options);
582 return resolved;
583 }
584 return r;
585 }
586 if (keySeparator) {
587 const resTypeIsArray = Array.isArray(res);
588 const copy = resTypeIsArray ? [] : {};
589 const newKeyToUse = resTypeIsArray ? resExactUsedKey : resUsedKey;
590 for (const m in res) {
591 if (Object.prototype.hasOwnProperty.call(res, m)) {
592 const deepKey = `${newKeyToUse}${keySeparator}${m}`;
593 copy[m] = this.translate(deepKey, {
594 ...options,
595 ...{
596 joinArrays: false,
597 ns: namespaces
598 }
599 });
600 if (copy[m] === deepKey) copy[m] = res[m];
601 }
602 }
603 res = copy;
604 }
605 } else if (handleAsObjectInI18nFormat && isString(joinArrays) && Array.isArray(res)) {
606 res = res.join(joinArrays);
607 if (res) res = this.extendTranslation(res, keys, options, lastKey);
608 } else {
609 let usedDefault = false;
610 let usedKey = false;
611 const needsPluralHandling = options.count !== undefined && !isString(options.count);
612 const hasDefaultValue = Translator.hasDefaultValue(options);
613 const defaultValueSuffix = needsPluralHandling ? this.pluralResolver.getSuffix(lng, options.count, options) : '';
614 const defaultValueSuffixOrdinalFallback = options.ordinal && needsPluralHandling ? this.pluralResolver.getSuffix(lng, options.count, {
615 ordinal: false
616 }) : '';
617 const needsZeroSuffixLookup = needsPluralHandling && !options.ordinal && options.count === 0 && this.pluralResolver.shouldUseIntlApi();
618 const defaultValue = needsZeroSuffixLookup && options[`defaultValue${this.options.pluralSeparator}zero`] || options[`defaultValue${defaultValueSuffix}`] || options[`defaultValue${defaultValueSuffixOrdinalFallback}`] || options.defaultValue;
619 if (!this.isValidLookup(res) && hasDefaultValue) {
620 usedDefault = true;
621 res = defaultValue;
622 }
623 if (!this.isValidLookup(res)) {
624 usedKey = true;
625 res = key;
626 }
627 const missingKeyNoValueFallbackToKey = options.missingKeyNoValueFallbackToKey || this.options.missingKeyNoValueFallbackToKey;
628 const resForMissing = missingKeyNoValueFallbackToKey && usedKey ? undefined : res;
629 const updateMissing = hasDefaultValue && defaultValue !== res && this.options.updateMissing;
630 if (usedKey || usedDefault || updateMissing) {
631 this.logger.log(updateMissing ? 'updateKey' : 'missingKey', lng, namespace, key, updateMissing ? defaultValue : res);
632 if (keySeparator) {
633 const fk = this.resolve(key, {
634 ...options,
635 keySeparator: false
636 });
637 if (fk && fk.res) this.logger.warn('Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.');
638 }
639 let lngs = [];
640 const fallbackLngs = this.languageUtils.getFallbackCodes(this.options.fallbackLng, options.lng || this.language);
641 if (this.options.saveMissingTo === 'fallback' && fallbackLngs && fallbackLngs[0]) {
642 for (let i = 0; i < fallbackLngs.length; i++) {
643 lngs.push(fallbackLngs[i]);
644 }
645 } else if (this.options.saveMissingTo === 'all') {
646 lngs = this.languageUtils.toResolveHierarchy(options.lng || this.language);
647 } else {
648 lngs.push(options.lng || this.language);
649 }
650 const send = (l, k, specificDefaultValue) => {
651 const defaultForMissing = hasDefaultValue && specificDefaultValue !== res ? specificDefaultValue : resForMissing;
652 if (this.options.missingKeyHandler) {
653 this.options.missingKeyHandler(l, namespace, k, defaultForMissing, updateMissing, options);
654 } else if (this.backendConnector && this.backendConnector.saveMissing) {
655 this.backendConnector.saveMissing(l, namespace, k, defaultForMissing, updateMissing, options);
656 }
657 this.emit('missingKey', l, namespace, k, res);
658 };
659 if (this.options.saveMissing) {
660 if (this.options.saveMissingPlurals && needsPluralHandling) {
661 lngs.forEach(language => {
662 const suffixes = this.pluralResolver.getSuffixes(language, options);
663 if (needsZeroSuffixLookup && options[`defaultValue${this.options.pluralSeparator}zero`] && suffixes.indexOf(`${this.options.pluralSeparator}zero`) < 0) {
664 suffixes.push(`${this.options.pluralSeparator}zero`);
665 }
666 suffixes.forEach(suffix => {
667 send([language], key + suffix, options[`defaultValue${suffix}`] || defaultValue);
668 });
669 });
670 } else {
671 send(lngs, key, defaultValue);
672 }
673 }
674 }
675 res = this.extendTranslation(res, keys, options, resolved, lastKey);
676 if (usedKey && res === key && this.options.appendNamespaceToMissingKey) res = `${namespace}:${key}`;
677 if ((usedKey || usedDefault) && this.options.parseMissingKeyHandler) {
678 if (this.options.compatibilityAPI !== 'v1') {
679 res = this.options.parseMissingKeyHandler(this.options.appendNamespaceToMissingKey ? `${namespace}:${key}` : key, usedDefault ? res : undefined);
680 } else {
681 res = this.options.parseMissingKeyHandler(res);
682 }
683 }
684 }
685 if (returnDetails) {
686 resolved.res = res;
687 resolved.usedParams = this.getUsedParamsDetails(options);
688 return resolved;
689 }
690 return res;
691 }
692 extendTranslation(res, key, options, resolved, lastKey) {
693 var _this = this;
694 if (this.i18nFormat && this.i18nFormat.parse) {
695 res = this.i18nFormat.parse(res, {
696 ...this.options.interpolation.defaultVariables,
697 ...options
698 }, options.lng || this.language || resolved.usedLng, resolved.usedNS, resolved.usedKey, {
699 resolved
700 });
701 } else if (!options.skipInterpolation) {
702 if (options.interpolation) this.interpolator.init({
703 ...options,
704 ...{
705 interpolation: {
706 ...this.options.interpolation,
707 ...options.interpolation
708 }
709 }
710 });
711 const skipOnVariables = isString(res) && (options && options.interpolation && options.interpolation.skipOnVariables !== undefined ? options.interpolation.skipOnVariables : this.options.interpolation.skipOnVariables);
712 let nestBef;
713 if (skipOnVariables) {
714 const nb = res.match(this.interpolator.nestingRegexp);
715 nestBef = nb && nb.length;
716 }
717 let data = options.replace && !isString(options.replace) ? options.replace : options;
718 if (this.options.interpolation.defaultVariables) data = {
719 ...this.options.interpolation.defaultVariables,
720 ...data
721 };
722 res = this.interpolator.interpolate(res, data, options.lng || this.language || resolved.usedLng, options);
723 if (skipOnVariables) {
724 const na = res.match(this.interpolator.nestingRegexp);
725 const nestAft = na && na.length;
726 if (nestBef < nestAft) options.nest = false;
727 }
728 if (!options.lng && this.options.compatibilityAPI !== 'v1' && resolved && resolved.res) options.lng = this.language || resolved.usedLng;
729 if (options.nest !== false) res = this.interpolator.nest(res, function () {
730 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
731 args[_key] = arguments[_key];
732 }
733 if (lastKey && lastKey[0] === args[0] && !options.context) {
734 _this.logger.warn(`It seems you are nesting recursively key: ${args[0]} in key: ${key[0]}`);
735 return null;
736 }
737 return _this.translate(...args, key);
738 }, options);
739 if (options.interpolation) this.interpolator.reset();
740 }
741 const postProcess = options.postProcess || this.options.postProcess;
742 const postProcessorNames = isString(postProcess) ? [postProcess] : postProcess;
743 if (res !== undefined && res !== null && postProcessorNames && postProcessorNames.length && options.applyPostProcessor !== false) {
744 res = postProcessor.handle(postProcessorNames, res, key, this.options && this.options.postProcessPassResolved ? {
745 i18nResolved: {
746 ...resolved,
747 usedParams: this.getUsedParamsDetails(options)
748 },
749 ...options
750 } : options, this);
751 }
752 return res;
753 }
754 resolve(keys) {
755 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
756 let found;
757 let usedKey;
758 let exactUsedKey;
759 let usedLng;
760 let usedNS;
761 if (isString(keys)) keys = [keys];
762 keys.forEach(k => {
763 if (this.isValidLookup(found)) return;
764 const extracted = this.extractFromKey(k, options);
765 const key = extracted.key;
766 usedKey = key;
767 let namespaces = extracted.namespaces;
768 if (this.options.fallbackNS) namespaces = namespaces.concat(this.options.fallbackNS);
769 const needsPluralHandling = options.count !== undefined && !isString(options.count);
770 const needsZeroSuffixLookup = needsPluralHandling && !options.ordinal && options.count === 0 && this.pluralResolver.shouldUseIntlApi();
771 const needsContextHandling = options.context !== undefined && (isString(options.context) || typeof options.context === 'number') && options.context !== '';
772 const codes = options.lngs ? options.lngs : this.languageUtils.toResolveHierarchy(options.lng || this.language, options.fallbackLng);
773 namespaces.forEach(ns => {
774 if (this.isValidLookup(found)) return;
775 usedNS = ns;
776 if (!checkedLoadedFor[`${codes[0]}-${ns}`] && this.utils && this.utils.hasLoadedNamespace && !this.utils.hasLoadedNamespace(usedNS)) {
777 checkedLoadedFor[`${codes[0]}-${ns}`] = true;
778 this.logger.warn(`key "${usedKey}" for languages "${codes.join(', ')}" won't get resolved as namespace "${usedNS}" was not yet loaded`, 'This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!');
779 }
780 codes.forEach(code => {
781 if (this.isValidLookup(found)) return;
782 usedLng = code;
783 const finalKeys = [key];
784 if (this.i18nFormat && this.i18nFormat.addLookupKeys) {
785 this.i18nFormat.addLookupKeys(finalKeys, key, code, ns, options);
786 } else {
787 let pluralSuffix;
788 if (needsPluralHandling) pluralSuffix = this.pluralResolver.getSuffix(code, options.count, options);
789 const zeroSuffix = `${this.options.pluralSeparator}zero`;
790 const ordinalPrefix = `${this.options.pluralSeparator}ordinal${this.options.pluralSeparator}`;
791 if (needsPluralHandling) {
792 finalKeys.push(key + pluralSuffix);
793 if (options.ordinal && pluralSuffix.indexOf(ordinalPrefix) === 0) {
794 finalKeys.push(key + pluralSuffix.replace(ordinalPrefix, this.options.pluralSeparator));
795 }
796 if (needsZeroSuffixLookup) {
797 finalKeys.push(key + zeroSuffix);
798 }
799 }
800 if (needsContextHandling) {
801 const contextKey = `${key}${this.options.contextSeparator}${options.context}`;
802 finalKeys.push(contextKey);
803 if (needsPluralHandling) {
804 finalKeys.push(contextKey + pluralSuffix);
805 if (options.ordinal && pluralSuffix.indexOf(ordinalPrefix) === 0) {
806 finalKeys.push(contextKey + pluralSuffix.replace(ordinalPrefix, this.options.pluralSeparator));
807 }
808 if (needsZeroSuffixLookup) {
809 finalKeys.push(contextKey + zeroSuffix);
810 }
811 }
812 }
813 }
814 let possibleKey;
815 while (possibleKey = finalKeys.pop()) {
816 if (!this.isValidLookup(found)) {
817 exactUsedKey = possibleKey;
818 found = this.getResource(code, ns, possibleKey, options);
819 }
820 }
821 });
822 });
823 });
824 return {
825 res: found,
826 usedKey,
827 exactUsedKey,
828 usedLng,
829 usedNS
830 };
831 }
832 isValidLookup(res) {
833 return res !== undefined && !(!this.options.returnNull && res === null) && !(!this.options.returnEmptyString && res === '');
834 }
835 getResource(code, ns, key) {
836 let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
837 if (this.i18nFormat && this.i18nFormat.getResource) return this.i18nFormat.getResource(code, ns, key, options);
838 return this.resourceStore.getResource(code, ns, key, options);
839 }
840 getUsedParamsDetails() {
841 let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
842 const optionsKeys = ['defaultValue', 'ordinal', 'context', 'replace', 'lng', 'lngs', 'fallbackLng', 'ns', 'keySeparator', 'nsSeparator', 'returnObjects', 'returnDetails', 'joinArrays', 'postProcess', 'interpolation'];
843 const useOptionsReplaceForData = options.replace && !isString(options.replace);
844 let data = useOptionsReplaceForData ? options.replace : options;
845 if (useOptionsReplaceForData && typeof options.count !== 'undefined') {
846 data.count = options.count;
847 }
848 if (this.options.interpolation.defaultVariables) {
849 data = {
850 ...this.options.interpolation.defaultVariables,
851 ...data
852 };
853 }
854 if (!useOptionsReplaceForData) {
855 data = {
856 ...data
857 };
858 for (const key of optionsKeys) {
859 delete data[key];
860 }
861 }
862 return data;
863 }
864 static hasDefaultValue(options) {
865 const prefix = 'defaultValue';
866 for (const option in options) {
867 if (Object.prototype.hasOwnProperty.call(options, option) && prefix === option.substring(0, prefix.length) && undefined !== options[option]) {
868 return true;
869 }
870 }
871 return false;
872 }
873 }
874
875 const capitalize = string => string.charAt(0).toUpperCase() + string.slice(1);
876 class LanguageUtil {
877 constructor(options) {
878 this.options = options;
879 this.supportedLngs = this.options.supportedLngs || false;
880 this.logger = baseLogger.create('languageUtils');
881 }
882 getScriptPartFromCode(code) {
883 code = getCleanedCode(code);
884 if (!code || code.indexOf('-') < 0) return null;
885 const p = code.split('-');
886 if (p.length === 2) return null;
887 p.pop();
888 if (p[p.length - 1].toLowerCase() === 'x') return null;
889 return this.formatLanguageCode(p.join('-'));
890 }
891 getLanguagePartFromCode(code) {
892 code = getCleanedCode(code);
893 if (!code || code.indexOf('-') < 0) return code;
894 const p = code.split('-');
895 return this.formatLanguageCode(p[0]);
896 }
897 formatLanguageCode(code) {
898 if (isString(code) && code.indexOf('-') > -1) {
899 if (typeof Intl !== 'undefined' && typeof Intl.getCanonicalLocales !== 'undefined') {
900 try {
901 let formattedCode = Intl.getCanonicalLocales(code)[0];
902 if (formattedCode && this.options.lowerCaseLng) {
903 formattedCode = formattedCode.toLowerCase();
904 }
905 if (formattedCode) return formattedCode;
906 } catch (e) {}
907 }
908 const specialCases = ['hans', 'hant', 'latn', 'cyrl', 'cans', 'mong', 'arab'];
909 let p = code.split('-');
910 if (this.options.lowerCaseLng) {
911 p = p.map(part => part.toLowerCase());
912 } else if (p.length === 2) {
913 p[0] = p[0].toLowerCase();
914 p[1] = p[1].toUpperCase();
915 if (specialCases.indexOf(p[1].toLowerCase()) > -1) p[1] = capitalize(p[1].toLowerCase());
916 } else if (p.length === 3) {
917 p[0] = p[0].toLowerCase();
918 if (p[1].length === 2) p[1] = p[1].toUpperCase();
919 if (p[0] !== 'sgn' && p[2].length === 2) p[2] = p[2].toUpperCase();
920 if (specialCases.indexOf(p[1].toLowerCase()) > -1) p[1] = capitalize(p[1].toLowerCase());
921 if (specialCases.indexOf(p[2].toLowerCase()) > -1) p[2] = capitalize(p[2].toLowerCase());
922 }
923 return p.join('-');
924 }
925 return this.options.cleanCode || this.options.lowerCaseLng ? code.toLowerCase() : code;
926 }
927 isSupportedCode(code) {
928 if (this.options.load === 'languageOnly' || this.options.nonExplicitSupportedLngs) {
929 code = this.getLanguagePartFromCode(code);
930 }
931 return !this.supportedLngs || !this.supportedLngs.length || this.supportedLngs.indexOf(code) > -1;
932 }
933 getBestMatchFromCodes(codes) {
934 if (!codes) return null;
935 let found;
936 codes.forEach(code => {
937 if (found) return;
938 const cleanedLng = this.formatLanguageCode(code);
939 if (!this.options.supportedLngs || this.isSupportedCode(cleanedLng)) found = cleanedLng;
940 });
941 if (!found && this.options.supportedLngs) {
942 codes.forEach(code => {
943 if (found) return;
944 const lngOnly = this.getLanguagePartFromCode(code);
945 if (this.isSupportedCode(lngOnly)) return found = lngOnly;
946 found = this.options.supportedLngs.find(supportedLng => {
947 if (supportedLng === lngOnly) return supportedLng;
948 if (supportedLng.indexOf('-') < 0 && lngOnly.indexOf('-') < 0) return;
949 if (supportedLng.indexOf('-') > 0 && lngOnly.indexOf('-') < 0 && supportedLng.substring(0, supportedLng.indexOf('-')) === lngOnly) return supportedLng;
950 if (supportedLng.indexOf(lngOnly) === 0 && lngOnly.length > 1) return supportedLng;
951 });
952 });
953 }
954 if (!found) found = this.getFallbackCodes(this.options.fallbackLng)[0];
955 return found;
956 }
957 getFallbackCodes(fallbacks, code) {
958 if (!fallbacks) return [];
959 if (typeof fallbacks === 'function') fallbacks = fallbacks(code);
960 if (isString(fallbacks)) fallbacks = [fallbacks];
961 if (Array.isArray(fallbacks)) return fallbacks;
962 if (!code) return fallbacks.default || [];
963 let found = fallbacks[code];
964 if (!found) found = fallbacks[this.getScriptPartFromCode(code)];
965 if (!found) found = fallbacks[this.formatLanguageCode(code)];
966 if (!found) found = fallbacks[this.getLanguagePartFromCode(code)];
967 if (!found) found = fallbacks.default;
968 return found || [];
969 }
970 toResolveHierarchy(code, fallbackCode) {
971 const fallbackCodes = this.getFallbackCodes(fallbackCode || this.options.fallbackLng || [], code);
972 const codes = [];
973 const addCode = c => {
974 if (!c) return;
975 if (this.isSupportedCode(c)) {
976 codes.push(c);
977 } else {
978 this.logger.warn(`rejecting language code not found in supportedLngs: ${c}`);
979 }
980 };
981 if (isString(code) && (code.indexOf('-') > -1 || code.indexOf('_') > -1)) {
982 if (this.options.load !== 'languageOnly') addCode(this.formatLanguageCode(code));
983 if (this.options.load !== 'languageOnly' && this.options.load !== 'currentOnly') addCode(this.getScriptPartFromCode(code));
984 if (this.options.load !== 'currentOnly') addCode(this.getLanguagePartFromCode(code));
985 } else if (isString(code)) {
986 addCode(this.formatLanguageCode(code));
987 }
988 fallbackCodes.forEach(fc => {
989 if (codes.indexOf(fc) < 0) addCode(this.formatLanguageCode(fc));
990 });
991 return codes;
992 }
993 }
994
995 let sets = [{
996 lngs: ['ach', 'ak', 'am', 'arn', 'br', 'fil', 'gun', 'ln', 'mfe', 'mg', 'mi', 'oc', 'pt', 'pt-BR', 'tg', 'tl', 'ti', 'tr', 'uz', 'wa'],
997 nr: [1, 2],
998 fc: 1
999 }, {
1000 lngs: ['af', 'an', 'ast', 'az', 'bg', 'bn', 'ca', 'da', 'de', 'dev', 'el', 'en', 'eo', 'es', 'et', 'eu', 'fi', 'fo', 'fur', 'fy', 'gl', 'gu', 'ha', 'hi', 'hu', 'hy', 'ia', 'it', 'kk', 'kn', 'ku', 'lb', 'mai', 'ml', 'mn', 'mr', 'nah', 'nap', 'nb', 'ne', 'nl', 'nn', 'no', 'nso', 'pa', 'pap', 'pms', 'ps', 'pt-PT', 'rm', 'sco', 'se', 'si', 'so', 'son', 'sq', 'sv', 'sw', 'ta', 'te', 'tk', 'ur', 'yo'],
1001 nr: [1, 2],
1002 fc: 2
1003 }, {
1004 lngs: ['ay', 'bo', 'cgg', 'fa', 'ht', 'id', 'ja', 'jbo', 'ka', 'km', 'ko', 'ky', 'lo', 'ms', 'sah', 'su', 'th', 'tt', 'ug', 'vi', 'wo', 'zh'],
1005 nr: [1],
1006 fc: 3
1007 }, {
1008 lngs: ['be', 'bs', 'cnr', 'dz', 'hr', 'ru', 'sr', 'uk'],
1009 nr: [1, 2, 5],
1010 fc: 4
1011 }, {
1012 lngs: ['ar'],
1013 nr: [0, 1, 2, 3, 11, 100],
1014 fc: 5
1015 }, {
1016 lngs: ['cs', 'sk'],
1017 nr: [1, 2, 5],
1018 fc: 6
1019 }, {
1020 lngs: ['csb', 'pl'],
1021 nr: [1, 2, 5],
1022 fc: 7
1023 }, {
1024 lngs: ['cy'],
1025 nr: [1, 2, 3, 8],
1026 fc: 8
1027 }, {
1028 lngs: ['fr'],
1029 nr: [1, 2],
1030 fc: 9
1031 }, {
1032 lngs: ['ga'],
1033 nr: [1, 2, 3, 7, 11],
1034 fc: 10
1035 }, {
1036 lngs: ['gd'],
1037 nr: [1, 2, 3, 20],
1038 fc: 11
1039 }, {
1040 lngs: ['is'],
1041 nr: [1, 2],
1042 fc: 12
1043 }, {
1044 lngs: ['jv'],
1045 nr: [0, 1],
1046 fc: 13
1047 }, {
1048 lngs: ['kw'],
1049 nr: [1, 2, 3, 4],
1050 fc: 14
1051 }, {
1052 lngs: ['lt'],
1053 nr: [1, 2, 10],
1054 fc: 15
1055 }, {
1056 lngs: ['lv'],
1057 nr: [1, 2, 0],
1058 fc: 16
1059 }, {
1060 lngs: ['mk'],
1061 nr: [1, 2],
1062 fc: 17
1063 }, {
1064 lngs: ['mnk'],
1065 nr: [0, 1, 2],
1066 fc: 18
1067 }, {
1068 lngs: ['mt'],
1069 nr: [1, 2, 11, 20],
1070 fc: 19
1071 }, {
1072 lngs: ['or'],
1073 nr: [2, 1],
1074 fc: 2
1075 }, {
1076 lngs: ['ro'],
1077 nr: [1, 2, 20],
1078 fc: 20
1079 }, {
1080 lngs: ['sl'],
1081 nr: [5, 1, 2, 3],
1082 fc: 21
1083 }, {
1084 lngs: ['he', 'iw'],
1085 nr: [1, 2, 20, 21],
1086 fc: 22
1087 }];
1088 let _rulesPluralsTypes = {
1089 1: n => Number(n > 1),
1090 2: n => Number(n != 1),
1091 3: n => 0,
1092 4: n => Number(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2),
1093 5: n => Number(n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5),
1094 6: n => Number(n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2),
1095 7: n => Number(n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2),
1096 8: n => Number(n == 1 ? 0 : n == 2 ? 1 : n != 8 && n != 11 ? 2 : 3),
1097 9: n => Number(n >= 2),
1098 10: n => Number(n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4),
1099 11: n => Number(n == 1 || n == 11 ? 0 : n == 2 || n == 12 ? 1 : n > 2 && n < 20 ? 2 : 3),
1100 12: n => Number(n % 10 != 1 || n % 100 == 11),
1101 13: n => Number(n !== 0),
1102 14: n => Number(n == 1 ? 0 : n == 2 ? 1 : n == 3 ? 2 : 3),
1103 15: n => Number(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2),
1104 16: n => Number(n % 10 == 1 && n % 100 != 11 ? 0 : n !== 0 ? 1 : 2),
1105 17: n => Number(n == 1 || n % 10 == 1 && n % 100 != 11 ? 0 : 1),
1106 18: n => Number(n == 0 ? 0 : n == 1 ? 1 : 2),
1107 19: n => Number(n == 1 ? 0 : n == 0 || n % 100 > 1 && n % 100 < 11 ? 1 : n % 100 > 10 && n % 100 < 20 ? 2 : 3),
1108 20: n => Number(n == 1 ? 0 : n == 0 || n % 100 > 0 && n % 100 < 20 ? 1 : 2),
1109 21: n => Number(n % 100 == 1 ? 1 : n % 100 == 2 ? 2 : n % 100 == 3 || n % 100 == 4 ? 3 : 0),
1110 22: n => Number(n == 1 ? 0 : n == 2 ? 1 : (n < 0 || n > 10) && n % 10 == 0 ? 2 : 3)
1111 };
1112 const nonIntlVersions = ['v1', 'v2', 'v3'];
1113 const intlVersions = ['v4'];
1114 const suffixesOrder = {
1115 zero: 0,
1116 one: 1,
1117 two: 2,
1118 few: 3,
1119 many: 4,
1120 other: 5
1121 };
1122 const createRules = () => {
1123 const rules = {};
1124 sets.forEach(set => {
1125 set.lngs.forEach(l => {
1126 rules[l] = {
1127 numbers: set.nr,
1128 plurals: _rulesPluralsTypes[set.fc]
1129 };
1130 });
1131 });
1132 return rules;
1133 };
1134 class PluralResolver {
1135 constructor(languageUtils) {
1136 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1137 this.languageUtils = languageUtils;
1138 this.options = options;
1139 this.logger = baseLogger.create('pluralResolver');
1140 if ((!this.options.compatibilityJSON || intlVersions.includes(this.options.compatibilityJSON)) && (typeof Intl === 'undefined' || !Intl.PluralRules)) {
1141 this.options.compatibilityJSON = 'v3';
1142 this.logger.error('Your environment seems not to be Intl API compatible, use an Intl.PluralRules polyfill. Will fallback to the compatibilityJSON v3 format handling.');
1143 }
1144 this.rules = createRules();
1145 this.pluralRulesCache = {};
1146 }
1147 addRule(lng, obj) {
1148 this.rules[lng] = obj;
1149 }
1150 clearCache() {
1151 this.pluralRulesCache = {};
1152 }
1153 getRule(code) {
1154 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1155 if (this.shouldUseIntlApi()) {
1156 try {
1157 const cleanedCode = getCleanedCode(code === 'dev' ? 'en' : code);
1158 const type = options.ordinal ? 'ordinal' : 'cardinal';
1159 const cacheKey = JSON.stringify({
1160 cleanedCode,
1161 type
1162 });
1163 if (cacheKey in this.pluralRulesCache) {
1164 return this.pluralRulesCache[cacheKey];
1165 }
1166 const rule = new Intl.PluralRules(cleanedCode, {
1167 type
1168 });
1169 this.pluralRulesCache[cacheKey] = rule;
1170 return rule;
1171 } catch (err) {
1172 return;
1173 }
1174 }
1175 return this.rules[code] || this.rules[this.languageUtils.getLanguagePartFromCode(code)];
1176 }
1177 needsPlural(code) {
1178 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1179 const rule = this.getRule(code, options);
1180 if (this.shouldUseIntlApi()) {
1181 return rule && rule.resolvedOptions().pluralCategories.length > 1;
1182 }
1183 return rule && rule.numbers.length > 1;
1184 }
1185 getPluralFormsOfKey(code, key) {
1186 let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1187 return this.getSuffixes(code, options).map(suffix => `${key}${suffix}`);
1188 }
1189 getSuffixes(code) {
1190 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1191 const rule = this.getRule(code, options);
1192 if (!rule) {
1193 return [];
1194 }
1195 if (this.shouldUseIntlApi()) {
1196 return rule.resolvedOptions().pluralCategories.sort((pluralCategory1, pluralCategory2) => suffixesOrder[pluralCategory1] - suffixesOrder[pluralCategory2]).map(pluralCategory => `${this.options.prepend}${options.ordinal ? `ordinal${this.options.prepend}` : ''}${pluralCategory}`);
1197 }
1198 return rule.numbers.map(number => this.getSuffix(code, number, options));
1199 }
1200 getSuffix(code, count) {
1201 let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1202 const rule = this.getRule(code, options);
1203 if (rule) {
1204 if (this.shouldUseIntlApi()) {
1205 return `${this.options.prepend}${options.ordinal ? `ordinal${this.options.prepend}` : ''}${rule.select(count)}`;
1206 }
1207 return this.getSuffixRetroCompatible(rule, count);
1208 }
1209 this.logger.warn(`no plural rule found for: ${code}`);
1210 return '';
1211 }
1212 getSuffixRetroCompatible(rule, count) {
1213 const idx = rule.noAbs ? rule.plurals(count) : rule.plurals(Math.abs(count));
1214 let suffix = rule.numbers[idx];
1215 if (this.options.simplifyPluralSuffix && rule.numbers.length === 2 && rule.numbers[0] === 1) {
1216 if (suffix === 2) {
1217 suffix = 'plural';
1218 } else if (suffix === 1) {
1219 suffix = '';
1220 }
1221 }
1222 const returnSuffix = () => this.options.prepend && suffix.toString() ? this.options.prepend + suffix.toString() : suffix.toString();
1223 if (this.options.compatibilityJSON === 'v1') {
1224 if (suffix === 1) return '';
1225 if (typeof suffix === 'number') return `_plural_${suffix.toString()}`;
1226 return returnSuffix();
1227 } else if (this.options.compatibilityJSON === 'v2') {
1228 return returnSuffix();
1229 } else if (this.options.simplifyPluralSuffix && rule.numbers.length === 2 && rule.numbers[0] === 1) {
1230 return returnSuffix();
1231 }
1232 return this.options.prepend && idx.toString() ? this.options.prepend + idx.toString() : idx.toString();
1233 }
1234 shouldUseIntlApi() {
1235 return !nonIntlVersions.includes(this.options.compatibilityJSON);
1236 }
1237 }
1238
1239 const deepFindWithDefaults = function (data, defaultData, key) {
1240 let keySeparator = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '.';
1241 let ignoreJSONStructure = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
1242 let path = getPathWithDefaults(data, defaultData, key);
1243 if (!path && ignoreJSONStructure && isString(key)) {
1244 path = deepFind(data, key, keySeparator);
1245 if (path === undefined) path = deepFind(defaultData, key, keySeparator);
1246 }
1247 return path;
1248 };
1249 const regexSafe = val => val.replace(/\$/g, '$$$$');
1250 class Interpolator {
1251 constructor() {
1252 let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1253 this.logger = baseLogger.create('interpolator');
1254 this.options = options;
1255 this.format = options.interpolation && options.interpolation.format || (value => value);
1256 this.init(options);
1257 }
1258 init() {
1259 let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1260 if (!options.interpolation) options.interpolation = {
1261 escapeValue: true
1262 };
1263 const {
1264 escape: escape$1,
1265 escapeValue,
1266 useRawValueToEscape,
1267 prefix,
1268 prefixEscaped,
1269 suffix,
1270 suffixEscaped,
1271 formatSeparator,
1272 unescapeSuffix,
1273 unescapePrefix,
1274 nestingPrefix,
1275 nestingPrefixEscaped,
1276 nestingSuffix,
1277 nestingSuffixEscaped,
1278 nestingOptionsSeparator,
1279 maxReplaces,
1280 alwaysFormat
1281 } = options.interpolation;
1282 this.escape = escape$1 !== undefined ? escape$1 : escape;
1283 this.escapeValue = escapeValue !== undefined ? escapeValue : true;
1284 this.useRawValueToEscape = useRawValueToEscape !== undefined ? useRawValueToEscape : false;
1285 this.prefix = prefix ? regexEscape(prefix) : prefixEscaped || '{{';
1286 this.suffix = suffix ? regexEscape(suffix) : suffixEscaped || '}}';
1287 this.formatSeparator = formatSeparator || ',';
1288 this.unescapePrefix = unescapeSuffix ? '' : unescapePrefix || '-';
1289 this.unescapeSuffix = this.unescapePrefix ? '' : unescapeSuffix || '';
1290 this.nestingPrefix = nestingPrefix ? regexEscape(nestingPrefix) : nestingPrefixEscaped || regexEscape('$t(');
1291 this.nestingSuffix = nestingSuffix ? regexEscape(nestingSuffix) : nestingSuffixEscaped || regexEscape(')');
1292 this.nestingOptionsSeparator = nestingOptionsSeparator || ',';
1293 this.maxReplaces = maxReplaces || 1000;
1294 this.alwaysFormat = alwaysFormat !== undefined ? alwaysFormat : false;
1295 this.resetRegExp();
1296 }
1297 reset() {
1298 if (this.options) this.init(this.options);
1299 }
1300 resetRegExp() {
1301 const getOrResetRegExp = (existingRegExp, pattern) => {
1302 if (existingRegExp && existingRegExp.source === pattern) {
1303 existingRegExp.lastIndex = 0;
1304 return existingRegExp;
1305 }
1306 return new RegExp(pattern, 'g');
1307 };
1308 this.regexp = getOrResetRegExp(this.regexp, `${this.prefix}(.+?)${this.suffix}`);
1309 this.regexpUnescape = getOrResetRegExp(this.regexpUnescape, `${this.prefix}${this.unescapePrefix}(.+?)${this.unescapeSuffix}${this.suffix}`);
1310 this.nestingRegexp = getOrResetRegExp(this.nestingRegexp, `${this.nestingPrefix}(.+?)${this.nestingSuffix}`);
1311 }
1312 interpolate(str, data, lng, options) {
1313 let match;
1314 let value;
1315 let replaces;
1316 const defaultData = this.options && this.options.interpolation && this.options.interpolation.defaultVariables || {};
1317 const handleFormat = key => {
1318 if (key.indexOf(this.formatSeparator) < 0) {
1319 const path = deepFindWithDefaults(data, defaultData, key, this.options.keySeparator, this.options.ignoreJSONStructure);
1320 return this.alwaysFormat ? this.format(path, undefined, lng, {
1321 ...options,
1322 ...data,
1323 interpolationkey: key
1324 }) : path;
1325 }
1326 const p = key.split(this.formatSeparator);
1327 const k = p.shift().trim();
1328 const f = p.join(this.formatSeparator).trim();
1329 return this.format(deepFindWithDefaults(data, defaultData, k, this.options.keySeparator, this.options.ignoreJSONStructure), f, lng, {
1330 ...options,
1331 ...data,
1332 interpolationkey: k
1333 });
1334 };
1335 this.resetRegExp();
1336 const missingInterpolationHandler = options && options.missingInterpolationHandler || this.options.missingInterpolationHandler;
1337 const skipOnVariables = options && options.interpolation && options.interpolation.skipOnVariables !== undefined ? options.interpolation.skipOnVariables : this.options.interpolation.skipOnVariables;
1338 const todos = [{
1339 regex: this.regexpUnescape,
1340 safeValue: val => regexSafe(val)
1341 }, {
1342 regex: this.regexp,
1343 safeValue: val => this.escapeValue ? regexSafe(this.escape(val)) : regexSafe(val)
1344 }];
1345 todos.forEach(todo => {
1346 replaces = 0;
1347 while (match = todo.regex.exec(str)) {
1348 const matchedVar = match[1].trim();
1349 value = handleFormat(matchedVar);
1350 if (value === undefined) {
1351 if (typeof missingInterpolationHandler === 'function') {
1352 const temp = missingInterpolationHandler(str, match, options);
1353 value = isString(temp) ? temp : '';
1354 } else if (options && Object.prototype.hasOwnProperty.call(options, matchedVar)) {
1355 value = '';
1356 } else if (skipOnVariables) {
1357 value = match[0];
1358 continue;
1359 } else {
1360 this.logger.warn(`missed to pass in variable ${matchedVar} for interpolating ${str}`);
1361 value = '';
1362 }
1363 } else if (!isString(value) && !this.useRawValueToEscape) {
1364 value = makeString(value);
1365 }
1366 const safeValue = todo.safeValue(value);
1367 str = str.replace(match[0], safeValue);
1368 if (skipOnVariables) {
1369 todo.regex.lastIndex += value.length;
1370 todo.regex.lastIndex -= match[0].length;
1371 } else {
1372 todo.regex.lastIndex = 0;
1373 }
1374 replaces++;
1375 if (replaces >= this.maxReplaces) {
1376 break;
1377 }
1378 }
1379 });
1380 return str;
1381 }
1382 nest(str, fc) {
1383 let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1384 let match;
1385 let value;
1386 let clonedOptions;
1387 const handleHasOptions = (key, inheritedOptions) => {
1388 const sep = this.nestingOptionsSeparator;
1389 if (key.indexOf(sep) < 0) return key;
1390 const c = key.split(new RegExp(`${sep}[ ]*{`));
1391 let optionsString = `{${c[1]}`;
1392 key = c[0];
1393 optionsString = this.interpolate(optionsString, clonedOptions);
1394 const matchedSingleQuotes = optionsString.match(/'/g);
1395 const matchedDoubleQuotes = optionsString.match(/"/g);
1396 if (matchedSingleQuotes && matchedSingleQuotes.length % 2 === 0 && !matchedDoubleQuotes || matchedDoubleQuotes.length % 2 !== 0) {
1397 optionsString = optionsString.replace(/'/g, '"');
1398 }
1399 try {
1400 clonedOptions = JSON.parse(optionsString);
1401 if (inheritedOptions) clonedOptions = {
1402 ...inheritedOptions,
1403 ...clonedOptions
1404 };
1405 } catch (e) {
1406 this.logger.warn(`failed parsing options string in nesting for key ${key}`, e);
1407 return `${key}${sep}${optionsString}`;
1408 }
1409 if (clonedOptions.defaultValue && clonedOptions.defaultValue.indexOf(this.prefix) > -1) delete clonedOptions.defaultValue;
1410 return key;
1411 };
1412 while (match = this.nestingRegexp.exec(str)) {
1413 let formatters = [];
1414 clonedOptions = {
1415 ...options
1416 };
1417 clonedOptions = clonedOptions.replace && !isString(clonedOptions.replace) ? clonedOptions.replace : clonedOptions;
1418 clonedOptions.applyPostProcessor = false;
1419 delete clonedOptions.defaultValue;
1420 let doReduce = false;
1421 if (match[0].indexOf(this.formatSeparator) !== -1 && !/{.*}/.test(match[1])) {
1422 const r = match[1].split(this.formatSeparator).map(elem => elem.trim());
1423 match[1] = r.shift();
1424 formatters = r;
1425 doReduce = true;
1426 }
1427 value = fc(handleHasOptions.call(this, match[1].trim(), clonedOptions), clonedOptions);
1428 if (value && match[0] === str && !isString(value)) return value;
1429 if (!isString(value)) value = makeString(value);
1430 if (!value) {
1431 this.logger.warn(`missed to resolve ${match[1]} for nesting ${str}`);
1432 value = '';
1433 }
1434 if (doReduce) {
1435 value = formatters.reduce((v, f) => this.format(v, f, options.lng, {
1436 ...options,
1437 interpolationkey: match[1].trim()
1438 }), value.trim());
1439 }
1440 str = str.replace(match[0], value);
1441 this.regexp.lastIndex = 0;
1442 }
1443 return str;
1444 }
1445 }
1446
1447 const parseFormatStr = formatStr => {
1448 let formatName = formatStr.toLowerCase().trim();
1449 const formatOptions = {};
1450 if (formatStr.indexOf('(') > -1) {
1451 const p = formatStr.split('(');
1452 formatName = p[0].toLowerCase().trim();
1453 const optStr = p[1].substring(0, p[1].length - 1);
1454 if (formatName === 'currency' && optStr.indexOf(':') < 0) {
1455 if (!formatOptions.currency) formatOptions.currency = optStr.trim();
1456 } else if (formatName === 'relativetime' && optStr.indexOf(':') < 0) {
1457 if (!formatOptions.range) formatOptions.range = optStr.trim();
1458 } else {
1459 const opts = optStr.split(';');
1460 opts.forEach(opt => {
1461 if (opt) {
1462 const [key, ...rest] = opt.split(':');
1463 const val = rest.join(':').trim().replace(/^'+|'+$/g, '');
1464 const trimmedKey = key.trim();
1465 if (!formatOptions[trimmedKey]) formatOptions[trimmedKey] = val;
1466 if (val === 'false') formatOptions[trimmedKey] = false;
1467 if (val === 'true') formatOptions[trimmedKey] = true;
1468 if (!isNaN(val)) formatOptions[trimmedKey] = parseInt(val, 10);
1469 }
1470 });
1471 }
1472 }
1473 return {
1474 formatName,
1475 formatOptions
1476 };
1477 };
1478 const createCachedFormatter = fn => {
1479 const cache = {};
1480 return (val, lng, options) => {
1481 let optForCache = options;
1482 if (options && options.interpolationkey && options.formatParams && options.formatParams[options.interpolationkey] && options[options.interpolationkey]) {
1483 optForCache = {
1484 ...optForCache,
1485 [options.interpolationkey]: undefined
1486 };
1487 }
1488 const key = lng + JSON.stringify(optForCache);
1489 let formatter = cache[key];
1490 if (!formatter) {
1491 formatter = fn(getCleanedCode(lng), options);
1492 cache[key] = formatter;
1493 }
1494 return formatter(val);
1495 };
1496 };
1497 class Formatter {
1498 constructor() {
1499 let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1500 this.logger = baseLogger.create('formatter');
1501 this.options = options;
1502 this.formats = {
1503 number: createCachedFormatter((lng, opt) => {
1504 const formatter = new Intl.NumberFormat(lng, {
1505 ...opt
1506 });
1507 return val => formatter.format(val);
1508 }),
1509 currency: createCachedFormatter((lng, opt) => {
1510 const formatter = new Intl.NumberFormat(lng, {
1511 ...opt,
1512 style: 'currency'
1513 });
1514 return val => formatter.format(val);
1515 }),
1516 datetime: createCachedFormatter((lng, opt) => {
1517 const formatter = new Intl.DateTimeFormat(lng, {
1518 ...opt
1519 });
1520 return val => formatter.format(val);
1521 }),
1522 relativetime: createCachedFormatter((lng, opt) => {
1523 const formatter = new Intl.RelativeTimeFormat(lng, {
1524 ...opt
1525 });
1526 return val => formatter.format(val, opt.range || 'day');
1527 }),
1528 list: createCachedFormatter((lng, opt) => {
1529 const formatter = new Intl.ListFormat(lng, {
1530 ...opt
1531 });
1532 return val => formatter.format(val);
1533 })
1534 };
1535 this.init(options);
1536 }
1537 init(services) {
1538 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
1539 interpolation: {}
1540 };
1541 this.formatSeparator = options.interpolation.formatSeparator || ',';
1542 }
1543 add(name, fc) {
1544 this.formats[name.toLowerCase().trim()] = fc;
1545 }
1546 addCached(name, fc) {
1547 this.formats[name.toLowerCase().trim()] = createCachedFormatter(fc);
1548 }
1549 format(value, format, lng) {
1550 let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
1551 const formats = format.split(this.formatSeparator);
1552 if (formats.length > 1 && formats[0].indexOf('(') > 1 && formats[0].indexOf(')') < 0 && formats.find(f => f.indexOf(')') > -1)) {
1553 const lastIndex = formats.findIndex(f => f.indexOf(')') > -1);
1554 formats[0] = [formats[0], ...formats.splice(1, lastIndex)].join(this.formatSeparator);
1555 }
1556 const result = formats.reduce((mem, f) => {
1557 const {
1558 formatName,
1559 formatOptions
1560 } = parseFormatStr(f);
1561 if (this.formats[formatName]) {
1562 let formatted = mem;
1563 try {
1564 const valOptions = options && options.formatParams && options.formatParams[options.interpolationkey] || {};
1565 const l = valOptions.locale || valOptions.lng || options.locale || options.lng || lng;
1566 formatted = this.formats[formatName](mem, l, {
1567 ...formatOptions,
1568 ...options,
1569 ...valOptions
1570 });
1571 } catch (error) {
1572 this.logger.warn(error);
1573 }
1574 return formatted;
1575 } else {
1576 this.logger.warn(`there was no format function for ${formatName}`);
1577 }
1578 return mem;
1579 }, value);
1580 return result;
1581 }
1582 }
1583
1584 const removePending = (q, name) => {
1585 if (q.pending[name] !== undefined) {
1586 delete q.pending[name];
1587 q.pendingCount--;
1588 }
1589 };
1590 class Connector extends EventEmitter {
1591 constructor(backend, store, services) {
1592 let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
1593 super();
1594 this.backend = backend;
1595 this.store = store;
1596 this.services = services;
1597 this.languageUtils = services.languageUtils;
1598 this.options = options;
1599 this.logger = baseLogger.create('backendConnector');
1600 this.waitingReads = [];
1601 this.maxParallelReads = options.maxParallelReads || 10;
1602 this.readingCalls = 0;
1603 this.maxRetries = options.maxRetries >= 0 ? options.maxRetries : 5;
1604 this.retryTimeout = options.retryTimeout >= 1 ? options.retryTimeout : 350;
1605 this.state = {};
1606 this.queue = [];
1607 if (this.backend && this.backend.init) {
1608 this.backend.init(services, options.backend, options);
1609 }
1610 }
1611 queueLoad(languages, namespaces, options, callback) {
1612 const toLoad = {};
1613 const pending = {};
1614 const toLoadLanguages = {};
1615 const toLoadNamespaces = {};
1616 languages.forEach(lng => {
1617 let hasAllNamespaces = true;
1618 namespaces.forEach(ns => {
1619 const name = `${lng}|${ns}`;
1620 if (!options.reload && this.store.hasResourceBundle(lng, ns)) {
1621 this.state[name] = 2;
1622 } else if (this.state[name] < 0) ; else if (this.state[name] === 1) {
1623 if (pending[name] === undefined) pending[name] = true;
1624 } else {
1625 this.state[name] = 1;
1626 hasAllNamespaces = false;
1627 if (pending[name] === undefined) pending[name] = true;
1628 if (toLoad[name] === undefined) toLoad[name] = true;
1629 if (toLoadNamespaces[ns] === undefined) toLoadNamespaces[ns] = true;
1630 }
1631 });
1632 if (!hasAllNamespaces) toLoadLanguages[lng] = true;
1633 });
1634 if (Object.keys(toLoad).length || Object.keys(pending).length) {
1635 this.queue.push({
1636 pending,
1637 pendingCount: Object.keys(pending).length,
1638 loaded: {},
1639 errors: [],
1640 callback
1641 });
1642 }
1643 return {
1644 toLoad: Object.keys(toLoad),
1645 pending: Object.keys(pending),
1646 toLoadLanguages: Object.keys(toLoadLanguages),
1647 toLoadNamespaces: Object.keys(toLoadNamespaces)
1648 };
1649 }
1650 loaded(name, err, data) {
1651 const s = name.split('|');
1652 const lng = s[0];
1653 const ns = s[1];
1654 if (err) this.emit('failedLoading', lng, ns, err);
1655 if (!err && data) {
1656 this.store.addResourceBundle(lng, ns, data, undefined, undefined, {
1657 skipCopy: true
1658 });
1659 }
1660 this.state[name] = err ? -1 : 2;
1661 if (err && data) this.state[name] = 0;
1662 const loaded = {};
1663 this.queue.forEach(q => {
1664 pushPath(q.loaded, [lng], ns);
1665 removePending(q, name);
1666 if (err) q.errors.push(err);
1667 if (q.pendingCount === 0 && !q.done) {
1668 Object.keys(q.loaded).forEach(l => {
1669 if (!loaded[l]) loaded[l] = {};
1670 const loadedKeys = q.loaded[l];
1671 if (loadedKeys.length) {
1672 loadedKeys.forEach(n => {
1673 if (loaded[l][n] === undefined) loaded[l][n] = true;
1674 });
1675 }
1676 });
1677 q.done = true;
1678 if (q.errors.length) {
1679 q.callback(q.errors);
1680 } else {
1681 q.callback();
1682 }
1683 }
1684 });
1685 this.emit('loaded', loaded);
1686 this.queue = this.queue.filter(q => !q.done);
1687 }
1688 read(lng, ns, fcName) {
1689 let tried = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
1690 let wait = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : this.retryTimeout;
1691 let callback = arguments.length > 5 ? arguments[5] : undefined;
1692 if (!lng.length) return callback(null, {});
1693 if (this.readingCalls >= this.maxParallelReads) {
1694 this.waitingReads.push({
1695 lng,
1696 ns,
1697 fcName,
1698 tried,
1699 wait,
1700 callback
1701 });
1702 return;
1703 }
1704 this.readingCalls++;
1705 const resolver = (err, data) => {
1706 this.readingCalls--;
1707 if (this.waitingReads.length > 0) {
1708 const next = this.waitingReads.shift();
1709 this.read(next.lng, next.ns, next.fcName, next.tried, next.wait, next.callback);
1710 }
1711 if (err && data && tried < this.maxRetries) {
1712 setTimeout(() => {
1713 this.read.call(this, lng, ns, fcName, tried + 1, wait * 2, callback);
1714 }, wait);
1715 return;
1716 }
1717 callback(err, data);
1718 };
1719 const fc = this.backend[fcName].bind(this.backend);
1720 if (fc.length === 2) {
1721 try {
1722 const r = fc(lng, ns);
1723 if (r && typeof r.then === 'function') {
1724 r.then(data => resolver(null, data)).catch(resolver);
1725 } else {
1726 resolver(null, r);
1727 }
1728 } catch (err) {
1729 resolver(err);
1730 }
1731 return;
1732 }
1733 return fc(lng, ns, resolver);
1734 }
1735 prepareLoading(languages, namespaces) {
1736 let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1737 let callback = arguments.length > 3 ? arguments[3] : undefined;
1738 if (!this.backend) {
1739 this.logger.warn('No backend was added via i18next.use. Will not load resources.');
1740 return callback && callback();
1741 }
1742 if (isString(languages)) languages = this.languageUtils.toResolveHierarchy(languages);
1743 if (isString(namespaces)) namespaces = [namespaces];
1744 const toLoad = this.queueLoad(languages, namespaces, options, callback);
1745 if (!toLoad.toLoad.length) {
1746 if (!toLoad.pending.length) callback();
1747 return null;
1748 }
1749 toLoad.toLoad.forEach(name => {
1750 this.loadOne(name);
1751 });
1752 }
1753 load(languages, namespaces, callback) {
1754 this.prepareLoading(languages, namespaces, {}, callback);
1755 }
1756 reload(languages, namespaces, callback) {
1757 this.prepareLoading(languages, namespaces, {
1758 reload: true
1759 }, callback);
1760 }
1761 loadOne(name) {
1762 let prefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
1763 const s = name.split('|');
1764 const lng = s[0];
1765 const ns = s[1];
1766 this.read(lng, ns, 'read', undefined, undefined, (err, data) => {
1767 if (err) this.logger.warn(`${prefix}loading namespace ${ns} for language ${lng} failed`, err);
1768 if (!err && data) this.logger.log(`${prefix}loaded namespace ${ns} for language ${lng}`, data);
1769 this.loaded(name, err, data);
1770 });
1771 }
1772 saveMissing(languages, namespace, key, fallbackValue, isUpdate) {
1773 let options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
1774 let clb = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : () => {};
1775 if (this.services.utils && this.services.utils.hasLoadedNamespace && !this.services.utils.hasLoadedNamespace(namespace)) {
1776 this.logger.warn(`did not save key "${key}" as the namespace "${namespace}" was not yet loaded`, 'This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!');
1777 return;
1778 }
1779 if (key === undefined || key === null || key === '') return;
1780 if (this.backend && this.backend.create) {
1781 const opts = {
1782 ...options,
1783 isUpdate
1784 };
1785 const fc = this.backend.create.bind(this.backend);
1786 if (fc.length < 6) {
1787 try {
1788 let r;
1789 if (fc.length === 5) {
1790 r = fc(languages, namespace, key, fallbackValue, opts);
1791 } else {
1792 r = fc(languages, namespace, key, fallbackValue);
1793 }
1794 if (r && typeof r.then === 'function') {
1795 r.then(data => clb(null, data)).catch(clb);
1796 } else {
1797 clb(null, r);
1798 }
1799 } catch (err) {
1800 clb(err);
1801 }
1802 } else {
1803 fc(languages, namespace, key, fallbackValue, clb, opts);
1804 }
1805 }
1806 if (!languages || !languages[0]) return;
1807 this.store.addResource(languages[0], namespace, key, fallbackValue);
1808 }
1809 }
1810
1811 const get = () => ({
1812 debug: false,
1813 initImmediate: true,
1814 ns: ['translation'],
1815 defaultNS: ['translation'],
1816 fallbackLng: ['dev'],
1817 fallbackNS: false,
1818 supportedLngs: false,
1819 nonExplicitSupportedLngs: false,
1820 load: 'all',
1821 preload: false,
1822 simplifyPluralSuffix: true,
1823 keySeparator: '.',
1824 nsSeparator: ':',
1825 pluralSeparator: '_',
1826 contextSeparator: '_',
1827 partialBundledLanguages: false,
1828 saveMissing: false,
1829 updateMissing: false,
1830 saveMissingTo: 'fallback',
1831 saveMissingPlurals: true,
1832 missingKeyHandler: false,
1833 missingInterpolationHandler: false,
1834 postProcess: false,
1835 postProcessPassResolved: false,
1836 returnNull: false,
1837 returnEmptyString: true,
1838 returnObjects: false,
1839 joinArrays: false,
1840 returnedObjectHandler: false,
1841 parseMissingKeyHandler: false,
1842 appendNamespaceToMissingKey: false,
1843 appendNamespaceToCIMode: false,
1844 overloadTranslationOptionHandler: args => {
1845 let ret = {};
1846 if (typeof args[1] === 'object') ret = args[1];
1847 if (isString(args[1])) ret.defaultValue = args[1];
1848 if (isString(args[2])) ret.tDescription = args[2];
1849 if (typeof args[2] === 'object' || typeof args[3] === 'object') {
1850 const options = args[3] || args[2];
1851 Object.keys(options).forEach(key => {
1852 ret[key] = options[key];
1853 });
1854 }
1855 return ret;
1856 },
1857 interpolation: {
1858 escapeValue: true,
1859 format: value => value,
1860 prefix: '{{',
1861 suffix: '}}',
1862 formatSeparator: ',',
1863 unescapePrefix: '-',
1864 nestingPrefix: '$t(',
1865 nestingSuffix: ')',
1866 nestingOptionsSeparator: ',',
1867 maxReplaces: 1000,
1868 skipOnVariables: true
1869 }
1870 });
1871 const transformOptions = options => {
1872 if (isString(options.ns)) options.ns = [options.ns];
1873 if (isString(options.fallbackLng)) options.fallbackLng = [options.fallbackLng];
1874 if (isString(options.fallbackNS)) options.fallbackNS = [options.fallbackNS];
1875 if (options.supportedLngs && options.supportedLngs.indexOf('cimode') < 0) {
1876 options.supportedLngs = options.supportedLngs.concat(['cimode']);
1877 }
1878 return options;
1879 };
1880
1881 const noop = () => {};
1882 const bindMemberFunctions = inst => {
1883 const mems = Object.getOwnPropertyNames(Object.getPrototypeOf(inst));
1884 mems.forEach(mem => {
1885 if (typeof inst[mem] === 'function') {
1886 inst[mem] = inst[mem].bind(inst);
1887 }
1888 });
1889 };
1890 class I18n extends EventEmitter {
1891 constructor() {
1892 let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1893 let callback = arguments.length > 1 ? arguments[1] : undefined;
1894 super();
1895 this.options = transformOptions(options);
1896 this.services = {};
1897 this.logger = baseLogger;
1898 this.modules = {
1899 external: []
1900 };
1901 bindMemberFunctions(this);
1902 if (callback && !this.isInitialized && !options.isClone) {
1903 if (!this.options.initImmediate) {
1904 this.init(options, callback);
1905 return this;
1906 }
1907 setTimeout(() => {
1908 this.init(options, callback);
1909 }, 0);
1910 }
1911 }
1912 init() {
1913 var _this = this;
1914 let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1915 let callback = arguments.length > 1 ? arguments[1] : undefined;
1916 this.isInitializing = true;
1917 if (typeof options === 'function') {
1918 callback = options;
1919 options = {};
1920 }
1921 if (!options.defaultNS && options.defaultNS !== false && options.ns) {
1922 if (isString(options.ns)) {
1923 options.defaultNS = options.ns;
1924 } else if (options.ns.indexOf('translation') < 0) {
1925 options.defaultNS = options.ns[0];
1926 }
1927 }
1928 const defOpts = get();
1929 this.options = {
1930 ...defOpts,
1931 ...this.options,
1932 ...transformOptions(options)
1933 };
1934 if (this.options.compatibilityAPI !== 'v1') {
1935 this.options.interpolation = {
1936 ...defOpts.interpolation,
1937 ...this.options.interpolation
1938 };
1939 }
1940 if (options.keySeparator !== undefined) {
1941 this.options.userDefinedKeySeparator = options.keySeparator;
1942 }
1943 if (options.nsSeparator !== undefined) {
1944 this.options.userDefinedNsSeparator = options.nsSeparator;
1945 }
1946 const createClassOnDemand = ClassOrObject => {
1947 if (!ClassOrObject) return null;
1948 if (typeof ClassOrObject === 'function') return new ClassOrObject();
1949 return ClassOrObject;
1950 };
1951 if (!this.options.isClone) {
1952 if (this.modules.logger) {
1953 baseLogger.init(createClassOnDemand(this.modules.logger), this.options);
1954 } else {
1955 baseLogger.init(null, this.options);
1956 }
1957 let formatter;
1958 if (this.modules.formatter) {
1959 formatter = this.modules.formatter;
1960 } else if (typeof Intl !== 'undefined') {
1961 formatter = Formatter;
1962 }
1963 const lu = new LanguageUtil(this.options);
1964 this.store = new ResourceStore(this.options.resources, this.options);
1965 const s = this.services;
1966 s.logger = baseLogger;
1967 s.resourceStore = this.store;
1968 s.languageUtils = lu;
1969 s.pluralResolver = new PluralResolver(lu, {
1970 prepend: this.options.pluralSeparator,
1971 compatibilityJSON: this.options.compatibilityJSON,
1972 simplifyPluralSuffix: this.options.simplifyPluralSuffix
1973 });
1974 if (formatter && (!this.options.interpolation.format || this.options.interpolation.format === defOpts.interpolation.format)) {
1975 s.formatter = createClassOnDemand(formatter);
1976 s.formatter.init(s, this.options);
1977 this.options.interpolation.format = s.formatter.format.bind(s.formatter);
1978 }
1979 s.interpolator = new Interpolator(this.options);
1980 s.utils = {
1981 hasLoadedNamespace: this.hasLoadedNamespace.bind(this)
1982 };
1983 s.backendConnector = new Connector(createClassOnDemand(this.modules.backend), s.resourceStore, s, this.options);
1984 s.backendConnector.on('*', function (event) {
1985 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
1986 args[_key - 1] = arguments[_key];
1987 }
1988 _this.emit(event, ...args);
1989 });
1990 if (this.modules.languageDetector) {
1991 s.languageDetector = createClassOnDemand(this.modules.languageDetector);
1992 if (s.languageDetector.init) s.languageDetector.init(s, this.options.detection, this.options);
1993 }
1994 if (this.modules.i18nFormat) {
1995 s.i18nFormat = createClassOnDemand(this.modules.i18nFormat);
1996 if (s.i18nFormat.init) s.i18nFormat.init(this);
1997 }
1998 this.translator = new Translator(this.services, this.options);
1999 this.translator.on('*', function (event) {
2000 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
2001 args[_key2 - 1] = arguments[_key2];
2002 }
2003 _this.emit(event, ...args);
2004 });
2005 this.modules.external.forEach(m => {
2006 if (m.init) m.init(this);
2007 });
2008 }
2009 this.format = this.options.interpolation.format;
2010 if (!callback) callback = noop;
2011 if (this.options.fallbackLng && !this.services.languageDetector && !this.options.lng) {
2012 const codes = this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);
2013 if (codes.length > 0 && codes[0] !== 'dev') this.options.lng = codes[0];
2014 }
2015 if (!this.services.languageDetector && !this.options.lng) {
2016 this.logger.warn('init: no languageDetector is used and no lng is defined');
2017 }
2018 const storeApi = ['getResource', 'hasResourceBundle', 'getResourceBundle', 'getDataByLanguage'];
2019 storeApi.forEach(fcName => {
2020 this[fcName] = function () {
2021 return _this.store[fcName](...arguments);
2022 };
2023 });
2024 const storeApiChained = ['addResource', 'addResources', 'addResourceBundle', 'removeResourceBundle'];
2025 storeApiChained.forEach(fcName => {
2026 this[fcName] = function () {
2027 _this.store[fcName](...arguments);
2028 return _this;
2029 };
2030 });
2031 const deferred = defer();
2032 const load = () => {
2033 const finish = (err, t) => {
2034 this.isInitializing = false;
2035 if (this.isInitialized && !this.initializedStoreOnce) this.logger.warn('init: i18next is already initialized. You should call init just once!');
2036 this.isInitialized = true;
2037 if (!this.options.isClone) this.logger.log('initialized', this.options);
2038 this.emit('initialized', this.options);
2039 deferred.resolve(t);
2040 callback(err, t);
2041 };
2042 if (this.languages && this.options.compatibilityAPI !== 'v1' && !this.isInitialized) return finish(null, this.t.bind(this));
2043 this.changeLanguage(this.options.lng, finish);
2044 };
2045 if (this.options.resources || !this.options.initImmediate) {
2046 load();
2047 } else {
2048 setTimeout(load, 0);
2049 }
2050 return deferred;
2051 }
2052 loadResources(language) {
2053 let callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : noop;
2054 let usedCallback = callback;
2055 const usedLng = isString(language) ? language : this.language;
2056 if (typeof language === 'function') usedCallback = language;
2057 if (!this.options.resources || this.options.partialBundledLanguages) {
2058 if (usedLng && usedLng.toLowerCase() === 'cimode' && (!this.options.preload || this.options.preload.length === 0)) return usedCallback();
2059 const toLoad = [];
2060 const append = lng => {
2061 if (!lng) return;
2062 if (lng === 'cimode') return;
2063 const lngs = this.services.languageUtils.toResolveHierarchy(lng);
2064 lngs.forEach(l => {
2065 if (l === 'cimode') return;
2066 if (toLoad.indexOf(l) < 0) toLoad.push(l);
2067 });
2068 };
2069 if (!usedLng) {
2070 const fallbacks = this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);
2071 fallbacks.forEach(l => append(l));
2072 } else {
2073 append(usedLng);
2074 }
2075 if (this.options.preload) {
2076 this.options.preload.forEach(l => append(l));
2077 }
2078 this.services.backendConnector.load(toLoad, this.options.ns, e => {
2079 if (!e && !this.resolvedLanguage && this.language) this.setResolvedLanguage(this.language);
2080 usedCallback(e);
2081 });
2082 } else {
2083 usedCallback(null);
2084 }
2085 }
2086 reloadResources(lngs, ns, callback) {
2087 const deferred = defer();
2088 if (typeof lngs === 'function') {
2089 callback = lngs;
2090 lngs = undefined;
2091 }
2092 if (typeof ns === 'function') {
2093 callback = ns;
2094 ns = undefined;
2095 }
2096 if (!lngs) lngs = this.languages;
2097 if (!ns) ns = this.options.ns;
2098 if (!callback) callback = noop;
2099 this.services.backendConnector.reload(lngs, ns, err => {
2100 deferred.resolve();
2101 callback(err);
2102 });
2103 return deferred;
2104 }
2105 use(module) {
2106 if (!module) throw new Error('You are passing an undefined module! Please check the object you are passing to i18next.use()');
2107 if (!module.type) throw new Error('You are passing a wrong module! Please check the object you are passing to i18next.use()');
2108 if (module.type === 'backend') {
2109 this.modules.backend = module;
2110 }
2111 if (module.type === 'logger' || module.log && module.warn && module.error) {
2112 this.modules.logger = module;
2113 }
2114 if (module.type === 'languageDetector') {
2115 this.modules.languageDetector = module;
2116 }
2117 if (module.type === 'i18nFormat') {
2118 this.modules.i18nFormat = module;
2119 }
2120 if (module.type === 'postProcessor') {
2121 postProcessor.addPostProcessor(module);
2122 }
2123 if (module.type === 'formatter') {
2124 this.modules.formatter = module;
2125 }
2126 if (module.type === '3rdParty') {
2127 this.modules.external.push(module);
2128 }
2129 return this;
2130 }
2131 setResolvedLanguage(l) {
2132 if (!l || !this.languages) return;
2133 if (['cimode', 'dev'].indexOf(l) > -1) return;
2134 for (let li = 0; li < this.languages.length; li++) {
2135 const lngInLngs = this.languages[li];
2136 if (['cimode', 'dev'].indexOf(lngInLngs) > -1) continue;
2137 if (this.store.hasLanguageSomeTranslations(lngInLngs)) {
2138 this.resolvedLanguage = lngInLngs;
2139 break;
2140 }
2141 }
2142 }
2143 changeLanguage(lng, callback) {
2144 var _this2 = this;
2145 this.isLanguageChangingTo = lng;
2146 const deferred = defer();
2147 this.emit('languageChanging', lng);
2148 const setLngProps = l => {
2149 this.language = l;
2150 this.languages = this.services.languageUtils.toResolveHierarchy(l);
2151 this.resolvedLanguage = undefined;
2152 this.setResolvedLanguage(l);
2153 };
2154 const done = (err, l) => {
2155 if (l) {
2156 setLngProps(l);
2157 this.translator.changeLanguage(l);
2158 this.isLanguageChangingTo = undefined;
2159 this.emit('languageChanged', l);
2160 this.logger.log('languageChanged', l);
2161 } else {
2162 this.isLanguageChangingTo = undefined;
2163 }
2164 deferred.resolve(function () {
2165 return _this2.t(...arguments);
2166 });
2167 if (callback) callback(err, function () {
2168 return _this2.t(...arguments);
2169 });
2170 };
2171 const setLng = lngs => {
2172 if (!lng && !lngs && this.services.languageDetector) lngs = [];
2173 const l = isString(lngs) ? lngs : this.services.languageUtils.getBestMatchFromCodes(lngs);
2174 if (l) {
2175 if (!this.language) {
2176 setLngProps(l);
2177 }
2178 if (!this.translator.language) this.translator.changeLanguage(l);
2179 if (this.services.languageDetector && this.services.languageDetector.cacheUserLanguage) this.services.languageDetector.cacheUserLanguage(l);
2180 }
2181 this.loadResources(l, err => {
2182 done(err, l);
2183 });
2184 };
2185 if (!lng && this.services.languageDetector && !this.services.languageDetector.async) {
2186 setLng(this.services.languageDetector.detect());
2187 } else if (!lng && this.services.languageDetector && this.services.languageDetector.async) {
2188 if (this.services.languageDetector.detect.length === 0) {
2189 this.services.languageDetector.detect().then(setLng);
2190 } else {
2191 this.services.languageDetector.detect(setLng);
2192 }
2193 } else {
2194 setLng(lng);
2195 }
2196 return deferred;
2197 }
2198 getFixedT(lng, ns, keyPrefix) {
2199 var _this3 = this;
2200 const fixedT = function (key, opts) {
2201 let options;
2202 if (typeof opts !== 'object') {
2203 for (var _len3 = arguments.length, rest = new Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) {
2204 rest[_key3 - 2] = arguments[_key3];
2205 }
2206 options = _this3.options.overloadTranslationOptionHandler([key, opts].concat(rest));
2207 } else {
2208 options = {
2209 ...opts
2210 };
2211 }
2212 options.lng = options.lng || fixedT.lng;
2213 options.lngs = options.lngs || fixedT.lngs;
2214 options.ns = options.ns || fixedT.ns;
2215 if (options.keyPrefix !== '') options.keyPrefix = options.keyPrefix || keyPrefix || fixedT.keyPrefix;
2216 const keySeparator = _this3.options.keySeparator || '.';
2217 let resultKey;
2218 if (options.keyPrefix && Array.isArray(key)) {
2219 resultKey = key.map(k => `${options.keyPrefix}${keySeparator}${k}`);
2220 } else {
2221 resultKey = options.keyPrefix ? `${options.keyPrefix}${keySeparator}${key}` : key;
2222 }
2223 return _this3.t(resultKey, options);
2224 };
2225 if (isString(lng)) {
2226 fixedT.lng = lng;
2227 } else {
2228 fixedT.lngs = lng;
2229 }
2230 fixedT.ns = ns;
2231 fixedT.keyPrefix = keyPrefix;
2232 return fixedT;
2233 }
2234 t() {
2235 return this.translator && this.translator.translate(...arguments);
2236 }
2237 exists() {
2238 return this.translator && this.translator.exists(...arguments);
2239 }
2240 setDefaultNamespace(ns) {
2241 this.options.defaultNS = ns;
2242 }
2243 hasLoadedNamespace(ns) {
2244 let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2245 if (!this.isInitialized) {
2246 this.logger.warn('hasLoadedNamespace: i18next was not initialized', this.languages);
2247 return false;
2248 }
2249 if (!this.languages || !this.languages.length) {
2250 this.logger.warn('hasLoadedNamespace: i18n.languages were undefined or empty', this.languages);
2251 return false;
2252 }
2253 const lng = options.lng || this.resolvedLanguage || this.languages[0];
2254 const fallbackLng = this.options ? this.options.fallbackLng : false;
2255 const lastLng = this.languages[this.languages.length - 1];
2256 if (lng.toLowerCase() === 'cimode') return true;
2257 const loadNotPending = (l, n) => {
2258 const loadState = this.services.backendConnector.state[`${l}|${n}`];
2259 return loadState === -1 || loadState === 0 || loadState === 2;
2260 };
2261 if (options.precheck) {
2262 const preResult = options.precheck(this, loadNotPending);
2263 if (preResult !== undefined) return preResult;
2264 }
2265 if (this.hasResourceBundle(lng, ns)) return true;
2266 if (!this.services.backendConnector.backend || this.options.resources && !this.options.partialBundledLanguages) return true;
2267 if (loadNotPending(lng, ns) && (!fallbackLng || loadNotPending(lastLng, ns))) return true;
2268 return false;
2269 }
2270 loadNamespaces(ns, callback) {
2271 const deferred = defer();
2272 if (!this.options.ns) {
2273 if (callback) callback();
2274 return Promise.resolve();
2275 }
2276 if (isString(ns)) ns = [ns];
2277 ns.forEach(n => {
2278 if (this.options.ns.indexOf(n) < 0) this.options.ns.push(n);
2279 });
2280 this.loadResources(err => {
2281 deferred.resolve();
2282 if (callback) callback(err);
2283 });
2284 return deferred;
2285 }
2286 loadLanguages(lngs, callback) {
2287 const deferred = defer();
2288 if (isString(lngs)) lngs = [lngs];
2289 const preloaded = this.options.preload || [];
2290 const newLngs = lngs.filter(lng => preloaded.indexOf(lng) < 0 && this.services.languageUtils.isSupportedCode(lng));
2291 if (!newLngs.length) {
2292 if (callback) callback();
2293 return Promise.resolve();
2294 }
2295 this.options.preload = preloaded.concat(newLngs);
2296 this.loadResources(err => {
2297 deferred.resolve();
2298 if (callback) callback(err);
2299 });
2300 return deferred;
2301 }
2302 dir(lng) {
2303 if (!lng) lng = this.resolvedLanguage || (this.languages && this.languages.length > 0 ? this.languages[0] : this.language);
2304 if (!lng) return 'rtl';
2305 const rtlLngs = ['ar', 'shu', 'sqr', 'ssh', 'xaa', 'yhd', 'yud', 'aao', 'abh', 'abv', 'acm', 'acq', 'acw', 'acx', 'acy', 'adf', 'ads', 'aeb', 'aec', 'afb', 'ajp', 'apc', 'apd', 'arb', 'arq', 'ars', 'ary', 'arz', 'auz', 'avl', 'ayh', 'ayl', 'ayn', 'ayp', 'bbz', 'pga', 'he', 'iw', 'ps', 'pbt', 'pbu', 'pst', 'prp', 'prd', 'ug', 'ur', 'ydd', 'yds', 'yih', 'ji', 'yi', 'hbo', 'men', 'xmn', 'fa', 'jpr', 'peo', 'pes', 'prs', 'dv', 'sam', 'ckb'];
2306 const languageUtils = this.services && this.services.languageUtils || new LanguageUtil(get());
2307 return rtlLngs.indexOf(languageUtils.getLanguagePartFromCode(lng)) > -1 || lng.toLowerCase().indexOf('-arab') > 1 ? 'rtl' : 'ltr';
2308 }
2309 static createInstance() {
2310 let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2311 let callback = arguments.length > 1 ? arguments[1] : undefined;
2312 return new I18n(options, callback);
2313 }
2314 cloneInstance() {
2315 let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
2316 let callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : noop;
2317 const forkResourceStore = options.forkResourceStore;
2318 if (forkResourceStore) delete options.forkResourceStore;
2319 const mergedOptions = {
2320 ...this.options,
2321 ...options,
2322 ...{
2323 isClone: true
2324 }
2325 };
2326 const clone = new I18n(mergedOptions);
2327 if (options.debug !== undefined || options.prefix !== undefined) {
2328 clone.logger = clone.logger.clone(options);
2329 }
2330 const membersToCopy = ['store', 'services', 'language'];
2331 membersToCopy.forEach(m => {
2332 clone[m] = this[m];
2333 });
2334 clone.services = {
2335 ...this.services
2336 };
2337 clone.services.utils = {
2338 hasLoadedNamespace: clone.hasLoadedNamespace.bind(clone)
2339 };
2340 if (forkResourceStore) {
2341 clone.store = new ResourceStore(this.store.data, mergedOptions);
2342 clone.services.resourceStore = clone.store;
2343 }
2344 clone.translator = new Translator(clone.services, mergedOptions);
2345 clone.translator.on('*', function (event) {
2346 for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
2347 args[_key4 - 1] = arguments[_key4];
2348 }
2349 clone.emit(event, ...args);
2350 });
2351 clone.init(mergedOptions, callback);
2352 clone.translator.options = mergedOptions;
2353 clone.translator.backendConnector.services.utils = {
2354 hasLoadedNamespace: clone.hasLoadedNamespace.bind(clone)
2355 };
2356 return clone;
2357 }
2358 toJSON() {
2359 return {
2360 options: this.options,
2361 store: this.store,
2362 language: this.language,
2363 languages: this.languages,
2364 resolvedLanguage: this.resolvedLanguage
2365 };
2366 }
2367 }
2368 const instance = I18n.createInstance();
2369 instance.createInstance = I18n.createInstance;
2370
2371 return instance;
2372
2373}));