update
This commit is contained in:
parent
105a75bc59
commit
e7318e2de4
@ -4,13 +4,20 @@ import {test,vi,describe,expect,afterAll,beforeAll} from 'vitest'
|
||||
import { VoerkaI18nScope } from '../scope'
|
||||
import zhFormatters from '../formatters/zh';
|
||||
import enFormatters from '../formatters/en';
|
||||
import { VoerkaI18nManager } from '../manager';
|
||||
import { VoerkaI18nFormatterRegistry } from '../formatterRegistry';
|
||||
import { VoerkaI18nLanguageMessages } from '../types';
|
||||
|
||||
|
||||
const zhMessages={
|
||||
const zhMessages:VoerkaI18nLanguageMessages = {
|
||||
$config:{
|
||||
x:{a:1},
|
||||
y:{b:1}
|
||||
},
|
||||
"1": "你好",
|
||||
"2": "你好,{name}",
|
||||
"3": "中国",
|
||||
"4": ["我有一部车","我有很多部车"]
|
||||
"4": ["我有一部车","我有很多部车"] ,
|
||||
}
|
||||
const enMessages={
|
||||
"1": "hello",
|
||||
@ -37,7 +44,8 @@ const languages = [
|
||||
|
||||
const formatters ={
|
||||
zh:zhFormatters,
|
||||
en:enFormatters
|
||||
en:enFormatters,
|
||||
jp:()=>{}
|
||||
}
|
||||
|
||||
describe("VoerkaI18nScope", () => {
|
||||
@ -56,8 +64,19 @@ describe("VoerkaI18nScope", () => {
|
||||
expect(scope.default).toEqual(zhMessages)
|
||||
expect(scope.current).toEqual(zhMessages)
|
||||
expect(scope.idMap).toEqual(idMap)
|
||||
// 格式化器配置
|
||||
expect(scope.formatters).toBeInstanceOf(VoerkaI18nFormatterRegistry)
|
||||
expect(scope.formatters.language).toBe("zh")
|
||||
expect(scope.formatters.formatters).toEqual(formatters)
|
||||
expect(scope.formatters.config).toBe(zhFormatters.$config)
|
||||
expect(scope.formatters.types).toBe(zhFormatters.$types)
|
||||
|
||||
|
||||
// 全局管理器
|
||||
expect(scope.global).toBeInstanceOf(VoerkaI18nManager)
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
test('translate', () => {})
|
||||
|
@ -32,8 +32,8 @@ export class VoerkaI18nFormatterRegistry{
|
||||
#ready:boolean = false
|
||||
#formatters:VoerkaI18nLanguageFormatters = {}
|
||||
#language:string = 'zh'
|
||||
constructor(formatters?:VoerkaI18nLanguageFormatters){
|
||||
this.#formatters = formatters || {}
|
||||
constructor(){
|
||||
|
||||
}
|
||||
/**
|
||||
* 当前语言
|
||||
@ -65,20 +65,16 @@ export class VoerkaI18nFormatterRegistry{
|
||||
语言名称,语言名称数组,或者使用,分割的语言名称字符串
|
||||
*/
|
||||
register(name:string | SupportedDateTypes, formatter:VoerkaI18nFormatter, {language = "*"}:{ language: string | string[] } ) {
|
||||
if (!isFunction(formatter) || typeof name !== "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(!(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 = {}
|
||||
}
|
||||
if(!lngFormatters.$types) lngFormatters.$types = {}
|
||||
lngFormatters.$types![name] = formatter
|
||||
} else {
|
||||
lngFormatters[name] = formatter;
|
||||
@ -187,9 +183,5 @@ export class VoerkaI18nFormatterRegistry{
|
||||
if(!this.#ready) throw new FormattersNotLoadedError(this.#language)
|
||||
return (this.#formatters[this.#language] as VoerkaI18nFormatters).$types as VoerkaI18nFormatterConfigs
|
||||
}
|
||||
get items(){
|
||||
if(!this.#ready) throw new FormattersNotLoadedError(this.#language)
|
||||
return this.#formatters[this.#language] as VoerkaI18nFormatterConfigs
|
||||
}
|
||||
|
||||
}
|
@ -5,6 +5,7 @@ import inlineFormatters from "./formatters"
|
||||
import type { VoerkaI18nScope } from "./scope"
|
||||
import type { VoerkaI18nLanguageDefine, VoerkaI18nLanguageFormatters, VoerkaI18nDefaultMessageLoader, VoerkaI18nFormatter, VoerkaI18nTypesFormatters } from "./types"
|
||||
import { VoerkaI18nFormatterRegistry } from "./formatterRegistry"
|
||||
import { DataTypes } from "./utils"
|
||||
|
||||
// 默认语言配置
|
||||
const defaultLanguageSettings = {
|
||||
@ -46,7 +47,7 @@ export class VoerkaI18nManager extends EventEmitter{
|
||||
#options?:Required<VoerkaI18nManagerOptions>
|
||||
#scopes:VoerkaI18nScope[] = []
|
||||
#defaultMessageLoader?:VoerkaI18nDefaultMessageLoader
|
||||
#formatterRegistry!:VoerkaI18nFormatterRegistry
|
||||
#formatters:VoerkaI18nLanguageFormatters = {}
|
||||
constructor(options?:VoerkaI18nManagerOptions){
|
||||
super()
|
||||
if(VoerkaI18nManager.instance){
|
||||
@ -54,7 +55,6 @@ 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实例
|
||||
}
|
||||
@ -65,14 +65,14 @@ export class VoerkaI18nManager extends EventEmitter{
|
||||
get defaultLanguage(){ return this.#options!.defaultLanguage} // 默认语言名称
|
||||
get languages(){ return this.#options!.languages} // 支持的语言列表
|
||||
get defaultMessageLoader(){ return this.#defaultMessageLoader} // 默认语言包加载器
|
||||
get formatters(){return this.#formatterRegistry!}
|
||||
get formatters(){return this.#formatters!}
|
||||
|
||||
/**
|
||||
* 初始加载格式化器
|
||||
*/
|
||||
private loadInitialFormatters(){
|
||||
if(this.#options?.formatters){
|
||||
this.#formatterRegistry.loadInitials(this.#options!.formatters)
|
||||
this.#formatters = this.#options!.formatters
|
||||
delete (this.#options as any).formatters
|
||||
}
|
||||
}
|
||||
@ -146,7 +146,22 @@ export class VoerkaI18nManager extends EventEmitter{
|
||||
language : 声明该格式化器适用语言
|
||||
*/
|
||||
registerFormatter(name:string,formatter:VoerkaI18nFormatter,{language="*"}:{language:string | string[] | '*'}){
|
||||
this.#formatterRegistry.register(name,formatter,{language})
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 注册默认文本信息加载器
|
||||
|
@ -24,6 +24,7 @@ VoerkaI18nMessageLoader,
|
||||
} from "./types"
|
||||
import { VoerkaI18nFormatterRegistry } from './formatterRegistry';
|
||||
import { InvalidLanguageError } from "./errors"
|
||||
import { randomId } from "./utils"
|
||||
|
||||
export interface VoerkaI18nScopeOptions {
|
||||
id?: string
|
||||
@ -38,20 +39,20 @@ export interface VoerkaI18nScopeOptions {
|
||||
|
||||
export class VoerkaI18nScope {
|
||||
#options:Required<VoerkaI18nScopeOptions>
|
||||
#global:VoerkaI18nManager // 引用全局VoerkaI18nManager配置,注册后自动引用
|
||||
#global:VoerkaI18nManager // 引用全局VoerkaI18nManager配置,注册后自动引用
|
||||
#refreshing:boolean = false
|
||||
#patchMessages:VoerkaI18nLanguagePack = {}
|
||||
#t:VoerkaI18nTranslate
|
||||
#activeFormatters:VoerkaI18nFormatters = {}
|
||||
#activeFormatterConfig: VoerkaI18nFormatterConfigs={}
|
||||
#cache:VoerkaI18nScopeCache
|
||||
#formatterRegistry:VoerkaI18nFormatterRegistry
|
||||
#formatterRegistry:VoerkaI18nFormatterRegistry = new VoerkaI18nFormatterRegistry()
|
||||
#defaultLanguage:string ='zh'
|
||||
#activeLanguage:string='zh'
|
||||
#currentMessages:VoerkaI18nLanguageMessages = {} // 当前语言包
|
||||
#patchedMessages:VoerkaI18nLanguagePack = {} // 补丁语言包
|
||||
constructor(options:VoerkaI18nScopeOptions, callback?:(e?:Error)=>void) {
|
||||
this.#options = assignObject({
|
||||
id : Date.now().toString() + parseInt(String(Math.random() * 1000)),
|
||||
id : randomId(), // 作用域唯一id
|
||||
debug : false,
|
||||
languages : {}, // 当前作用域支持的语言列表
|
||||
messages : {}, // 所有语言包={[language]:VoerkaI18nLanguageMessages}
|
||||
@ -63,24 +64,16 @@ export class VoerkaI18nScope {
|
||||
activeLanguage : this.#options.activeLanguage,
|
||||
typedFormatters: {},
|
||||
formatters : {},
|
||||
};
|
||||
};
|
||||
// 初始化
|
||||
this._initiLanguages()
|
||||
// 如果不存在全局VoerkaI18n实例,说明当前Scope是唯一或第一个加载的作用域,则自动创建全局VoerkaI18n实例
|
||||
if (!globalThis.VoerkaI18n) {
|
||||
globalThis.VoerkaI18n = new VoerkaI18nManager({
|
||||
debug : this.debug,
|
||||
defaultLanguage: this.#defaultLanguage,
|
||||
activeLanguage : this.#activeLanguage,
|
||||
languages : options.languages,
|
||||
});
|
||||
}
|
||||
this.init()
|
||||
// 将当前实例注册到全局单例VoerkaI18nManager中
|
||||
this.#global = this.registerToManager(callback)
|
||||
// 从本地缓存中读取并合并补丁语言包
|
||||
this._mergePatchedMessages();
|
||||
// 延后执行补丁命令,该命令会向远程下载补丁包
|
||||
this._patch(this.#currentMessages, this.activeLanguage);
|
||||
this.#t = translate.bind(this)
|
||||
this.#global = globalThis.VoerkaI18n as unknown as VoerkaI18nManager;
|
||||
this.#formatterRegistry = new VoerkaI18nFormatterRegistry()
|
||||
this._mergePatchedMessages(); // 从本地缓存中读取并合并补丁语言包
|
||||
this._patch(this.#currentMessages, this.activeLanguage); // 延后执行补丁命令,该命令会向远程下载补丁包
|
||||
if(callback) this.register(callback); // 在全局注册作用域
|
||||
}
|
||||
get id() {return this.#options.id;} // 作用域唯一id
|
||||
get debug() {return this.#options.debug;} // 调试开关
|
||||
@ -106,7 +99,7 @@ export class VoerkaI18nScope {
|
||||
* - 将en配置为默认回退语言
|
||||
* - 确保提供了有效的默认语言和活动语言
|
||||
*/
|
||||
_initiLanguages(){
|
||||
private init(){
|
||||
if(!Array.isArray(this.languages)){
|
||||
console.warn("[VoerkaI18n] invalid languages config,use default languages config instead.")
|
||||
this.#options.languages = [
|
||||
@ -138,16 +131,29 @@ export class VoerkaI18nScope {
|
||||
throw new Error("[VoerkaI18n] invalid <defaultLanguage> config, messages must be static.")
|
||||
}
|
||||
this.#currentMessages = this.messages[this.#activeLanguage] as VoerkaI18nLanguageMessages
|
||||
|
||||
// 初始化格式化器
|
||||
this.loadInitialFormatters()
|
||||
}
|
||||
|
||||
/**
|
||||
* 在全局注册作用域当前作用域
|
||||
* @param {*} callback 注册成功后的回调
|
||||
*/
|
||||
register(callback:(e?:Error)=>void) {
|
||||
/**
|
||||
* 注册当前作用域到全局作用域
|
||||
* @param callback
|
||||
*/
|
||||
private registerToManager(callback?:(e?:Error)=>void){
|
||||
// 如果不存在全局VoerkaI18n实例,说明当前Scope是唯一或第一个加载的作用域,则自动创建全局VoerkaI18n实例
|
||||
if (!globalThis.VoerkaI18n) {
|
||||
globalThis.VoerkaI18n = new VoerkaI18nManager({
|
||||
debug : this.debug,
|
||||
defaultLanguage: this.#defaultLanguage,
|
||||
activeLanguage : this.#activeLanguage,
|
||||
languages : this.#options.languages,
|
||||
});
|
||||
}
|
||||
this.#global = globalThis.VoerkaI18n as unknown as VoerkaI18nManager;
|
||||
if (!isFunction(callback)) callback = () => {};
|
||||
this.global.register(this).then(()=>callback()).catch((e)=>callback(e));
|
||||
}
|
||||
this.#global.register(this).then(()=>(callback as any)()).catch((e)=>(callback as any)(e));
|
||||
return this.#global
|
||||
}
|
||||
/**
|
||||
* 注册格式化器
|
||||
*
|
||||
@ -199,10 +205,9 @@ export class VoerkaI18nScope {
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
private loadFormatters(newLanguage:string){
|
||||
private loadInitialFormatters(){
|
||||
// 初始化格式化器
|
||||
this.formatters.loadInitials(this.#options.formatters)
|
||||
this.#formatterRegistry.language = newLanguage
|
||||
if(this.#options.formatters)
|
||||
// 将配置中的指定为全局的格式化器注册到全局
|
||||
Object.entries(this.#options.formatters).forEach(([langName,formatters])=>{
|
||||
@ -219,17 +224,8 @@ export class VoerkaI18nScope {
|
||||
})
|
||||
// 保存到Registry中,就可以从options中删除了
|
||||
delete (this.#options as any).formatters
|
||||
// 初始化格式化器
|
||||
try {
|
||||
if (this.formatters.hasLanguage(newLanguage)) {
|
||||
|
||||
} else {
|
||||
if (this.debug) console.warn(`Not initialize <${newLanguage}> formatters.`);
|
||||
}
|
||||
this._generateFormatterConfig(newLanguage)
|
||||
} catch (e:any) {
|
||||
if (this.debug) console.error(`Error while initialize ${newLanguage} formatters: ${e.message}`);
|
||||
}
|
||||
// 切换到当前语言的格式化器上下文
|
||||
this.changeFormatters(this.activeLanguage)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,7 +240,7 @@ export class VoerkaI18nScope {
|
||||
*
|
||||
* @param {*} language
|
||||
*/
|
||||
async _changeFormatters(newLanguage:string) {
|
||||
private async changeFormatters(newLanguage:string) {
|
||||
try {
|
||||
if (this.formatters.hasLanguage(newLanguage)) {
|
||||
this.formatters.language = newLanguage
|
||||
@ -260,16 +256,9 @@ export class VoerkaI18nScope {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 生成格式化器的配置参数,该参数由以下合并而成:
|
||||
*
|
||||
* - global.formatters[*].$config
|
||||
* - global.formatters[fallbackLanguage].$config
|
||||
* - global.formatters[language].$config
|
||||
* - this.formatters[*].$config
|
||||
* - this.formatters[fallbackLanguage].$config
|
||||
* - this.formatters[language].$config
|
||||
* 生成格式化器的配置参数,该参数由以下合并而成:
|
||||
*/
|
||||
_generateFormatterConfig(language:string){
|
||||
private _generateFormatterConfig(language:string){
|
||||
try{
|
||||
const fallbackLanguage = this.getLanguage(language)?.fallback;
|
||||
let configSources = [
|
||||
@ -375,7 +364,7 @@ export class VoerkaI18nScope {
|
||||
if (newLanguage === this.defaultLanguage) {
|
||||
this.#currentMessages = this.default;
|
||||
await this._patch(this.#currentMessages, newLanguage); // 异步补丁
|
||||
await this._changeFormatters(newLanguage);
|
||||
await this.changeFormatters(newLanguage);
|
||||
this.#refreshing = false
|
||||
return;
|
||||
}else{ // 非默认语言可以是静态语言包也可以是异步加载语言包
|
||||
@ -390,7 +379,7 @@ export class VoerkaI18nScope {
|
||||
await this._patch(this.#currentMessages, newLanguage);
|
||||
}
|
||||
// 切换到对应语言的格式化器
|
||||
await this._changeFormatters(newLanguage);
|
||||
await this.changeFormatters(newLanguage);
|
||||
}else{
|
||||
this._fallback();
|
||||
}
|
||||
|
@ -9,8 +9,9 @@ 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
|
||||
$remote?: boolean
|
||||
}
|
||||
|
||||
export type VoerkaI18nLanguageMessagePack = Record<string, VoerkaI18nLanguageMessages | VoerkaI18nMessageLoader>
|
||||
|
@ -71,3 +71,8 @@ export function toDate(value:any) {
|
||||
export function toBoolean(value:any){
|
||||
return !!value
|
||||
}
|
||||
|
||||
|
||||
export function randomId():string{
|
||||
return Date.now().toString() + parseInt(String(Math.random() * 1000))
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user