update typescript
runtime
This commit is contained in:
parent
f59c165e71
commit
64f9eaa93a
@ -530,6 +530,7 @@ export default {
|
||||
- 指定`global=true`将该文件声明的所有格式化器均注册到全局中
|
||||
```javascript | pure
|
||||
export default {
|
||||
global:true,
|
||||
$config:{...},
|
||||
$types:{... },
|
||||
[格式化名称]:(value,...args,$config)=>{.....},
|
||||
@ -544,6 +545,7 @@ export default {
|
||||
$config:{...},
|
||||
$types:{... },
|
||||
[格式化名称]:(value,...args,$config)=>{.....},
|
||||
// 以下全部注册到全局`VoerkaI18n`实例中
|
||||
global:{
|
||||
$config:{...},
|
||||
$types:{... },
|
||||
|
154
packages/runtime/src/formatterRegistry.ts
Normal file
154
packages/runtime/src/formatterRegistry.ts
Normal 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
|
||||
}
|
||||
}
|
@ -3,10 +3,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
const {
|
||||
CN_DATETIME_UNITS,CN_WEEK_DAYS,CN_SHORT_WEEK_DAYS, CN_MONTH_NAMES, CN_SHORT_MONTH_NAMES,
|
||||
chineseNumberFormatter,rmbFormater
|
||||
} = require("../datatypes/chinese")
|
||||
import { chineseNumberFormatter,rmbFormater } from "../datatypes/chinese"
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
// 配置参数: 格式化器函数的最后一个参数就是该配置参数
|
||||
@ -71,7 +70,7 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
$types: {
|
||||
Boolean : value =>value ? "是":"否"
|
||||
Boolean : (value:any) =>value ? "是":"否"
|
||||
},
|
||||
// 中文货币,big=true代表大写形式
|
||||
rmb : rmbFormater,
|
||||
|
@ -306,7 +306,7 @@ function executeFormatter(value, formatters, scope, template) {
|
||||
for (let formatter of formatters) {
|
||||
try {
|
||||
result = formatter(result, scope.activeFormatterConfig);
|
||||
} catch (e) {
|
||||
} catch (e:any) {
|
||||
e.formatter = formatter.$name;
|
||||
if (scope.debug)
|
||||
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 // 替换插值变量
|
||||
};
|
||||
|
@ -4,8 +4,9 @@ import {DataTypes} from "./utils"
|
||||
import { EventEmitter } from "./eventemitter"
|
||||
import inlineFormatters from "./formatters"
|
||||
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 { VoerkaI18nFormatterRegistry } from "./formatterRegistry"
|
||||
|
||||
// 默认语言配置
|
||||
const defaultLanguageSettings = {
|
||||
@ -23,8 +24,8 @@ export interface VoerkaI18nManagerOptions {
|
||||
debug?: boolean
|
||||
defaultLanguage: string
|
||||
activeLanguage: string
|
||||
formatters: VoerkaI18nFormatters
|
||||
languages: VoerkaI18nLanguage[]
|
||||
formatters: VoerkaI18nLanguageFormatters
|
||||
languages: VoerkaI18nLanguageDefine[]
|
||||
}
|
||||
/**
|
||||
* 多语言管理类
|
||||
@ -47,6 +48,7 @@ export class VoerkaI18nManager extends EventEmitter{
|
||||
#options?:Required<VoerkaI18nManagerOptions>
|
||||
#scopes:VoerkaI18nScope[] = []
|
||||
#defaultMessageLoader?:VoerkaI18nDefaultMessageLoader
|
||||
#formatterRegistry!:VoerkaI18nFormatterRegistry
|
||||
constructor(options?:VoerkaI18nManagerOptions){
|
||||
super()
|
||||
if(VoerkaI18nManager.instance){
|
||||
@ -54,6 +56,8 @@ export class VoerkaI18nManager extends EventEmitter{
|
||||
}
|
||||
VoerkaI18nManager.instance = this;
|
||||
this.#options = deepMerge(defaultLanguageSettings,options) as Required<VoerkaI18nManagerOptions>
|
||||
this.#formatterRegistry = new VoerkaI18nFormatterRegistry()
|
||||
this.loadInitialFormatters() // 加载初始格式化器
|
||||
this.#scopes=[] // 保存VoerkaI18nScope实例
|
||||
}
|
||||
get debug(){return this.#options!.debug}
|
||||
@ -62,8 +66,18 @@ export class VoerkaI18nManager extends EventEmitter{
|
||||
get activeLanguage(){ return this.#options!.activeLanguage} // 当前激活语言 名称
|
||||
get defaultLanguage(){ return this.#options!.defaultLanguage} // 默认语言名称
|
||||
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 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){
|
||||
@ -134,17 +148,7 @@ export class VoerkaI18nManager extends EventEmitter{
|
||||
language : 声明该格式化器适用语言
|
||||
*/
|
||||
registerFormatter(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((lng:string)=>{
|
||||
if(DataTypes.includes(name)){
|
||||
(this.formatters[lng].$types as VoerkaI18nTypesFormatters)[name] = formatter
|
||||
}else{
|
||||
this.formatters[lng][name] = formatter
|
||||
}
|
||||
})
|
||||
this.#formatterRegistry.register(name,formatter,{language})
|
||||
}
|
||||
/**
|
||||
* 注册默认文本信息加载器
|
||||
|
@ -11,22 +11,25 @@ import type {
|
||||
VoerkaI18nFormatterConfigs,
|
||||
VoerkaI18nDefaultMessageLoader,
|
||||
VoerkaI18nFormatter,
|
||||
VoerkaI18nFormatters,
|
||||
VoerkaI18nLanguageFormatters,
|
||||
Voerkai18nIdMap,
|
||||
VoerkaI18nLanguage,
|
||||
VoerkaI18nLanguageDefine,
|
||||
VoerkaI18nLanguageMessages,
|
||||
VoerkaI18nLanguagePack,
|
||||
VoerkaI18nScopeCache,
|
||||
VoerkaI18nTranslate,
|
||||
VoerkaI18nLoaders,
|
||||
VoerkaI18nTypesFormatters,
|
||||
VoerkaI18nLanguageFormatters
|
||||
VoerkaI18nFormatters,
|
||||
VoerkaI18nDynamicLanguageMessages,
|
||||
VoerkaI18nTypesFormatterConfigs
|
||||
} from "./types"
|
||||
import { VoerkaI18nFormatterRegistry } from './formatterRegistry';
|
||||
|
||||
export interface VoerkaI18nScopeOptions {
|
||||
id?: string
|
||||
debug?: boolean
|
||||
languages: VoerkaI18nLanguage[] // 当前作用域支持的语言列表
|
||||
languages: VoerkaI18nLanguageDefine[] // 当前作用域支持的语言列表
|
||||
defaultLanguage: string // 默认语言名称
|
||||
activeLanguage: string // 当前语言名称
|
||||
default: VoerkaI18nLanguageMessages // 默认语言包
|
||||
@ -42,10 +45,11 @@ export class VoerkaI18nScope {
|
||||
#refreshing:boolean = false
|
||||
#patchMessages:VoerkaI18nLanguagePack = {}
|
||||
#t:VoerkaI18nTranslate
|
||||
#activeFormatters:VoerkaI18nLanguageFormatters = {}
|
||||
#activeFormatters:VoerkaI18nFormatters = {}
|
||||
#activeFormatterConfig: VoerkaI18nFormatterConfigs={}
|
||||
#cache:VoerkaI18nScopeCache
|
||||
#messages:VoerkaI18nLanguageMessages = {}
|
||||
#formatterRegistry:VoerkaI18nFormatterRegistry
|
||||
constructor(options:VoerkaI18nScopeOptions, callback:(e?:Error)=>void) {
|
||||
this.#options = assignObject({
|
||||
id : Date.now().toString() + parseInt(String(Math.random() * 1000)),
|
||||
@ -81,7 +85,8 @@ export class VoerkaI18nScope {
|
||||
}
|
||||
this.#t = translate.bind(this)
|
||||
this.#global = globalThis.VoerkaI18n as unknown as VoerkaI18nManager;
|
||||
this._initFormatters(this.activeLanguage) // 初始化活动的格式化器
|
||||
this.#formatterRegistry = new VoerkaI18nFormatterRegistry()
|
||||
this.loadFormatters(this.activeLanguage) // 初始化活动的格式化器
|
||||
this._mergePatchedMessages(); // 从本地缓存中读取并合并补丁语言包
|
||||
this._patch(this.messages, this.activeLanguage); // 延后执行补丁命令,该命令会向远程下载补丁包
|
||||
this.register(callback); // 在全局注册作用域
|
||||
@ -96,8 +101,8 @@ export class VoerkaI18nScope {
|
||||
get languages() {return this.#options.languages;} // 当前作用域支持的语言列表[{name,title,fallback}]
|
||||
get loaders() { return this.#options.loaders;} // 异步加载语言文件的函数列表
|
||||
get global() { return this.#global;} // 引用全局VoerkaI18n配置,注册后自动引用
|
||||
get formatters() { return this.#options.formatters;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}}
|
||||
get activeFormatters() {return this.#activeFormatters} // 当前作用域激活的格式化器定义 {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () = >{}}
|
||||
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 }
|
||||
@ -110,7 +115,7 @@ export class VoerkaI18nScope {
|
||||
*/
|
||||
_initiLanguages(){
|
||||
if(!Array.isArray(this.languages)){
|
||||
console.warn("[VoerkaI18n] 无效的语言配置")
|
||||
console.warn("[VoerkaI18n] invalid languages config,use default languages config instead.")
|
||||
this.#options.languages = [
|
||||
{name: "zh",title: "中文"},
|
||||
{name: "en",title: "英文"}
|
||||
@ -148,59 +153,68 @@ export class VoerkaI18nScope {
|
||||
asGlobal : 注册到全局
|
||||
*/
|
||||
registerFormatter(name:string, formatter:VoerkaI18nFormatter, {language = "*", asGlobal= true}:{ language: string | string[] | "*", asGlobal :boolean } ) {
|
||||
if (!isFunction(formatter) || typeof name !== "string") {
|
||||
throw new TypeError("Formatter must be a function");
|
||||
}
|
||||
const languages = Array.isArray(language) ? language: language ? language.split(","): [];
|
||||
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;
|
||||
}
|
||||
});
|
||||
this.#formatterRegistry.register(name, formatter, {language});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 注册多种格式化器
|
||||
* registerFormatters({"*":{...},zh:{...},en:{...}})
|
||||
* registerFormatters({"*":{...},zh:{...},en:{...}},true) 在全局注册
|
||||
* @param {*} formatters ={"*":{...},zh:{...},en:{...}}
|
||||
* @returns
|
||||
*/
|
||||
registerFormatters(formatters:VoerkaI18nFormatters,asGlobal=false) {
|
||||
Object.entries(formatters).forEach(([language,fns])=>{
|
||||
Object.entries(fns).forEach(([name,formatter])=>{
|
||||
this.registerFormatter(name,formatter,{language,asGlobal})
|
||||
})
|
||||
})
|
||||
}
|
||||
// /**
|
||||
// * 注册多种语言的格式化器
|
||||
// * '*': 代表适用于所有语言
|
||||
// * registerFormatters({"*":{...},zh:{...},en:VoerkaI18nFormatters})
|
||||
// * registerFormatters({"*":{...},zh:{...},en:{...}},true) 在全局注册
|
||||
// * @param {*} langformatters ={"*":{...},zh:{...},en:{...}}
|
||||
// * @returns
|
||||
// */
|
||||
// registerFormatters(langformatters:VoerkaI18nLanguageFormatters,asGlobal=false) {
|
||||
// Object.entries(langformatters).forEach(([langName,formatters])=>{
|
||||
// if(isPlainObject(formatters)){
|
||||
// this.#options.formatters[langName] = formatters
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
/**
|
||||
* 初始化格式化器
|
||||
* 激活和默认语言的格式化器采用静态导入的形式,而没有采用异步块的形式,这是为了确保首次加载时的能马上读取,而不能采用延迟加载方式
|
||||
* #activeFormatters={$config:{...},$types:{...},[格式化器名称]:()=>{...},[格式化器名称]:()=>{...},...}}
|
||||
* #activeFormatters={
|
||||
* global:{...} // 或true代表注册到全局
|
||||
* $config:{...},
|
||||
* $types:{...},
|
||||
* [格式化器名称]:()=>{...},
|
||||
* [格式化器名称]:()=>{...},
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
private _initFormatters(newLanguage:string){
|
||||
// 全局格式化器,用来注册到全局
|
||||
Object.entries(this.formatters).forEach(([langName,formatters])=>{
|
||||
if(formatters.global===true){
|
||||
this.registerFormatters({[langName]:formatters},true)
|
||||
private loadFormatters(newLanguage:string){
|
||||
this.#formatterRegistry.language = newLanguage
|
||||
// 初始化格式化器
|
||||
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)){
|
||||
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 {
|
||||
if (newLanguage in this.formatters) {
|
||||
this.#activeFormatters = this.formatters[newLanguage];
|
||||
if (this.formatters.hasLanguage(newLanguage)) {
|
||||
|
||||
} else {
|
||||
if (this.debug) console.warn(`Not initialize <${newLanguage}> formatters.`);
|
||||
}
|
||||
this._generateFormatterConfig(newLanguage)
|
||||
} catch (e) {
|
||||
} catch (e:any) {
|
||||
if (this.debug) console.error(`Error while initialize ${newLanguage} formatters: ${e.message}`);
|
||||
}
|
||||
}
|
||||
@ -219,13 +233,10 @@ export class VoerkaI18nScope {
|
||||
*/
|
||||
async _changeFormatters(newLanguage:string) {
|
||||
try {
|
||||
if (newLanguage in this.formatters) {
|
||||
let loader = this.formatters[newLanguage];
|
||||
if (isPlainObject(loader)) {
|
||||
this.#activeFormatters = loader as unknown as VoerkaI18nFormatters;
|
||||
} else if (isFunction(loader)) {
|
||||
this.#activeFormatters = (await (loader as Function).call(this)).default;
|
||||
}
|
||||
if (this.formatters.hasLanguage(newLanguage)) {
|
||||
this.formatters.language = newLanguage
|
||||
// 如果该语言的格式化器集合是异步加载,需要等待加载完成
|
||||
await this.formatters.load()
|
||||
// 合并生成格式化器的配置参数,当执行格式化器时该参数将被传递给格式化器
|
||||
this._generateFormatterConfig(newLanguage)
|
||||
} else {
|
||||
@ -237,25 +248,31 @@ export class VoerkaI18nScope {
|
||||
}
|
||||
/**
|
||||
* 生成格式化器的配置参数,该参数由以下合并而成:
|
||||
*
|
||||
* - global.formatters[*].$config
|
||||
* - global.formatters[fallbackLanguage].$config
|
||||
* - global.formatters[language].$config
|
||||
* - scope.activeFormatters.$config 当前优先
|
||||
* - this.formatters[*].$config
|
||||
* - this.formatters[fallbackLanguage].$config
|
||||
* - this.formatters[language].$config
|
||||
*/
|
||||
_generateFormatterConfig(language:string){
|
||||
try{
|
||||
const fallbackLanguage = this.getLanguage(language).fallback;
|
||||
const fallbackLanguage = this.getLanguage(language)?.fallback;
|
||||
let configSources = [
|
||||
getByPath(this.#global.formatters,`${fallbackLanguage}.$config`,{}),
|
||||
getByPath(this.#global.formatters,"*.$config",{}),
|
||||
getByPath(this.formatters,`${fallbackLanguage}.$config`,{}),
|
||||
getByPath(this._formatter,"*.$config",{}),
|
||||
getByPath(this.#global.formatters,`${language}.$config`,{}),
|
||||
getByPath(this.#activeFormatters,"$config",{})
|
||||
// 从全局读取
|
||||
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, config)=>{
|
||||
if(isPlainObject(config)) deepMerge(finalConfig,config,{newObject:false})
|
||||
return this.#activeFormatterConfig = configSources.reduce((finalConfig, curConfig)=>{
|
||||
if(isPlainObject(curConfig)) deepMerge(finalConfig,curConfig,{newObject:false,array:'replace'})
|
||||
return finalConfig
|
||||
},deepClone(getByPath(this.#global.formatters,`*.$config`,{})))
|
||||
},{})
|
||||
|
||||
}catch(e){
|
||||
if(this.debug) console.error(`Error while generate <${language}> formatter options: `,e)
|
||||
@ -275,7 +292,7 @@ export class VoerkaI18nScope {
|
||||
* @param {*} language
|
||||
* @returns
|
||||
*/
|
||||
getLanguage(language:string):VoerkaI18nLanguage | undefined{
|
||||
private getLanguage(language:string):VoerkaI18nLanguageDefine | undefined{
|
||||
let index = this.languages.findIndex((lng) => lng.name == language);
|
||||
if (index !== -1) return this.languages[index];
|
||||
}
|
||||
@ -285,12 +302,12 @@ export class VoerkaI18nScope {
|
||||
* @returns
|
||||
*/
|
||||
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.activeLanguage = this.defaultLanguage;
|
||||
}
|
||||
@ -319,25 +336,25 @@ export class VoerkaI18nScope {
|
||||
} else if (isFunction(loader)) { // 语言包异步chunk
|
||||
newMessages = (await loader()).default;
|
||||
} 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)){
|
||||
useRemote = true
|
||||
// 需要保存动态语言包中的$config,合并到对应语言的格式化器配置
|
||||
if(isPlainObject(loadedMessages.$config)){
|
||||
this._formatters[newLanguage] = {
|
||||
$config : loadedMessages.$config
|
||||
this.formatters[newLanguage] = {
|
||||
$config : loadedMessages.$config as any
|
||||
}
|
||||
delete loadedMessages.$config
|
||||
}
|
||||
newMessages = Object.assign({},this._default,loadedMessages);
|
||||
newMessages = Object.assign({},this.default,loadedMessages);
|
||||
}
|
||||
}
|
||||
if(newMessages){
|
||||
this._messages = newMessages
|
||||
this._activeLanguage = newLanguage;
|
||||
this.#options.messages = newMessages
|
||||
this.#options.activeLanguage = newLanguage;
|
||||
// 打语言包补丁, 如果是从远程加载语言包则不需要再打补丁了
|
||||
if(!useRemote) {
|
||||
await this._patch(this._messages, newLanguage);
|
||||
await this._patch(this.#options.messages, newLanguage);
|
||||
}
|
||||
// 切换到对应语言的格式化器
|
||||
await this._changeFormatters(newLanguage);
|
||||
@ -361,10 +378,10 @@ export class VoerkaI18nScope {
|
||||
* @param {*} newLanguage
|
||||
* @returns
|
||||
*/
|
||||
async _patch(messages, newLanguage) {
|
||||
private async _patch(messages:VoerkaI18nLanguageMessages, newLanguage:string) {
|
||||
if (!isFunction(this.global.loadMessagesFromDefaultLoader)) return;
|
||||
try {
|
||||
let pachedMessages = await this.global.loadMessagesFromDefaultLoader(newLanguage,this);
|
||||
let pachedMessages = (await this.global.loadMessagesFromDefaultLoader(newLanguage,this)) as unknown as VoerkaI18nLanguageMessages;
|
||||
if (isPlainObject(pachedMessages)) {
|
||||
Object.assign(messages, pachedMessages);
|
||||
this._savePatchedMessages(pachedMessages, newLanguage);
|
||||
@ -376,10 +393,10 @@ export class VoerkaI18nScope {
|
||||
/**
|
||||
* 从本地存储中读取语言包补丁合并到当前语言包中
|
||||
*/
|
||||
_mergePatchedMessages() {
|
||||
private _mergePatchedMessages() {
|
||||
let patchedMessages = this._getPatchedMessages(this.activeLanguage);
|
||||
if (isPlainObject(patchedMessages)) {
|
||||
Object.assign(this._messages, patchedMessages);
|
||||
Object.assign(this.#options.messages, patchedMessages);
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -400,13 +417,13 @@ export class VoerkaI18nScope {
|
||||
*
|
||||
* @param {*} messages
|
||||
*/
|
||||
_savePatchedMessages(messages, language) {
|
||||
private _savePatchedMessages(messages:VoerkaI18nLanguageMessages, language:string) {
|
||||
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);
|
||||
} catch (e:any) {
|
||||
if (this.debug) console.error("Error while save voerkai18n patched messages:",e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -414,18 +431,18 @@ export class VoerkaI18nScope {
|
||||
* @param {*} language
|
||||
* @returns
|
||||
*/
|
||||
_getPatchedMessages(language) {
|
||||
private _getPatchedMessages(language:string) {
|
||||
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) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
// 以下方法引用全局VoerkaI18n实例的方法
|
||||
on() {return this.#global.on(...arguments); }
|
||||
off() {return this.#global.off(...arguments); }
|
||||
offAll() {return this.#global.offAll(...arguments);}
|
||||
async change(language) {
|
||||
on(callback:Function) {return this.#global.on(callback); }
|
||||
off(callback:Function) {return this.#global.off(callback); }
|
||||
offAll() {return this.#global.offAll();}
|
||||
async change(language:string) {
|
||||
await this.#global.change(language);
|
||||
}
|
||||
};
|
||||
|
@ -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 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 {
|
||||
[language: string]: VoerkaI18nLanguageMessages
|
||||
}
|
||||
|
||||
export type Voerkai18nIdMap = Record<string, number>
|
||||
|
||||
export interface VoerkaI18nLanguage {
|
||||
export interface VoerkaI18nLanguageDefine {
|
||||
name: string
|
||||
title?: string
|
||||
default?: boolean
|
||||
@ -24,22 +29,25 @@ export interface VoerkaI18nLanguage {
|
||||
|
||||
|
||||
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 VoerkaI18nTypesFormatterConfigs= Record<SupportedDateTypes | string, Record<string,any>>
|
||||
//
|
||||
export type VoerkaI18nLanguageFormatters = {
|
||||
global?: true | Exclude<VoerkaI18nFormatters,'global'> // 是否全局格式化器
|
||||
export type VoerkaI18nFormattersLoader = (()=>Promise<VoerkaI18nFormatters>)
|
||||
// 每一个语言的格式化器定义={$types:{...},$config:{...},[格式化器名称]: () => {},[格式化器名称]: () => {}
|
||||
// 在formatters/xxxx.ts里面进行配置
|
||||
export type VoerkaI18nFormatters = ({
|
||||
global?: boolean | Omit<VoerkaI18nFormatters,'global'> // 是否全局格式化器
|
||||
$types?:VoerkaI18nTypesFormatters
|
||||
$config?: VoerkaI18nTypesFormatterConfigs
|
||||
} & {
|
||||
[DataTypeName in SupportedDateTypes]?: VoerkaI18nFormatter
|
||||
} & {
|
||||
[key: string]: VoerkaI18nFormatter
|
||||
}
|
||||
})
|
||||
|
||||
// 包括语言的{"*":{...},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{
|
||||
activeLanguage :string | null,
|
||||
typedFormatters: VoerkaI18nFormatters,
|
||||
formatters : VoerkaI18nFormatters,
|
||||
typedFormatters: VoerkaI18nLanguageFormatters,
|
||||
formatters : VoerkaI18nLanguageFormatters,
|
||||
}
|
||||
|
||||
export type TranslateMessageVars = number | boolean | string | Function | Date
|
||||
@ -63,7 +71,7 @@ export interface VoerkaI18nTranslate {
|
||||
(message: string, vars?: Record<string, TranslateMessageVars>): string
|
||||
}
|
||||
export interface VoerkaI18nSupportedLanguages {
|
||||
[key: string]: VoerkaI18nLanguage
|
||||
[key: string]: VoerkaI18nLanguageDefine
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user