update
This commit is contained in:
parent
5ab0146e99
commit
a9f63c2c35
@ -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<string,VoerkaI18nFormatter>,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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["*"]
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ export class VoerkaI18nManager extends EventEmitter{
|
||||
#options?:Required<VoerkaI18nManagerOptions>
|
||||
#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())
|
||||
|
@ -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 : {}, // 当前作用域的格式化函数列表{<lang>: {$types,$config,[格式化器名称]: () => {},[格式化器名称]: () => {}}}
|
||||
},options) as Required<VoerkaI18nScopeOptions>
|
||||
// 用来缓存格式化器的引用,当使用格式化器时可以直接引用,减少检索遍历
|
||||
this.#cache = {
|
||||
activeLanguage : this.#options.activeLanguage,
|
||||
typedFormatters: {},
|
||||
formatters : {},
|
||||
};
|
||||
},options) as Required<VoerkaI18nScopeOptions>
|
||||
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();
|
||||
}
|
||||
|
@ -62,12 +62,6 @@ export interface VoerkaI18nMessageLoaders {
|
||||
|
||||
export type VoerkaI18nDefaultMessageLoader = (this:VoerkaI18nScope,newLanguage:string,scope:VoerkaI18nScope)=>Promise<VoerkaI18nLanguageMessages>
|
||||
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user