UNPKG

@enact/i18n

Version:

Internationalization support for Enact using iLib

461 lines (455 loc) 18.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Loader = EnyoLoader; exports["default"] = void 0; var _util = require("@enact/core/util"); var _Loader = _interopRequireDefault(require("ilib/lib/Loader")); var _LocaleInfo = _interopRequireDefault(require("ilib/lib/LocaleInfo")); var _xhr = _interopRequireDefault(require("xhr")); var _zoneinfo = _interopRequireDefault(require("./zoneinfo")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": 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); } 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 _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; } /* global XMLHttpRequest, ILIB_BASE_PATH, ILIB_RESOURCES_PATH, ILIB_ADDITIONAL_RESOURCES_PATH, ILIB_CACHE_ID */ var getImpl = function getImpl(url, callback, sync) { if (typeof XMLHttpRequest !== 'undefined') { _xhr["default"].XMLHttpRequest = XMLHttpRequest || _xhr["default"].XMLHttpRequest; var req; (0, _xhr["default"])({ url: url, sync: sync, beforeSend: function beforeSend(r) { return req = r; } }, function (err, resp, body) { var error = err || resp.statusCode !== 200 && resp.statusCode; // false failure from chrome and file:// urls if (error && req.status === 0 && req.response.length > 0) { body = req.response; error = false; } var json = null; try { json = error ? null : JSON.parse(body); } catch (e) { error = 'Failed to parse ILIB JSON data'; } callback(json, error); }); } else { callback(null, new Error('Not a web browser environment')); } }; var getSync = function getSync(url, callback) { return getImpl(url, callback, true); }; var get = (0, _util.memoize)(function (url) { return new Promise(function (resolve, reject) { getImpl(url, function (json, error) { if (error) { reject(error); } else { resolve(json); } }, false); }); }); var iLibBase = typeof ILIB_BASE_PATH === 'undefined' ? '/ilib' : ILIB_BASE_PATH; var iLibResources = typeof ILIB_RESOURCES_PATH === 'undefined' ? '/locale' : ILIB_RESOURCES_PATH; var cachePrefix = 'ENACT-ILIB-'; var cacheKey = cachePrefix + 'CACHE-ID'; var cacheID = typeof ILIB_CACHE_ID === 'undefined' ? '$ILIB' : ILIB_CACHE_ID; var timeStampKey = 'l10n_timestamp'; function setLocalStorageItem(keyName, keyValue) { var regex = new RegExp("".concat(cachePrefix).concat(iLibResources, "/([a-z]{2,3}/)+[a-z]+.json")); try { window.localStorage.setItem(keyName, keyValue); } catch (_unused) { Object.keys(window.localStorage).forEach(function (key) { if (regex.test(key) && !key.includes(keyName.slice(0, keyName.lastIndexOf('/')))) { window.localStorage.removeItem(key); } }); window.localStorage.setItem(keyName, keyValue); } } function EnyoLoader() { var _window$webOSSystem; this.base = iLibBase; // TODO: full enyo.platform implementation for improved accuracy if (typeof window === 'object' && typeof ((_window$webOSSystem = window.webOSSystem) !== null && _window$webOSSystem !== void 0 ? _window$webOSSystem : window.PalmSystem) === 'object') { this.webos = true; } } EnyoLoader.prototype = new _Loader["default"](); EnyoLoader.prototype.constructor = EnyoLoader; EnyoLoader.prototype._createZoneFile = function (path) { var zone = path.substring(path.indexOf('zoneinfo')); // remove the .json suffix to get the name of the zone zone = zone.substring(0, zone.length - 5); try { var zif = new _zoneinfo["default"]('/usr/share/' + zone); // only get the info for this year. Later we can get the info // for any historical or future year too return zif.getIlibZoneInfo(new Date()); } catch (e) { // no file, so just return nothing } }; EnyoLoader.prototype._pathjoin = function (_root, subpath) { if (!_root || !_root.length) { return subpath; } if (!subpath || !subpath.length) { return _root; } return _root + (_root.charAt(_root.length - 1) !== '/' ? '/' : '') + subpath; }; /** * Load the list of files asynchronously. This uses recursion in * order to create a queue of files that will be loaded serially. * Each layer, starting at the bottom, loads a file and then loads * the layer on top of it. The very top file on the stack will have * zero files to load, so instead it will be the one to call the * callback to notify the caller that all the content is loaded. * * @param {Array.<string>} paths array of strings containing relative paths for required locale data * files * @param {Array} results empty array in which to place the resulting json when it is loaded from a * file * @param {Object} params An object full of parameters that the caller is passing to this function * to help load the files * @param {function(Array.<Object>)} callback callback to call when this function is finished * attempting to load all the files that exist and can be loaded * * @returns {Promise} */ EnyoLoader.prototype._loadFilesAsync = function (path, params, cache, rootPath) { var _this = this; var _root = iLibResources; if (typeof rootPath !== 'undefined') { _root = rootPath; } else if (params && typeof params.root !== 'undefined') { _root = params.root; // Deprecated; to be removed in future } var cacheItem = cache.data.shift(), url; if (this.webos && path.indexOf('zoneinfo') !== -1) { // TODO: Sort out async zone file loading return this._createZoneFile(path); } else if (cacheItem) { return Promise.resolve(cacheItem); } return this.loadManifests(_root).then(function () { var isRootAvailable = _this.isAvailable(_root, path); if (isRootAvailable) { url = _this._pathjoin(_root, path); } else { var localeBase = _this._pathjoin(_this.base, 'locale'); var isBaseAvailable = _this.isAvailable(localeBase, path); if (isBaseAvailable) { url = _this._pathjoin(localeBase, path); } } if (url) { return get(url).then(function (json) { if (typeof json === 'object') { cache.update = true; return json; } else if (path === 'localeinfo.json') { return _LocaleInfo["default"].defaultInfo; } }); } return null; }); }; EnyoLoader.prototype._loadFilesCache = function (_root, paths) { this._validateCache(); if (typeof window !== 'undefined' && window.localStorage && paths.length > 0) { var stored = window.localStorage.getItem(cachePrefix + _root + '/' + paths[0]); if (stored) { var target = JSON.stringify(paths); var data = JSON.parse(stored); if (data.target === target) { return data.value; } else { window.localStorage.removeItem(cachePrefix + _root + '/' + paths[0]); } } } return new Array(paths.length); }; EnyoLoader.prototype._storeFilesCache = function (_root, paths, data) { if (typeof window !== 'undefined' && window.localStorage && paths.length > 0) { var target = JSON.stringify(paths); setLocalStorageItem(cachePrefix + _root + '/' + paths[0], JSON.stringify({ target: target, value: data })); } }; EnyoLoader.prototype._clearStringsCache = function () { if (typeof window !== 'undefined' && window.localStorage) { // Remove cache of app's strings for (var i = 0; i < window.localStorage.length; i++) { var currentKey = window.localStorage.key(i); if (currentKey.includes('strings.json')) { window.localStorage.removeItem(currentKey); } } } }; EnyoLoader.prototype._validateCache = function () { if (!this._cacheValidated && typeof window !== 'undefined' && window.localStorage) { var activeID = window.localStorage.getItem(cacheKey); if (activeID !== cacheID) { for (var i = 0; i < window.localStorage.length; i++) { var key = window.localStorage.key(i); if (key.indexOf(cachePrefix) === 0) { window.localStorage.removeItem(key); i--; } } setLocalStorageItem(cacheKey, cacheID); } } this._cacheValidated = true; }; EnyoLoader.prototype.loadFiles = function (paths, sync, params, callback, rootPath) { var _this2 = this; var _root = iLibResources; if (typeof rootPath !== 'undefined') { _root = rootPath; } else if (params && typeof params.root !== 'undefined') { _root = params.root; // Deprecated; to be removed in future } if (sync) { this.loadManifestsSync(_root); if (this.addPaths && Array.isArray(this.addPaths)) { var _iterator = _createForOfIteratorHelper(this.addPaths), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var addedRoot = _step.value; this.loadManifestsSync(addedRoot); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } var cache = { data: this._loadFilesCache(_root, paths) }; var ret = []; var locdata = this._pathjoin(this.base, 'locale'); // synchronous paths.forEach(function (path, index) { if (this.webos && path.indexOf('zoneinfo') !== -1) { ret.push(this._createZoneFile(path)); } else if (cache.data[index]) { ret.push(cache.data[index]); } else { var found = false; var handler = function handler(json, err) { if (!err && typeof json === 'object') { cache.update = true; ret.push(json); found = true; } }; var handleAdditionalResourcesPath = function handleAdditionalResourcesPath(json, err) { if (!err && typeof json === 'object') { if (found) { // Overwrite the _root/path result Object.assign(ret[ret.length - 1], json); } else { // This case is where the file is only in the additional resources path cache.update = true; ret.push(json); found = true; } } }; if (this.isAvailable(_root, path)) { getSync(this._pathjoin(_root, path), handler); } if (this.addPaths && Array.isArray(this.addPaths) && index === paths.length - 1) { var _iterator2 = _createForOfIteratorHelper(this.addPaths), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var _addedRoot = _step2.value; for (var i = 0; i <= index; i++) { if (this.isAvailable(_addedRoot, paths[i])) { getSync(this._pathjoin(_addedRoot, paths[i]), handleAdditionalResourcesPath); } } } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } } if (!found && this.isAvailable(locdata, path)) { getSync(this._pathjoin(locdata, path), handler); } if (!found) { if (path === 'localeinfo.json') { // Use default locale info when xhr on root localeinfo.json fails/skips ret.push(_LocaleInfo["default"].defaultInfo); } else { // not there, so fill in a blank entry in the array // eslint-disable-next-line no-undefined ret.push(undefined); } } } }.bind(this)); if (cache.update) { this._storeFilesCache(_root, paths, ret); } if (typeof callback === 'function') { callback.call(this, ret); } return ret; } else { // asynchronous var _cache = { data: this._loadFilesCache(_root, paths) }; Promise.all(paths.map(function (path) { return _this2._loadFilesAsync(path, params, _cache, rootPath); })).then(function (results) { if (_cache.update) { _this2._storeFilesCache(_root, paths, results); } if (typeof callback === 'function') { callback.call(_this2, results); } }); } }; EnyoLoader.prototype._handleManifest = function (dirpath, filepath, json) { var isAdditionalPath = typeof ILIB_ADDITIONAL_RESOURCES_PATH !== 'undefined' ? dirpath.includes(ILIB_ADDITIONAL_RESOURCES_PATH) : false; // star indicates there was no ilibmanifest.json, so always try to load files from // that dir if (json != null) { if (typeof window !== 'undefined' && window.localStorage) { setLocalStorageItem(cachePrefix + filepath, JSON.stringify(json)); } // Need to clear string cache this._clearStringsCache(); this.manifest[dirpath] = json.files; } else if (isAdditionalPath) { // If the path is an ilib additional resources path and json is null then make it null // so that we prevent loading everything. this.manifest[dirpath] = []; if (typeof window !== 'undefined' && window.localStorage) { setLocalStorageItem(cachePrefix + filepath, JSON.stringify(_defineProperty({}, timeStampKey, new Date().getTime()))); } } else { this.manifest[dirpath] = '*'; } return this.manifest[dirpath]; }; EnyoLoader.prototype._validateManifest = function (cachedManifest, filepath, sync) { if (cachedManifest) { var cachedTimeStamp = JSON.parse(cachedManifest)[timeStampKey]; if (cachedTimeStamp) { var newManifest; if (sync) { getSync(filepath, function (json) { newManifest = json; }); } else { get(filepath).then(function (json) { newManifest = json; }); } if (newManifest === null && typeof ILIB_ADDITIONAL_RESOURCES_PATH !== 'undefined' && filepath.includes(ILIB_ADDITIONAL_RESOURCES_PATH)) { // If new manifest is null and the filepath has ILIB_ADDITIONAL_RESOURCES_PATH, // meaning we need to clear string cache this._clearStringsCache(); return false; } else if (newManifest && newManifest[timeStampKey]) { // If new manifest has timestamp, compare old one and see if it's the same return cachedTimeStamp === newManifest[timeStampKey]; } else { return false; } } else { // Use cachedManifest as usual return true; } } return false; }; EnyoLoader.prototype._loadManifest = function (_root, subpath, sync) { var _this3 = this; if (!this.manifest) { this.manifest = {}; } var dirpath = this._pathjoin(_root, subpath); var filepath = this._pathjoin(dirpath, 'ilibmanifest.json'); var cachedManifest; if (typeof window !== 'undefined' && window.localStorage) { cachedManifest = window.localStorage.getItem(cachePrefix + filepath); } if (this._validateManifest(cachedManifest, filepath, sync)) { this.manifest[dirpath] = JSON.parse(cachedManifest).files; return sync ? this.manifest[dirpath] : Promise.resolve(this.manifest[dirpath]); } if (sync) { getSync(filepath, function (json) { _this3._handleManifest(dirpath, filepath, json); }); return; } return get(filepath).then(function (json) { return _this3._handleManifest(dirpath, filepath, json); }); }; EnyoLoader.prototype.loadManifests = function (_root) { this._validateCache(); return Promise.all([ // standard ilib locale data this._loadManifest(this.base, 'locale'), // the app's resources dir this._loadManifest('', iLibResources), // maybe it's a custom root? If so, try to load // the manifest file first in case it is there this._loadManifest(_root, '')]); }; EnyoLoader.prototype.loadManifestsSync = function (_root) { // load standard manifests if (!this.manifest) { this._validateCache(); this._loadManifest(this.base, 'locale', true); // standard ilib locale data this._loadManifest('', iLibResources, true); // the app's resources dir } if (!this.manifest[_root]) { // maybe it's a custom root? If so, try to load // the manifest file first in case it is there this._loadManifest(_root, '', true); } }; EnyoLoader.prototype.isAvailable = function (_root, path) { // util.print('enyo loader: isAvailable ' + path + '? '); // star means attempt to load everything because there was no manifest in that dir if (this.manifest[_root] === '*' || this.manifest[_root] && this.manifest[_root].indexOf(path) !== -1) { // util.print('true\n'); return true; } // util.print('false\n'); return false; }; var _default = exports["default"] = EnyoLoader;