From a9f63c2c356da46b311707144e935627431afcfa Mon Sep 17 00:00:00 2001 From: wxzhang Date: Fri, 7 Apr 2023 21:54:31 +0800 Subject: [PATCH] update --- packages/runtime/src/formatterRegistry.ts | 271 +++++++++++++++------- packages/runtime/src/formatterStore.ts | 37 --- packages/runtime/src/interpolate.ts | 115 +-------- packages/runtime/src/manager.ts | 21 +- packages/runtime/src/scope.ts | 93 +------- packages/runtime/src/types.ts | 6 - 6 files changed, 205 insertions(+), 338 deletions(-) delete mode 100644 packages/runtime/src/formatterStore.ts diff --git a/packages/runtime/src/formatterRegistry.ts b/packages/runtime/src/formatterRegistry.ts index ec5c0b8..a7e9f35 100644 --- a/packages/runtime/src/formatterRegistry.ts +++ b/packages/runtime/src/formatterRegistry.ts @@ -4,12 +4,14 @@ * * */ -import { FormatterStore } from './formatterStore'; +import { isPlainObject } from 'flex-tools/typecheck/isPlainObject'; import type { VoerkaI18nScope } from './scope'; -import { VoerkaI18nFormatter, VoerkaI18nFormatters, VoerkaI18nFormattersLoader, VoerkaI18nLanguageFormatters, SupportedDateTypes, VoerkaI18nFormatterConfigs } from './types'; +import { VoerkaI18nFormatter, VoerkaI18nFormatters, VoerkaI18nFormattersLoader, VoerkaI18nLanguageFormatters, SupportedDateTypes, VoerkaI18nFormatterConfigs, VoerkaI18nTypesFormatters } from './types'; import { DataTypes } from './utils'; import { get as getByPath } from "flex-tools/object/get" import { isFunction } from 'flex-tools/typecheck/isFunction'; +import { deepMerge } from 'flex-tools/object/deepMerge'; +import { assignObject } from 'flex-tools/object/assignObject'; export interface VoerkaI18nScopeCache{ @@ -23,30 +25,100 @@ export class FormattersNotLoadedError extends Error{ super(`Formatters of language<${language}> is not loaded,try to call load()`) } } + +export interface GetFormatterOptions{ + on:"types" | "scope" + inGlobal?:boolean // 在全局中查找 +} + const EmptyFormatters:any = { $config:{}, $types:{} } -export class VoerkaI18nFormatterRegistry extends FormatterStore{ - // 由于语言的格式化器集合允许是一个异步加载块,所以需要一个ready标志 - // 当语言格式化器集合加载完成后,ready标志才会变为true - #ready:boolean = false - #language:string = 'zh' - constructor(scope:VoerkaI18nScope){ - super() +export interface VoerkaI18nScopeFormatterCache{ + typedFormatters:VoerkaI18nTypesFormatters, + formatters : Record, +} + +export class VoerkaI18nFormatterRegistry{ + #formatters:VoerkaI18nLanguageFormatters = {} + #activeFormatters:VoerkaI18nFormatters = {} + #activeFormattersConfigs :VoerkaI18nFormatterConfigs = {} + #scope?:VoerkaI18nScope + #language:string // 当前语言 + #formatterCache:VoerkaI18nScopeFormatterCache = {typedFormatters:{},formatters:{}} + constructor(scope?:VoerkaI18nScope){ + this.#scope = scope + this.#language = scope?.activeLanguage || "zh" + } + get activeLanguage(){ return this.#language } // 当前语言 + get activeFormatters(){ return this.#activeFormatters } // 当前语言的格式化器集合 + get scope(){ return this.#scope } + /** + * 当切换语言时,切换当前语言的格式化器 + * 当切换语言时,如果当前语言的格式化器集合还没有加载,则会自动加载 + * @param language + */ + async change(language:string){ + try { + if (language in this.formatters) { + this.#language = language + const formatters = this.formatters[language] + if(isFunction(formatters)){ + this.#activeFormatters = await (formatters as Function)() // 如果格式化器集合是异步加载,则需要等待加载完成 + }else{ + this.#activeFormatters = formatters as VoerkaI18nFormatters + } + // 合并生成格式化器的配置参数,当执行格式化器时该参数将被传递给格式化器 + this.generateFormattersConfigs(language) + // 清空缓存 + this.#formatterCache = {typedFormatters:{},formatters:{}} + } else { + if (this.scope?.debug) console.warn(`Not configured <${language}> formatters.`); + } + } catch (e:any) { + if (this.scope?.debug) console.error(`Error loading ${language} formatters: ${e.message}`); + } + } + private async generateFormattersConfigs(language:string){ + try{ + const configSources = [ ] + const fallbackLanguage = this.scope?.getLanguage(language)?.fallback || 'en'; + if(this.scope){ // 从全局Scope读取 + configSources.push(this.scope.global.formatters.getConfig('*')) + configSources.push(this.scope.global.formatters.getConfig(fallbackLanguage)) + configSources.push(this.scope.global.formatters.config) + } + // 从当前Scope读取 + configSources.push(this.getConfig('*')) + configSources.push(this.getConfig(fallbackLanguage)) + configSources.push(this.config) + // 合并当前语言的格式化器配置参数 + this.#activeFormattersConfigs = configSources.reduce((finalConfig, curConfig)=>{ + if(isPlainObject(curConfig)) deepMerge(finalConfig,curConfig,{newObject:false,array:'replace'}) + return finalConfig + },{}) + }catch(e){ + if(this.scope?.debug) console.error(`Error while generate <${language}> formatter options: `,e) + this.#activeFormattersConfigs = {} + } + } + updateConfig(language:string,config:VoerkaI18nFormatterConfigs){ + if(language in this.#formatters ){ + let formatters = this.#formatters[language] as VoerkaI18nFormatters + if(!("$config" in formatters)) formatters.$config = {} + assignObject(formatters.$config as object ,config) + } + if(language === this.#language){ + this.generateFormattersConfigs(language) + } } /** - * 当前语言 + * 注册指定语言的格式化器 + * @param language + * @param formatters */ - get language(){ return this.#language } - set language(language:string){ - this.#language = language - if(!(language in this.formatters)){ - (this.#formatters[language] as any) = Object.assign({},EmptyFormatters) - } - this.#ready = typeof(this.#formatters[language]) != 'function' - } registerLanguageFormatters(language:string,formatters:VoerkaI18nFormatters | VoerkaI18nFormattersLoader){ this.#formatters[language] = formatters } @@ -55,98 +127,131 @@ export class VoerkaI18nFormatterRegistry extends FormatterStore{ * @param formatters */ loadInitials(formatters:VoerkaI18nLanguageFormatters){ - this.#formatters=formatters + this.#formatters = formatters } + /** + * 注册格式化器 + * @param name 格式化器名称,如果名称是一个支持的日期类型,则格式化器将被注册为语言类型的格式化器 + * @param formatter + * @param language 将格式化器注册到指定语言,如果不指定或'*',则注册到所有语言; + * 也可以指定将格式化器注册到多个语言 + * + */ + register(name:string | SupportedDateTypes, formatter:VoerkaI18nFormatter, {language = "*"}:{ language: string | string[] } ) { + if (!isFunction(formatter) || typeof name !== "string") { + throw new TypeError("Formatter must be a function"); + } + const languages = Array.isArray(language) ? language: language ? language.split(","): []; + languages.forEach((lngName:string) => { + if(!(lngName in this.#formatters)) this.#formatters[lngName] = {} + if(typeof(this.#formatters[lngName])!="function"){ + let lngFormatters = this.#formatters[lngName] as any + if (DataTypes.includes(name)) { + if(!lngFormatters.$types) lngFormatters.$types = {} + lngFormatters.$types![name] = formatter + } else { + lngFormatters[name] = formatter; + } + } + }); + } + //****************** 以下方法可以获取指定语言的格式化器 *********************** */ /** * 获取指定语言的格式化器配置 * @param language */ getConfig(language?:string){ + if(language== this.#language) return this.#activeFormattersConfigs return language ? getByPath(this.#formatters,`${language}.$config`,{defaultValue:{}}) : {} } /** 获取指定语言中为每个数据类型指定的格式化器 */ getTypes(language?:string){ + if(language== this.#language) return this.activeFormatters.$types return language ? getByPath(this.#formatters,`${language}.$types`,{defaultValue:{}}) : {} } /** 获取指定语言中为每个数据类型指定的格式化器 */ getFormatters(language?:string){ + if(language== this.#language) return this.activeFormatters return language ? getByPath(this.#formatters,language,{defaultValue:{}}) : {} - } - /** - * 更新指定语言的格式化器配置参数 - * @param language - * @param config - */ - updateConfig(language:string,config:VoerkaI18nFormatterConfigs){ - if(language in this.#formatters){ - const formatters = this.#formatters[language] - if(typeof(formatters)=='function'){ - throw new FormattersNotLoadedError(language) - }else{ - if(!formatters.$config){ - formatters.$config = {} - } - Object.assign(formatters.$config,config) - } - } - } - /** - * 返回指定语言是否具有格式化器集合 - * @param language - */ - hasLanguage(language:string){ - return language in this.#formatters - } + } //****************** 以下方法和属性只作用于当前语言 *********************** */ - - /** - * 当格式化器是一个()=>import(...)或一个返回格式化器集合的Promise时,需要调用load()方法加载格式化器 - * - * 这是由于不同语言的格式化器允许声明为异步加载块,因此需要在切换语言使用时才加载 - * - */ - async load(){ - let loader = this.#formatters[this.#language] as VoerkaI18nFormattersLoader - if(typeof(loader) === 'function'){ - this.#formatters[this.#language] = await loader() - this.#ready = true - }// 如果是一个对象,则直接返回,不需要调用 - } - /** - * 当前语言的格式化器集合 - */ - get formatters(){ - if(!this.#ready) throw new FormattersNotLoadedError(this.#language) - return this.#formatters[this.#language] as VoerkaI18nFormatters - } - /** - * 返回当前语言的格式化器 - * @param name 格式化名称 - */ - get(name:string,dataType?:SupportedDateTypes):VoerkaI18nFormatter | undefined{ - if(!this.#ready) throw new FormattersNotLoadedError(this.#language) - const lngFormatters = this.#formatters[this.#language] as any - if(dataType && (dataType in lngFormatters.$types!)){ - return lngFormatters.$types![dataType] - }else if(name in lngFormatters){ - return lngFormatters[name] - } - } + + get formatters(){ return this.#formatters } // 所有语言的格式化器集合 /** * 当前语言的格式化器配置 */ get config(){ - if(!this.#ready) throw new FormattersNotLoadedError(this.#language) - return (this.#formatters[this.#language] as VoerkaI18nFormatters).$config as VoerkaI18nFormatterConfigs + return (this.#activeFormatters as VoerkaI18nFormatters).$config as VoerkaI18nFormatterConfigs } get types(){ - if(!this.#ready) throw new FormattersNotLoadedError(this.#language) - return (this.#formatters[this.#language] as VoerkaI18nFormatters).$types as VoerkaI18nFormatterConfigs + return (this.#activeFormatters as VoerkaI18nFormatters).$types as VoerkaI18nFormatterConfigs } + /** + * 返回当前语言的的指定名称格式化器 + * @param name 格式化名称 也可以是数据类型名称 + * + * @param options = { + * inGlobal: true, // 是否从全局作用域中查找 + * inTypes: true, // 是否从全局作用域中查找数据类型的格式化器 + * inScope: true, // 是否从当前作用域中查找 + * } + * + */ + get(name:string,options?:GetFormatterOptions):VoerkaI18nFormatter | undefined{ + const {on,inGlobal} = assignObject({ + on:"scope", + inGlobal:true + },options) + + // 直接从缓存中获取 + if(on=="types" && name in this.#formatterCache.typedFormatters) return this.#formatterCache.typedFormatters[name as SupportedDateTypes] + if(on=="scope" && name in this.#formatterCache.formatters) return this.#formatterCache.formatters[name] + + const fallbackLanguage = this.scope?.getLanguage(this.activeLanguage)?.fallback || 'en'; + + // 先在当前作用域中查找,再在全局查找 + const targets =[] + + if(on=="types"){ + targets.push(this.types) + targets.push(this.getTypes(fallbackLanguage)) + targets.push(this.getTypes("*")) + if(inGlobal){ + targets.push(this.scope?.global.formatters.types) + targets.push(this.scope?.global.formatters.getTypes(fallbackLanguage)) + targets.push(this.scope?.global.formatters.getTypes("*")) + } + }else if(on=='scope'){ + targets.push(this.formatters) + targets.push(this.getFormatters(fallbackLanguage)) + targets.push(this.getFormatters("*")) + if(inGlobal){ + targets.push(this.scope?.global.formatters.types) + targets.push(this.scope?.global.formatters.getTypes(fallbackLanguage)) + targets.push(this.scope?.global.formatters.getTypes("*")) + } + } + // 查找指定名称的格式化器 + for (const target of targets) { + if (!target) continue; + if(target && name in target){ + const formatter = target[name] + if (isFunction(formatter)) { + // 缓存起来,下次直接返回避免重复查找 + if(on=="types"){ + this.#formatterCache.typedFormatters[name as SupportedDateTypes] = formatter + }else{ + this.#formatterCache.formatters[name] = formatter + } + return formatter + } + } + } + } } \ No newline at end of file diff --git a/packages/runtime/src/formatterStore.ts b/packages/runtime/src/formatterStore.ts deleted file mode 100644 index 872c8ce..0000000 --- a/packages/runtime/src/formatterStore.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { isFunction } from "flex-tools/typecheck/isFunction"; -import { SupportedDateTypes, VoerkaI18nFormatter, VoerkaI18nLanguageFormatters } from "./types"; -import { DataTypes } from "./utils"; - - -export class FormatterStore{ - #formatters:VoerkaI18nLanguageFormatters = {} - constructor(formatters?:VoerkaI18nLanguageFormatters){ - if(formatters) this.#formatters = formatters - } - get formatters(){ return this.#formatters } - register(name:string | SupportedDateTypes, formatter:VoerkaI18nFormatter, {language = "*"}:{ language: string | string[] } ) { - if (!isFunction(formatter) || typeof name !== "string") { - throw new TypeError("Formatter must be a function"); - } - const languages = Array.isArray(language) ? language: language ? language.split(","): []; - languages.forEach((lngName:string) => { - if(!(lngName in this.#formatters)) this.#formatters[lngName] = {} - if(typeof(this.#formatters[lngName])!="function"){ - let lngFormatters = this.#formatters[lngName] as any - if (DataTypes.includes(name)) { - if(!lngFormatters.$types) lngFormatters.$types = {} - lngFormatters.$types![name] = formatter - } else { - lngFormatters[name] = formatter; - } - } - }); - } - getLanguageFormatters(language:string){ - if(language in this.#formatters){ - return this.#formatters[language] - }else{ - return this.#formatters["*"] - } - } -} \ No newline at end of file diff --git a/packages/runtime/src/interpolate.ts b/packages/runtime/src/interpolate.ts index f3b27d5..8ffc559 100644 --- a/packages/runtime/src/interpolate.ts +++ b/packages/runtime/src/interpolate.ts @@ -98,111 +98,8 @@ function forEachInterpolatedVars(str:string, replacer:InterpolatedVarReplacer, o return result; } -/** - * 清空指定语言的缓存 - * @param {*} scope - * @param {*} activeLanguage - */ -function resetScopeCache(scope:VoerkaI18nScope, activeLanguage:string | null) { - scope.cache = { activeLanguage, typedFormatters: {}, formatters: {} }; -} - -/** - * 取得指定数据类型的默认格式化器 - * - * 可以为每一个数据类型指定一个默认的格式化器,当传入插值变量时, - * 会自动调用该格式化器来对值进行格式化转换 - const formatters = { - "*":{ - $types:{...} // 在所有语言下只作用于特定数据类型的格式化器 - }, // 在所有语言下生效的格式化器 - zh:{ - $types:{ - [数据类型]:(value)=>{...} // 默认 - }, - [格式化器名称]:(value)=>{...}, - [格式化器名称]:(value)=>{...}, - [格式化器名称]:(value)=>{...}, - }, - en:{.....} - } - * @param {*} scope - * @param {*} activeLanguage - * @param {*} dataType 数字类型 - * @returns {Function} 格式化函数 - */ -function getDataTypeDefaultFormatter(scope:VoerkaI18nScope, activeLanguage:string , dataType:SupportedDateTypes) { - // 当指定数据类型的的默认格式化器的缓存处理 - if (!scope.cache) resetScopeCache(scope,activeLanguage); - if (scope.cache.activeLanguage === activeLanguage) { - if (scope.cache.typedFormatters && dataType in scope.cache.typedFormatters) - return scope.cache.typedFormatters[dataType]; - } else { - resetScopeCache(scope, activeLanguage); // 当语言切换时清空缓存 - } - const fallbackLanguage = scope.getLanguage(activeLanguage)?.fallback; - // 先在当前作用域中查找,再在全局查找 - const targets = [ - scope.formatters.types, - scope.formatters.getTypes(fallbackLanguage), - scope.formatters.getTypes("*"), - scope.global.formatters.types, - scope.global.formatters.getTypes(fallbackLanguage), - scope.global.formatters.getTypes("*"), - ]; - - for (const target of targets) { - if (!target) continue; - if(target){ - if (isPlainObject(target.$types) && isFunction(target.$types?.[dataType])) { - return (scope.cache.typedFormatters[dataType] = target.$types[dataType]); - } - } - } -} - -/** - * 获取指定名称的格式化器函数 - * - * 查找逻辑 - * - 在当前作用域中查找 - * - 当前语言 - * - 回退语言 - * - 全局语言 - * - 在全局作用域中查找 - * - * @param {*} scope - * @param {*} activeLanguage 当前激活语言名称 - * @param {*} name 格式化器名称 - * @returns {Function} 格式化函数 - */ -function getFormatter(scope:VoerkaI18nScope, activeLanguage:string, name:string) { - // 1. 从缓存中直接读取: 缓存格式化器引用,避免重复检索 - if (!scope.cache) resetScopeCache(scope,activeLanguage); - if (scope.cache.activeLanguage === activeLanguage) { - if (name in scope.cache.formatters) - return scope.cache.formatters[name]; - } else { // 当语言切换时清空缓存 - resetScopeCache(scope, activeLanguage); - } - const fallbackLanguage = scope.getLanguage(activeLanguage)?.fallback; - // 2. 先在当前作用域中查找,再在全局查找 formatters={$types,$config,[格式化器名称]:()=>{},[格式化器名称]:()=>{}} - const range = [ - scope.formatters.formatters, - scope.formatters.getFormatters(fallbackLanguage), - scope.formatters.getFormatters('*'), - - scope.global.formatters.formatters, - scope.global.formatters.getFormatters(fallbackLanguage), - scope.global.formatters.getFormatters('*'), - ]; - for (const formatters of range) { - if (!formatters) continue; - if (isFunction(formatters[name])) { - return (scope.cache.formatters[name] = formatters[name]); - } - } -} + + export type FormatterChecker = ((value:any,config?:VoerkaI18nFormatterConfigs)=>any) & { $name:string } @@ -334,7 +231,7 @@ function wrapperFormatters(scope:VoerkaI18nScope, activeLanguage:string, formatt let wrappedFormatters:VoerkaI18nFormatter[] = []; addDefaultFormatters(formatters); for (let [name, args] of formatters) { - let fn = getFormatter(scope, activeLanguage, name); + let fn = scope.formatters.get(name,{on:'scope'}) let formatter; if (isFunction(fn)) { formatter = (value:any, args?:any[],config?:VoerkaI18nFormatterConfigs) =>fn.call(scope.activeFormatterConfig, value, args, config); @@ -371,11 +268,7 @@ function getFormattedValue(scope:VoerkaI18nScope, activeLanguage:string, formatt // EMPTY和ERROR是默认两个格式化器,如果只有两个则说明在t(...)中没有指定格式化器 if (formatterFuncs.length == 2) { // 当没有格式化器时,查询是否指定了默认数据类型的格式化器,如果有则执行 - const defaultFormatter = getDataTypeDefaultFormatter( - scope, - activeLanguage, - getDataTypeName(value) - ); + const defaultFormatter = scope.formatters.get(getDataTypeName(value),{on:'types'}) if (defaultFormatter) { return executeFormatter(value, [defaultFormatter], scope, template); } diff --git a/packages/runtime/src/manager.ts b/packages/runtime/src/manager.ts index 8b43b25..b78928b 100644 --- a/packages/runtime/src/manager.ts +++ b/packages/runtime/src/manager.ts @@ -47,7 +47,7 @@ export class VoerkaI18nManager extends EventEmitter{ #options?:Required #scopes:VoerkaI18nScope[] = [] #defaultMessageLoader?:VoerkaI18nDefaultMessageLoader - #formatters:VoerkaI18nLanguageFormatters = {} + #formatters:VoerkaI18nFormatterRegistry = new VoerkaI18nFormatterRegistry() constructor(options?:VoerkaI18nManagerOptions){ super() if(VoerkaI18nManager.instance){ @@ -72,7 +72,7 @@ export class VoerkaI18nManager extends EventEmitter{ */ private loadInitialFormatters(){ if(this.#options?.formatters){ - this.#formatters = this.#options!.formatters + this.#formatters.loadInitials(this.#options.formatters) delete (this.#options as any).formatters } } @@ -149,19 +149,7 @@ export class VoerkaI18nManager extends EventEmitter{ if (!isFunction(formatter) || typeof name !== "string") { throw new TypeError("Formatter must be a function"); } - const languages = Array.isArray(language) ? language: language ? language.split(","): []; - languages.forEach((lngName:string) => { - if(!(lngName in this.#formatters)) this.#formatters[lngName] = {} - if(typeof(this.#formatters[lngName])!="function"){ - let lngFormatters = this.#formatters[lngName] as any - if (DataTypes.includes(name)) { - if(!lngFormatters.$types) lngFormatters.$types = {} - lngFormatters.$types![name] = formatter - } else { - lngFormatters[name] = formatter; - } - } - }); + this.#formatters.register(name,formatter,{language}) } /** * 注册默认文本信息加载器 @@ -171,6 +159,9 @@ export class VoerkaI18nManager extends EventEmitter{ this.#defaultMessageLoader = fn this.refresh() } + /** + * 刷新所有作用域 + */ async refresh(){ try{ let requests = this.#scopes.map(scope=>scope.refresh()) diff --git a/packages/runtime/src/scope.ts b/packages/runtime/src/scope.ts index 360fbd8..322fe71 100644 --- a/packages/runtime/src/scope.ts +++ b/packages/runtime/src/scope.ts @@ -1,7 +1,6 @@ import { isPlainObject } from "flex-tools/typecheck/isPlainObject" import { isFunction } from "flex-tools/typecheck/isFunction" import { translate } from "./translate" -import { deepMerge } from "flex-tools/object/deepMerge" import { assignObject } from "flex-tools/object/assignObject" import {VoerkaI18nManager } from "./manager" import type { @@ -13,7 +12,6 @@ import type { VoerkaI18nLanguageDefine, VoerkaI18nLanguageMessages, VoerkaI18nLanguagePack, - VoerkaI18nScopeCache, VoerkaI18nTranslate, VoerkaI18nMessageLoaders, VoerkaI18nTypesFormatters, @@ -44,8 +42,7 @@ export class VoerkaI18nScope { #t:VoerkaI18nTranslate #activeFormatters:VoerkaI18nFormatters = {} #activeFormatterConfig: VoerkaI18nFormatterConfigs={} - #cache:VoerkaI18nScopeCache - #formatterRegistry:VoerkaI18nFormatterRegistry = new VoerkaI18nFormatterRegistry() + #formatterRegistry:VoerkaI18nFormatterRegistry #defaultLanguage:string ='zh' #activeLanguage:string='zh' #currentMessages:VoerkaI18nLanguageMessages = {} // 当前语言包 @@ -58,13 +55,8 @@ export class VoerkaI18nScope { messages : {}, // 所有语言包={[language]:VoerkaI18nLanguageMessages} idMap : {}, // 消息id映射列表 formatters : {}, // 当前作用域的格式化函数列表{: {$types,$config,[格式化器名称]: () => {},[格式化器名称]: () => {}}} - },options) as Required - // 用来缓存格式化器的引用,当使用格式化器时可以直接引用,减少检索遍历 - this.#cache = { - activeLanguage : this.#options.activeLanguage, - typedFormatters: {}, - formatters : {}, - }; + },options) as Required + this.#formatterRegistry= new VoerkaI18nFormatterRegistry(this) // 初始化 this.init() // 将当前实例注册到全局单例VoerkaI18nManager中 @@ -89,8 +81,6 @@ export class VoerkaI18nScope { get formatters() { return this.#formatterRegistry;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}} get activeFormatters() {return this.#formatterRegistry.formatters} // 当前作用域激活的格式化器定义 {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () = >{}} get activeFormatterConfig(){return this.#activeFormatterConfig} // 当前格式化器合并后的配置参数,参数已经合并了全局格式化器中的参数 - get cache(){return this.#cache } - set cache(value:VoerkaI18nScopeCache){ this.#cache=value } get translate(){return this.#t} get t(){return this.#t} @@ -208,80 +198,11 @@ export class VoerkaI18nScope { private loadInitialFormatters(){ // 初始化格式化器 this.formatters.loadInitials(this.#options.formatters) - if(this.#options.formatters) - // 将配置中的指定为全局的格式化器注册到全局 - Object.entries(this.#options.formatters).forEach(([langName,formatters])=>{ - if(typeof(formatters)=='function'){ - this.#formatterRegistry.registerLanguageFormatters(langName,formatters) - }else if(isPlainObject(formatters.global)){ - if(formatters.global===true){ - this.#formatterRegistry.registerLanguageFormatters(langName,formatters) - }else if(isPlainObject(formatters.global)){ - this.global.formatters.registerLanguageFormatters(langName,formatters.global as VoerkaI18nFormatters) - } - delete formatters.global - } - }) + // 切换到当前语言的格式化器上下文 + this.formatters.change(this.activeLanguage) // 保存到Registry中,就可以从options中删除了 delete (this.#options as any).formatters - // 切换到当前语言的格式化器上下文 - this.changeFormatters(this.activeLanguage) } - - /** - * - * 切换到对应语言的格式化器 - * - * 当切换语言时,格式化器应该切换到对应语言的格式化器 - * - * 重要需要处理: - * $config参数采用合并继承机制,从全局读取 - * - * - * @param {*} language - */ - private async changeFormatters(newLanguage:string) { - try { - if (this.formatters.hasLanguage(newLanguage)) { - this.formatters.language = newLanguage - // 如果该语言的格式化器集合是异步加载,需要等待加载完成 - await this.formatters.load() - // 合并生成格式化器的配置参数,当执行格式化器时该参数将被传递给格式化器 - this._generateFormatterConfig(newLanguage) - } else { - if (this.debug) console.warn(`Not configured <${newLanguage}> formatters.`); - } - } catch (e:any) { - if (this.debug) console.error(`Error loading ${newLanguage} formatters: ${e.message}`); - } - } - /** - * 生成格式化器的配置参数,该参数由以下合并而成: - */ - private _generateFormatterConfig(language:string){ - try{ - const fallbackLanguage = this.getLanguage(language)?.fallback; - let configSources = [ - // 从全局读取 - this.#global.formatters.getConfig('*'), - this.#global.formatters.getConfig(fallbackLanguage), - this.#global.formatters.config, - // 从当前Scope读取 - this.formatters.getConfig('*'), - this.formatters.getConfig(fallbackLanguage), - this.formatters.config - ] - return this.#activeFormatterConfig = configSources.reduce((finalConfig, curConfig)=>{ - if(isPlainObject(curConfig)) deepMerge(finalConfig,curConfig,{newObject:false,array:'replace'}) - return finalConfig - },{}) - - }catch(e){ - if(this.debug) console.error(`Error while generate <${language}> formatter options: `,e) - return this.#activeFormatters.$config || {} - } - } - /** * 注册默认文本信息加载器 * @param {Function} 必须是异步函数或者是返回Promise @@ -364,7 +285,7 @@ export class VoerkaI18nScope { if (newLanguage === this.defaultLanguage) { this.#currentMessages = this.default; await this._patch(this.#currentMessages, newLanguage); // 异步补丁 - await this.changeFormatters(newLanguage); + await this.formatters.change(newLanguage); this.#refreshing = false return; }else{ // 非默认语言可以是静态语言包也可以是异步加载语言包 @@ -379,7 +300,7 @@ export class VoerkaI18nScope { await this._patch(this.#currentMessages, newLanguage); } // 切换到对应语言的格式化器 - await this.changeFormatters(newLanguage); + await this.formatters.change(newLanguage); }else{ this._fallback(); } diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts index 4e3c6fa..0fc77ce 100644 --- a/packages/runtime/src/types.ts +++ b/packages/runtime/src/types.ts @@ -62,12 +62,6 @@ export interface VoerkaI18nMessageLoaders { export type VoerkaI18nDefaultMessageLoader = (this:VoerkaI18nScope,newLanguage:string,scope:VoerkaI18nScope)=>Promise -export interface VoerkaI18nScopeCache{ - activeLanguage :string | null, - typedFormatters: VoerkaI18nLanguageFormatters, - formatters : VoerkaI18nLanguageFormatters, -} - export type TranslateMessageVars = number | boolean | string | Function | Date export interface VoerkaI18nTranslate { (message: string, ...args: TranslateMessageVars[]): string