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