This commit is contained in:
wxzhang 2023-04-13 22:04:27 +08:00
parent e060827dc1
commit d945c2092d
8 changed files with 255 additions and 261 deletions

View File

@ -87,6 +87,8 @@ const formatters ={
let scope:VoerkaI18nScope;
describe("所有测试", () => {
beforeAll(async ()=>{
return new Promise((resolve)=>{
scope = new VoerkaI18nScope({
@ -103,7 +105,6 @@ beforeAll(async ()=>{
beforeEach(async ()=>{
await scope.change("zh")
})
describe("VoerkaI18nScope", () => {
test("成功创建实例", () => {
expect(scope).toBeInstanceOf(VoerkaI18nScope)
@ -112,7 +113,6 @@ describe("VoerkaI18nScope", () => {
expect(scope.messages).toEqual(messages)
expect(scope.default).toEqual(zhMessages)
expect(scope.current).toEqual(zhMessages)
expect(scope.idMap).toEqual(idMap)
// 全局管理器
expect(scope.global).toBeInstanceOf(VoerkaI18nManager)
})
@ -125,7 +125,6 @@ describe("VoerkaI18nScope", () => {
expect(scope.messages).toEqual(messages)
expect(scope.default).toEqual(zhMessages)
expect(scope.current).toEqual(enMessages)
expect(scope.idMap).toEqual(idMap)
resolve()
})
scope.change("en")
@ -325,3 +324,4 @@ describe('插值变量格式化器', () => {
describe('内置格式化器', () => {
})
})

View File

@ -5,12 +5,12 @@
*/
import { FlexFormatter, IFormatter } from '../formatter'
import { FlexFormatter, Formatter } from '../formatter'
import { toChineseNumber } from "flex-tools/chinese/toChineseNumber"
import { toChineseCurrency } from "flex-tools/chinese/toChineseCurrency"
export const chineseNumberFormatter = Formatter((value: any, [isBig]:[isBig: boolean], $config) => {
export const chineseNumberFormatter = Formatter<any,[isBig: boolean]>((value: any, [isBig], config: any) => {
return toChineseNumber(value, isBig) as string
}, {
params: ["isBig"]

View File

@ -4,14 +4,14 @@
*/
import { toDate } from '../utils'
import { IFormatter } from '../formatter';
import { formatDateTime } from "flex-tools/misc/formatDateTime"
import { relativeTime } from "flex-tools/misc/relativeTime"
import { assignObject } from "flex-tools/object/assignObject"
import { isFunction } from "flex-tools/typecheck/isFunction"
import { Formatter } from '../formatter'
function formatTime(value:number ,template="HH:mm:ss"){
function formatTime(value:number ,[template]="HH:mm:ss"){
return formatDateTime(value,template,{})
}
@ -21,8 +21,8 @@ function formatTime(value:number ,template="HH:mm:ss"){
*
* 1. format参数
* 2. format参数取值可以是若干预设的值long,short等
* 3. format值时$config[configKey]$config[configKey][format]
* 4. !(format in $config[configKey])format值是一个模板字符串
* 3. format值时config[configKey]config[configKey][format]
* 4. !(format in config[configKey])format值是一个模板字符串
* 5. format in presets, presets[format ](value)=>{....}
*
**/
@ -30,15 +30,15 @@ export type FormatterTransformer = (value:any,format:string)=>string
export function createDateTimeFormatter(options={},transformer:FormatterTransformer){
let opts = assignObject({presets:{}},options)
return Formatter(function(this:any,value:any,[format]:any[],$config:Record<string,any>){
return Formatter(function(this:any,value:any,[format]:any[],config:Record<string,any>){
if((format in opts.presets) && isFunction(opts.presets[format])){
return opts.presets[format](value)
}else if((format in $config)){
format = $config[format]
}else if((format in config)){
format = config[format]
}else if(format == "number"){
return value
}
try{// this指向的是activeFormatter.$config
try{// this指向的是activeFormatter.config
return format==null ? value : transformer.call(this,value,format)
}catch(e){
return value
@ -50,7 +50,7 @@ export function createDateTimeFormatter(options={},transformer:FormatterTransfor
/**
*
* - format取值local,long,short,iso,gmt,utc,<模板字符串>
* - $config.datetime.date.format指定
* - config.datetime.date.format指定
*/
export const dateFormatter = createDateTimeFormatter({
normalize: toDate,
@ -104,7 +104,7 @@ export const weekdayFormatter = createDateTimeFormatter({
/**
*
* - format取值local,long,short,timestamp,<模板字符串>
* - $config.datetime.time.format指定
* - config.datetime.time.format指定
*/
export const timeFormatter = createDateTimeFormatter({
normalize : toDate,
@ -121,9 +121,9 @@ export const timeFormatter = createDateTimeFormatter({
* @param {*} value
* @param {*} baseTime
*/
export const relativeTimeFormatter = Formatter((value:any,[baseTime]:[Date],$config:any)=>{
//const { units,now,before,base = Date.now() , after } = $config
return relativeTime(value, $config)
export const relativeTimeFormatter = Formatter<any,[Date]>((value:any,[baseTime],config:any)=>{
//const { units,now,before,base = Date.now() , after } = config
return relativeTime(value,baseTime,config)
},{
normalize:toDate,
params:["base"],

View File

@ -9,12 +9,12 @@
*
*/
import { toNumber } from "../utils"
import { IFormatter } from "../formatter"
import { toCurrency } from "./currency"
import { VoerkaI18nFormatter } from '../types';
import { Formatter } from "../formatter";
export const numberFormartter = Formatter(function(value:any,[precision,division]:[number,number],$config:any){
return toCurrency(value, { division, precision},$config)
export const numberFormartter = Formatter<any,any[]>(function(value:any,[precision,division],config:Record<string,any>){
return toCurrency(value, { division, precision},config)
},{
normalize: toNumber,
params:["precision","division"],

View File

@ -16,7 +16,7 @@ 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 { VoerkaI18nFormatterConfigs } from './types';
import { VoerkaI18nFormatterConfigs, VoerkaI18nFormatter, Primitive } from './types';
/**
使便
@ -271,18 +271,14 @@ function parseFormaterParams(strParams: string): any[] {
* @returns
*/
export type VarValueType = string | Error | undefined | null
export type IFormatter = (this: any, value: VarValueType, args: any[],config:VoerkaI18nFormatterConfigs) => string
export interface FormatterOptions {
export interface CreateFormatterOptions {
normalize?: (value: string) => any // 对输入值进行规范化处理,如进行时间格式化时,为了提高更好的兼容性,支持数字时间戳/字符串/Date等需要对输入值进行处理如强制类型转换等
params?: Record<string, any> | null, // 可选的声明参数顺序如果是变参的则需要传入null
configKey?: string // 声明该格式化器在$config中的路径支持简单的使用.的路径语法
}
export function createFormatter(fn: IFormatter, options?: FormatterOptions, defaultParams?: Record<string, any>) {
export function createFormatter<Value=any,Args extends any[] = any[],Return=any>(fn: VoerkaI18nFormatter, options?: CreateFormatterOptions, defaultParams?: Record<string, any>) {
let opts = assignObject({
normalize: null, // 对输入值进行规范化处理,如进行时间格式化时,为了提高更好的兼容性,支持数字时间戳/字符串/Date等需要对输入值进行处理如强制类型转换等
params: null, // 可选的声明参数顺序如果是变参的则需要传入null
@ -291,7 +287,7 @@ export function createFormatter(fn: IFormatter, options?: FormatterOptions, defa
// 最后一个参数是传入activeFormatterConfig参数
// 并且格式化器的this指向的是activeFormatterConfig
const $formatter = function (this: any, value: string, args: any[],$config:Record<string,any>) {
const $formatter = function (this: any, value: Value, args: Args,config:Record<string,any>):Return {
let finalValue = value
// 1. 输入值规范处理,主要是进行类型转换,确保输入的数据类型及相关格式的正确性,提高数据容错性
if (isFunction(opts.normalize)) {
@ -312,7 +308,6 @@ export function createFormatter(fn: IFormatter, options?: FormatterOptions, defa
})
// 4. 将翻译函数执行格式化器时传入的参数覆盖默认参数
for (let i = 0; i < finalArgs.length; i++) {
if (i == args.length - 1) break // 最后一参数是配置
if (args[i] !== undefined) finalArgs[i] = args[i]
}
}
@ -339,14 +334,14 @@ export function createFormatter(fn: IFormatter, options?: FormatterOptions, defa
* @param {*} options
* @param {*} defaultParams
*/
export const createFlexFormatter = function (fn: IFormatter, options: FormatterOptions, defaultParams?: Record<string, any>) {
export const createFlexFormatter = function<Value=any,Args extends any[]=any[],Return=any>(fn: VoerkaI18nFormatter, options: CreateFormatterOptions, defaultParams?: Record<string, any>) {
const opts = assignObject({
params: {}
}, options)
const $flexFormatter = Formatter(function (this: any, value: VarValueType,args: string[],$config:Record<string,any> ) {
const $flexFormatter = Formatter<Value,Args,Return>(function (this: any, value: any,args,config:Record<string,any> ) {
// 2. 从语言配置中读取默认参数
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]
return r
}, {})
// 3. 从格式化器中传入的参数具有最高优先级,覆盖默认参数
@ -357,13 +352,12 @@ export const createFlexFormatter = function (fn: IFormatter, options: FormatterO
if (args[i] !== undefined) finalParams[opts.params[i]] = args[i]
}
}
return fn.call(this, value, finalParams, $config)
return fn.call(this, value, finalParams, config)
}, { ...options, params: null }) // 变参工式化器需要指定params=null
return $flexFormatter
}
export const Formatter = createFormatter
export const FlexFormatter = createFlexFormatter

View File

@ -1,7 +1,7 @@
import { assignObject } from 'flex-tools/object/assignObject';
import { VoerkaI18nFormatterConfigs } from '../types';
import { toNumber } from "../utils"
import { IFormatter, VarValueType,Formatter } from "../formatter"
import { Formatter } from "../formatter"
/**
*
@ -39,7 +39,7 @@ export function dict(key:string, values:any) {
* @paran {String} next true/false,break,skip,break
* @param {*} config
*/
export const empty = Formatter(function(value:VarValueType,[escapeValue,next]:[escapeValue:any,next: 'break' | 'ignore'],config:VoerkaI18nFormatterConfigs){
export const empty = Formatter<any,[escapeValue:any,next: 'break' | 'ignore']>(function(value:any,[escapeValue,next],config:VoerkaI18nFormatterConfigs){
let opts = assignObject({escape:"",next:'break',values:[]},config)
if(escapeValue!=undefined) opts.escape = escapeValue
let emptyValues = [undefined,null]
@ -72,10 +72,10 @@ export const empty = Formatter(function(value:VarValueType,[escapeValue,next]:[e
* @param {*} config
* @returns
*/
export const error = Formatter(function(value:string,escapeValue:any,next:'break' | 'ignore',$config:VoerkaI18nFormatterConfigs){
export const error = Formatter<any,[escapeValue:any,next:'break' | 'ignore']>(function(value,[escapeValue,next],config:VoerkaI18nFormatterConfigs){
if(typeof(value)=='object' && (value instanceof Error)){
try{
let opts = assignObject({escape:null,next:'break'},$config)
let opts = assignObject({escape:null,next:'break'},config)
if(escapeValue!=undefined) opts.escape = escapeValue
if(next!=undefined) opts.next = next
return {
@ -141,7 +141,7 @@ const FILE_SIZE_WHOLE_UNITS = ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "
* @param {*} brief
* @param {*} options
*/
export const filesize= Formatter((value:string,unit:string,brief:boolean=true,$config:VoerkaI18nFormatterConfigs)=>{
export const filesize= Formatter<any,[unit:string,brief:boolean]>((value:string,[unit,brief],config:VoerkaI18nFormatterConfigs)=>{
let v = toNumber(value)
let unitIndex
if(unit==undefined || unit=="auto"){
@ -151,13 +151,13 @@ const FILE_SIZE_WHOLE_UNITS = ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "
unitIndex =["B","BYTE","BYTES"].includes(unit) ? 0 : FILE_SIZE_BRIEF_UNITS.indexOf(unit)
}
if(unitIndex<0 || unitIndex>=FILE_SIZE_BRIEF_UNITS.length) unitIndex= 0
let result = (unitIndex == 0 ? v : v / FILE_SIZE_SECTIONS[unitIndex]).toFixed($config.precision)
let result = (unitIndex == 0 ? v : v / FILE_SIZE_SECTIONS[unitIndex]).toFixed(config.precision)
if( unitIndex>0 && (v % FILE_SIZE_SECTIONS[unitIndex])!==0) result = result+"+"
// 去除尾部的0
while(["0","."].includes(result[result.length-1])){
result = result.substring(0, result.length-2)
}
return brief ? `${result} ${$config.brief[unitIndex]}` : `${result} ${$config.whole[unitIndex]}`
return brief ? `${result} ${config.brief[unitIndex]}` : `${result} ${config.whole[unitIndex]}`
},{
params:["unit","brief"],
configKey:"fileSize"

View File

@ -5,7 +5,7 @@
import enFormatters from "./en"
import zhFormatters from "./zh"
import defaultFormatters from "./default"
import * as defaultFormatters from "./default"
export default {
"*":defaultFormatters,

View File

@ -35,7 +35,7 @@ export interface VoerkaI18nLanguageDefine {
export type VoerkaI18nFormatterConfigs = Record<string, any>
export type VoerkaI18nFormatter = ((value: string,args: any[],config: VoerkI18nFormatterConfigs) => string)
export type VoerkaI18nFormatter = ((value: any,args: any[],config: VoerkI18nFormatterConfigs) => any)
export type VoerkaI18nTypesFormatters=Partial<Record<SupportedDateTypes, VoerkaI18nFormatter>>
export type VoerkaI18nTypesFormatterConfig= Partial<Record<string, any>>
export type VoerkaI18nTypesFormatterConfigs= Partial<Record<SupportedDateTypes | string, Record<string,any>>>