update for ts

This commit is contained in:
wxzhang 2023-04-05 16:05:56 +08:00
parent e02835fa50
commit 105a75bc59
18 changed files with 252 additions and 170 deletions

View File

@ -0,0 +1,64 @@
import {test,vi,describe,expect,afterAll,beforeAll} from 'vitest'
import { VoerkaI18nScope } from '../scope'
import zhFormatters from '../formatters/zh';
import enFormatters from '../formatters/en';
const zhMessages={
"1": "你好",
"2": "你好,{name}",
"3": "中国",
"4": ["我有一部车","我有很多部车"]
}
const enMessages={
"1": "hello",
"2": "hello,{name}",
"3": "china",
"4": "I have {} cars"
}
const messages = {
zh: zhMessages,
en: enMessages
}
const idMap={
"你好":1,
"你好,{name}":2,
"中国":3,
"我有{}部车":4
}
const languages = [
{ name: "zh",default:true,active:true},
{ name: "en"}
]
const formatters ={
zh:zhFormatters,
en:enFormatters
}
describe("VoerkaI18nScope", () => {
let scope = new VoerkaI18nScope({
id: "test",
languages,
idMap,
messages,
formatters
})
test("成功创建实例", () => {
expect(scope).toBeInstanceOf(VoerkaI18nScope)
expect(scope.activeLanguage).toBe("zh")
expect(scope.defaultLanguage).toBe("zh")
expect(scope.messages).toEqual(messages)
expect(scope.default).toEqual(zhMessages)
expect(scope.current).toEqual(zhMessages)
expect(scope.idMap).toEqual(idMap)
})
})
test('translate', () => {})

View File

