Skip to content

Commit

Permalink
Merge pull request #73 from zhangfisher/3.0
Browse files Browse the repository at this point in the history
3.0
  • Loading branch information
zhangfisher authored Dec 9, 2024
2 parents 9401033 + 807e51c commit cd45315
Show file tree
Hide file tree
Showing 14 changed files with 380 additions and 239 deletions.
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"dependencies": {
"@lite-tree/icons": "^1.0.6",
"@lite-tree/vue": "^1.1.4",
"flex-tools": "^1.4.11",
"flex-tools": "^1.4.12",
"flexstyled": "^2.1.2"
}
}
4 changes: 3 additions & 1 deletion packages/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
"author": "wxzhang",
"license": "MIT",
"dependencies": {
"flex-tools": "^1.4.11"
"flex-tools": "^1.4.11",
"flexvars": "^1.0.1",
"ts-mixer": "^6.0.4"
},
"devDependencies": {
"@swc/core": "^1.3.44",
Expand Down
1 change: 1 addition & 0 deletions packages/runtime/src/interpolate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { FormatterDefineChain, parseFormatters } from "./formatter"
import { VoerkaI18nScope } from "./scope"
import { VoerkaI18nFormatterConfigs } from './types';


// 用来提取字符里面的插值变量参数 , 支持管道符 { var | formatter | formatter }
// 支持参数: { var | formatter(x,x,..) | formatter }
// v1 采用命名捕获组
Expand Down
10 changes: 6 additions & 4 deletions packages/runtime/src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { LiteEvent } from "flex-tools/events/liteEvent"
import defaultStoage from "./storage"
import { assignObject } from 'flex-tools/object/assignObject';
import { createLogger } from "./logger"
import { isScope } from "./utils"


// 默认语言配置
Expand All @@ -24,11 +25,11 @@ const defaultLanguageSettings = {
} as VoerkaI18nManagerOptions

export interface VoerkaI18nManagerOptions {
debug?: boolean
debug? : boolean
defaultLanguage: string
activeLanguage: string
languages: VoerkaI18nLanguageDefine[]
storage?:IVoerkaI18nStorage // 语言包存储器
activeLanguage : string
languages : VoerkaI18nLanguageDefine[]
storage? : IVoerkaI18nStorage // 语言包存储器
}

export type VoerkaI18nEvents =
Expand Down Expand Up @@ -192,6 +193,7 @@ export class VoerkaI18nManager extends LiteEvent<any,VoerkaI18nEvents>{
* @param {*} scope
*/
async register(scope:VoerkaI18nScope){
if(!isScope(scope)) throw new Error("注册的作用域必须是VoerkaI18nScope的实例")
const isInit = this._scopes.length===0 && !scope.options.library
this._scopes.push(scope)
if(this._scopes.length===1) this._appScopeId = scope.id
Expand Down
67 changes: 67 additions & 0 deletions packages/runtime/src/mixins/formatter/formatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
*
* 提供格式化相关逻辑
*
*/

import type { VoerkaI18nScope } from "../../scope";
import { VoerkaI18nFormatter } from "../../types";
import { VoerkaI18nFormatterManager } from "./manager";


export class FormatterMixin{
private _formatters:VoerkaI18nFormatterManager | null = null
get formatters() { return this._formatters!;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}}

protected initFormatters(this:VoerkaI18nScope){
this._formatters = new VoerkaI18nFormatterManager(this)
}



/**
* 初始化格式化器
* 激活和默认语言的格式化器采用静态导入的形式,而没有采用异步块的形式,这是为了确保首次加载时的能马上读取,而不能采用延迟加载方式
* #activeFormatters={
* global:{...} // 或true代表注册到全局
* $config:{...},
* $types:{...},
* [格式化器名称]:()=>{...},
* [格式化器名称]:()=>{...},
* ...
* }
*/
// private _loadInitialFormatters(){
// this._formatterRegistry= new VoerkaI18nFormatterManager(this)
// // 初始化格式化器
// this.formatters.loadInitials(this._options.formatters)
// // 保存到Registry中,就可以从options中删除了
// delete (this.options as any).formatters
// }

/**
* 注册格式化器
*
* 格式化器是一个简单的同步函数value=>{...},用来对输入进行格式化后返回结果
*
* registerFormatter(name,value=>{...}) // 注册到所有语言
* registerFormatter(name,value=>{...},{langauge:"zh"}) // 注册到zh语言
* registerFormatter(name,value=>{...},{langauge:"en"}) // 注册到en语言
* registerFormatter("Date",value=>{...},{langauge:"en"}) // 注册到en语言的默认数据类型格式化器
* registerFormatter(name,value=>{...},{langauge:["zh","cht"]}) // 注册到zh和cht语言
* registerFormatter(name,value=>{...},{langauge:"zh,cht"})
* @param {*} formatter 格式化器
language : 字符串或数组,声明该格式化器适用语言
*代表适用于所有语言
语言名称,语言名称数组,或者使用,分割的语言名称字符串
asGlobal : 注册到全局
*/
registerFormatter(this:VoerkaI18nScope,name:string, formatter:VoerkaI18nFormatter, options?:{ language?: string | string[] | "*", asGlobal?:boolean } ) {
const {language = "*", asGlobal= true} = options || {}
if(asGlobal){
this.global.registerFormatter(name, formatter, {language});
}else{
this.formatters.register(name, formatter, {language});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
*/


import { escapeRegexpStr } from "./utils"
import { escapeRegexpStr } from "../../utils"
import { get as getByPath } from "flex-tools/object/get"
import { isNumber } from "flex-tools/typecheck/isNumber"
import { isFunction } from "flex-tools/typecheck/isFunction"
import { isPlainObject } from "flex-tools/typecheck/isPlainObject"
import { safeParseJson } from "flex-tools/object/safeParseJson"
import { assignObject } from "flex-tools/object/assignObject"
import { VoerkaI18nFormatter } from './types';
import { VoerkaI18nFormatter } from '../../types';

/**
使用正则表达式对原始文本内容进行解析匹配后得到的便以处理的数组
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
/**
*
* 保存所有格式化器数据 *
* 保存所有格式化器数据
*
*
*/
import { isPlainObject } from 'flex-tools/typecheck/isPlainObject';
import type { VoerkaI18nScope } from './scope';
import { DataTypes, loadAsyncModule } from './utils';
import type { VoerkaI18nScope } from '../../scope';
import { DataTypes, loadAsyncModule } 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';
import { DefaultFallbackLanguage } from './consts';
import { DefaultFallbackLanguage } from '../../consts';
import { VoerkaI18nFormatter,
VoerkaI18nFormatters,
VoerkaI18nFormattersLoader,
VoerkaI18nLanguageFormatters,
SupportedDateTypes,
VoerkaI18nFormatterConfigs,
VoerkaI18nTypesFormatters
} from './types';
} from '../../types';
import { deepClone } from 'flex-tools/object/deepClone';

export interface VoerkaI18nScopeCache{
Expand Down Expand Up @@ -49,21 +49,35 @@ export interface VoerkaI18nScopeFormatterCache{
formatters : Record<string,VoerkaI18nFormatter>,
}

export class VoerkaI18nFormatterRegistry{
export class VoerkaI18nFormatterManager{
private _formatters:VoerkaI18nLanguageFormatters = {}
private _activeFormatters:VoerkaI18nFormatters = {}
private _activeFormattersConfigs :VoerkaI18nFormatterConfigs = {}
private _scope?:VoerkaI18nScope
private _language?:string // 当前语言
private _formatterCache:VoerkaI18nScopeFormatterCache = {typedFormatters:{},formatters:{}}
constructor(scope?:VoerkaI18nScope){
this._scope = scope
private _cache:VoerkaI18nScopeFormatterCache = {typedFormatters:{},formatters:{}}

constructor(scope:VoerkaI18nScope){
this._scope = scope
this._formatters = scope?.options.formatters
this._scope.on("change",this._onChange.bind(this))
}
get scope(){ return this._scope }
get language(){ return this._language }
get cache(){ return this._cache }
/**
* 当语言变化时,重新生成格式化器
*/
private _onChange(language:string){
if(language !== this._language){
this._clearCache()
this.change(language)
}
}
private _clearCache(){
this._cache = {typedFormatters:{},formatters:{}}
}
get activeLanguage(){
if(!this._language) this._language = this._scope?.activeLanguage || "zh"
return this._language
} // 当前语言
get scope(){ return this._scope }
// 当前语言
/**
* 当切换语言时,切换当前语言的格式化器
* 当切换语言时,如果当前语言的格式化器集合还没有加载,则会自动加载
Expand All @@ -84,7 +98,7 @@ export class VoerkaI18nFormatterRegistry{
this._activeFormatters = formatters as VoerkaI18nFormatters
}
// 合并生成格式化器的配置参数,当执行格式化器时该参数将被传递给格式化器
this._formatterCache = {typedFormatters:{},formatters:{}}
this._cache = {typedFormatters:{},formatters:{}}
this.generateFormattersConfigs(useLanguage)
this._language = language
} else {
Expand Down Expand Up @@ -224,8 +238,8 @@ export class VoerkaI18nFormatterRegistry{
},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]
if(on=="types" && name in this._cache.typedFormatters) return this._cache.typedFormatters[name as SupportedDateTypes]
if(on=="scope" && name in this._cache.formatters) return this._cache.formatters[name]

const fallbackLanguage = this.scope?.getLanguage(this.activeLanguage)?.fallback

Expand Down Expand Up @@ -265,9 +279,9 @@ export class VoerkaI18nFormatterRegistry{
if (isFunction(formatter)) {
// 缓存起来,下次直接返回避免重复查找
if(on=="types"){
this._formatterCache.typedFormatters[name as SupportedDateTypes] = formatter
this._cache.typedFormatters[name as SupportedDateTypes] = formatter
}else{
this._formatterCache.formatters[name] = formatter
this._cache.formatters[name] = formatter
}
return formatter
}
Expand Down
106 changes: 106 additions & 0 deletions packages/runtime/src/mixins/translate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
*
* 提供翻译函数
*
*/
import { isNumber } from "flex-tools/typecheck/isNumber"
import { isPlainObject } from "flex-tools/typecheck/isPlainObject"
import { isFunction } from "flex-tools/typecheck/isFunction"
import type { VoerkaI18nScope } from "../scope"
import { isMessageId } from "../utils/isMessageId"




export class TranslateMixin{

/**
* 根据值的单数和复数形式,从messages中取得相应的消息
*
* @param {*} messages 复数形式的文本内容 = [<=0时的内容>,<=1时的内容>,<=2时的内容>,...]
* @param {*} value
*/
private _getPluraMessage(messages:string | string[],value:number){
try{
if(Array.isArray(messages)){
return messages.length > value ? messages[value] : messages[messages.length-1]
}else{
return messages
}
}catch{
return Array.isArray(messages) ? messages[0] : messages
}
}

translate(this:VoerkaI18nScope,message:string,...args:any[]):string {
// 如果内容是复数,则其值是一个数组,数组中的每个元素是从1-N数量形式的文本内容
let result:string | string[] = message
let vars=[] // 插值变量列表
let pluraValue = null // 复数值

if(!(typeof(message)==="string")) return message
try{
// 1. 预处理变量: 复数变量保存至pluralVars中 , 变量如果是Function则调用
if(arguments.length === 2 && isPlainObject(arguments[1])){// 字典插值
const dictVars:Record<string,any>=arguments[1]
for(const [name,value] of Object.entries(dictVars)){
if(isFunction(value)){
try{
dictVars[name] = value()
}catch{
dictVars[name] = value
}
}
// 以$开头的视为复数变量,记录下来
const isNum:boolean = typeof(dictVars[name])==="number"
if((pluraValue==null && isNum) || name.startsWith("$") && isNum){
pluraValue = dictVars[name]
}
}
vars = [dictVars]
}else if(arguments.length >= 2){ // 位置插值
vars = [...arguments].splice(1).map((arg)=>{
try{
arg = isFunction(arg) ? arg() : arg
// 约定:位置参数中以第一个数值变量作为指示复数变量
if(isNumber(arg)) pluraValue = parseInt(arg)
}catch{
return String(arg)
}
return arg
})
}

if(isMessageId(message)){ // 如果是数字id,
result = (this.current as any)[message] || message
}else{
const msgId = this.idMap[message]
// 语言包可能是使用idMap映射过的,则需要转换
result = (msgId ? (this.current as any)[msgId] : (this.current as any)[message]) ?? message
}

// 2. 处理复数
// 经过上面的处理,content可能是字符串或者数组
// content = "原始文本内容" || 复数形式["原始文本内容","原始文本内容"....]
// 如果是数组说明要启用复数机制,需要根据插值变量中的某个变量来判断复数形式
if(Array.isArray(result) && result.length>0){
// 如果存在复数命名变量,只取第一个复数变量
if(pluraValue!==null){ // 启用的是位置插值
result = this._getPluraMessage(result,pluraValue)
}else{ // 如果找不到复数变量,则使用第一个内容
result = result[0]
}
}
// 如果没有传入插值变量,则直接返回
if(args.length===0) return result as string
// 进行插值处理
return this.interpolator.replace(result as string,...vars)
}catch{
return result as any // 出错则返回原始文本
}

}
}



Loading

0 comments on commit cd45315

Please sign in to comment.