UNPKG

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