更新fortmatters

This commit is contained in:
wxzhang 2022-08-13 22:08:31 +08:00
parent b8f59c43bc
commit 6be922d6fe
7 changed files with 287 additions and 164 deletions

View File

@ -1,4 +1,4 @@
import { toNumber,isFunction } from "../utils"
const { toNumber,isFunction } = require("../utils")
/**
@ -42,9 +42,9 @@ import { toNumber,isFunction } from "../utils"
*
* 空值 null,undefined
*
* 当输入空值时的行为
* 当输入空值时的处理逻辑
*
* { value | empty } == 转换显示为''
* { value | empty } == 转换显示为''并且忽略
* { value | empty('无') } ==
* { value | unit('KB') | empty('0') } == 0KB
*
@ -54,12 +54,14 @@ import { toNumber,isFunction } from "../utils"
*
* @param {*} value
* @param {String} escapeValue
* @paran {String} next 下一步行为取值true/false,break,skip,默认是break
* @param {*} options
*/
function empty(value,escapeValue,next) {
if(next===true) next = 'break'
let opts = Object.assign({escape:"",next:'ignore'},options.empty || {})
if(!escapeValue) opts.escape = escapeValue
function empty(value,escapeValue,next,options) {
if(next===false) next = 'break'
if(next===true) next = 'skip'
let opts = Object.assign({escape:"",next:'break',values:[]},options.empty || {})
if(escapeValue!=undefined) opts.escape = escapeValue
let emptyValues = [undefined,null]
if(Array.isArray(opts.values)) emptyValues.push(...opts.values)
if(emptyValues.includes(value)){
@ -73,13 +75,13 @@ empty.paramCount = 2
/**
* 当执行格式化器出错时的显示内容.
{ value | error } ==
{ value | error } == 默认
{ value | error('') } == 显示空字符串
{ value | error('ERROR') } == 显示ERROR字样
{ value | error('ERROR:{ message}') } == 显示error.message
{ value | error('ERROR:{ error}') } == 显示error.constructor.name
{ value | error('ERROR:{ error}',) } == 显示error.constructor.name
this--> scope实例
* @param {*} value
* @param {*} escapeValue
@ -88,20 +90,21 @@ this--> scope实例
* @returns
*/
function error(value,escapeValue,next,options) {
if(value instanceof Error){
if(scope.debug) console.error(`Error while execute formatter<${value.formatter}>:`,e)
const scope = this
try{
let opts = Object.assign({escape:"",next:'break'},options.error || {})
if(!escapeValue) opts.escape = escapeValue
if(!next) opts.next = next
let opts = Object.assign({escape:null,next:'break'},options.error || {})
if(escapeValue!=undefined) opts.escape = escapeValue
if(next!=undefined) opts.next = next
return {
value : String(opts.escape).replace(/\{\s*message\s*\}/g,value.message)
.replace(/\{\s*error\s*\}/g,value.constructor.name)
value : opts.escape ? String(opts.escape).replace(/\{\s*message\s*\}/g,value.message).replace(/\{\s*error\s*\}/g,value.constructor.name) : null,
next : opts.next
}
}cache(e){
if(this.debug) console.error(`Error while execute formatter: ${e.message}`)
}catch(e){
if(scope.debug) console.error(`Error while execute formatter:`,e.message)
}
return value
}else{
return value
}
@ -142,7 +145,6 @@ function suffix(value,suffix="") {
]
const FILE_SIZE_BRIEF_UNITS = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB","NB","DB"]
const FILE_SIZE_WHOLE_UNITS = ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "TeraBytes", "PetaBytes", "ExaBytes", "ZetaBytes", "YottaBytes","DoggaBytes"]
//const getSizePoint= index=>index ==0 ? 0 : new Array(index).fill(0).reduce((pv,cv)=>pv*1024,1)
/**
* 输出文件大小
@ -156,7 +158,7 @@ const FILE_SIZE_WHOLE_UNITS = ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "
* @param {*} brief
* @param {*} options
*/
function fileSize(value,unit,brief=true,options={}){
function filesize(value,unit,brief=true,options={}){
let opts = Object.assign({
precision: 2,
brief : FILE_SIZE_BRIEF_UNITS,
@ -179,8 +181,10 @@ function fileSize(value,unit,brief=true,options={}){
}
return brief ? `${result} ${opts.brief[unitIndex]}` : `${result} ${opts.brief[whole]}`
}
fileSize.paramCount = 2
fileSize.escape = 0
filesize.paramCount = 2
module.exports = {
dict,
@ -190,3 +194,4 @@ fileSize.escape = 0
error,
empty
}

View File

@ -32,12 +32,12 @@
empty:{
//values : [], // 可选定义空值如果想让0,''也为空值可以指定values=[0,'']
escape : "", // 当空值时显示的备用值
next : null // 当空值时下一步的行为: break=中止;ignore=忽略
next : 'break' // 当空值时下一步的行为: break=中止;skip=跳过
},
error : {
//当错误时显示的内容支持的插值变量有message=错误信息,error=错误类名,也可以是一个返回上面内容的同步函数
escape : "", // 默认当错误时显示空内容
next : null // 当出错时下一步的行为: break=中止;ignore=忽略
escape : null, // 默认当错误时显示空内容
next : 'break' // 当出错时下一步的行为: break=中止;skip=忽略
},
fileSize:{
//brief: ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB","NB","DB"],

View File

@ -5,12 +5,12 @@
const enFormatters = require("./en")
const zhFormatters = require("./zh")
const commonFormatters = require("./common")
const defaultFormatters = require("./default")
module.exports = {
"*":{
...enFormatters,
...commonFormatters
...defaultFormatters
},
zh:zhFormatters
}

View File

@ -17,7 +17,7 @@ module.exports = {
shorMonthNames: CN_SHORT_MONTH_NAMES
},
currency : {
unit : "$",
unit : "",
prefix : "",
suffix : "",
division : 3,
@ -32,7 +32,7 @@ module.exports = {
Date: value => {const d = toDate(value);return `${d.getFullYear()}${d.getMonth() + 1}${d.getDate()}${d.getHours()}${d.getMinutes()}${d.getSeconds()}`}
},
// 日期
date : value => `${value.getFullYear()}${value.getMonth() + 1}${value.getDate()}`,
date : value => { const d = toDate(value); return `${d.getFullYear()}${d.getMonth() + 1}${d.getDate()}` },
weekday : value => CN_WEEK_DAYS[toDate(value).getDay()],
shortWeekday : value => CN_SHORT_WEEK_DAYS[toDate(value).getDay()],
monthName : value => CN_MONTH_NAMES[toDate(value).getMonth()],

View File

@ -1,8 +1,7 @@
const { getDataTypeName,isNumber,isPlainObject,deepMerge } = require("./utils")
const {createFormatter,getDataTypeName,isNumber,isPlainObject,deepMerge,isFunction,isNothing,deepMixin,replaceAll} = require("./utils")
const EventEmitter = require("./eventemitter")
const i18nScope = require("./scope.js")
let inlineFormatters = require("./formatters")
const inlineFormatters = require("./formatters")
const i18nScope = require("./scope")
// 用来提取字符里面的插值变量参数 , 支持管道符 { var | formatter | formatter }
@ -150,10 +149,11 @@ function forEachInterpolatedVars(str,callback,options={}){
const formatters = parseFormatters(match.groups.formatters)
if(isFunction(callback)){
try{
const finalValue = callback(varname,formatters,match[0])
if(opts.replaceAll){ // 在某此版本上可能没有
result=result.replaceAll(match[0],callback(varname,formatters,match[0]))
result=result.replaceAll(match[0],finalValue)
}else{
result=result.replace(new RegExp(match[0],"gm"),callback(varname,formatters,match[0]))
result=replaceAll(result,match[0],finalValue)
}
}catch{// callback函数可能会抛出异常如果抛出异常则中断匹配过程
break
@ -286,6 +286,22 @@ function getFormatter(scope,activeLanguage,name){
}
}
function executeChecker(checker,value){
let result ={ value, next:"skip"}
if(!isFunction(checker)) return result
try{
const r = checker(value)
if(isPlainObject(r)) {
Object.assign(result,r)
}else{
result.value = r
}
if(!["break","skip"].includes(result.next)) result.next="break"
}catch(e){
}
return result
}
/**
* 执行格式化器并返回结果
*
@ -296,40 +312,68 @@ function getFormatter(scope,activeLanguage,name){
* @param {*} value
* @param {Array[Function]} formatters 多个格式化器函数(经过包装过的)顺序执行前一个输出作为下一个格式化器的输入
*/
function executeFormatter(value,formatters,scope){
function executeFormatter(value,formatters,scope,template){
if(formatters.length===0) return value
let result = value
const emptyChecker = formatters.find(func=>func.$name==='empty')
const errorChecker = formatters.find(func=>func.$name==='error')
// 当输入是一个空值时
// 1. 空值检查
if(emptyChecker){
const { value,next } = emptyChecker(result)
if(next == 'break') return value
const { value,next } = executeChecker(emptyChecker,result)
if(next == 'break') {
return value
}else{
result = value
}
if(result instanceof Error && errorChecker){
const { value,next } = errorChecker(result)
if(next == 'break') return value
}
// 2. 错误检查
if((result instanceof Error) && errorChecker){
result.formatter = formatter.$name
const { value,next } = executeChecker(emptyChecker,result)
if(next == 'break') {
return value
}else{
result = value
}
}
// 3. 分别执行格式化器函数
for(let formatter of formatters){
try{
result = formatter(result)
}catch(e){
if(scope.debug) console.error(`Error while execute i18n formatter<${formatter.$name}> for ${value}: ${e.message} ` )
const { value,next } = errorChecker(e)
e.formatter = formatter.$name
if(scope.debug) console.error(`Error while execute i18n formatter<${formatter.$name}> for ${template}: ${e.message} ` )
const { value,next } = executeChecker(errorChecker,result)
if(next=="break"){
if(value!==undefined) result = value
break
}else if(next=="ignore"){
}else if(next=="skip"){
continue
}
if(value!==undefined) result = value
}
}
return result
}
/**
* 添加默认的empty和error格式化器用来提供默认的空值和错误处理逻辑
* @param {*} formatters
*/
function addDefaultFormatters(formatters){
// 默认的空值处理逻辑: 转换为"",然后继续执行接下来的逻辑
if(formatters.findIndex(([name])=>name=="empty")===-1){
formatters.push(["empty",[]])
}
// 默认的错误处理逻辑: 开启DEBUG时会显示ERROR:message关闭DEBUG时会保持最近值不变然后中止后续执行
if(formatters.findIndex(([name])=>name=="error")===-1){
formatters.push(["error",[]])
}
}
/**
*
* [[格式化器名称,[参数,参数,...]][格式化器名称,[参数,参数,...]]]格式化器包装转化为
@ -343,6 +387,7 @@ function executeFormatter(value,formatters,scope){
*/
function wrapperFormatters(scope,activeLanguage,formatters){
let wrappedFormatters = []
addDefaultFormatters(formatters)
for(let [name,args] of formatters){
if(name){
const func = getFormatter(scope,activeLanguage,name)
@ -386,7 +431,7 @@ function wrapperFormatters(scope,activeLanguage,formatters){
* @param {*} value
* @returns
*/
function getFormattedValue(scope,activeLanguage,formatters,value){
function getFormattedValue(scope,activeLanguage,formatters,value,template){
// 1. 取得格式化器函数列表
const formatterFuncs = wrapperFormatters(scope,activeLanguage,formatters)
// 3. 执行格式化器
@ -394,10 +439,10 @@ function getFormattedValue(scope,activeLanguage,formatters,value){
// 当没有格式化器时,查询是否指定了默认数据类型的格式化器,如果有则执行
const defaultFormatter = getDataTypeDefaultFormatter(scope,activeLanguage,getDataTypeName(value))
if(defaultFormatter){
return executeFormatter(value,[defaultFormatter],scope)
return executeFormatter(value,[defaultFormatter],scope,template)
}
}
value = executeFormatter(value,formatterFuncs,scope)
value = executeFormatter(value,formatterFuncs,scope,template)
return value
}
@ -432,9 +477,9 @@ function replaceInterpolatedVars(template,...args) {
// 读取模板字符串中的插值变量列表
// [[var1,[formatter,formatter,...],match],[var2,[formatter,formatter,...],match],...}
let varValues = args[0]
return forEachInterpolatedVars(template,(varname,formatters)=>{
return forEachInterpolatedVars(template,(varname,formatters,match)=>{
let value = (varname in varValues) ? varValues[varname] : ''
return getFormattedValue(scope,activeLanguage,formatters,value)
return getFormattedValue(scope,activeLanguage,formatters,value,template)
})
}else{
// ****************************位置插值****************************
@ -442,9 +487,9 @@ function replaceInterpolatedVars(template,...args) {
const params=(args.length===1 && Array.isArray(args[0])) ? [...args[0]] : args
if(params.length===0) return template // 没有变量则不需要进行插值处理,返回原字符串
let i = 0
return forEachInterpolatedVars(template,(varname,formatters)=>{
return forEachInterpolatedVars(template,(varname,formatters,match)=>{
if(params.length>i){
return getFormattedValue(scope,activeLanguage,formatters,params[i++])
return getFormattedValue(scope,activeLanguage,formatters,params[i++],template)
}else{
throw new Error() // 抛出异常,停止插值处理
}
@ -603,19 +648,16 @@ function translate(message) {
I18nManager.instance = this;
this._settings = deepMerge(defaultLanguageSettings,settings)
this._scopes=[] // 保存i18nScope实例
this._defaultMessageLoader = null // 默认文本加载器
this._defaultMessageLoader = null // 默认语言包加载器
}
get settings(){ return this._settings }
get scopes(){ return this._scopes }
// 当前激活语言
get activeLanguage(){ return this._settings.activeLanguage}
// 默认语言
get defaultLanguage(){ return this._settings.defaultLanguage}
// 支持的语言列表
get languages(){ return this._settings.languages}
// 内置格式化器
get formatters(){ return inlineFormatters }
get defaultMessageLoader(){ return this._defaultMessageLoader}
get settings(){ return this._settings } // 配置参数
get scopes(){ return this._scopes } // 注册的报有i18nScope实例q
get activeLanguage(){ return this._settings.activeLanguage} // 当前激活语言 名称
get defaultLanguage(){ return this._settings.defaultLanguage} // 默认语言名称
get languages(){ return this._settings.languages} // 支持的语言列表
get formatters(){ return this._settings.formatters } // 内置格式化器{*:{$options,$types,...},zh:{$options,$types,...},en:{$options,$types,...}}
get defaultMessageLoader(){ return this._defaultMessageLoader} // 默认语言包加载器
// 通过默认加载器加载文件
async loadMessagesFromDefaultLoader(newLanguage,scope){
if(!isFunction(this._defaultMessageLoader)) return //throw new Error("No default message loader specified")
@ -627,10 +669,9 @@ function translate(message) {
async change(value){
value=value.trim()
if(this.languages.findIndex(lang=>lang.name === value)!==-1 || isFunction(this._defaultMessageLoader)){
// 通知所有作用域刷新到对应的语言包
await this._refreshScopes(value)
await this._refreshScopes(value) // 通知所有作用域刷新到对应的语言包
this._settings.activeLanguage = value
await this.emit(value) /// 触发语言切换事件
await this.emit(value) // 触发语言切换事件
}else{
throw new Error("Not supported language:"+value)
}
@ -640,7 +681,6 @@ function translate(message) {
* @param {*} newLanguage
*/
async _refreshScopes(newLanguage){
// 并发执行所有作用域语言包的加载
try{
const scopeRefreshers = this._scopes.map(scope=>{
return scope.refresh(newLanguage)
@ -728,8 +768,13 @@ module.exports ={
I18nManager,
translate,
i18nScope,
createFormatter,
defaultLanguageSettings,
getDataTypeName,
isNumber,
isPlainObject
isNothing,
isPlainObject,
isFunction,
deepMerge,
deepMixin
}

View File

@ -18,9 +18,8 @@ const DataTypes = [
module.exports = class i18nScope {
constructor(options = {}, callback) {
this._id = options.id || Date.now().toString() + parseInt(Math.random() * 1000);
// 当出错时是否在控制台台输出错误信息
this._debug = options.debug == undefined ? process && process.env && process.env.NODE_ENV === "development" : options.debug;
this._languages = options.languages; // 当前作用域的语言列表
this._debug = options.debug == undefined ? process && process.env && process.env.NODE_ENV === "development" : options.debug; // 当出错时是否在控制台台输出错误信息
this._languages = options.languages; // 当前作用域支持的语言列表
this._defaultLanguage = options.defaultLanguage || "zh"; // 默认语言名称
this._activeLanguage = options.activeLanguage; // 当前语言名称
this._default = options.default; // 默认语言包
@ -29,8 +28,8 @@ module.exports = class i18nScope {
this._formatters = options.formatters; // 当前作用域的格式化函数列表{<lang>: {$types,$options,[格式化器名称]: () => {},[格式化器名称]: () => {}}}
this._loaders = options.loaders; // 异步加载语言文件的函数列表
this._global = null; // 引用全局VoerkaI18n配置注册后自动引用
this._activeFormatters = options.formatters[options.activeLanguage]; // 激活使用的格式化器,查找格式化器时在此查找
this._patchMessages = {}; // 语言包补丁信息{<language>: {....},<language>:{....}}
this._refreshing = false; // 正在加载语言包标识
// 用来缓存格式化器的引用,当使用格式化器时可以直接引用,减少检索遍历
this.$cache = {
activeLanguage : null,
@ -48,40 +47,27 @@ module.exports = class i18nScope {
});
}
this._global = globalThis.VoerkaI18n;
// 合并补丁语言包
this._mergePatchedMessages();
this._patch(this._messages, this.activeLanguage);
// 正在加载语言包标识
this._refreshing = false;
// 在全局注册作用域
this.register(callback);
this._initFormatters(this.activeLanguage) // 初始化活动的格式化器
this._mergePatchedMessages(); // 从本地缓存中读取并合并补丁语言包
this._patch(this._messages, this.activeLanguage); // 延后执行补丁命令,该命令会向远程下载补丁包
this.register(callback); // 在全局注册作用域
}
// 作用域
get id() {return this._id;}
// 调试开关
get debug() {return this._debug;}
// 默认语言名称
get defaultLanguage() {return this._defaultLanguage;}
// 默认语言名称
get activeLanguage() {return this._activeLanguage;}
// 默认语言包
get default() {return this._default;}
// 当前语言包
get messages() {return this._messages; }
// 消息id映射列表
get idMap() {return this._idMap;}
// 当前作用域的格式化器 {<lang>:{$types,$options,[格式化器名称]:()=>{},[格式化器名称]:()=>{}}}
get formatters() { return this._formatters;}
// 当前作用域支持的语言列表[{name,title,fallback}]
get languages() {return this._languages;}
// 异步加载语言文件的函数列表
get loaders() { return this._loaders;}
// 引用全局VoerkaI18n配置注册后自动引用
get global() { return this._global;}
// 当前格式化器配置参数
get activeFormatterOptions(){return this._activeFormatterOptions}
get id() {return this._id;} // 作用域唯一id
get debug() {return this._debug;} // 调试开关
get defaultLanguage() {return this._defaultLanguage;} // 默认语言名称
get activeLanguage() {return this._activeLanguage;} // 默认语言名称
get default() {return this._default;} // 默认语言包
get messages() {return this._messages; } // 当前语言包
get idMap() {return this._idMap;} // 消息id映射列表
get languages() {return this._languages;} // 当前作用域支持的语言列表[{name,title,fallback}]
get loaders() { return this._loaders;} // 异步加载语言文件的函数列表
get global() { return this._global;} // 引用全局VoerkaI18n配置注册后自动引用
get formatters() { return this._formatters;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$options,[格式化器名称]: () = >{},[格式化器名称]: () => {}}}
get activeFormatters() {return this._activeFormatters} // 当前作用域激活的格式化器定义 {$types,$options,[格式化器名称]: () = >{},[格式化器名称]: () = >{}}
get activeFormatterOptions(){return this._activeFormatterOptions} // 当前格式化器合并后的配置参数,参数已经合并了全局格式化器中的参数
/**
* 在全局注册作用域
* 在全局注册作用域当前作用域
* @param {*} callback 注册成功后的回调
*/
register(callback) {
@ -156,6 +142,28 @@ module.exports = class i18nScope {
this._activeLanguage = this.defaultLanguage;
}
/**
* 初始化格式化器
* 激活和默认语言的格式化器采用静态导入的形式而没有采用异步块的形式这是为了确保首次加载时的能马上读取而减少延迟加载
* _activeFormatters={$options:{...},$types:{...},[格式化器名称]:()=>{...},[格式化器名称]:()=>{...},...}}
*/
_initFormatters(newLanguage){
this._activeFormatters = {}
try {
if (newLanguage in this._formatters) {
this._activeFormatters = this._formatters[newLanguage];
} else {
if (this._debug) console.warn(`Not initialize <${newLanguage}> formatters.`);
}
this._generateFormatterOptions(newLanguage)
} catch (e) {
if (this._debug) console.error(`Error while initialize ${newLanguage} formatters: ${e.message}`);
}
}
/**
*
* 切换到对应语言的格式化器
*
* 当切换语言时格式化器应该切换到对应语言的格式化器
*
* 重要需要处理
@ -186,12 +194,18 @@ module.exports = class i18nScope {
* 生成格式化器的配置参数该参数由以下合并而成
* - global.formatters[*].$options
* - global.formatters[language].$options
* - scope.activeFormattes.$options 优先
* - scope.activeFormatters.$options 当前优先
*/
_generateFormatterOptions(language){
let options = Object.assign({},getByPath(global.formatters,`*.$options`,{}))
deepMixin(options,getByPath(global.formatters,`${language}.$options`,{}))
deepMixin(options,getByPath(scope.activeFormattes,"$options",{}))
let options
try{
options = Object.assign({},getByPath(this._global.formatters,`*.$options`,{}))
deepMixin(options,getByPath(this._global.formatters,`${language}.$options`,{}))
deepMixin(options,getByPath(this._activeFormatters,"$options",{}))
}catch(e){
if(this.debug) console.error(`Error while generate <${language}> formatter options: `,e)
if(!options) options = this._activeFormatters.$options || {}
}
return this._activeFormatterOptions = options
}
/**
@ -256,17 +270,13 @@ module.exports = class i18nScope {
async _patch(messages, newLanguage) {
if (!isFunction(this.global.loadMessagesFromDefaultLoader)) return;
try {
let pachedMessages =
await this.global.loadMessagesFromDefaultLoader(
newLanguage,
this
);
let pachedMessages = await this.global.loadMessagesFromDefaultLoader(newLanguage,this);
if (isPlainObject(pachedMessages)) {
Object.assign(messages, pachedMessages);
this._savePatchedMessages(pachedMessages, newLanguage);
}
} catch (e) {
if (this._debug) console.error(`Error while loading <${newLanguage}> messages from remote:${error.message}`);
if (this._debug) console.error(`Error while loading <${newLanguage}> patch messages from remote:`,e);
}
}
/**
@ -299,17 +309,10 @@ module.exports = class i18nScope {
_savePatchedMessages(messages, language) {
try {
if (globalThis.localStorage) {
globalThis.localStorage.setItem(
`voerkai18n_${this.id}_${language}_patched_messages`,
JSON.stringify(messages)
);
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.message
);
if (this.$cache._debug) console.error("Error while save voerkai18n patched messages:",e);
}
}
/**
@ -319,11 +322,7 @@ module.exports = class i18nScope {
*/
_getPatchedMessages(language) {
try {
return JSON.parse(
localStorage.getItem(
`voerkai18n_${this.id}_${language}_patched_messages`
)
);
return JSON.parse(localStorage.getItem(`voerkai18n_${this.id}_${language}_patched_messages`));
} catch (e) {
return {};
}

View File

@ -254,21 +254,91 @@ function formatDatetime(value,templ="YYYY/MM/DD HH:mm:ss"){
return result
}
/**
* 替换所有字符串
* 低版本ES未提供replaceAll,此函数用来替代
*
*
* @param {*} str
* @param {*} findValue
* @param {*} replaceValue
*/
function replaceAll(str,findValue,replaceValue){
if(typeof(str)!=="string" || findValue=="" || findValue==replaceValue) return str
let result = str
try{
while(result.search(findValue)!=-1){
result = result.replace(findValue,replaceValue)
}
}catch{}
return result
}
/**
* 创建格式化器
*
* 格式化器是一个普通的函数具有以下特点
*
* - 函数第一个参数是上一上格式化器的输出
* - 支持0-N个简单类型的入参
* - 格式化器可以在格式化器的$options参数指定一个键值来配置不同语言时的参数
*
* createFormatter((value,prefix,suffix, division ,precision,options)=>{
*
* },
* {
* unit:"$",
* prefix,
* suffix,
* division,
* precision
* },
* {
* params:["prefix","suffix", "division" ,"precision"] // 声明参数顺序
* optionKey:"currency" // 声明特定语言下的配置在$options.currency
* }
* )
*
* @param {*} fn
* @param {*} defaultParams 默认参数
* @param {*} meta
* @returns
*/
function createFormatter(fn,meta={}){
function createFormatter(fn,defaultParams={},meta={}){
let opts = Object.assign({
checker : false, // true时代表该格式化器是作为校验器存在会在执行每一次格式化器函数后调用进行检查
paramCount : 0, // 声明该该格式化器具有几个参数,0代表未知
error : 0, // 当执行格式化化器函数出错时的行为取值0-break,1-skip,2-default
empty : false, // 当输入为空时的输出
default : null // 当出错默认输出
},meta)
Object.entries(opts).forEach(([key,value])=>fn[key] = value)
return fn
normalize : null, // 对输入值进行规范化处理,如进行时间格式化时,为了提高更好的兼容性,支持数字时间戳/字符串/Date等需要对输入值进行处理如强制类型转换等
params : [], // 声明参数顺序
optionKeyPath: null // 声明该格式化器在$options中的路径支持简单的使用.的路径语法
})
// 最后一个参数是传入activeFormatterOptions参数
const wrappedFn = function(value,...args){
let finalValue = value
// 1. 输入值规范处理,主要的类型转换等
if(isFunction(opts.normalize)){
try{
finalValue = opts.normalize(finalValue)
}catch{}
}
// 2. 读取activeFormatterOptions
let activeFormatterOpts = args.length>0 ? args[args.length-1] : {}
if(!isPlainObject( activeFormatterOpts)) activeFormatterOpts ={}
// 3. 从当前语言的激活语言中读取配置参数
const activeOptions =Object.assign({},defaultParams,getByPath(activeFormatterOpts,opts.optionKey,{}))
let finalArgs = opts.params.map(param=>getByPath(activeOptions,param,undefined))
// 4. 将翻译函数执行格式化器时传入的参数具有高优先级
for(let i =0; i<finalArgs.length-1;i++){
if(i>=args.length-1) break // 最后一参数是配置
if(args[i]!==undefined) finalArgs[i] = args[i]
}
return fn(finalValue,...finalArgs,activeFormatterOpts)
}
fn.paramCount = opts.paramCount
return wrappedFn
}
const Formatter = createFormatter
module.exports ={
isPlainObject,
@ -277,9 +347,13 @@ module.exports ={
isNothing,
deepMerge,
deepMixin,
Formatter,
createFormatter,
replaceAll,
getByPath,
getDataTypeName,
toDate,
toNumber,
toCurrency
toCurrency,
createFormatter
}