@react-native-localize-ext/core
Version:
make i18n in react-native much smoother
217 lines (216 loc) • 7.63 kB
JavaScript
;
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_._';