@enact/i18n
Version:
Internationalization support for Enact using iLib
461 lines (455 loc) • 18.3 kB
JavaScript
"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;