update typescript runtime

This commit is contained in:
wxzhang 2023-04-02 22:01:20 +08:00
parent f59c165e71
commit 64f9eaa93a
7 changed files with 316 additions and 138 deletions

View File

@ -530,6 +530,7 @@ export default {
- 指定`global=true`将该文件声明的所有格式化器均注册到全局中 - 指定`global=true`将该文件声明的所有格式化器均注册到全局中
```javascript | pure ```javascript | pure
export default { export default {
global:true,
$config:{...}, $config:{...},
$types:{... }, $types:{... },
[格式化名称]:(value,...args,$config)=>{.....}, [格式化名称]:(value,...args,$config)=>{.....},
@ -544,6 +545,7 @@ export default {
$config:{...}, $config:{...},
$types:{... }, $types:{... },
[格式化名称]:(value,...args,$config)=>{.....}, [格式化名称]:(value,...args,$config)=>{.....},
// 以下全部注册到全局`VoerkaI18n`实例中
global:{ global:{
$config:{...}, $config:{...},
$types:{... }, $types:{... },

View File

@ -0,0 +1,154 @@
/**
*
* *
*
*
*/
import { VoerkaI18nFormatter, VoerkaI18nFormatters, VoerkaI18nFormattersLoader, VoerkaI18nLanguageFormatters, SupportedDateTypes, VoerkaI18nFormatterConfigs, Formatter } from './types';
import { DataTypes } from './utils';
import { get as getByPath } from "flex-tools/object/get"
export interface VoerkaI18nScopeCache{
activeLanguage :string | null,
typedFormatters: VoerkaI18nLanguageFormatters,
formatters : VoerkaI18nLanguageFormatters,
}
export class FormattersNotLoadedError extends Error{
constructor(language:string){
super(`Formatters of language<${language}> is not loaded,try to call load()`)
}
}
const EmptyFormatters:any = {
$config:{},
$types:{}
}
export class VoerkaI18nFormatterRegistry{
// 由于语言的格式化器集合允许是一个异步加载块所以需要一个ready标志
// 当语言格式化器集合加载完成后ready标志才会变为true
#ready:boolean = false
#formatters:VoerkaI18nLanguageFormatters = {}
#language:string = 'zh'
constructor(formatters?:VoerkaI18nLanguageFormatters){
this.#formatters = 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'
}
/**
*
value=>{...}
register(name,value=>{...}) // 注册到所有语言
register(name,value=>{...},{langauge:"zh"}) // 注册到zh语言
register(name,value=>{...},{langauge:"en"}) // 注册到en语言
register("Date",value=>{...},{langauge:"en"}) // 注册到en语言的默认数据类型格式化器
register(name,value=>{...},{langauge:["zh","cht"]}) // 注册到zh和cht语言
register(name,value=>{...},{langauge:"zh,cht"})
@param {*} formatter
@param language : 字符串或数组
*
使,
*/
register(name:string, 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 VoerkaI18nFormatters
if (DataTypes.includes(name)) {
if(!lngFormatters.$types){
lngFormatters.$types = {}
}
lngFormatters.$types![name] = formatter
} else {
lngFormatters[name] = formatter;
}
}
});
}
registerLanguageFormatters(language:string,formatters:VoerkaI18nFormatters | VoerkaI18nFormattersLoader){
this.#formatters[language] = formatters
}
/**
*
* @param formatters
*/
loadInitials(formatters:VoerkaI18nLanguageFormatters){
this.#formatters=formatters
}
/**
*
* @param language
*/
getConfig(language?:string){
return language ? getByPath(this.#formatters,`${language}.$config`,{defaultValue:{}}) : {}
}
/**
*
* @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 VoerkaI18nFormatters
if(dataType && (dataType in lngFormatters.$types!)){
return lngFormatters.$types![dataType]
}else if(name in lngFormatters){
return lngFormatters[name]
}
}
/**
*
*/
get config(){
if(!this.#ready) throw new FormattersNotLoadedError(this.#language)
return (this.#formatters[this.#language] as VoerkaI18nFormatters).$config as VoerkaI18nFormatterConfigs
}
}

View File

@ -3,10 +3,9 @@
* *
*/ */
const { import { chineseNumberFormatter,rmbFormater } from "../datatypes/chinese"
CN_DATETIME_UNITS,CN_WEEK_DAYS,CN_SHORT_WEEK_DAYS, CN_MONTH_NAMES, CN_SHORT_MONTH_NAMES,
chineseNumberFormatter,rmbFormater
} = require("../datatypes/chinese")
module.exports = { module.exports = {
// 配置参数: 格式化器函数的最后一个参数就是该配置参数 // 配置参数: 格式化器函数的最后一个参数就是该配置参数
@ -71,7 +70,7 @@ module.exports = {
} }
}, },
$types: { $types: {
Boolean : value =>value ? "是":"否" Boolean : (value:any) =>value ? "是":"否"
}, },
// 中文货币big=true代表大写形式 // 中文货币big=true代表大写形式
rmb : rmbFormater, rmb : rmbFormater,

View File

@ -306,7 +306,7 @@ function executeFormatter(value, formatters, scope, template) {
for (let formatter of formatters) { for (let formatter of formatters) {
try { try {
result = formatter(result, scope.activeFormatterConfig); result = formatter(result, scope.activeFormatterConfig);
} catch (e) { } catch (e:any) {
e.formatter = formatter.$name; e.formatter = formatter.$name;
if (scope.debug) if (scope.debug)
console.error(`Error while execute i18n formatter<${formatter.$name}> for ${template}: ${e.message} `); console.error(`Error while execute i18n formatter<${formatter.$name}> for ${template}: ${e.message} `);
@ -463,9 +463,3 @@ export function replaceInterpolatedVars(this:VoerkaI18nScope,template:string, ..
); );
} }
} }
module.exports = {
forEachInterpolatedVars, // 遍历插值变量并替换
getInterpolatedVars, // 获取指定字符串中的插件值变量列表
replaceInterpolatedVars // 替换插值变量
};

View File

@ -4,8 +4,9 @@ import {DataTypes} from "./utils"
import { EventEmitter } from "./eventemitter" import { EventEmitter } from "./eventemitter"
import inlineFormatters from "./formatters" import inlineFormatters from "./formatters"
import { VoerkaI18nScope } from "./scope" import { VoerkaI18nScope } from "./scope"
import type { VoerkaI18nLanguage, VoerkaI18nFormatters, VoerkaI18nDefaultMessageLoader, VoerkaI18nFormatter, VoerkaI18nTypesFormatters } from "./types" import type { VoerkaI18nLanguageDefine, VoerkaI18nLanguageFormatters, VoerkaI18nDefaultMessageLoader, VoerkaI18nFormatter, VoerkaI18nTypesFormatters } from "./types"
import { SupportedDateTypes } from './types'; import { SupportedDateTypes } from './types';
import { VoerkaI18nFormatterRegistry } from "./formatterRegistry"
// 默认语言配置 // 默认语言配置
const defaultLanguageSettings = { const defaultLanguageSettings = {
@ -23,8 +24,8 @@ export interface VoerkaI18nManagerOptions {
debug?: boolean debug?: boolean
defaultLanguage: string defaultLanguage: string
activeLanguage: string activeLanguage: string
formatters: VoerkaI18nFormatters formatters: VoerkaI18nLanguageFormatters
languages: VoerkaI18nLanguage[] languages: VoerkaI18nLanguageDefine[]
} }
/** /**
* *
@ -47,6 +48,7 @@ export class VoerkaI18nManager extends EventEmitter{
#options?:Required<VoerkaI18nManagerOptions> #options?:Required<VoerkaI18nManagerOptions>
#scopes:VoerkaI18nScope[] = [] #scopes:VoerkaI18nScope[] = []
#defaultMessageLoader?:VoerkaI18nDefaultMessageLoader #defaultMessageLoader?:VoerkaI18nDefaultMessageLoader
#formatterRegistry!:VoerkaI18nFormatterRegistry
constructor(options?:VoerkaI18nManagerOptions){ constructor(options?:VoerkaI18nManagerOptions){
super() super()
if(VoerkaI18nManager.instance){ if(VoerkaI18nManager.instance){
@ -54,6 +56,8 @@ export class VoerkaI18nManager extends EventEmitter{
} }
VoerkaI18nManager.instance = this; VoerkaI18nManager.instance = this;
this.#options = deepMerge(defaultLanguageSettings,options) as Required<VoerkaI18nManagerOptions> this.#options = deepMerge(defaultLanguageSettings,options) as Required<VoerkaI18nManagerOptions>
this.#formatterRegistry = new VoerkaI18nFormatterRegistry()
this.loadInitialFormatters() // 加载初始格式化器
this.#scopes=[] // 保存VoerkaI18nScope实例 this.#scopes=[] // 保存VoerkaI18nScope实例
} }
get debug(){return this.#options!.debug} get debug(){return this.#options!.debug}
@ -62,8 +66,18 @@ export class VoerkaI18nManager extends EventEmitter{
get activeLanguage(){ return this.#options!.activeLanguage} // 当前激活语言 名称 get activeLanguage(){ return this.#options!.activeLanguage} // 当前激活语言 名称
get defaultLanguage(){ return this.#options!.defaultLanguage} // 默认语言名称 get defaultLanguage(){ return this.#options!.defaultLanguage} // 默认语言名称
get languages(){ return this.#options!.languages} // 支持的语言列表 get languages(){ return this.#options!.languages} // 支持的语言列表
get formatters(){ return this.#options!.formatters } // 内置格式化器{*:{$config,$types,...},zh:{$config,$types,...},en:{$config,$types,...}}
get defaultMessageLoader(){ return this.#defaultMessageLoader} // 默认语言包加载器 get defaultMessageLoader(){ return this.#defaultMessageLoader} // 默认语言包加载器
get formatters(){return this.#formatterRegistry!}
/**
*
*/
private loadInitialFormatters(){
if(this.#options?.formatters){
this.#formatterRegistry.loadInitials(this.#options!.formatters)
delete (this.#options as any).formatters
}
}
// 通过默认加载器加载文件 // 通过默认加载器加载文件
async loadMessagesFromDefaultLoader(newLanguage:string,scope:VoerkaI18nScope){ async loadMessagesFromDefaultLoader(newLanguage:string,scope:VoerkaI18nScope){
@ -134,17 +148,7 @@ export class VoerkaI18nManager extends EventEmitter{
language : 声明该格式化器适用语言 language : 声明该格式化器适用语言
*/ */
registerFormatter(name:string,formatter:VoerkaI18nFormatter,{language="*"}:{language:string | string[] | '*'}){ registerFormatter(name:string,formatter:VoerkaI18nFormatter,{language="*"}:{language:string | string[] | '*'}){
if(!isFunction(formatter) || typeof(name)!=="string"){ this.#formatterRegistry.register(name,formatter,{language})
throw new TypeError("Formatter must be a function")
}
const languages = Array.isArray(language) ? language : (language ? language.split(",") : [])
languages.forEach((lng:string)=>{
if(DataTypes.includes(name)){
(this.formatters[lng].$types as VoerkaI18nTypesFormatters)[name] = formatter
}else{
this.formatters[lng][name] = formatter
}
})
} }
/** /**
* *

View File

@ -11,22 +11,25 @@ import type {
VoerkaI18nFormatterConfigs, VoerkaI18nFormatterConfigs,
VoerkaI18nDefaultMessageLoader, VoerkaI18nDefaultMessageLoader,
VoerkaI18nFormatter, VoerkaI18nFormatter,
VoerkaI18nFormatters, VoerkaI18nLanguageFormatters,
Voerkai18nIdMap, Voerkai18nIdMap,
VoerkaI18nLanguage, VoerkaI18nLanguageDefine,
VoerkaI18nLanguageMessages, VoerkaI18nLanguageMessages,
VoerkaI18nLanguagePack, VoerkaI18nLanguagePack,
VoerkaI18nScopeCache, VoerkaI18nScopeCache,
VoerkaI18nTranslate, VoerkaI18nTranslate,
VoerkaI18nLoaders, VoerkaI18nLoaders,
VoerkaI18nTypesFormatters, VoerkaI18nTypesFormatters,
VoerkaI18nLanguageFormatters VoerkaI18nFormatters,
VoerkaI18nDynamicLanguageMessages,
VoerkaI18nTypesFormatterConfigs
} from "./types" } from "./types"
import { VoerkaI18nFormatterRegistry } from './formatterRegistry';
export interface VoerkaI18nScopeOptions { export interface VoerkaI18nScopeOptions {
id?: string id?: string
debug?: boolean debug?: boolean
languages: VoerkaI18nLanguage[] // 当前作用域支持的语言列表 languages: VoerkaI18nLanguageDefine[] // 当前作用域支持的语言列表
defaultLanguage: string // 默认语言名称 defaultLanguage: string // 默认语言名称
activeLanguage: string // 当前语言名称 activeLanguage: string // 当前语言名称
default: VoerkaI18nLanguageMessages // 默认语言包 default: VoerkaI18nLanguageMessages // 默认语言包
@ -42,10 +45,11 @@ export class VoerkaI18nScope {
#refreshing:boolean = false #refreshing:boolean = false
#patchMessages:VoerkaI18nLanguagePack = {} #patchMessages:VoerkaI18nLanguagePack = {}
#t:VoerkaI18nTranslate #t:VoerkaI18nTranslate
#activeFormatters:VoerkaI18nLanguageFormatters = {} #activeFormatters:VoerkaI18nFormatters = {}
#activeFormatterConfig: VoerkaI18nFormatterConfigs={} #activeFormatterConfig: VoerkaI18nFormatterConfigs={}
#cache:VoerkaI18nScopeCache #cache:VoerkaI18nScopeCache
#messages:VoerkaI18nLanguageMessages = {} #messages:VoerkaI18nLanguageMessages = {}
#formatterRegistry:VoerkaI18nFormatterRegistry
constructor(options:VoerkaI18nScopeOptions, callback:(e?:Error)=>void) { constructor(options:VoerkaI18nScopeOptions, callback:(e?:Error)=>void) {
this.#options = assignObject({ this.#options = assignObject({
id : Date.now().toString() + parseInt(String(Math.random() * 1000)), id : Date.now().toString() + parseInt(String(Math.random() * 1000)),
@ -81,7 +85,8 @@ export class VoerkaI18nScope {
} }
this.#t = translate.bind(this) this.#t = translate.bind(this)
this.#global = globalThis.VoerkaI18n as unknown as VoerkaI18nManager; this.#global = globalThis.VoerkaI18n as unknown as VoerkaI18nManager;
this._initFormatters(this.activeLanguage) // 初始化活动的格式化器 this.#formatterRegistry = new VoerkaI18nFormatterRegistry()
this.loadFormatters(this.activeLanguage) // 初始化活动的格式化器
this._mergePatchedMessages(); // 从本地缓存中读取并合并补丁语言包 this._mergePatchedMessages(); // 从本地缓存中读取并合并补丁语言包
this._patch(this.messages, this.activeLanguage); // 延后执行补丁命令,该命令会向远程下载补丁包 this._patch(this.messages, this.activeLanguage); // 延后执行补丁命令,该命令会向远程下载补丁包
this.register(callback); // 在全局注册作用域 this.register(callback); // 在全局注册作用域
@ -96,8 +101,8 @@ export class VoerkaI18nScope {
get languages() {return this.#options.languages;} // 当前作用域支持的语言列表[{name,title,fallback}] get languages() {return this.#options.languages;} // 当前作用域支持的语言列表[{name,title,fallback}]
get loaders() { return this.#options.loaders;} // 异步加载语言文件的函数列表 get loaders() { return this.#options.loaders;} // 异步加载语言文件的函数列表
get global() { return this.#global;} // 引用全局VoerkaI18n配置注册后自动引用 get global() { return this.#global;} // 引用全局VoerkaI18n配置注册后自动引用
get formatters() { return this.#options.formatters;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}} get formatters() { return this.#formatterRegistry;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}}
get activeFormatters() {return this.#activeFormatters} // 当前作用域激活的格式化器定义 {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () = >{}} get activeFormatters() {return this.#formatterRegistry.formatters} // 当前作用域激活的格式化器定义 {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () = >{}}
get activeFormatterConfig(){return this.#activeFormatterConfig} // 当前格式化器合并后的配置参数,参数已经合并了全局格式化器中的参数 get activeFormatterConfig(){return this.#activeFormatterConfig} // 当前格式化器合并后的配置参数,参数已经合并了全局格式化器中的参数
get cache(){return this.#cache } get cache(){return this.#cache }
set cache(value:VoerkaI18nScopeCache){ this.#cache=value } set cache(value:VoerkaI18nScopeCache){ this.#cache=value }
@ -110,7 +115,7 @@ export class VoerkaI18nScope {
*/ */
_initiLanguages(){ _initiLanguages(){
if(!Array.isArray(this.languages)){ if(!Array.isArray(this.languages)){
console.warn("[VoerkaI18n] 无效的语言配置") console.warn("[VoerkaI18n] invalid languages config,use default languages config instead.")
this.#options.languages = [ this.#options.languages = [
{name: "zh",title: "中文"}, {name: "zh",title: "中文"},
{name: "en",title: "英文"} {name: "en",title: "英文"}
@ -148,59 +153,68 @@ export class VoerkaI18nScope {
asGlobal : 注册到全局 asGlobal : 注册到全局
*/ */
registerFormatter(name:string, formatter:VoerkaI18nFormatter, {language = "*", asGlobal= true}:{ language: string | string[] | "*", asGlobal :boolean } ) { registerFormatter(name:string, formatter:VoerkaI18nFormatter, {language = "*", asGlobal= true}:{ language: string | string[] | "*", asGlobal :boolean } ) {
if (!isFunction(formatter) || typeof name !== "string") { if(asGlobal){
throw new TypeError("Formatter must be a function"); this.global.registerFormatter(name, formatter, {language});
} }else{
const languages = Array.isArray(language) ? language: language ? language.split(","): []; this.#formatterRegistry.register(name, formatter, {language});
if (asGlobal) {
this.global.registerFormatter(name, formatter, { language });
} else {
languages.forEach((lng) => {
if(!(lng in this.formatters)) this.formatters[lng] = {}
if (DataTypes.includes(name)) {
(this.formatters[lng].$types as VoerkaI18nTypesFormatters)[name] = formatter
} else {
this.formatters[lng][name] = formatter;
}
});
} }
} }
/** // /**
* // * 注册多种语言的格式化器
* registerFormatters({"*":{...},zh:{...},en:{...}}) // * '*': 代表适用于所有语言
* registerFormatters({"*":{...},zh:{...},en:{...}},true) // * registerFormatters({"*":{...},zh:{...},en:VoerkaI18nFormatters})
* @param {*} formatters ={"*":{...},zh:{...},en:{...}} // * registerFormatters({"*":{...},zh:{...},en:{...}},true) 在全局注册
* @returns // * @param {*} langformatters ={"*":{...},zh:{...},en:{...}}
*/ // * @returns
registerFormatters(formatters:VoerkaI18nFormatters,asGlobal=false) { // */
Object.entries(formatters).forEach(([language,fns])=>{ // registerFormatters(langformatters:VoerkaI18nLanguageFormatters,asGlobal=false) {
Object.entries(fns).forEach(([name,formatter])=>{ // Object.entries(langformatters).forEach(([langName,formatters])=>{
this.registerFormatter(name,formatter,{language,asGlobal}) // if(isPlainObject(formatters)){
}) // this.#options.formatters[langName] = formatters
}) // }
} // })
// }
/** /**
* *
* *
* #activeFormatters={$config:{...},$types:{...},[]:()=>{...},[]:()=>{...},...}} * #activeFormatters={
* global:{...} // 或true代表注册到全局
* $config:{...},
* $types:{...},
* []:()=>{...},
* []:()=>{...},
* ...
* }
*/ */
private _initFormatters(newLanguage:string){ private loadFormatters(newLanguage:string){
// 全局格式化器,用来注册到全局 this.#formatterRegistry.language = newLanguage
Object.entries(this.formatters).forEach(([langName,formatters])=>{ // 初始化格式化器
if(formatters.global===true){ this.formatters.loadInitials(this.#options.formatters)
this.registerFormatters({[langName]:formatters},true) 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)){ }else if(isPlainObject(formatters.global)){
this.registerFormatters({[langName]:formatters.global as any},true) 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
} }
}) })
// 保存到Registry中就可以从options中删除了
delete (this.#options as any).formatters
// 初始化格式化器
try { try {
if (newLanguage in this.formatters) { if (this.formatters.hasLanguage(newLanguage)) {
this.#activeFormatters = this.formatters[newLanguage];
} else { } else {
if (this.debug) console.warn(`Not initialize <${newLanguage}> formatters.`); if (this.debug) console.warn(`Not initialize <${newLanguage}> formatters.`);
} }
this._generateFormatterConfig(newLanguage) this._generateFormatterConfig(newLanguage)
} catch (e) { } catch (e:any) {
if (this.debug) console.error(`Error while initialize ${newLanguage} formatters: ${e.message}`); if (this.debug) console.error(`Error while initialize ${newLanguage} formatters: ${e.message}`);
} }
} }
@ -219,13 +233,10 @@ export class VoerkaI18nScope {
*/ */
async _changeFormatters(newLanguage:string) { async _changeFormatters(newLanguage:string) {
try { try {
if (newLanguage in this.formatters) { if (this.formatters.hasLanguage(newLanguage)) {
let loader = this.formatters[newLanguage]; this.formatters.language = newLanguage
if (isPlainObject(loader)) { // 如果该语言的格式化器集合是异步加载,需要等待加载完成
this.#activeFormatters = loader as unknown as VoerkaI18nFormatters; await this.formatters.load()
} else if (isFunction(loader)) {
this.#activeFormatters = (await (loader as Function).call(this)).default;
}
// 合并生成格式化器的配置参数,当执行格式化器时该参数将被传递给格式化器 // 合并生成格式化器的配置参数,当执行格式化器时该参数将被传递给格式化器
this._generateFormatterConfig(newLanguage) this._generateFormatterConfig(newLanguage)
} else { } else {
@ -237,25 +248,31 @@ export class VoerkaI18nScope {
} }
/** /**
* *
*
* - global.formatters[*].$config * - global.formatters[*].$config
* - global.formatters[fallbackLanguage].$config
* - global.formatters[language].$config * - global.formatters[language].$config
* - scope.activeFormatters.$config * - this.formatters[*].$config
* - this.formatters[fallbackLanguage].$config
* - this.formatters[language].$config
*/ */
_generateFormatterConfig(language:string){ _generateFormatterConfig(language:string){
try{ try{
const fallbackLanguage = this.getLanguage(language).fallback; const fallbackLanguage = this.getLanguage(language)?.fallback;
let configSources = [ let configSources = [
getByPath(this.#global.formatters,`${fallbackLanguage}.$config`,{}), // 从全局读取
getByPath(this.#global.formatters,"*.$config",{}), this.#global.formatters.getConfig('*'),
getByPath(this.formatters,`${fallbackLanguage}.$config`,{}), this.#global.formatters.getConfig(fallbackLanguage),
getByPath(this._formatter,"*.$config",{}), this.#global.formatters.config,
getByPath(this.#global.formatters,`${language}.$config`,{}), // 从当前Scope读取
getByPath(this.#activeFormatters,"$config",{}) this.formatters.getConfig('*'),
this.formatters.getConfig(fallbackLanguage),
this.formatters.config
] ]
return this.#activeFormatterConfig = configSources.reduce((finalConfig, config)=>{ return this.#activeFormatterConfig = configSources.reduce((finalConfig, curConfig)=>{
if(isPlainObject(config)) deepMerge(finalConfig,config,{newObject:false}) if(isPlainObject(curConfig)) deepMerge(finalConfig,curConfig,{newObject:false,array:'replace'})
return finalConfig return finalConfig
},deepClone(getByPath(this.#global.formatters,`*.$config`,{}))) },{})
}catch(e){ }catch(e){
if(this.debug) console.error(`Error while generate <${language}> formatter options: `,e) if(this.debug) console.error(`Error while generate <${language}> formatter options: `,e)
@ -275,7 +292,7 @@ export class VoerkaI18nScope {
* @param {*} language * @param {*} language
* @returns * @returns
*/ */
getLanguage(language:string):VoerkaI18nLanguage | undefined{ private getLanguage(language:string):VoerkaI18nLanguageDefine | undefined{
let index = this.languages.findIndex((lng) => lng.name == language); let index = this.languages.findIndex((lng) => lng.name == language);
if (index !== -1) return this.languages[index]; if (index !== -1) return this.languages[index];
} }
@ -285,12 +302,12 @@ export class VoerkaI18nScope {
* @returns * @returns
*/ */
hasLanguage(language:string) { hasLanguage(language:string) {
return this.languages.indexOf((lang:VoerkaI18nLanguage) => lang.name == language) !== -1; return this.languages.indexOf((lang:VoerkaI18nLanguageDefine) => lang.name == language) !== -1;
} }
/** /**
* 退 * 退
*/ */
_fallback() { private _fallback() {
this.#options.messages = this.default; this.#options.messages = this.default;
this.#options.activeLanguage = this.defaultLanguage; this.#options.activeLanguage = this.defaultLanguage;
} }
@ -319,25 +336,25 @@ export class VoerkaI18nScope {
} else if (isFunction(loader)) { // 语言包异步chunk } else if (isFunction(loader)) { // 语言包异步chunk
newMessages = (await loader()).default; newMessages = (await loader()).default;
} else if (isFunction(this.global.defaultMessageLoader)) { // 从远程加载语言包:如果该语言没有指定加载器,则使用全局配置的默认加载器 } else if (isFunction(this.global.defaultMessageLoader)) { // 从远程加载语言包:如果该语言没有指定加载器,则使用全局配置的默认加载器
const loadedMessages = await this.global.loadMessagesFromDefaultLoader(newLanguage,this); const loadedMessages = (await this.global.loadMessagesFromDefaultLoader(newLanguage,this)) as unknown as VoerkaI18nDynamicLanguageMessages;
if(isPlainObject(loadedMessages)){ if(isPlainObject(loadedMessages)){
useRemote = true useRemote = true
// 需要保存动态语言包中的$config合并到对应语言的格式化器配置 // 需要保存动态语言包中的$config合并到对应语言的格式化器配置
if(isPlainObject(loadedMessages.$config)){ if(isPlainObject(loadedMessages.$config)){
this._formatters[newLanguage] = { this.formatters[newLanguage] = {
$config : loadedMessages.$config $config : loadedMessages.$config as any
} }
delete loadedMessages.$config delete loadedMessages.$config
} }
newMessages = Object.assign({},this._default,loadedMessages); newMessages = Object.assign({},this.default,loadedMessages);
} }
} }
if(newMessages){ if(newMessages){
this._messages = newMessages this.#options.messages = newMessages
this._activeLanguage = newLanguage; this.#options.activeLanguage = newLanguage;
// 打语言包补丁, 如果是从远程加载语言包则不需要再打补丁了 // 打语言包补丁, 如果是从远程加载语言包则不需要再打补丁了
if(!useRemote) { if(!useRemote) {
await this._patch(this._messages, newLanguage); await this._patch(this.#options.messages, newLanguage);
} }
// 切换到对应语言的格式化器 // 切换到对应语言的格式化器
await this._changeFormatters(newLanguage); await this._changeFormatters(newLanguage);
@ -361,10 +378,10 @@ export class VoerkaI18nScope {
* @param {*} newLanguage * @param {*} newLanguage
* @returns * @returns
*/ */
async _patch(messages, newLanguage) { private async _patch(messages:VoerkaI18nLanguageMessages, newLanguage:string) {
if (!isFunction(this.global.loadMessagesFromDefaultLoader)) return; if (!isFunction(this.global.loadMessagesFromDefaultLoader)) return;
try { try {
let pachedMessages = await this.global.loadMessagesFromDefaultLoader(newLanguage,this); let pachedMessages = (await this.global.loadMessagesFromDefaultLoader(newLanguage,this)) as unknown as VoerkaI18nLanguageMessages;
if (isPlainObject(pachedMessages)) { if (isPlainObject(pachedMessages)) {
Object.assign(messages, pachedMessages); Object.assign(messages, pachedMessages);
this._savePatchedMessages(pachedMessages, newLanguage); this._savePatchedMessages(pachedMessages, newLanguage);
@ -376,10 +393,10 @@ export class VoerkaI18nScope {
/** /**
* *
*/ */
_mergePatchedMessages() { private _mergePatchedMessages() {
let patchedMessages = this._getPatchedMessages(this.activeLanguage); let patchedMessages = this._getPatchedMessages(this.activeLanguage);
if (isPlainObject(patchedMessages)) { if (isPlainObject(patchedMessages)) {
Object.assign(this._messages, patchedMessages); Object.assign(this.#options.messages, patchedMessages);
} }
} }
/** /**
@ -400,13 +417,13 @@ export class VoerkaI18nScope {
* *
* @param {*} messages * @param {*} messages
*/ */
_savePatchedMessages(messages, language) { private _savePatchedMessages(messages:VoerkaI18nLanguageMessages, language:string) {
try { try {
if (globalThis.localStorage) { if (globalThis.localStorage) {
globalThis.localStorage.setItem(`voerkai18n_${this.id}_${language}_patched_messages`,JSON.stringify(messages)); globalThis.localStorage.setItem(`voerkai18n_${this.id}_${language}_patched_messages`,JSON.stringify(messages));
} }
} catch (e) { } catch (e:any) {
if (this.cache._debug) console.error("Error while save voerkai18n patched messages:",e); if (this.debug) console.error("Error while save voerkai18n patched messages:",e);
} }
} }
/** /**
@ -414,18 +431,18 @@ export class VoerkaI18nScope {
* @param {*} language * @param {*} language
* @returns * @returns
*/ */
_getPatchedMessages(language) { private _getPatchedMessages(language:string) {
try { try {
return JSON.parse(localStorage.getItem(`voerkai18n_${this.id}_${language}_patched_messages`)); return JSON.parse((localStorage as any).getItem(`voerkai18n_${this.id}_${language}_patched_messages`));
} catch (e) { } catch (e) {
return {}; return {};
} }
} }
// 以下方法引用全局VoerkaI18n实例的方法 // 以下方法引用全局VoerkaI18n实例的方法
on() {return this.#global.on(...arguments); } on(callback:Function) {return this.#global.on(callback); }
off() {return this.#global.off(...arguments); } off(callback:Function) {return this.#global.off(callback); }
offAll() {return this.#global.offAll(...arguments);} offAll() {return this.#global.offAll();}
async change(language) { async change(language:string) {
await this.#global.change(language); await this.#global.change(language);
} }
}; };

View File

@ -8,14 +8,19 @@ declare global {
export type SupportedDateTypes = "String" | "Number" | "Boolean" | "Object" | "Array" | "Function" | "Error" | "Symbol" | "RegExp" | "Date" | "Null" | "Undefined" | "Set" | "Map" | "WeakSet" | "WeakMap" export type SupportedDateTypes = "String" | "Number" | "Boolean" | "Object" | "Array" | "Function" | "Error" | "Symbol" | "RegExp" | "Date" | "Null" | "Undefined" | "Set" | "Map" | "WeakSet" | "WeakMap"
export type VoerkaI18nLanguageMessages = Record<string, string | string[]> export type VoerkaI18nLanguageMessages = Record<string, string | string[]> & {
$config?: VoerkaI18nTypesFormatterConfigs
}
export type VoerkaI18nDynamicLanguageMessages = Record<string, string | string[]> & {
$config?: VoerkaI18nTypesFormatterConfigs
}
export interface VoerkaI18nLanguagePack { export interface VoerkaI18nLanguagePack {
[language: string]: VoerkaI18nLanguageMessages [language: string]: VoerkaI18nLanguageMessages
} }
export type Voerkai18nIdMap = Record<string, number> export type Voerkai18nIdMap = Record<string, number>
export interface VoerkaI18nLanguage { export interface VoerkaI18nLanguageDefine {
name: string name: string
title?: string title?: string
default?: boolean default?: boolean
@ -24,22 +29,25 @@ export interface VoerkaI18nLanguage {
export type VoerkaI18nFormatterConfigs = Record<string, any> export type VoerkaI18nFormatterConfigs = Record<string, any>
export type VoerkaI18nFormatter = (value: string, ...args: any[]) => string export type VoerkaI18nFormatter = (value: string,args: any[],$config:VoerkI18nFormatterConfigs) => string
export type VoerkaI18nTypesFormatters=Record<SupportedDateTypes | string, VoerkaI18nFormatter> export type VoerkaI18nTypesFormatters=Record<SupportedDateTypes | string, VoerkaI18nFormatter>
export type VoerkaI18nTypesFormatterConfigs= Record<SupportedDateTypes | string, Record<string,any>> export type VoerkaI18nTypesFormatterConfigs= Record<SupportedDateTypes | string, Record<string,any>>
// export type VoerkaI18nFormattersLoader = (()=>Promise<VoerkaI18nFormatters>)
export type VoerkaI18nLanguageFormatters = { // 每一个语言的格式化器定义={$types:{...},$config:{...},[格式化器名称]: () => {},[格式化器名称]: () => {}
global?: true | Exclude<VoerkaI18nFormatters,'global'> // 是否全局格式化器 // 在formatters/xxxx.ts里面进行配置
$types?: VoerkaI18nTypesFormatters export type VoerkaI18nFormatters = ({
global?: boolean | Omit<VoerkaI18nFormatters,'global'> // 是否全局格式化器
$types?:VoerkaI18nTypesFormatters
$config?: VoerkaI18nTypesFormatterConfigs $config?: VoerkaI18nTypesFormatterConfigs
} & { } & {
[DataTypeName in SupportedDateTypes]?: VoerkaI18nFormatter [DataTypeName in SupportedDateTypes]?: VoerkaI18nFormatter
} & { } & {
[key: string]: VoerkaI18nFormatter [key: string]: VoerkaI18nFormatter
} })
// 包括语言的{"*":{...},zh:{...},en:{...}} // 包括语言的{"*":{...},zh:{...},en:{...}}
// 声明格式化器 // 声明格式化器
export type VoerkaI18nFormatters = Record<string,VoerkaI18nLanguageFormatters> export type VoerkaI18nLanguageFormatters = Record<string,VoerkaI18nFormatters | VoerkaI18nFormattersLoader>
@ -53,8 +61,8 @@ export type VoerkaI18nDefaultMessageLoader = (this:VoerkaI18nScope,newLanguage:s
export interface VoerkaI18nScopeCache{ export interface VoerkaI18nScopeCache{
activeLanguage :string | null, activeLanguage :string | null,
typedFormatters: VoerkaI18nFormatters, typedFormatters: VoerkaI18nLanguageFormatters,
formatters : VoerkaI18nFormatters, formatters : VoerkaI18nLanguageFormatters,
} }
export type TranslateMessageVars = number | boolean | string | Function | Date export type TranslateMessageVars = number | boolean | string | Function | Date
@ -63,7 +71,7 @@ export interface VoerkaI18nTranslate {
(message: string, vars?: Record<string, TranslateMessageVars>): string (message: string, vars?: Record<string, TranslateMessageVars>): string
} }
export interface VoerkaI18nSupportedLanguages { export interface VoerkaI18nSupportedLanguages {
[key: string]: VoerkaI18nLanguage [key: string]: VoerkaI18nLanguageDefine
} }