UNPKG

react-native-i18n-auto

Version:

Auto i18n tool for React Native with full TypeScript support

492 lines (465 loc) 18.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.i18n = exports.SUPPORTED_LANGUAGES = void 0; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } // Define supported language codes var SUPPORTED_LANGUAGES = exports.SUPPORTED_LANGUAGES = { ar: 'Arabic', zh: 'Chinese', en: 'English', fr: 'French', de: 'German', hi: 'Hindi', id: 'Indonesian', it: 'Italian', ja: 'Japanese', ko: 'Korean', ms: 'Malay', pt: 'Portuguese', ru: 'Russian', es: 'Spanish', th: 'Thai', vi: 'Vietnamese', cn: 'Chinese' }; // Language code type // Cache interface for translations // Logger configuration var LOG_PREFIX = '[i18n-auto]'; var log = { info: function info() { var _console; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return (_console = console).log.apply(_console, [LOG_PREFIX].concat(args)); }, warn: function warn() { var _console2; for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } return (_console2 = console).warn.apply(_console2, [LOG_PREFIX].concat(args)); }, error: function error() { var _console3; for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } return (_console3 = console).error.apply(_console3, [LOG_PREFIX].concat(args)); } }; /** * React Native Internationalization (i18n) Manager * * A powerful and efficient i18n manager for React Native applications. * Features include caching, memory optimization, and event handling. * * @example * // 1. Basic Usage * import { i18n } from 'react-native-i18n-auto'; * * * // Use translations * console.log(i18n.t('HELLO')); // 'Hello' * * // Change language * i18n.setLanguage('ko'); * console.log(i18n.t('HELLO')); // '안녕하세요' * * // 2. Event Listener Usage * const unsubscribe = i18n.onLanguageChange(() => { * console.log('Language changed to:', i18n.getLanguage()); * }); * * // 3. Performance Optimization * // Clear cache * i18n.clearCache(); * * // Optimize memory usage * i18n.optimize(); * * // 4. Language Support * const supported = i18n.getSupportedLanguages(); * const available = i18n.getAvailableLanguages(); */ var I18nManager = /*#__PURE__*/function () { // Maximum cache items per language function I18nManager() { var defaultLang = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'en'; _classCallCheck(this, I18nManager); _defineProperty(this, "translations", {}); _defineProperty(this, "listeners", new Set()); _defineProperty(this, "translationProxy", {}); // Cache and file tracking _defineProperty(this, "cache", {}); _defineProperty(this, "loadedFiles", new Set()); _defineProperty(this, "CACHE_DURATION", 5 * 60 * 1000); // 5 minutes cache duration _defineProperty(this, "MAX_CACHE_SIZE", 1000); // Memoized list of available languages _defineProperty(this, "availableLanguagesCache", null); this.currentLang = defaultLang; log.info("Initialized with default language: ".concat(defaultLang)); this.setupProxy(); } return _createClass(I18nManager, [{ key: "setupProxy", value: function setupProxy() { var _this = this; this.translationProxy = new Proxy({}, { get: function get(target, prop) { if (typeof prop !== 'string') return undefined; return _this.translate(prop); } }); } }, { key: "isValidLanguageCode", value: function isValidLanguageCode(code) { return code in SUPPORTED_LANGUAGES; } // Cache management }, { key: "manageCache", value: function manageCache(lang) { var _this2 = this; if (!this.cache[lang]) return; var now = Date.now(); var cache = this.cache[lang]; var entries = Object.entries(cache); // Clean up cache if it exceeds maximum size or has expired items if (entries.length > this.MAX_CACHE_SIZE) { // Remove oldest entries first var validEntries = entries.filter(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), timestamp = _ref2[1].timestamp; return now - timestamp < _this2.CACHE_DURATION; }).sort(function (_ref3, _ref4) { var _ref5 = _slicedToArray(_ref3, 2), a = _ref5[1]; var _ref6 = _slicedToArray(_ref4, 2), b = _ref6[1]; return b.timestamp - a.timestamp; }).slice(0, this.MAX_CACHE_SIZE); this.cache[lang] = Object.fromEntries(validEntries); } } /** * Get translation for a given key in the current language * @param key - Translation key * @returns Translated string or the key if translation not found * @example * i18n.t('HELLO'); // Returns 'Hello' if language is English * // or using proxy * i18n.t.HELLO // Returns 'Hello' if language is English */ }, { key: "translate", value: function translate(key) { var _this$cache$lang; var lang = this.currentLang; // 1. Check cache if ((_this$cache$lang = this.cache[lang]) !== null && _this$cache$lang !== void 0 && _this$cache$lang[key]) { var cached = this.cache[lang][key]; if (Date.now() - cached.timestamp < this.CACHE_DURATION) { return cached.value; } } // 2. Look up translation with nested key support var keyParts = key.split('.'); var translation = this.translations[lang]; var _iterator = _createForOfIteratorHelper(keyParts), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var part = _step.value; if (!translation || _typeof(translation) !== 'object') { log.warn("Translation not found for key: ".concat(key, " in language: ").concat(lang)); return key; } var nextValue = translation[part]; if (typeof nextValue === 'string') { translation = nextValue; } else if (nextValue && _typeof(nextValue) === 'object' && !Array.isArray(nextValue)) { translation = nextValue; } else { log.warn("Translation not found for key: ".concat(key, " in language: ").concat(lang)); return key; } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } if (typeof translation !== 'string') { log.warn("Invalid translation type for key: ".concat(key, " in language: ").concat(lang)); return key; } // 3. Cache and return result if (!this.cache[lang]) { this.cache[lang] = {}; } this.cache[lang][key] = { value: translation, timestamp: Date.now() }; this.manageCache(lang); return translation; } // Make t accessible as both function and proxy }, { key: "t", get: function get() { var _this3 = this; return new Proxy(this.translate.bind(this), { get: function get(target, prop) { if (typeof prop === 'string') { return _this3.translate(prop); } return undefined; } }); } /** * Change the current language * @param lang - Target language code * @returns Boolean indicating if the language change was successful * @example * i18n.setLanguage('en'); // Changes language to English */ }, { key: "setLanguage", value: function setLanguage(lang) { // Check if the language is supported and has translations if (!this.translations[lang]) { log.warn("Failed to set language: ".concat(lang, " - translations not available")); return false; } // Only change language if it's different from current if (this.currentLang !== lang) { var previousLang = this.currentLang; this.currentLang = lang; log.info("Language changed from ".concat(previousLang, " to ").concat(lang)); this.listeners.forEach(function (listener) { return listener(); }); } return true; } /** * Get the current language code * @returns Current language code * @example * const currentLang = i18n.getLanguage(); // Returns 'en' for English */ }, { key: "getLanguage", value: function getLanguage() { return this.currentLang; } }, { key: "getAvailableLanguages", value: /** * Get list of languages that have translations loaded * @returns Array of available languages with their codes and names * @example * const languages = i18n.getAvailableLanguages(); * // Returns [{ code: 'en', name: 'English' }, { code: 'ko', name: 'Korean' }] */ function getAvailableLanguages() { var _this4 = this; if (!this.availableLanguagesCache) { this.availableLanguagesCache = Object.entries(SUPPORTED_LANGUAGES).filter(function (_ref7) { var _ref8 = _slicedToArray(_ref7, 1), code = _ref8[0]; return code in _this4.translations; }).map(function (_ref9) { var _ref10 = _slicedToArray(_ref9, 2), code = _ref10[0], name = _ref10[1]; return { code: code, name: name }; }); } return this.availableLanguagesCache; } // Static list of all supported languages }, { key: "getSupportedLanguages", value: /** * Get list of all supported languages * @returns Array of all supported languages with their codes and names * @example * const languages = i18n.getSupportedLanguages(); * // Returns [{ code: 'en', name: 'English' }, ...] */ function getSupportedLanguages() { return I18nManager.SUPPORTED_LANGUAGES_LIST; } /** * Register a callback for language change events * @param listener - Callback function to be called when language changes * @returns Unsubscribe function to remove the listener * @example * const unsubscribe = i18n.onLanguageChange(() => { * console.log('Language changed to:', i18n.getLanguage()); * }); */ }, { key: "onLanguageChange", value: function onLanguageChange(listener) { var _this5 = this; this.listeners.add(listener); return function () { _this5.listeners["delete"](listener); }; } /** * Clear the translation cache * @example * i18n.clearCache(); // Clears all cached translations */ }, { key: "clearCache", value: function clearCache() { this.cache = {}; this.availableLanguagesCache = null; } /** * Optimize memory usage by clearing cache and cleaning up listeners * @example * i18n.optimize(); // Optimizes memory usage */ }, { key: "optimize", value: function optimize() { this.clearCache(); this.listeners = new Set(Array.from(this.listeners)); // Clean up listeners } /** * Get list of loaded locale files * @returns Array of loaded locale file names */ }, { key: "getLoadedFiles", value: function getLoadedFiles() { return Array.from(this.loadedFiles); } /** * Check if a locale file is loaded * @param lang - Language code to check * @returns boolean indicating if the locale file is loaded */ }, { key: "isLocaleLoaded", value: function isLocaleLoaded(lang) { return this.loadedFiles.has("".concat(lang, ".json")); } /** * Set translations for multiple languages * @param locales - Object containing translations for each language * @example * i18n.setLocales({ * en: { HELLO: 'Hello' }, * ko: { HELLO: '안녕하세요' } * }); */ }, { key: "setLocales", value: function setLocales(locales) { // Reset existing cache this.cache = {}; this.availableLanguagesCache = null; // Process translations with minimal copying var processedTranslations = {}; for (var _i = 0, _Object$entries = Object.entries(locales); _i < _Object$entries.length; _i++) { var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), lang = _Object$entries$_i[0], data = _Object$entries$_i[1]; if (!this.isValidLanguageCode(lang)) { log.warn("Invalid language code: ".concat(lang)); continue; } var processed = this.processLocaleData(data); if (processed) { processedTranslations[lang] = processed; } } // Update translations this.translations = processedTranslations; // Ensure current language is available, switch to first available if not if (!this.translations[this.currentLang]) { var availableLang = Object.keys(this.translations)[0]; if (availableLang) { var previousLang = this.currentLang; this.currentLang = availableLang; log.warn("Current language ".concat(previousLang, " not available, switched to: ").concat(availableLang)); } } } }, { key: "processLocaleData", value: function processLocaleData(data) { if (!data || _typeof(data) !== 'object') { return null; } var result = {}; var _processNestedObject = function processNestedObject(obj) { var prefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; for (var _i2 = 0, _Object$entries2 = Object.entries(obj); _i2 < _Object$entries2.length; _i2++) { var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2), _key4 = _Object$entries2$_i[0], value = _Object$entries2$_i[1]; var fullKey = prefix ? "".concat(prefix, ".").concat(_key4) : _key4; if (typeof value === 'string') { result[fullKey] = value; } else if (value && _typeof(value) === 'object' && !Array.isArray(value)) { _processNestedObject(value, fullKey); } } }; _processNestedObject(data); return result; } }], [{ key: "getInstance", value: function getInstance() { if (!I18nManager.instance) { I18nManager.instance = new I18nManager(); } return I18nManager.instance; } }]); }(); // Export singleton instance _defineProperty(I18nManager, "SUPPORTED_LANGUAGES_LIST", Object.entries(SUPPORTED_LANGUAGES).map(function (_ref11) { var _ref12 = _slicedToArray(_ref11, 2), code = _ref12[0], name = _ref12[1]; return { code: code, name: name }; })); var i18n = exports.i18n = I18nManager.getInstance(); // Export types