@ -10,14 +10,14 @@ import { toChineseNumber } from "flex-tools/chinese/toChineseNumber"
import { toChineseCurrency } from "flex-tools/chinese/toChineseCurrency" import { toChineseCurrency } from "flex-tools/chinese/toChineseCurrency"
export const chineseNumberFormatter = Formatter((value: number, isBig: boolean, $config: any) => { export const chineseNumberFormatter = Formatter((value: any, [isBig]:[isBig: boolean], $config) => {
return toChineseNumber(value, isBig) return toChineseNumber(value, isBig) as string
}, { }, {
params: ["isBig"] params: ["isBig"]
}) })
export const rmbFormater = FlexFormatter((value:number | string, params : any, $config:any) => { export const rmbFormater = FlexFormatter((value:number | string, [params] : any[], $config:any) => {
return toChineseCurrency(value, params, $config) return toChineseCurrency(value, params, $config)
}, { }, {
params: ["big", "prefix", "unit", "suffix"], params: ["big", "prefix", "unit", "suffix"],

View File

@ -98,7 +98,7 @@ export const currencyFormatter = FlexFormatter((value:string | number,params:Rec
} }
return toCurrency(value,params,$config) return toCurrency(value,params,$config)
},{ },{
normalize: toNumber, normalize: (value:string)=>toNumber(value),
params : ["format","unit","precision","prefix","suffix","division","symbol","radix"], params : ["format","unit","precision","prefix","suffix","division","symbol","radix"],
configKey: "currency" configKey: "currency"
},{ },{

View File

@ -3,18 +3,16 @@
*/ */
const { isFunction,replaceAll,toDate } = require('../utils') import { toDate } from '../utils'
const { Formatter } = require('../formatter'); import { Formatter } from '../formatter';
import { formatDateTime } from "flex-tools/misc/formatDateTime" import { formatDateTime } from "flex-tools/misc/formatDateTime"
import { relativeTime } from "flex-tools/misc/relativeTime" import { relativeTime } from "flex-tools/misc/relativeTime"
import { assignObject } from "flex-tools/object/assignObject" import { assignObject } from "flex-tools/object/assignObject"
import { isFunction } from "flex-tools/typecheck/isFunction"
function formatTime(value:number ,template="HH:mm:ss"){ function formatTime(value:number ,template="HH:mm:ss"){
return formatDateTime(value,template,{ return formatDateTime(value,template,{})
})
} }
@ -29,9 +27,10 @@ function formatTime(value:number ,template="HH:mm:ss"){
* *
**/ **/
export type FormatterTransformer = (value:any,format:string)=>string export type FormatterTransformer = (value:any,format:string)=>string
export function createDateTimeFormatter(options={},transformer:FormatterTransformer){ export function createDateTimeFormatter(options={},transformer:FormatterTransformer){
let opts = assignObject({presets:{}},options) let opts = assignObject({presets:{}},options)
return Formatter(function(value,format,$config){ return Formatter(function(this:any,value:any,[format]:any[],$config:Record<string,any>){
if((format in opts.presets) && isFunction(opts.presets[format])){ if((format in opts.presets) && isFunction(opts.presets[format])){
return opts.presets[format](value) return opts.presets[format](value)
}else if((format in $config)){ }else if((format in $config)){
@ -53,17 +52,17 @@ export function createDateTimeFormatter(options={},transformer:FormatterTransfor
* - format取值local,long,short,iso,gmt,utc,<模板字符串> * - format取值local,long,short,iso,gmt,utc,<模板字符串>
* - $config.datetime.date.format指定 * - $config.datetime.date.format指定
*/ */
const dateFormatter = createDateTimeFormatter({ export const dateFormatter = createDateTimeFormatter({
normalize: toDate, normalize: toDate,
params : ["format"], params : ["format"],
configKey: "datetime.date", configKey: "datetime.date",
presets : { presets : {
local: value=>value.toLocaleString(), local: (value:any)=>value.toLocaleString(),
iso : value=>value.toISOString(), iso : (value:any)=>value.toISOString(),
utc : value=>value.toUTCString(), utc : (value:any)=>value.toUTCString(),
gmt : value=>value.toGMTString() gmt : (value:any)=>value.toGMTString()
} }
},formatDatetime) },formatDateTime)
/** /**
@ -71,8 +70,8 @@ export function createDateTimeFormatter(options={},transformer:FormatterTransfor
* - format: long,short,number * - format: long,short,number
* - short * - short
*/ */
const quarterFormatter = createDateTimeFormatter({ export const quarterFormatter = createDateTimeFormatter({
normalize : value=>{ normalize : (value:any)=>{
const month = value.getMonth() + 1 const month = value.getMonth() + 1
return Math.floor( ( month % 3 == 0 ? ( month / 3 ) : (month / 3 + 1 ) )) return Math.floor( ( month % 3 == 0 ? ( month / 3 ) : (month / 3 + 1 ) ))
}, },
@ -85,7 +84,7 @@ const quarterFormatter = createDateTimeFormatter({
* - format: long,short,number * - format: long,short,number
* - short * - short
*/ */
const monthFormatter = createDateTimeFormatter({ export const monthFormatter = createDateTimeFormatter({
normalize: (value:Date)=> value.getMonth() + 1, normalize: (value:Date)=> value.getMonth() + 1,
params : ["format"], params : ["format"],
configKey: "datetime.month" configKey: "datetime.month"
@ -96,7 +95,7 @@ const monthFormatter = createDateTimeFormatter({
* - format: long,short,number * - format: long,short,number
* - long * - long
*/ */
const weekdayFormatter = createDateTimeFormatter({ export const weekdayFormatter = createDateTimeFormatter({
normalize: (value:Date)=> value.getDay(), normalize: (value:Date)=> value.getDay(),
params : ["format"], params : ["format"],
configKey: "datetime.weekday" configKey: "datetime.weekday"
@ -107,7 +106,7 @@ const weekdayFormatter = createDateTimeFormatter({
* - format取值local,long,short,timestamp,<模板字符串> * - format取值local,long,short,timestamp,<模板字符串>
* - $config.datetime.time.format指定 * - $config.datetime.time.format指定
*/ */
const timeFormatter = createDateTimeFormatter({ export const timeFormatter = createDateTimeFormatter({
normalize : toDate, normalize : toDate,
params : ["format"], params : ["format"],
configKey : "datetime.time", configKey : "datetime.time",
@ -122,7 +121,7 @@ const weekdayFormatter = createDateTimeFormatter({
* @param {*} value * @param {*} value
* @param {*} baseTime * @param {*} baseTime
*/ */
const relativeTimeFormatter = Formatter((value:Date,baseTime:Date,$config:any)=>{ export const relativeTimeFormatter = Formatter((value:any,[baseTime]:[Date],$config:any)=>{
//const { units,now,before,base = Date.now() , after } = $config //const { units,now,before,base = Date.now() , after } = $config
return relativeTime(value, $config) return relativeTime(value, $config)
},{ },{

View File

@ -8,16 +8,17 @@
* { value | number('big') } * { value | number('big') }
* *
*/ */
const { isNumber,toNumber } = require("../utils") import { toNumber } from "../utils"
const { Formatter } = require("../formatter") import { Formatter } from "../formatter"
const { toCurrency } = require("./currency") import { toCurrency } from "./currency"
import { VoerkaI18nFormatter } from '../types';
const numberFormartter = Formatter(function(value,precision,division,$config){ export const numberFormartter = Formatter(function(value:any,[precision,division]:[number,number],$config:any){
return toCurrency(value, { division, precision},$config) return toCurrency(value, { division, precision},$config)
},{ },{
normalize: toNumber, normalize: toNumber,
params:["precision","division"], params:["precision","division"],
configKey: "number" configKey: "number"
}) }) as VoerkaI18nFormatter

View File

@ -271,8 +271,8 @@ function parseFormaterParams(strParams: string): any[] {
*/ */
export type Formatter = (this: any, value: string, ...args: any[]) => string export type Formatter = (this: any, value: string, ...args: any[]) => string
export interface FormatterOptions { export interface FormatterOptions {
normalize?: (value: string) => string // 对输入值进行规范化处理,如进行时间格式化时,为了提高更好的兼容性,支持数字时间戳/字符串/Date等需要对输入值进行处理如强制类型转换等 normalize?: (value: string) => any // 对输入值进行规范化处理,如进行时间格式化时,为了提高更好的兼容性,支持数字时间戳/字符串/Date等需要对输入值进行处理如强制类型转换等
params?: Record<string, any>, // 可选的声明参数顺序如果是变参的则需要传入null params?: Record<string, any> | null, // 可选的声明参数顺序如果是变参的则需要传入null
configKey?: string // 声明该格式化器在$config中的路径支持简单的使用.的路径语法 configKey?: string // 声明该格式化器在$config中的路径支持简单的使用.的路径语法
} }
@ -285,7 +285,7 @@ export function createFormatter(fn: Formatter, options?: FormatterOptions, defau
// 最后一个参数是传入activeFormatterConfig参数 // 最后一个参数是传入activeFormatterConfig参数
// 并且格式化器的this指向的是activeFormatterConfig // 并且格式化器的this指向的是activeFormatterConfig
const $formatter = function (this: any, value: string, ...args: any[]) { const $formatter = function (this: any, value: string, args: any[],$config:Record<string,any>) {
let finalValue = value let finalValue = value
// 1. 输入值规范处理,主要是进行类型转换,确保输入的数据类型及相关格式的正确性,提高数据容错性 // 1. 输入值规范处理,主要是进行类型转换,确保输入的数据类型及相关格式的正确性,提高数据容错性
if (isFunction(opts.normalize)) { if (isFunction(opts.normalize)) {
@ -312,7 +312,7 @@ export function createFormatter(fn: Formatter, options?: FormatterOptions, defau
if (args[i] !== undefined) finalArgs[i] = args[i] if (args[i] !== undefined) finalArgs[i] = args[i]
} }
} }
return fn.call(this, finalValue, ...finalArgs, formatterConfig) return fn.call(this, finalValue, finalArgs, formatterConfig)
} }
$formatter.configurable = true $formatter.configurable = true
return $formatter return $formatter
@ -339,9 +339,7 @@ export const createFlexFormatter = function (fn: Formatter, options: FormatterOp
const opts = assignObject({ const opts = assignObject({
params: {} params: {}
}, options) }, options)
const $flexFormatter = Formatter(function (this: any, value: string, ...args: string[]) { const $flexFormatter = Formatter(function (this: any, value: string,args: string[],$config:Record<string,any> ) {
// 1. 最后一个参数总是代表格式化器的参数,不同语言不一样
let $config = args[args.length - 1] as unknown as Record<string, any>
// 2. 从语言配置中读取默认参数 // 2. 从语言配置中读取默认参数
let finalParams = (options.params || {}).reduce((r: Record<string, any>, name: string) => { let finalParams = (options.params || {}).reduce((r: Record<string, any>, name: string) => {
r[name] = $config[name] == undefined ? (defaultParams || {})[name] : $config[name] r[name] = $config[name] == undefined ? (defaultParams || {})[name] : $config[name]
@ -361,6 +359,7 @@ export const createFlexFormatter = function (fn: Formatter, options: FormatterOp
} }
export const Formatter = createFormatter
export const FlexFormatter = createFlexFormatter export const FlexFormatter = createFlexFormatter

View File

@ -4,9 +4,10 @@
* *
* *
*/ */
import { VoerkaI18nFormatter, VoerkaI18nFormatters, VoerkaI18nFormattersLoader, VoerkaI18nLanguageFormatters, SupportedDateTypes, VoerkaI18nFormatterConfigs, Formatter } from './types'; import { VoerkaI18nFormatter, VoerkaI18nFormatters, VoerkaI18nFormattersLoader, VoerkaI18nLanguageFormatters, SupportedDateTypes, VoerkaI18nFormatterConfigs } from './types';
import { DataTypes } from './utils'; import { DataTypes } from './utils';
import { get as getByPath } from "flex-tools/object/get" import { get as getByPath } from "flex-tools/object/get"
import { isFunction } from 'flex-tools/typecheck/isFunction';
export interface VoerkaI18nScopeCache{ export interface VoerkaI18nScopeCache{
@ -41,7 +42,7 @@ export class VoerkaI18nFormatterRegistry{
set language(language:string){ set language(language:string){
this.#language = language this.#language = language
if(!(language in this.formatters)){ if(!(language in this.formatters)){
(this.formatters[language] as any) = Object.assign({},EmptyFormatters) (this.#formatters[language] as any) = Object.assign({},EmptyFormatters)
} }
this.#ready = typeof(this.#formatters[language]) != 'function' this.#ready = typeof(this.#formatters[language]) != 'function'
} }
@ -63,7 +64,7 @@ export class VoerkaI18nFormatterRegistry{
* *
使, 使,
*/ */
register(name:string, formatter:VoerkaI18nFormatter, {language = "*"}:{ language: string | string[] } ) { 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"); throw new TypeError("Formatter must be a function");
} }
@ -73,7 +74,7 @@ export class VoerkaI18nFormatterRegistry{
this.#formatters[lngName] = { } this.#formatters[lngName] = { }
} }
if(typeof(this.#formatters[lngName])!="function"){ if(typeof(this.#formatters[lngName])!="function"){
let lngFormatters = this.#formatters[lngName] as VoerkaI18nFormatters let lngFormatters = this.#formatters[lngName] as any
if (DataTypes.includes(name)) { if (DataTypes.includes(name)) {
if(!lngFormatters.$types){ if(!lngFormatters.$types){
lngFormatters.$types = {} lngFormatters.$types = {}
@ -168,7 +169,7 @@ export class VoerkaI18nFormatterRegistry{
*/ */
get(name:string,dataType?:SupportedDateTypes):VoerkaI18nFormatter | undefined{ get(name:string,dataType?:SupportedDateTypes):VoerkaI18nFormatter | undefined{
if(!this.#ready) throw new FormattersNotLoadedError(this.#language) if(!this.#ready) throw new FormattersNotLoadedError(this.#language)
const lngFormatters = this.#formatters[this.#language] as VoerkaI18nFormatters const lngFormatters = this.#formatters[this.#language] as any
if(dataType && (dataType in lngFormatters.$types!)){ if(dataType && (dataType in lngFormatters.$types!)){
return lngFormatters.$types![dataType] return lngFormatters.$types![dataType]
}else if(name in lngFormatters){ }else if(name in lngFormatters){

View File

@ -1,5 +1,7 @@
const { toNumber,isFunction } = require("../utils") import { assignObject } from 'flex-tools/object/assignObject';
const { Formatter } = require("../formatter") import { VoerkaI18nFormatterConfigs } from '../types';
import { toNumber } from "../utils"
import { Formatter } from "../formatter"
/** /**
* *
@ -10,11 +12,11 @@ const { Formatter } = require("../formatter")
* @param {...any} args * @param {...any} args
* @returns * @returns
*/ */
function dict(key, values) { function dict(key:string, values:any) {
if(key in values){ if(key in values){
return values[key] return values[key]
}else{ }else{
return ("default" in values) ? values[key] : value return ("default" in values) ? values[key] : values
} }
} }
@ -37,10 +39,8 @@ const { Formatter } = require("../formatter")
* @paran {String} next true/false,break,skip,break * @paran {String} next true/false,break,skip,break
* @param {*} config * @param {*} config
*/ */
const empty = Formatter(function(value,escapeValue,next,$config){ const empty = Formatter(function(value:any,escapeValue:any,next: 'break' | 'ignore',$config:VoerkaI18nFormatterConfigs){
if(next===false) next = 'break' let opts = assignObject({escape:"",next:'break',values:[]},$config)
if(next===true) next = 'skip'
let opts = Object.assign({escape:"",next:'break',values:[]},$config)
if(escapeValue!=undefined) opts.escape = escapeValue if(escapeValue!=undefined) opts.escape = escapeValue
let emptyValues = [undefined,null] let emptyValues = [undefined,null]
if(Array.isArray(opts.values)) emptyValues.push(...opts.values) if(Array.isArray(opts.values)) emptyValues.push(...opts.values)
@ -72,10 +72,10 @@ const empty = Formatter(function(value,escapeValue,next,$config){
* @param {*} config * @param {*} config
* @returns * @returns
*/ */
const error = Formatter(function(value,escapeValue,next,$config){ const error = Formatter(function(value:any,escapeValue:any,next:'break' | 'ignore',$config:VoerkaI18nFormatterConfigs){
if(value instanceof Error){ if(value instanceof Error){
try{ try{
let opts = Object.assign({escape:null,next:'break'},$config) let opts = assignObject({escape:null,next:'break'},$config)
if(escapeValue!=undefined) opts.escape = escapeValue if(escapeValue!=undefined) opts.escape = escapeValue
if(next!=undefined) opts.next = next if(next!=undefined) opts.next = next
return { return {
@ -100,7 +100,7 @@ const error = Formatter(function(value,escapeValue,next,$config){
* @param {*} prefix * @param {*} prefix
* @returns * @returns
*/ */
function prefix(value,prefix="") { export function prefix(value:any,prefix:string="") {
return prefix ? `${prefix}${value}` : value return prefix ? `${prefix}${value}` : value
} }
/** /**
@ -109,7 +109,7 @@ function prefix(value,prefix="") {
* @param {*} suffix * @param {*} suffix
* @returns * @returns
*/ */
function suffix(value,suffix="") { export function suffix(value:any,suffix:string="") {
return suffix ? `${value}${suffix}` : value return suffix ? `${value}${suffix}` : value
} }
@ -141,7 +141,7 @@ const FILE_SIZE_WHOLE_UNITS = ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "
* @param {*} brief * @param {*} brief
* @param {*} options * @param {*} options
*/ */
const filesize= Formatter((value,unit,brief=true,$config)=>{ export const filesize= Formatter((value:any,unit:string,brief:boolean=true,$config:VoerkaI18nFormatterConfigs)=>{
let v = toNumber(value) let v = toNumber(value)
let unitIndex let unitIndex
if(unit==undefined || unit=="auto"){ if(unit==undefined || unit=="auto"){
@ -164,13 +164,11 @@ const FILE_SIZE_WHOLE_UNITS = ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "
}) })
export default {
module.exports = {
dict, dict,
empty,
error,
prefix, prefix,
suffix, suffix,
filesize, filesize
error,
empty
} }

View File

@ -4,7 +4,6 @@
*/ */
import { Formatter } fom "../formatter"
import { dateFormatter,quarterFormatter,monthFormatter,weekdayFormatter,timeFormatter,relativeTimeFormatter } from "../datatypes/datetime" import { dateFormatter,quarterFormatter,monthFormatter,weekdayFormatter,timeFormatter,relativeTimeFormatter } from "../datatypes/datetime"
import { numberFormartter } from "../datatypes/numeric" import { numberFormartter } from "../datatypes/numeric"
import { currencyFormatter } from "../datatypes/currency" import { currencyFormatter } from "../datatypes/currency"
@ -149,11 +148,11 @@ export default {
}, },
// 默认数据类型的格式化器 // 默认数据类型的格式化器
$types: { $types: {
Date : dateFormatter, // Date : dateFormatter,
Null : value =>"", Null : (value: any) =>"",
Undefined: value =>"", Undefined: (value: any) =>"",
Error : value => "ERROR", Error : (value: any) => "ERROR",
Boolean : value =>value ? "True":"False", Boolean : (value: any) =>value ? "True":"False",
Number : numberFormartter Number : numberFormartter
}, },
// 以下是格式化定义 // 以下是格式化定义

View File

@ -5,9 +5,10 @@
import { chineseNumberFormatter,rmbFormater } from "../datatypes/chinese" import { chineseNumberFormatter,rmbFormater } from "../datatypes/chinese"
import {CN_DATETIME_UNITS, CN_MONTH_NAMES, CN_SHORT_MONTH_NAMES, CN_SHORT_WEEK_DAYS, CN_WEEK_DAYS } from "flex-tools/chinese/consts"
module.exports = { export default {
// 配置参数: 格式化器函数的最后一个参数就是该配置参数 // 配置参数: 格式化器函数的最后一个参数就是该配置参数
$config:{ $config:{
datetime : { datetime : {

View File

@ -1,3 +1,4 @@
// @ts-ignore
import replaceAll from "string.prototype.replaceall" import replaceAll from "string.prototype.replaceall"
replaceAll.shim() replaceAll.shim()

View File

@ -203,7 +203,9 @@ function getFormatter(scope:VoerkaI18nScope, activeLanguage:string, name:string)
} }
} }
} }
export type FormatterChecker = (value:any,config?:VoerkaI18nFormatterConfigs)=>any; export type FormatterChecker = ((value:any,config?:VoerkaI18nFormatterConfigs)=>any) & {
$name:string
}
/** /**
* Checker是一种特殊的格式化器 * Checker是一种特殊的格式化器
@ -242,7 +244,7 @@ function executeChecker(checker:FormatterChecker, value:any,scope:VoerkaI18nScop
* @param {FormatterDefineChain} formatters () * @param {FormatterDefineChain} formatters ()
* formatters [ [<格式化器名称>,[<参数>,<参数>,...],[<格式化器名称>,[<参数>,<参数>,...]],...] * formatters [ [<格式化器名称>,[<参数>,<参数>,...],[<格式化器名称>,[<参数>,<参数>,...]],...]
*/ */
function executeFormatter(value:any, formatters:FormatterDefineChain, scope:VoerkaI18nScope, template:string) { function executeFormatter(value:any, formatters:VoerkaI18nFormatter[], scope:VoerkaI18nScope, template:string) {
if (formatters.length === 0) return value; if (formatters.length === 0) return value;
let result = value; let result = value;
// 1. 空值检查 // 1. 空值检查
@ -258,11 +260,11 @@ function executeFormatter(value:any, formatters:FormatterDefineChain, scope:Voer
} }
// 2. 错误检查 // 2. 错误检查
const errorCheckerIndex = formatters.findIndex((func) => (func as any).$name === "error" ); const errorCheckerIndex = formatters.findIndex((func) => (func as any).$name === "error" );
let errorChecker; let errorChecker:FormatterChecker;
if (errorCheckerIndex != -1) { if (errorCheckerIndex != -1) {
errorChecker = formatters.splice(errorCheckerIndex, 1)[0] as unknown as FormatterChecker errorChecker = formatters.splice(errorCheckerIndex, 1)[0] as unknown as FormatterChecker
if (result instanceof Error) { if (result instanceof Error) {
result.formatter = formatter.$name; (result as any).formatter = errorChecker.$name;
const { value, next } = executeChecker(errorChecker, result,scope); const { value, next } = executeChecker(errorChecker, result,scope);
if (next == "break") { if (next == "break") {
return value; return value;
@ -275,13 +277,13 @@ function executeFormatter(value:any, formatters:FormatterDefineChain, scope:Voer
// 3. 分别执行格式化器函数 // 3. 分别执行格式化器函数
for (let formatter of formatters) { for (let formatter of formatters) {
try { try {
result = formatter(result, scope.activeFormatterConfig); result = formatter(result, [result],scope.activeFormatterConfig);
} catch (e:any) { } catch (e:any) {
e.formatter = formatter.$name; e.formatter = (formatter as any).$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 as any).$name}> for ${template}: ${e.message} `);
if (isFunction(errorChecker)) { if (isFunction(errorChecker!)) {
const { value, next } = executeChecker(errorChecker, result); const { value, next } = executeChecker(errorChecker!, result,scope);
if (next == "break") { if (next == "break") {
if (value !== undefined) result = value; if (value !== undefined) result = value;
break; break;
@ -329,13 +331,13 @@ function addDefaultFormatters(formatters:FormatterDefineChain) {
* *
*/ */
function wrapperFormatters(scope:VoerkaI18nScope, activeLanguage:string, formatters:FormatterDefineChain) { function wrapperFormatters(scope:VoerkaI18nScope, activeLanguage:string, formatters:FormatterDefineChain) {
let wrappedFormatters = []; let wrappedFormatters:VoerkaI18nFormatter[] = [];
addDefaultFormatters(formatters); addDefaultFormatters(formatters);
for (let [name, args] of formatters) { for (let [name, args] of formatters) {
let fn = getFormatter(scope, activeLanguage, name); let fn = getFormatter(scope, activeLanguage, name);
let formatter; let formatter;
if (isFunction(fn)) { if (isFunction(fn)) {
formatter = (value:any, config:VoerkaI18nFormatterConfigs) =>fn.call(scope.activeFormatterConfig, value, args, config); formatter = (value:any, args?:any[],config?:VoerkaI18nFormatterConfigs) =>fn.call(scope.activeFormatterConfig, value, args, config);
} else { } else {
// 格式化器无效或者没有定义时,查看当前值是否具有同名的原型方法,如果有则执行调用 // 格式化器无效或者没有定义时,查看当前值是否具有同名的原型方法,如果有则执行调用
// 比如padStart格式化器是String的原型方法不需要配置就可以直接作为格式化器调用 // 比如padStart格式化器是String的原型方法不需要配置就可以直接作为格式化器调用
@ -362,7 +364,7 @@ function wrapperFormatters(scope:VoerkaI18nScope, activeLanguage:string, formatt
* @param {*} value * @param {*} value
* @returns * @returns
*/ */
function getFormattedValue(scope:VoerkaI18nScope, activeLanguage:string, formatters:FormatterDefineChain, value, template) { function getFormattedValue(scope:VoerkaI18nScope, activeLanguage:string, formatters:FormatterDefineChain, value:any, template:string) {
// 1. 取得格式化器函数列表,然后经过包装以传入当前格式化器的配置参数 // 1. 取得格式化器函数列表,然后经过包装以传入当前格式化器的配置参数
const formatterFuncs = wrapperFormatters(scope, activeLanguage, formatters); const formatterFuncs = wrapperFormatters(scope, activeLanguage, formatters);
// 3. 执行格式化器 // 3. 执行格式化器

View File

@ -1,11 +1,9 @@
import { isFunction } from "flex-tools/typecheck/isFunction" import { isFunction } from "flex-tools/typecheck/isFunction"
import { deepMerge } from "flex-tools/object/deepMerge" import { deepMerge } from "flex-tools/object/deepMerge"
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 type { VoerkaI18nScope } from "./scope"
import type { VoerkaI18nLanguageDefine, VoerkaI18nLanguageFormatters, VoerkaI18nDefaultMessageLoader, VoerkaI18nFormatter, VoerkaI18nTypesFormatters } from "./types" import type { VoerkaI18nLanguageDefine, VoerkaI18nLanguageFormatters, VoerkaI18nDefaultMessageLoader, VoerkaI18nFormatter, VoerkaI18nTypesFormatters } from "./types"
import { SupportedDateTypes } from './types';
import { VoerkaI18nFormatterRegistry } from "./formatterRegistry" import { VoerkaI18nFormatterRegistry } from "./formatterRegistry"
// 默认语言配置 // 默认语言配置
@ -24,7 +22,7 @@ export interface VoerkaI18nManagerOptions {
debug?: boolean debug?: boolean
defaultLanguage: string defaultLanguage: string
activeLanguage: string activeLanguage: string
formatters: VoerkaI18nLanguageFormatters formatters?: VoerkaI18nLanguageFormatters
languages: VoerkaI18nLanguageDefine[] languages: VoerkaI18nLanguageDefine[]
} }
/** /**
@ -126,9 +124,9 @@ export class VoerkaI18nManager extends EventEmitter{
* @param {*} scope * @param {*} scope
*/ */
async register(scope:VoerkaI18nScope){ async register(scope:VoerkaI18nScope){
if(!(scope instanceof VoerkaI18nScope)){ // if(!(scope instanceof VoerkaI18nScope)){
throw new TypeError("Scope must be an instance of VoerkaI18nScope") // throw new TypeError("Scope must be an instance of VoerkaI18nScope")
} // }
this.#scopes.push(scope) this.#scopes.push(scope)
return await scope.refresh(this.activeLanguage) return await scope.refresh(this.activeLanguage)
} }

View File

@ -1,12 +1,9 @@
import { DataTypes } from "./utils"
import { isPlainObject } from "flex-tools/typecheck/isPlainObject" import { isPlainObject } from "flex-tools/typecheck/isPlainObject"
import { isFunction } from "flex-tools/typecheck/isFunction" import { isFunction } from "flex-tools/typecheck/isFunction"
import { deepClone } from "flex-tools/object/deepClone"
import { get as getByPath } from "flex-tools/object/get"
import { translate } from "./translate" import { translate } from "./translate"
import { deepMerge } from "flex-tools/object/deepMerge" import { deepMerge } from "flex-tools/object/deepMerge"
import { assignObject } from "flex-tools/object/assignObject" import { assignObject } from "flex-tools/object/assignObject"
import type {VoerkaI18nManager } from "./manager" import {VoerkaI18nManager } from "./manager"
import type { import type {
VoerkaI18nFormatterConfigs, VoerkaI18nFormatterConfigs,
VoerkaI18nDefaultMessageLoader, VoerkaI18nDefaultMessageLoader,
@ -18,10 +15,12 @@ import type {
VoerkaI18nLanguagePack, VoerkaI18nLanguagePack,
VoerkaI18nScopeCache, VoerkaI18nScopeCache,
VoerkaI18nTranslate, VoerkaI18nTranslate,
VoerkaI18nLoaders, VoerkaI18nMessageLoaders,
VoerkaI18nTypesFormatters, VoerkaI18nTypesFormatters,
VoerkaI18nFormatters, VoerkaI18nFormatters,
VoerkaI18nDynamicLanguageMessages, VoerkaI18nDynamicLanguageMessages,
VoerkaI18nLanguageMessagePack,
VoerkaI18nMessageLoader,
} from "./types" } from "./types"
import { VoerkaI18nFormatterRegistry } from './formatterRegistry'; import { VoerkaI18nFormatterRegistry } from './formatterRegistry';
import { InvalidLanguageError } from "./errors" import { InvalidLanguageError } from "./errors"
@ -30,13 +29,11 @@ export interface VoerkaI18nScopeOptions {
id?: string id?: string
debug?: boolean debug?: boolean
languages: VoerkaI18nLanguageDefine[] // 当前作用域支持的语言列表 languages: VoerkaI18nLanguageDefine[] // 当前作用域支持的语言列表
defaultLanguage: string // 默认语言名称 defaultLanguage?: string // 默认语言名称
activeLanguage: string // 当前语言名称 activeLanguage?: string // 当前语言名称
default: VoerkaI18nLanguageMessages // 默认语言包 messages: VoerkaI18nLanguageMessagePack // 当前语言包
messages: VoerkaI18nLanguageMessages // 当前语言包
idMap: Voerkai18nIdMap // 消息id映射列表 idMap: Voerkai18nIdMap // 消息id映射列表
formatters: VoerkaI18nLanguageFormatters // 当前作用域的格式化函数列表{<lang>: {$types,$config,[格式化器名称]: () => {},[格式化器名称]: () => {}}} formatters: VoerkaI18nLanguageFormatters // 当前作用域的格式化函数列表{<lang>: {$types,$config,[格式化器名称]: () => {},[格式化器名称]: () => {}}}
loaders: VoerkaI18nLoaders; // 异步加载语言文件的函数列表
} }
export class VoerkaI18nScope { export class VoerkaI18nScope {
@ -48,58 +45,53 @@ export class VoerkaI18nScope {
#activeFormatters:VoerkaI18nFormatters = {} #activeFormatters:VoerkaI18nFormatters = {}
#activeFormatterConfig: VoerkaI18nFormatterConfigs={} #activeFormatterConfig: VoerkaI18nFormatterConfigs={}
#cache:VoerkaI18nScopeCache #cache:VoerkaI18nScopeCache
#messages:VoerkaI18nLanguageMessages = {}
#formatterRegistry:VoerkaI18nFormatterRegistry #formatterRegistry:VoerkaI18nFormatterRegistry
constructor(options:VoerkaI18nScopeOptions, callback:(e?:Error)=>void) { #defaultLanguage:string ='zh'
#activeLanguage:string='zh'
#currentMessages:VoerkaI18nLanguageMessages = {} // 当前语言包
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)),
debug : false, debug : false,
languages : {}, // 当前作用域支持的语言列表 languages : {}, // 当前作用域支持的语言列表
defaultLanguage: "zh", // 默认语言名称 messages : {}, // 所有语言包={[language]:VoerkaI18nLanguageMessages}
activeLanguage : "zh", // 当前语言名称
default : {}, // 默认语言包
messages : {}, // 当前语言包
idMap : {}, // 消息id映射列表 idMap : {}, // 消息id映射列表
formatters : {}, // 当前作用域的格式化函数列表{<lang>: {$types,$config,[格式化器名称]: () => {},[格式化器名称]: () => {}}} formatters : {}, // 当前作用域的格式化函数列表{<lang>: {$types,$config,[格式化器名称]: () => {},[格式化器名称]: () => {}}}
loaders : {} // 异步加载语言文件的函数列表
},options) as Required<VoerkaI18nScopeOptions> },options) as Required<VoerkaI18nScopeOptions>
this.#patchMessages = {}; // 语言包补丁信息{<language>: {....},<language>:{....}}
this.#refreshing = false; // 正在加载语言包标识
// 用来缓存格式化器的引用,当使用格式化器时可以直接引用,减少检索遍历 // 用来缓存格式化器的引用,当使用格式化器时可以直接引用,减少检索遍历
this.#cache = { this.#cache = {
activeLanguage : this.#options.activeLanguage, activeLanguage : this.#options.activeLanguage,
typedFormatters: {}, typedFormatters: {},
formatters : {}, formatters : {},
}; };
// 初始化
this._initiLanguages() this._initiLanguages()
// 如果不存在全局VoerkaI18n实例说明当前Scope是唯一或第一个加载的作用域则自动创建全局VoerkaI18n实例 // 如果不存在全局VoerkaI18n实例说明当前Scope是唯一或第一个加载的作用域则自动创建全局VoerkaI18n实例
if (!globalThis.VoerkaI18n) { if (!globalThis.VoerkaI18n) {
const { VoerkaI18nManager } = require("./");
globalThis.VoerkaI18n = new VoerkaI18nManager({ globalThis.VoerkaI18n = new VoerkaI18nManager({
debug : this.debug, debug : this.debug,
defaultLanguage: this.defaultLanguage, defaultLanguage: this.#defaultLanguage,
activeLanguage : this.activeLanguage, activeLanguage : this.#activeLanguage,
languages : options.languages, languages : options.languages,
}); });
} }
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.#formatterRegistry = new VoerkaI18nFormatterRegistry() this.#formatterRegistry = new VoerkaI18nFormatterRegistry()
this.loadFormatters(this.activeLanguage) // 初始化活动的格式化器
this._mergePatchedMessages(); // 从本地缓存中读取并合并补丁语言包 this._mergePatchedMessages(); // 从本地缓存中读取并合并补丁语言包
this._patch(this.messages, this.activeLanguage); // 延后执行补丁命令,该命令会向远程下载补丁包 this._patch(this.#currentMessages, this.activeLanguage); // 延后执行补丁命令,该命令会向远程下载补丁包
this.register(callback); // 在全局注册作用域 if(callback) this.register(callback); // 在全局注册作用域
} }
get id() {return this.#options.id;} // 作用域唯一id get id() {return this.#options.id;} // 作用域唯一id
get debug() {return this.#options.debug;} // 调试开关 get debug() {return this.#options.debug;} // 调试开关
get defaultLanguage() {return this.#options.defaultLanguage;} // 默认语言名称 get defaultLanguage() {return this.#global.defaultLanguage;} // 默认语言名称
get activeLanguage() {return this.#global.activeLanguage;} // 默认语言名称 get activeLanguage() {return this.#global.activeLanguage;} // 默认语言名称
get default() {return this.#options.default;} // 默认语言包 // 默认语言包,只能静态语言包,不能是动态语言包
get messages() {return this.#options.messages; } // 当前语言包 get default() {return this.#options.messages[this.#defaultLanguage] as VoerkaI18nLanguageMessages;}
get current() {return this.#currentMessages;} // 当前语言包
get messages() {return this.#options.messages; } // 所有语言包
get idMap() {return this.#options.idMap;} // 消息id映射列表 get idMap() {return this.#options.idMap;} // 消息id映射列表
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 global() { return this.#global;} // 引用全局VoerkaI18n配置注册后自动引用 get global() { return this.#global;} // 引用全局VoerkaI18n配置注册后自动引用
get formatters() { return this.#formatterRegistry;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}} get formatters() { return this.#formatterRegistry;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}}
get activeFormatters() {return this.#formatterRegistry.formatters} // 当前作用域激活的格式化器定义 {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () = >{}} get activeFormatters() {return this.#formatterRegistry.formatters} // 当前作用域激活的格式化器定义 {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () = >{}}
@ -112,19 +104,40 @@ export class VoerkaI18nScope {
/** /**
* *
* - en配置为默认回退语言 * - en配置为默认回退语言
* -
*/ */
_initiLanguages(){ _initiLanguages(){
if(!Array.isArray(this.languages)){ if(!Array.isArray(this.languages)){
console.warn("[VoerkaI18n] invalid languages config,use default languages config instead.") console.warn("[VoerkaI18n] invalid languages config,use default languages config instead.")
this.#options.languages = [ this.#options.languages = [
{name: "zh",title: "中文"}, {name: "zh",title: "中文",default:true,active:true},
{name: "en",title: "英文"} {name: "en",title: "英文"}
] ]
} }
// 将en配置为默认回退语言
this.languages.forEach(language=>{ this.languages.forEach(language=>{
if(!language.fallback) language.fallback = "en" if(!language.fallback) language.fallback = "en"
if(language.default) this.#defaultLanguage = language.name
if(language.active) this.#activeLanguage = language.name
}) })
// 确保提供了有效的默认语言和活动语言
const lanMessages = this.#options.messages
if(!(this.#defaultLanguage in lanMessages)) {
this.#defaultLanguage = Object.keys(lanMessages)[0]
}
if(!(this.#activeLanguage in lanMessages)){
this.#activeLanguage = this.#defaultLanguage
}
if(!(this.#defaultLanguage in lanMessages)){
throw new Error("[VoerkaI18n] invalid <defaultLanguage> config, messages not specified.")
}
// 初始化时,默认和激活的语言包只能是静态语言包,不能是动态语言包
// 因为初始化时激活语言需要马上显示,如果是异步语言包,会导致显示延迟
if(isFunction(this.messages[this.#defaultLanguage])){
throw new Error("[VoerkaI18n] invalid <defaultLanguage> config, messages must be static.")
}
this.#currentMessages = this.messages[this.#activeLanguage] as VoerkaI18nLanguageMessages
} }
/** /**
@ -187,9 +200,9 @@ export class VoerkaI18nScope {
* } * }
*/ */
private loadFormatters(newLanguage:string){ private loadFormatters(newLanguage:string){
this.#formatterRegistry.language = newLanguage
// 初始化格式化器 // 初始化格式化器
this.formatters.loadInitials(this.#options.formatters) this.formatters.loadInitials(this.#options.formatters)
this.#formatterRegistry.language = newLanguage
if(this.#options.formatters) if(this.#options.formatters)
// 将配置中的指定为全局的格式化器注册到全局 // 将配置中的指定为全局的格式化器注册到全局
Object.entries(this.#options.formatters).forEach(([langName,formatters])=>{ Object.entries(this.#options.formatters).forEach(([langName,formatters])=>{
@ -308,29 +321,31 @@ export class VoerkaI18nScope {
* 退 * 退
*/ */
private _fallback() { private _fallback() {
this.#options.messages = this.default; this.#currentMessages = this.default;
this.#options.activeLanguage = this.defaultLanguage; this.#activeLanguage = this.defaultLanguage;
} }
/** /**
* : *
*
*
* - {} * - {}
* - Promise<VoerkaI18nLanguageMessages> * - Promise<VoerkaI18nLanguageMessages>
* * -
* *
* *
* @param language * @param language
* @returns * @returns
*/ */
private async loadLanguageMessages(language:string):Promise<VoerkaI18nLanguageMessages>{ private async loadLanguageMessages(language:string):Promise<VoerkaI18nLanguageMessages>{
if(!this.hasLanguage(language)) throw new InvalidLanguageError(`Not found language <${language}>`) // if(!this.hasLanguage(language)) throw new InvalidLanguageError(`Not found language <${language}>`)
// 非默认语言可以是:语言包对象,也可以是一个异步加载语言包文件,加载器是一个异步函数 // 非默认语言可以是:语言包对象,也可以是一个异步加载语言包文件,加载器是一个异步函数
// 如果没有加载器,则无法加载语言包,因此回退到默认语言 // 如果没有加载器,则无法加载语言包,因此回退到默认语言
let loader = this.loaders[language]; let loader = this.messages[language];
let messages:VoerkaI18nLanguageMessages = {} let messages:VoerkaI18nLanguageMessages = {}
if (isPlainObject(loader)) { // 静态语言包 if (isPlainObject(loader)) { // 静态语言包
messages = loader as unknown as VoerkaI18nLanguageMessages; messages = loader as unknown as VoerkaI18nLanguageMessages;
} else if (isFunction(loader)) { // 语言包异步chunk } else if (isFunction(loader)) { // 语言包异步chunk
messages = (await loader()).default; messages = (await (loader as VoerkaI18nMessageLoader))().default;
} else if (isFunction(this.global.defaultMessageLoader)) { } else if (isFunction(this.global.defaultMessageLoader)) {
// 从远程加载语言包:如果该语言没有指定加载器,则使用全局配置的默认加载器 // 从远程加载语言包:如果该语言没有指定加载器,则使用全局配置的默认加载器
const loadedMessages = (await this.global.loadMessagesFromDefaultLoader(language,this)) as unknown as VoerkaI18nDynamicLanguageMessages; const loadedMessages = (await this.global.loadMessagesFromDefaultLoader(language,this)) as unknown as VoerkaI18nDynamicLanguageMessages;
@ -344,6 +359,8 @@ export class VoerkaI18nScope {
$remote : true // 添加一个标识,表示该语言包是从远程加载的 $remote : true // 添加一个标识,表示该语言包是从远程加载的
},this.default,loadedMessages); // 合并默认语言包和动态语言包,这样就可以局部覆盖默认语言包 },this.default,loadedMessages); // 合并默认语言包和动态语言包,这样就可以局部覆盖默认语言包
} }
}else{
throw new Error(`Not found loader for language <${language}>`)
} }
return messages return messages
} }
@ -356,8 +373,8 @@ export class VoerkaI18nScope {
if (!newLanguage) newLanguage = this.activeLanguage; if (!newLanguage) newLanguage = this.activeLanguage;
// 默认语言:由于默认语言采用静态加载方式而不是异步块,因此只需要简单的替换即可 // 默认语言:由于默认语言采用静态加载方式而不是异步块,因此只需要简单的替换即可
if (newLanguage === this.defaultLanguage) { if (newLanguage === this.defaultLanguage) {
this.#options.messages = this.default; this.#currentMessages = this.default;
await this._patch(this.#options.messages, newLanguage); // 异步补丁 await this._patch(this.#currentMessages, newLanguage); // 异步补丁
await this._changeFormatters(newLanguage); await this._changeFormatters(newLanguage);
this.#refreshing = false this.#refreshing = false
return; return;
@ -365,12 +382,12 @@ export class VoerkaI18nScope {
try{ try{
let messages = await this.loadLanguageMessages(newLanguage) let messages = await this.loadLanguageMessages(newLanguage)
if(messages){ if(messages){
this.#options.messages = messages this.#currentMessages = messages
this.#options.activeLanguage = newLanguage; this.#activeLanguage = newLanguage;
// 打语言包补丁, 如果是从远程加载语言包则不需要再打补丁了 // 打语言包补丁, 如果是从远程加载语言包则不需要再打补丁了
// 因为远程加载的语言包已经是补丁过的了 // 因为远程加载的语言包已经是补丁过的了
if(!messages.$remote) { if(!messages.$remote) {
await this._patch(this.#options.messages, newLanguage); await this._patch(this.#currentMessages, newLanguage);
} }
// 切换到对应语言的格式化器 // 切换到对应语言的格式化器
await this._changeFormatters(newLanguage); await this._changeFormatters(newLanguage);
@ -458,6 +475,7 @@ export class VoerkaI18nScope {
on(callback:Function) {return this.#global.on(callback); } on(callback:Function) {return this.#global.on(callback); }
off(callback:Function) {return this.#global.off(callback); } off(callback:Function) {return this.#global.off(callback); }
offAll() {return this.#global.offAll();} offAll() {return this.#global.offAll();}
async change(language:string) { async change(language:string) {
await this.#global.change(language); await this.#global.change(language);
} }

View File

@ -125,7 +125,7 @@ export function translate(this:VoerkaI18nScope,message:string,...args:any[]):str
// 2.2 从当前语言包中取得翻译文本模板字符串 // 2.2 从当前语言包中取得翻译文本模板字符串
// 如果没有启用babel插件将源文本转换为msgId需要先将文本内容转换为msgId // 如果没有启用babel插件将源文本转换为msgId需要先将文本内容转换为msgId
let msgId = isMessageId(result) ? result : scope.idMap[result] let msgId = isMessageId(result) ? result : scope.idMap[result]
result = scope.messages[msgId] || result result = scope.current[msgId] || result
} }
// 2. 处理复数 // 2. 处理复数
// 经过上面的处理content可能是字符串或者数组 // 经过上面的处理content可能是字符串或者数组

View File

@ -8,9 +8,13 @@ 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 $config?: VoerkaI18nTypesFormatterConfigs
} }
export type VoerkaI18nLanguageMessagePack = Record<string, VoerkaI18nLanguageMessages | VoerkaI18nMessageLoader>
export type VoerkaI18nDynamicLanguageMessages = Record<string, string | string[]> & { export type VoerkaI18nDynamicLanguageMessages = Record<string, string | string[]> & {
$config?: VoerkaI18nTypesFormatterConfigs $config?: VoerkaI18nTypesFormatterConfigs
} }
@ -23,38 +27,36 @@ export type Voerkai18nIdMap = Record<string, number>
export interface VoerkaI18nLanguageDefine { export interface VoerkaI18nLanguageDefine {
name: string name: string
title?: string title?: string
default?: boolean default?: boolean // 是否默认语言
active?: boolean
fallback?: string fallback?: string
} }
export type VoerkaI18nFormatterConfigs = Record<string, any> export type VoerkaI18nFormatterConfigs = Record<string, any>
export type VoerkaI18nFormatter = (value: string,args: any[],$config:VoerkI18nFormatterConfigs) => string export type VoerkaI18nFormatter = ((value: string,args?: any[],$config?:VoerkI18nFormatterConfigs) => string)
export type VoerkaI18nTypesFormatters=Record<SupportedDateTypes | string, VoerkaI18nFormatter> export type VoerkaI18nTypesFormatters=Partial<Record<SupportedDateTypes, VoerkaI18nFormatter>>
export type VoerkaI18nTypesFormatterConfigs= Record<SupportedDateTypes | string, Record<string,any>> export type VoerkaI18nTypesFormatterConfig= Partial<Record<string, any>>
export type VoerkaI18nTypesFormatterConfigs= Partial<Record<SupportedDateTypes | string, Record<string,any>>>
export type VoerkaI18nFormattersLoader = (()=>Promise<VoerkaI18nFormatters>) export type VoerkaI18nFormattersLoader = (()=>Promise<VoerkaI18nFormatters>)
// 每一个语言的格式化器定义={$types:{...},$config:{...},[格式化器名称]: () => {},[格式化器名称]: () => {} // 每一个语言的格式化器定义={$types:{...},$config:{...},[格式化器名称]: () => {},[格式化器名称]: () => {}
// 在formatters/xxxx.ts里面进行配置 // 在formatters/xxxx.ts里面进行配置
export type VoerkaI18nFormatters = ({ export type VoerkaI18nFormatters = {
global?: boolean | Omit<VoerkaI18nFormatters,'global'> // 是否全局格式化器 global?: boolean | Omit<VoerkaI18nFormatters,'global'> // 是否全局格式化器
$types?:VoerkaI18nTypesFormatters $types?:VoerkaI18nTypesFormatters
$config?: VoerkaI18nTypesFormatterConfigs $config?: VoerkaI18nTypesFormatterConfigs
} & { } | {
[DataTypeName in SupportedDateTypes]?: VoerkaI18nFormatter
} & {
[key: string]: VoerkaI18nFormatter [key: string]: VoerkaI18nFormatter
}) }
// 包括语言的{"*":{...},zh:{...},en:{...}} // 包括语言的{"*":{...},zh:{...},en:{...}}
// 声明格式化器 // 声明格式化器
export type VoerkaI18nLanguageFormatters = Record<string,VoerkaI18nFormatters | VoerkaI18nFormattersLoader> export type VoerkaI18nLanguageFormatters = Record<string,VoerkaI18nFormatters | VoerkaI18nMessageLoader>
export type VoerkaI18nMessageLoader = () => Awaited<Promise<any>>
// export interface VoerkaI18nMessageLoaders {
export type VoerkaI18nLoader = () => Awaited<Promise<any>> [key: string]: VoerkaI18nMessageLoader
export interface VoerkaI18nLoaders {
[key: string]: VoerkaI18nLoader
} }
export type VoerkaI18nDefaultMessageLoader = (this:VoerkaI18nScope,newLanguage:string,scope:VoerkaI18nScope)=>Promise<VoerkaI18nLanguageMessages> export type VoerkaI18nDefaultMessageLoader = (this:VoerkaI18nScope,newLanguage:string,scope:VoerkaI18nScope)=>Promise<VoerkaI18nLanguageMessages>
@ -145,14 +147,13 @@ export interface FormatterDefine {
} }
// 创建格式化器 // 创建格式化器
export type CreateFormatterType = (fn: Function, options: CreateFormatterOptions, defaultParams: Record<string, any>) => FormatterDefine export type CreateFormatterType = (fn: Function, options: CreateFormatterOptions, defaultParams: Record<string, any>) => FormatterDefine
export var createFormatter: CreateFormatterType // export var createFormatter: CreateFormatterType
export var Formatter: CreateFormatterType // export var Formatter: CreateFormatterType
export type CreateFlexFormatterType = (fn: Function, options: CreateFormatterOptions, defaultParams: Record<string, any>) => FormatterDefine export type CreateFlexFormatterType = (fn: Function, options: CreateFormatterOptions, defaultParams: Record<string, any>) => FormatterDefine
export var createFlexFormatter: CreateFlexFormatterType // export var createFlexFormatter: CreateFlexFormatterType
export var FlexFormatter: CreateFlexFormatterType // export var FlexFormatter: CreateFlexFormatterType
export var getDataTypeName: (value: any) => string // export var getDataTypeName: (value: any) => string
export var toDate: (value: any) => Date | number // export var toDate: (value: any) => Date | number
export var toNumber: (value: any, defaultValue: number) => number // export var toBoolean: (value: any) => boolean
export var toBoolean: (value: any) => boolean

View File

@ -44,12 +44,12 @@ export const DataTypes = ["String","Number","Boolean","Object","Array","Functio
/** /**
* *
*/ */
export function toNumber(value:any,defualt=0):number { export function toNumber(value:any):number {
try { try {
if (isNumber(value)) { if (isNumber(value)) {
return parseFloat(value) return parseFloat(value)
} else { } else {
return defualt return 0
} }
} catch { } catch {
return value return value