From 3c31d3fb67e5999b5bcb7b5b9e5677a61ba67603 Mon Sep 17 00:00:00 2001 From: wxzhang Date: Thu, 11 Aug 2022 22:06:31 +0800 Subject: [PATCH] update formatters --- packages/runtime/index.js | 53 ++-- packages/runtime/scope.js | 617 +++++++++++++++++++++----------------- packages/runtime/utils.js | 26 ++ 3 files changed, 388 insertions(+), 308 deletions(-) diff --git a/packages/runtime/index.js b/packages/runtime/index.js index b16e6a8..79fc29a 100644 --- a/packages/runtime/index.js +++ b/packages/runtime/index.js @@ -250,48 +250,39 @@ function getDataTypeDefaultFormatter(scope,activeLanguage,dataType){ } } - /** * 获取指定名称的格式化器函数 * * 查找逻辑 - * - 在当前作用域中查找 - * + * - 在当前作用域中查找 * - 在全局作用域中查找 * - * 全局作用域的格式化器优先 - * * @param {*} scope * @param {*} activeLanguage 当前激活语言名称 * @param {*} name 格式化器名称 * @returns {Function} 格式化函数 */ function getFormatter(scope,activeLanguage,name){ - // 缓存格式化器引用,避免重复检索 + // 1. 从缓存中直接读取: 缓存格式化器引用,避免重复检索 if(!scope.$cache) resetScopeCache(scope) if(scope.$cache.activeLanguage === activeLanguage) { if(name in scope.$cache.formatters) return scope.$cache.formatters[name] }else{// 当语言切换时清空缓存 resetScopeCache(scope,activeLanguage) } - // 先在当前作用域中查找,再在全局查找 - const targets = [scope.global.formatters,scope.formatters] - for(const target of targets){ - // 1. 优先在当前语言查找 - if(activeLanguage in target){ - let formatters = target[activeLanguage] || {} - if((name in formatters) && isFunction(formatters[name])) { - return scope.$cache.formatters[name] = formatters[name] - }else{ // 如果语言指定了fallback,则在其回退语言中查找 - let fallbackLangName = scope.getFallbackLanguage(activeLanguage) - if((fallbackLangName in formatters) && isFunction(formatters[fallbackLangName])) { - return scope.$cache.formatters[name] = formatters[fallbackLangName] - } - } - } - // 2. 全局作用域中查找 - let formatters = target["*"] || {} - if((name in formatters) && isFunction(formatters[name])) return scope.$cache.formatters[name] = formatters[name] + const fallbackLanguage = scope.getLanguage(activeLanguage).fallback + // 2. 先在当前作用域中查找,再在全局查找 formatters={$types,$options,[格式化器名称]:()=>{},[格式化器名称]:()=>{}} + const range = [ + scope.activeFormatters, + scope.formatters[fallbackLanguage], // 如果指定了回退语言时,也在该回退语言中查找 + scope.global.formatters[activeLanguage], // 适用于activeLanguage全局格式化器 + scope.global.formatters["*"], // 适用于所有语言的格式化器 + ] + for(const formatters of range){ + if(!formatters) continue + if(isFunction(formatters[name])) { + return scope.$cache.formatters[name] = formatters[name] + } } } @@ -317,8 +308,6 @@ function executeFormatter(value,formatters,scope){ return result } /** - * - * * * 将 [[格式化器名称,[参数,参数,...]],[格式化器名称,[参数,参数,...]]]格式化器转化为 * 格式化器的调用函数链 @@ -328,7 +317,6 @@ function executeFormatter(value,formatters,scope){ * @param {*} formatters * @returns {Array} [(v)=>{...},(v)=>{...},(v)=>{...}] * - * */ function buildFormatters(scope,activeLanguage,formatters){ let results = [] @@ -356,7 +344,7 @@ function buildFormatters(scope,activeLanguage,formatters){ } /** - * 将value经过格式化器处理后返回 + * 将value经过格式化器处理后返回的结果 * @param {*} scope * @param {*} activeLanguage * @param {*} formatters @@ -578,9 +566,8 @@ function translate(message) { } I18nManager.instance = this; this._settings = deepMerge(defaultLanguageSettings,settings) - this._scopes=[] - this._defaultMessageLoader = null // 默认文本加载器 - return I18nManager.instance; + this._scopes=[] // 保存i18nScope实例 + this._defaultMessageLoader = null // 默认文本加载器 } get settings(){ return this._settings } get scopes(){ return this._scopes } @@ -692,7 +679,9 @@ function translate(message) { }else{ await Promise.all(requests) } - }catch{} + }catch(e){ + if(this._debug) console.error(`Error while refresh voerkai18n scopes:${e.message}`) + } } } diff --git a/packages/runtime/scope.js b/packages/runtime/scope.js index d7869e2..fc0e71a 100644 --- a/packages/runtime/scope.js +++ b/packages/runtime/scope.js @@ -1,78 +1,114 @@ -const { isPlainObject,isFunction } = require("./utils") +const { isPlainObject, isFunction, getByPath, deepMixin } = require("./utils"); -const DataTypes = ["String","Number","Boolean","Object","Array","Function","Null","Undefined","Symbol","Date","RegExp","Error"]; +const DataTypes = [ + "String", + "Number", + "Boolean", + "Object", + "Array", + "Function", + "Null", + "Undefined", + "Symbol", + "Date", + "RegExp", + "Error", +]; module.exports = class i18nScope { - constructor(options={},callback){ - this._id = options.id || (new Date().getTime().toString()+parseInt(Math.random()*1000)) - this._debug = options.debug // 当出错时是否在控制台台输出错误信息 - this._languages = options.languages // 当前作用域的语言列表 - this._defaultLanguage = options.defaultLanguage || "zh" // 默认语言名称 - this._activeLanguage = options.activeLanguage // 当前语言名称 - this._default = options.default // 默认语言包 - this._messages = options.messages // 当前语言包 - this._idMap = options.idMap // 消息id映射列表 - this._formatters = options.formatters // 当前作用域的格式化函数列表{:{$types,$options,[格式化器名称]:()=>{},[格式化器名称]:()=>{}}} - this._loaders = options.loaders // 异步加载语言文件的函数列表 - this._global = null // 引用全局VoerkaI18n配置,注册后自动引用 - this._activeFormatters= options.formatters[options.activeLanguage] // 激活使用的格式化器,查找格式化器时在此查找 - this._patchMessages = {} // 语言包补丁信息 {:{....},:{....}} - // 用来缓存格式化器的引用,当使用格式化器时可以直接引用,减少检索遍历 - this.$cache={ - activeLanguage : null, - typedFormatters: {}, - formatters : {}, - } - // 如果不存在全局VoerkaI18n实例,说明当前Scope是唯一或第一个加载的作用域,则自动创建全局VoerkaI18n实例 - if(!globalThis.VoerkaI18n){ - const { I18nManager } = require("./") - globalThis.VoerkaI18n = new I18nManager({ - debug : this._debug, - defaultLanguage: this.defaultLanguage, - activeLanguage : this.activeLanguage, - languages : options.languages, - }) - } - this._global = globalThis.VoerkaI18n - // 合并补丁语言包 - this._mergePatchedMessages() - this._patch(this._messages,this.activeLanguage) - // 正在加载语言包标识 - this._refreshing=false - // 在全局注册作用域 - this.register(callback) - } - // 作用域 - get id(){return this._id} - // 调试开关 - get debug(){return this._debug} - // 默认语言名称 - get defaultLanguage(){return this._defaultLanguage} - // 默认语言名称 - get activeLanguage(){return this._activeLanguage} - // 默认语言包 - get default(){return this._default} - // 当前语言包 - get messages(){return this._messages} - // 消息id映射列表 - get idMap(){return this._idMap} - // 当前作用域的格式化器 {:{$types,$options,[格式化器名称]:()=>{},[格式化器名称]:()=>{}}} - get formatters(){return this._formatters} - // 当前作用域支持的语言列表[{name,title,fallback}] - get languages(){return this._languages} - // 异步加载语言文件的函数列表 - get loaders(){return this._loaders} - // 引用全局VoerkaI18n配置,注册后自动引用 - get global(){return this._global} - /** - * 在全局注册作用域 - * @param {*} callback 注册成功后的回调 - */ - register(callback){ - if(!isFunction(callback)) callback = ()=>{} - this.global.register(this).then(callback).catch(callback) - } - /** + constructor(options = {}, callback) { + this._id = options.id || Date.now().toString() + parseInt(Math.random() * 1000); + // 当出错时是否在控制台台输出错误信息 + this._debug = options.debug == undefined ? process && process.env && process.env.NODE_ENV === "development" : options.debug; + this._languages = options.languages; // 当前作用域的语言列表 + this._defaultLanguage = options.defaultLanguage || "zh"; // 默认语言名称 + this._activeLanguage = options.activeLanguage; // 当前语言名称 + this._default = options.default; // 默认语言包 + this._messages = options.messages; // 当前语言包 + this._idMap = options.idMap; // 消息id映射列表 + this._formatters = options.formatters; // 当前作用域的格式化函数列表{:{$types,$options,[格式化器名称]:()=>{},[格式化器名称]:()=>{}}} + this._loaders = options.loaders; // 异步加载语言文件的函数列表 + this._global = null; // 引用全局VoerkaI18n配置,注册后自动引用 + this._activeFormatters = options.formatters[options.activeLanguage]; // 激活使用的格式化器,查找格式化器时在此查找 + this._patchMessages = {}; // 语言包补丁信息 {:{....},:{....}} + // 用来缓存格式化器的引用,当使用格式化器时可以直接引用,减少检索遍历 + this.$cache = { + activeLanguage: null, + typedFormatters: {}, + formatters: {}, + }; + // 如果不存在全局VoerkaI18n实例,说明当前Scope是唯一或第一个加载的作用域,则自动创建全局VoerkaI18n实例 + if (!globalThis.VoerkaI18n) { + const { I18nManager } = require("./"); + globalThis.VoerkaI18n = new I18nManager({ + debug: this._debug, + defaultLanguage: this.defaultLanguage, + activeLanguage: this.activeLanguage, + languages: options.languages, + }); + } + this._global = globalThis.VoerkaI18n; + // 合并补丁语言包 + this._mergePatchedMessages(); + this._patch(this._messages, this.activeLanguage); + // 正在加载语言包标识 + this._refreshing = false; + // 在全局注册作用域 + this.register(callback); + } + // 作用域 + get id() { + return this._id; + } + // 调试开关 + get debug() { + return this._debug; + } + // 默认语言名称 + get defaultLanguage() { + return this._defaultLanguage; + } + // 默认语言名称 + get activeLanguage() { + return this._activeLanguage; + } + // 默认语言包 + get default() { + return this._default; + } + // 当前语言包 + get messages() { + return this._messages; + } + // 消息id映射列表 + get idMap() { + return this._idMap; + } + // 当前作用域的格式化器 {:{$types,$options,[格式化器名称]:()=>{},[格式化器名称]:()=>{}}} + get formatters() { + return this._formatters; + } + // 当前作用域支持的语言列表[{name,title,fallback}] + get languages() { + return this._languages; + } + // 异步加载语言文件的函数列表 + get loaders() { + return this._loaders; + } + // 引用全局VoerkaI18n配置,注册后自动引用 + get global() { + return this._global; + } + /** + * 在全局注册作用域 + * @param {*} callback 注册成功后的回调 + */ + register(callback) { + if (!isFunction(callback)) callback = () => {}; + this.global.register(this).then(callback).catch(callback); + } + /** * 注册格式化器 * * 格式化器是一个简单的同步函数value=>{...},用来对输入进行格式化后返回结果 @@ -87,213 +123,242 @@ module.exports = class i18nScope { language : 声明该格式化器适用语言 asGlobal : 注册到全局 */ - registerFormatter(name,formatter,{language="*",asGlobal}={}){ - if(!isFunction(formatter) || typeof(name)!=="string"){ - throw new TypeError("Formatter must be a function") - } - language = Array.isArray(language) ? language : (language ? language.split(",") : []) - if(asGlobal){ - this.global.registerFormatter(name,formatter,{language}) - }else{ - language.forEach(lng=>{ - if(DataTypes.includes(name)){ - this._formatters[lng].$types[name] = formatter - }else{ - this._formatters[lng][name] = formatter - } - }) - } - } - /** - * 注册默认文本信息加载器 - * @param {Function} 必须是异步函数或者是返回Promise - */ - registerDefaultLoader(fn){ - this.global.registerDefaultLoader(fn) - } - /** - * 获取指定语言信息 - * @param {*} language - * @returns - */ - _getLanguage(language){ - let index = this._languages.findIndex(lng=>lng.name==language) - if(index!==-1) return this._languages[index] - } - /** - * 返回是否存在指定的语言 - * @param {*} language 语言名称 - * @returns - */ - hasLanguage(language){ - return this._languages.indexOf(lang=>lang.name==language)!==-1 - } - /** - * 获取指定语言的回退语言名称 + registerFormatter(name, formatter, { language = "*", asGlobal } = {}) { + if (!isFunction(formatter) || typeof name !== "string") { + throw new TypeError("Formatter must be a function"); + } + language = Array.isArray(language) + ? language + : language + ? language.split(",") + : []; + if (asGlobal) { + this.global.registerFormatter(name, formatter, { language }); + } else { + language.forEach((lng) => { + if (DataTypes.includes(name)) { + this._formatters[lng].$types[name] = formatter; + } else { + this._formatters[lng][name] = formatter; + } + }); + } + } + /** + * 注册默认文本信息加载器 + * @param {Function} 必须是异步函数或者是返回Promise + */ + registerDefaultLoader(fn) { + this.global.registerDefaultLoader(fn); + } + /** + * 获取指定语言信息 + * @param {*} language + * @returns + */ + getLanguage(language) { + let index = this._languages.findIndex((lng) => lng.name == language); + if (index !== -1) return this._languages[index]; + } + /** + * 返回是否存在指定的语言 + * @param {*} language 语言名称 + * @returns + */ + hasLanguage(language) { + return this._languages.indexOf((lang) => lang.name == language) !== -1; + } + /** + * 回退到默认语言 + */ + _fallback() { + this._messages = this._default; + this._activeLanguage = this.defaultLanguage; + } + /** + * 当切换语言时,格式化器应该切换到对应语言的格式化器 * - * 如果没有指定,则总是回退到到默认语言 + * 重要需要处理: + * $options参数采用合并继承机制 * - * 但是不支持回退链 * - * 可以在配置中指定回退语言 - * // settings.json - * { - * languages:[ - * {name:"zh"}, - * {name:"cht",fallback:"zh"}, //繁体中文可以回退到简体中文, - * {name:"en"}, - * ] - * } - * - * @param {*} language - * @returns {Object} 语言信息数据 {name,title,fallback,...} - */ - getFallbackLanguage(language){ - let lang = this._getLanguage(language) - if(lang){ - return this.hasLanguage(lang.fallback) ? lang.fallback : this.defaultLanguage - } - } + * @param {*} language + */ + async _changeFormatters(newLanguage) { + try { + if (newLanguage in this._formatters) { + let loader = this._formatters[newLanguage]; + if (isPlainObject(loader)) { + this._activeFormatters = loader; + } else if (isFunction(loader)) { + this._activeFormatters = (await loader()).default; + } + // 合并生成格式化器的配置参数,当执行格式化器时该参数将被传递给格式化器 + this._generateFormatterOptions(newLanguage) + } else { + if (this._debug) console.warn(`Not configured <${newLanguage}> formatters.`); + } + } catch (e) { + if (this._debug) console.error(`Error loading ${newLanguage} formatters: ${e.message}`); + } + } /** - * 回退到默认语言 + * 生成格式化器的配置参数,该参数由以下合并而成: + * - global.formatters[*].$options + * - global.formatters[language].$options + * - scope.activeFormattes.$options 优先 */ - _fallback(){ - this._messages = this._default - this._activeLanguage = this.defaultLanguage + _generateFormatterOptions(language){ + let options = Object.assign({},getByPath(global.formatters,`*.$options`,{})) + deepMixin(options,getByPath(global.formatters,`${language}.$options`,{})) + deepMixin(options,getByPath(scope.activeFormattes,"$options",{})) + return this._activeFormatterOptions = options } - /** - * 当切换语言时,格式化器应该切换到对应语言的格式化器 - * @param {*} language - */ - async _loadFormatters(newLanguage){ - try{ - if(newLanguage in this._formatters){ - let loader = this._formatters[newLanguage] - if(isPlainObject(loader)){ - this._activeFormatters = loader - }else if(isFunction(loader)){ - this._activeFormatters = (await loader()).default - } - }else{ - if(this._debug) console.warn(`Not configured <${newLanguage}> formatters.`) - } - }catch(e){ - if(this._debug) console.error(`Error loading ${newLanguage} formatters: ${e.message}`) - } - } - /** - * 刷新当前语言包 - * @param {*} newLanguage - */ - async refresh(newLanguage){ - this._refreshing = true - if(!newLanguage) newLanguage = this.activeLanguage - // 默认语言:由于默认语言采用静态加载方式而不是异步块,因此只需要简单的替换即可 - if(newLanguage === this.defaultLanguage){ - this._messages = this._default - await this._patch(this._messages,newLanguage) // 异步补丁 - this._loadFormatters(newLanguage) - return - } - // 非默认语言需要异步加载语言包文件,加载器是一个异步函数 - // 如果没有加载器,则无法加载语言包,因此回退到默认语言 - let loader = this.loaders[newLanguage] - try{ - if(isPlainObject(loader)){ - this._messages = loader - await this._patch(this._messages,newLanguage) - }else if(isFunction(loader)){ - this._messages = (await loader()).default - this._activeLanguage = newLanguage - await this._patch(this._messages,newLanguage) - }else if(isFunction(this.global.defaultMessageLoader)){// 如果该语言没有指定加载器,则使用全局配置的默认加载器 - const loadedMessages = await this.global.loadMessagesFromDefaultLoader(newLanguage,this) - this._messages = Object.assign({},this._default,loadedMessages) - this._activeLanguage = newLanguage - }else{ - this._fallback() - } - // 应该切换到对应语言的格式化器 - this._loadFormatters(newLanguage) - }catch(e){ - if(this._debug) console.warn(`Error while loading language <${newLanguage}> on i18nScope(${this.id}): ${e.message}`) - this._fallback() - }finally{ - this._refreshing = false - } - } - /** - * 当指定了默认语言包加载器后,会从服务加载语言补丁包来更新本地的语言包 - * - * 补丁包会自动存储到本地的LocalStorage中 - * - * @param {*} messages - * @param {*} newLanguage - * @returns - */ - async _patch(messages,newLanguage){ - if(!isFunction(this.global.loadMessagesFromDefaultLoader)) return - try{ - let pachedMessages = await this.global.loadMessagesFromDefaultLoader(newLanguage,this) - if(isPlainObject(pachedMessages)){ - Object.assign(messages,pachedMessages) - this._savePatchedMessages(pachedMessages,newLanguage) - } - }catch(e){ - if(this._debug) console.error(`Error while loading <${newLanguage}> messages from remote:${error.message}`) - } - } - /** - * 从本地存储中读取语言包补丁合并到当前语言包中 - */ - _mergePatchedMessages(){ - let patchedMessages= this._getPatchedMessages(this.activeLanguage) - if(isPlainObject(patchedMessages)){ - Object.assign(this._messages,patchedMessages) - } - } - /** - * 将读取的补丁包保存到本地的LocalStorage中 - * - * 为什么要保存到本地的LocalStorage中? - * - * 因为默认语言是静态嵌入到源码中的,而加载语言包补丁是延后异步的, - * 当应用启动第一次就会渲染出来的是没有打过补丁的内容。 - * - * - 如果还需要等待从服务器加载语言补丁合并后再渲染会影响速度 - * - 如果不等待从服务器加载语言补丁就渲染,则会先显示未打补丁的内容,然后在打完补丁后再对应用进行重新渲染生效 - * 这明显不是个好的方式 - * - * 因此,采用的方式是: - * - 加载语言包补丁后,将之保存到到本地的LocalStorage中 - * - 当应用加载时会查询是否存在补丁,如果存在就会合并渲染 - * - * @param {*} messages - */ - _savePatchedMessages(messages,language){ - try{ - if(globalThis.localStorage){ - globalThis.localStorage.setItem(`voerkai18n_${this.id}_${language}_patched_messages`, JSON.stringify(messages)); - } - }catch(e){ - if(this.$cache._debug) console.error("Error while save voerkai18n patched messages:",e.message) - } - } - /** - * 从本地缓存中读取补丁语言包 - * @param {*} language - * @returns - */ - _getPatchedMessages(language){ - try{ - return JSON.parse(localStorage.getItem(`voerkai18n_${this.id}_${language}_patched_messages`)) - }catch(e){ - return {} - } - } - // 以下方法引用全局VoerkaI18n实例的方法 - get on(){return this._global.on.bind(this._global)} - get off(){return this._global.off.bind(this._global)} - get offAll(){return this._global.offAll.bind(this._global)} - get change(){return this._global.change.bind(this._global)} -} \ No newline at end of file + /** + * 刷新当前语言包 + * @param {*} newLanguage + */ + async refresh(newLanguage) { + this._refreshing = true; + if (!newLanguage) newLanguage = this.activeLanguage; + // 默认语言:由于默认语言采用静态加载方式而不是异步块,因此只需要简单的替换即可 + if (newLanguage === this.defaultLanguage) { + this._messages = this._default; + await this._patch(this._messages, newLanguage); // 异步补丁 + this._changeFormatters(newLanguage); + return; + } + // 非默认语言需要异步加载语言包文件,加载器是一个异步函数 + // 如果没有加载器,则无法加载语言包,因此回退到默认语言 + let loader = this.loaders[newLanguage]; + try { + if (isPlainObject(loader)) { + this._messages = loader; + await this._patch(this._messages, newLanguage); + } else if (isFunction(loader)) { + this._messages = (await loader()).default; + this._activeLanguage = newLanguage; + await this._patch(this._messages, newLanguage); + } else if (isFunction(this.global.defaultMessageLoader)) { + // 如果该语言没有指定加载器,则使用全局配置的默认加载器 + const loadedMessages = + await this.global.loadMessagesFromDefaultLoader( + newLanguage, + this + ); + this._messages = Object.assign( + {}, + this._default, + loadedMessages + ); + this._activeLanguage = newLanguage; + } else { + this._fallback(); + } + // 应该切换到对应语言的格式化器 + this._changeFormatters(newLanguage); + } catch (e) { + if (this._debug) console.warn(`Error while loading language <${newLanguage}> on i18nScope(${this.id}): ${e.message}`); + this._fallback(); + } finally { + this._refreshing = false; + } + } + /** + * 当指定了默认语言包加载器后,会从服务加载语言补丁包来更新本地的语言包 + * + * 补丁包会自动存储到本地的LocalStorage中 + * + * @param {*} messages + * @param {*} newLanguage + * @returns + */ + async _patch(messages, newLanguage) { + if (!isFunction(this.global.loadMessagesFromDefaultLoader)) return; + try { + let pachedMessages = + await this.global.loadMessagesFromDefaultLoader( + newLanguage, + this + ); + if (isPlainObject(pachedMessages)) { + Object.assign(messages, pachedMessages); + this._savePatchedMessages(pachedMessages, newLanguage); + } + } catch (e) { + if (this._debug) console.error(`Error while loading <${newLanguage}> messages from remote:${error.message}`); + } + } + /** + * 从本地存储中读取语言包补丁合并到当前语言包中 + */ + _mergePatchedMessages() { + let patchedMessages = this._getPatchedMessages(this.activeLanguage); + if (isPlainObject(patchedMessages)) { + Object.assign(this._messages, patchedMessages); + } + } + /** + * 将读取的补丁包保存到本地的LocalStorage中 + * + * 为什么要保存到本地的LocalStorage中? + * + * 因为默认语言是静态嵌入到源码中的,而加载语言包补丁是延后异步的, + * 当应用启动第一次就会渲染出来的是没有打过补丁的内容。 + * + * - 如果还需要等待从服务器加载语言补丁合并后再渲染会影响速度 + * - 如果不等待从服务器加载语言补丁就渲染,则会先显示未打补丁的内容,然后在打完补丁后再对应用进行重新渲染生效 + * 这明显不是个好的方式 + * + * 因此,采用的方式是: + * - 加载语言包补丁后,将之保存到到本地的LocalStorage中 + * - 当应用加载时会查询是否存在补丁,如果存在就会合并渲染 + * + * @param {*} messages + */ + _savePatchedMessages(messages, language) { + try { + if (globalThis.localStorage) { + globalThis.localStorage.setItem( + `voerkai18n_${this.id}_${language}_patched_messages`, + JSON.stringify(messages) + ); + } + } catch (e) { + if (this.$cache._debug) + console.error( + "Error while save voerkai18n patched messages:", + e.message + ); + } + } + /** + * 从本地缓存中读取补丁语言包 + * @param {*} language + * @returns + */ + _getPatchedMessages(language) { + try { + return JSON.parse( + localStorage.getItem( + `voerkai18n_${this.id}_${language}_patched_messages` + ) + ); + } catch (e) { + return {}; + } + } + // 以下方法引用全局VoerkaI18n实例的方法 + get on() { + return this._global.on.bind(this._global); + } + get off() { + return this._global.off.bind(this._global); + } + get offAll() { + return this._global.offAll.bind(this._global); + } + get change() { + return this._global.change.bind(this._global); + } +}; diff --git a/packages/runtime/utils.js b/packages/runtime/utils.js index bc81672..b7cbe58 100644 --- a/packages/runtime/utils.js +++ b/packages/runtime/utils.js @@ -172,6 +172,31 @@ function toNumber(value,defualt=0) { return prefix + result.join("") + suffix } +/** + * 根据路径获取指定值 + * + * getByPath({a:{b:1}},"a.b") == 1 + * getByPath({a:{b:1}},"a.c",2) == 2 + * + * @param {*} obj + * @param {*} path 使用.分割的路径 + * @param {*} defaultValue 默认值 + * @returns + */ +function getByPath(obj,path,defaultValue){ + if(typeof(obj)!="object") return defaultValue + let paths = path.split(".") + let cur = obj + for(let key of paths){ + if(typeof(cur)=="object" && key in cur ){ + cur = cur[key] + }else{ + return defaultValue + } + } + return cur +} + /** * 返回value相对rel的相对时间 * @@ -191,6 +216,7 @@ module.exports ={ isNothing, deepMerge, deepMixin, + getByPath, getDataTypeName, toDate, toNumber,