UNPKG

@react-native-localize-ext/core

Version:
217 lines (216 loc) 7.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.I18n = void 0; class I18n { constructor(config) { this.locales = {}; this.hashes = {}; // @ts-ignore this.current = {}; this.loadings = []; this.currentName = ''; this.listeners = []; this.caches = {}; this.defaultLocale = config.defaultLocale.values; this.loader = config.loader; this.define(config.defaultLocale.key, config.defaultLocale.values).locale(config.defaultLocale.key).then(r => r); } define(localeName, values) { this.locales[localeName] = values; return this; } getLocaleName() { return this.currentName; } async locale(name) { const resolved = new Promise((resolve) => resolve()); if (name === this.currentName) { this.clearLoading(); return resolved; } if (this.isLoading(name)) { // Make sure this locale can publish this.loadings.push(name); return resolved; } const language = this.locales[name]; if (language) { this.publish(name, language); this.clearLoading(); return resolved; } if (this.loader) { this.loadings.push(name); try { const response = await this.loader(name); let locale; if (response && response.__esModule) { locale = response.default; if (!this.isValidLocale(locale)) { throw new TypeError(`The locale named "${name}" has no default export`); } } else { locale = response; if (!this.isValidLocale(locale)) { throw new TypeError(`The locale data named "${name}" is invalid`); } } this.define(name, locale); if (this.canPublishFromLoader(name)) { this.publish(name, locale); } this.clearLoading(); } catch (error) { // Fallback to previous locale name (if have) this.removeLoading(name); throw error; } return; } return new Promise((_, reject) => reject(new ReferenceError(`I18n can't find locale "${name}"`))); } listen(fn) { this.listeners.push(fn); return () => { this.listeners = this.listeners.filter((item) => item !== fn); }; } t(key) { if (this.caches[key]) { return this.caches[key]; } const properties = key.split('.'); const firstProperty = properties.shift(); let result = this.chain()[firstProperty]; if (properties.length) { let index = 0; for (; index < properties.length; ++index) { result = result[properties[index]]; if (result === undefined) { break; } } if (result === undefined) { result = this.notFound(key); } } return result; } chain() { const key = I18n.CACHE_ROOT_KEY; if (!this.caches[key]) { this.caches[key] = this.proxy(this.current, [], false); } return this.caches[key]; } proxy(data, allProperties, useDefaultLocal) { if (Array.isArray(data)) { return (params) => { let message = data[0]; const defaultParams = data[1]; const newParams = Object.assign({}, defaultParams, params); for (const key of Object.keys(newParams)) { let replaceValue; if (typeof newParams[key] === 'function') { // Indeed, it's assigned from defaultParams. // It means that user doesn't input the value of this key, it only happens when Function has a default value. replaceValue = newParams[key](); } else if (typeof defaultParams[key] === 'function') { replaceValue = defaultParams[key](newParams[key]); } else { replaceValue = newParams[key]; } message = message.replace(new RegExp(`\{\{${key}\}\}`, 'gm'), replaceValue); } return message; }; } if (typeof data === 'object') { return this.createProxy(data, allProperties, useDefaultLocal); } return data; } createProxy(data, allProperties, useDefaultLocal) { return new Proxy(data, { get: (target, property) => { if (!this.isValidProperty(property)) { return undefined; } return this.getProxyData(target, allProperties, property, useDefaultLocal); }, }); } getProxyData(target, allProperties, property, useDefaultLocal) { const newAllProperties = allProperties.concat(property); const cacheKey = newAllProperties.join('.'); let result; if (this.caches[cacheKey]) { return this.caches[cacheKey]; } let proxyData = target[property]; if (proxyData === undefined) { if (useDefaultLocal) { proxyData = this.notFound(newAllProperties); } else { // Fallback to default locale proxyData = this.recursiveDefaultData(newAllProperties); if (proxyData === undefined) { proxyData = this.notFound(newAllProperties); } else { // Found key in default locale useDefaultLocal = true; } } } result = this.proxy(proxyData, newAllProperties, useDefaultLocal); this.caches[cacheKey] = result; return result; } recursiveDefaultData(allProperties) { let proxyData = this.defaultLocale; for (const name of allProperties) { proxyData = proxyData[name]; if (proxyData === undefined) { break; } } return proxyData; } notFound(properties) { const data = typeof properties === 'string' ? properties : properties.join('.'); console.error(`I18n can't find property "${data}"`); return data; } isValidProperty(property) { return property !== '$$typeof' && typeof property === 'string'; } isValidLocale(locale) { return locale !== null && typeof locale === 'object'; } isLoading(name) { return this.loadings.indexOf(name) >= 0; } canPublishFromLoader(name) { return this.loadings.length > 0 && this.loadings[this.loadings.length - 1] === name; } removeLoading(name) { this.loadings = this.loadings.filter((item) => item !== name); } clearLoading() { this.loadings = []; } publish(name, values) { this.currentName = name; this.current = values; this.caches = {}; this.listeners.forEach((listener) => listener(this.currentName)); } } exports.I18n = I18n; I18n.CACHE_ROOT_KEY = '_._i18n_root_._';