diff --git a/src/modules/i18n.js b/src/modules/i18n.js index bb8d665f..9d014832 100644 --- a/src/modules/i18n.js +++ b/src/modules/i18n.js @@ -197,10 +197,11 @@ layui.define('lay', function(exports) { var INDEX_REPLACE_REGEX = /\{(\d+)\}/g; /** - * 获取对象中的值,lodash _.get 简易版 - * @param {Record} obj - * @param {string} path - * @param {any} defaultValue + * 获取对象中指定路径的值,类似于 lodash 的 _.get 方法 + * @param {Record} obj - 要查找的对象 + * @param {string} path - 要查找的路径,支持类似 'a[0].b.c' 的格式 + * @param {any} defaultValue - 若未找到对应值时返回的默认值 + * @returns {any} - 找到的值或默认值 */ function get(obj, path, defaultValue) { // 'a[0].b.c' ==> ['a', '0', 'b', 'c'] @@ -232,7 +233,7 @@ layui.define('lay', function(exports) { var val = origFn.apply(this, arguments) return typeof val === 'string' ? lay.escape(val) : val; } - }else if(layui.type(value) === 'array'){ + }else if(Array.isArray(value)){ value = value.map(function(v){ return typeof v === 'string' ? lay.escape(v) : v; }); @@ -241,13 +242,70 @@ layui.define('lay', function(exports) { return value } + /** + * 为纯函数创建具有缓存功能的版本,类似于 lodash 的 _.memoize 方法 + * @template T + * @param {(key: string, ...args) => T} fn - 需要缓存的函数,第一个参数为键 + * @returns {{(key: string, ...args): T, cleanup: () => void, cache: Record}} - 带有缓存的函数 + */ + function memoize(fn){ + /** @type Record */ + var cache = Object.create(null); + + function cachedFn(key) { + var hit = cache[key]; + return hit || (cache[key] = fn.apply(cache, arguments)); + } + cachedFn.cache = cache; + cachedFn.cleanup = function() { + cache = cachedFn.cache = Object.create(null); + } + return cachedFn; + } + + /** + * 清理指定命名空间下的缓存 + * @param {string} namespace - 要清理的命名空间 + */ + function pruneCache(namespace, cache) { + Object.keys(cache).map(function(key){ + if (buildNamespaceRegex(namespace).test(key)) { + cache[key] = null; + } + }) + } + var i18n = { config: config, - set: function(options) { + set: function (options) { + var oldLocale = config.locale; lay.extend(config, options); + + // locale 或 namespace 变更后清理缓存 + if (options.locale && oldLocale !== options.locale) { + getValueForKeyPathCached.cleanup(); + getMessageEscapeCached.cleanup(); + return; + } + if (options.messages && options.messages[ config.locale]) { + Object.keys(options.messages[ config.locale]).map(function(namespace){ + pruneCache(namespace, getValueForKeyPathCached.cache); + pruneCache(namespace, getMessageEscapeCached.cache); + }) + } } }; + var buildNamespaceRegex = memoize(function(namespace){ + return new RegExp('^' + namespace + '(?:\\.|$)'); + }) + var getValueForKeyPathCached = memoize(function(key, obj){ + return get(obj, key, key); + }); + var getMessageEscapeCached = memoize(function(_, value){ + return escape(value); + }); + /** * 根据给定的键从国际化消息中获取翻译后的内容 * 未文档化的私有方法,仅限内部使用 @@ -281,14 +339,14 @@ layui.define('lay', function(exports) { i18n.translation = function(key) { var options = config; var args = arguments; - var i18nMessage = options.messages[options.locale]; + var i18nMessages = options.messages[options.locale]; - if (!i18nMessage) { + if (!i18nMessages) { hint.error('Locale "' + options.locale + '" not found. Please add i18n messages for this locale first.', 'warn'); return key; } - var result = get(i18nMessage, key, key); + var result = getValueForKeyPathCached(key, i18nMessages); // 替换占位符 if (typeof result === 'string' && args.length > 1) { @@ -307,6 +365,11 @@ layui.define('lay', function(exports) { } } + // 仅缓存无额外参数的 key + if(args.length === 1){ + return getMessageEscapeCached(key, result) + } + return escape(result); };