update formatters
This commit is contained in:
parent
3c31d3fb67
commit
b8f59c43bc
@ -7,7 +7,7 @@ export default defineConfig({
|
||||
base:"/voerka-i18n/",
|
||||
publicPath:"/voerka-i18n/",
|
||||
mode: 'site',
|
||||
logo: "/images/i18n.png",
|
||||
logo: "/voerka-i18n/images/i18n.png",
|
||||
outputPath:"docs/dist",
|
||||
resolve:{
|
||||
includes:["docs/src"]
|
||||
|
@ -1,4 +1,7 @@
|
||||
/**
|
||||
import { toNumber,isFunction } from "../utils"
|
||||
|
||||
|
||||
/**
|
||||
* 字典格式化器
|
||||
* 根据输入data的值,返回后续参数匹配的结果
|
||||
* dict(data,<value1>,<result1>,<value2>,<result1>,<value3>,<result1>,...)
|
||||
@ -34,7 +37,156 @@
|
||||
if (args.length > 0 && (args.length % 2 !== 0)) return args[args.length - 1]
|
||||
return value
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 空值: null,undefined
|
||||
*
|
||||
* 当输入空值时的行为
|
||||
*
|
||||
* { value | empty } == 转换显示为''
|
||||
* { value | empty('无') } == 无
|
||||
* { value | unit('KB') | empty('0') } == 0KB
|
||||
*
|
||||
* 有时在处理其他类型时,可能希望将0或者''也视为空值
|
||||
* { value | empty('没钱了') } ==
|
||||
*
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {String} escapeValue
|
||||
* @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
|
||||
let emptyValues = [undefined,null]
|
||||
if(Array.isArray(opts.values)) emptyValues.push(...opts.values)
|
||||
if(emptyValues.includes(value)){
|
||||
return {value:opts.escape,next: opts.next}
|
||||
}else{
|
||||
return value
|
||||
}
|
||||
}
|
||||
empty.paramCount = 2
|
||||
|
||||
/**
|
||||
* 当执行格式化器出错时的显示内容.
|
||||
|
||||
{ value | error } ==
|
||||
{ value | error('') } == 显示空字符串
|
||||
{ value | error('ERROR') } == 显示ERROR字样
|
||||
{ value | error('ERROR:{ message}') } == 显示error.message
|
||||
{ value | error('ERROR:{ error}') } == 显示error.constructor.name
|
||||
|
||||
this--> scope实例
|
||||
|
||||
* @param {*} value
|
||||
* @param {*} escapeValue
|
||||
* @param {*} next 下一步的行为,取值,break,ignore
|
||||
* @param {*} options 格式化器的全局配置参数
|
||||
* @returns
|
||||
*/
|
||||
function error(value,escapeValue,next,options) {
|
||||
|
||||
if(value instanceof Error){
|
||||
try{
|
||||
let opts = Object.assign({escape:"",next:'break'},options.error || {})
|
||||
if(!escapeValue) opts.escape = escapeValue
|
||||
if(!next) opts.next = next
|
||||
return {
|
||||
value : String(opts.escape).replace(/\{\s*message\s*\}/g,value.message)
|
||||
.replace(/\{\s*error\s*\}/g,value.constructor.name)
|
||||
next : opts.next
|
||||
}
|
||||
}cache(e){
|
||||
if(this.debug) console.error(`Error while execute formatter: ${e.message}`)
|
||||
}
|
||||
}else{
|
||||
return value
|
||||
}
|
||||
}
|
||||
error.paramCount = 2 // 声明该格式化器支持两个参数
|
||||
|
||||
/**
|
||||
* 添加前缀
|
||||
* @param {*} value
|
||||
* @param {*} prefix
|
||||
* @returns
|
||||
*/
|
||||
function prefix(value,prefix="") {
|
||||
return prefix ? `${prefix}${value}` : value
|
||||
}
|
||||
/**
|
||||
* 添加后缀
|
||||
* @param {*} value
|
||||
* @param {*} suffix
|
||||
* @returns
|
||||
*/
|
||||
function suffix(value,suffix="") {
|
||||
return suffix ? `${value}${suffix}` : value
|
||||
}
|
||||
|
||||
const FILE_SIZE_SECTIONS = [
|
||||
0,
|
||||
1024,
|
||||
1048576,
|
||||
1073741824,
|
||||
1099511627776,
|
||||
1125899906842624,
|
||||
1152921504606847000,
|
||||
1.1805916207174113e+21,
|
||||
1.2089258196146292e+24,
|
||||
1.2379400392853803e+27,
|
||||
1.2676506002282294e+30
|
||||
]
|
||||
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)
|
||||
|
||||
/**
|
||||
* 输出文件大小
|
||||
*
|
||||
* { value | fileSize }
|
||||
* { value | fileSize('KB') }
|
||||
* { value | fileSize('MB') }
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {*} unit 单位,未指定时采用自动方式,即<1024用字节,1024<v<1024*1024显示KB,...
|
||||
* @param {*} brief
|
||||
* @param {*} options
|
||||
*/
|
||||
function fileSize(value,unit,brief=true,options={}){
|
||||
let opts = Object.assign({
|
||||
precision: 2,
|
||||
brief : FILE_SIZE_BRIEF_UNITS,
|
||||
whole : FILE_SIZE_WHOLE_UNITS
|
||||
},options.fileSize || {})
|
||||
let v = toNumber(value)
|
||||
let unitIndex
|
||||
if(unit==undefined || unit=="auto"){
|
||||
unitIndex = FILE_SIZE_SECTIONS.findIndex(x=>v<x) - 1
|
||||
}else{
|
||||
unit = unit.toUpperCase()
|
||||
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(opts.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} ${opts.brief[unitIndex]}` : `${result} ${opts.brief[whole]}`
|
||||
}
|
||||
fileSize.paramCount = 2
|
||||
fileSize.escape = 0
|
||||
|
||||
module.exports = {
|
||||
dict
|
||||
dict,
|
||||
prefix,
|
||||
suffix,
|
||||
filesize,
|
||||
error,
|
||||
empty
|
||||
}
|
@ -5,6 +5,9 @@
|
||||
|
||||
const { toDate,toCurrency } = require("../utils")
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
// 配置参数: 格式化器函数的最后一个参数就是该配置参数
|
||||
$options:{
|
||||
@ -25,6 +28,21 @@
|
||||
number : {
|
||||
division : 3,
|
||||
precision : 2
|
||||
},
|
||||
empty:{
|
||||
//values : [], // 可选,定义空值,如果想让0,''也为空值,可以指定values=[0,'']
|
||||
escape : "", // 当空值时显示的备用值
|
||||
next : null // 当空值时下一步的行为: break=中止;ignore=忽略
|
||||
},
|
||||
error : {
|
||||
//当错误时显示的内容,支持的插值变量有message=错误信息,error=错误类名,也可以是一个返回上面内容的同步函数
|
||||
escape : "", // 默认当错误时显示空内容
|
||||
next : null // 当出错时下一步的行为: break=中止;ignore=忽略
|
||||
},
|
||||
fileSize:{
|
||||
//brief: ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB","NB","DB"],
|
||||
//whole:["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "TeraBytes", "PetaBytes", "ExaBytes", "ZetaBytes", "YottaBytes","DoggaBytes"],
|
||||
//precision: 2 // 小数精度
|
||||
}
|
||||
},
|
||||
// 默认数据类型的格式化器
|
||||
|
@ -288,28 +288,51 @@ function getFormatter(scope,activeLanguage,name){
|
||||
|
||||
/**
|
||||
* 执行格式化器并返回结果
|
||||
*
|
||||
* 格式化器this指向当前scope,并且最后一个参数是当前scope格式化器的$options
|
||||
*
|
||||
* 这样格式化器可以读取$options
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {*} formatters 多个格式化器顺序执行,前一个输出作为下一个格式化器的输入
|
||||
* @param {Array[Function]} formatters 多个格式化器函数(经过包装过的)顺序执行,前一个输出作为下一个格式化器的输入
|
||||
*/
|
||||
function executeFormatter(value,formatters,scope){
|
||||
if(formatters.length===0) return value
|
||||
let result = value
|
||||
try{
|
||||
for(let formatter of formatters){
|
||||
if(isFunction(formatter)) {
|
||||
result = formatter.call(scope,result)
|
||||
}else{// 如果碰到无效的格式化器,则跳过过续的格式化器
|
||||
return result
|
||||
|
||||
const emptyChecker = formatters.find(func=>func.$name==='empty')
|
||||
const errorChecker = formatters.find(func=>func.$name==='error')
|
||||
|
||||
// 当输入是一个空值时
|
||||
if(emptyChecker){
|
||||
const { value,next } = emptyChecker(result)
|
||||
if(next == 'break') return value
|
||||
}
|
||||
if(result instanceof Error && errorChecker){
|
||||
const { value,next } = errorChecker(result)
|
||||
if(next == 'break') return value
|
||||
}
|
||||
|
||||
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)
|
||||
if(next=="break"){
|
||||
if(value!==undefined) result = value
|
||||
break
|
||||
}else if(next=="ignore"){
|
||||
continue
|
||||
}
|
||||
}
|
||||
}catch(e){
|
||||
//console.error(`Error while execute i18n formatter for ${value}: ${e.message} ` )
|
||||
}
|
||||
if(value!==undefined) result = value
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 将 [[格式化器名称,[参数,参数,...]],[格式化器名称,[参数,参数,...]]]格式化器转化为
|
||||
* 将[[格式化器名称,[参数,参数,...]],[格式化器名称,[参数,参数,...]]]格式化器包装转化为
|
||||
* 格式化器的调用函数链
|
||||
*
|
||||
* @param {*} scope
|
||||
@ -318,29 +341,41 @@ function executeFormatter(value,formatters,scope){
|
||||
* @returns {Array} [(v)=>{...},(v)=>{...},(v)=>{...}]
|
||||
*
|
||||
*/
|
||||
function buildFormatters(scope,activeLanguage,formatters){
|
||||
let results = []
|
||||
for(let formatter of formatters){
|
||||
if(formatter[0]){
|
||||
const func = getFormatter(scope,activeLanguage,formatter[0])
|
||||
function wrapperFormatters(scope,activeLanguage,formatters){
|
||||
let wrappedFormatters = []
|
||||
for(let [name,args] of formatters){
|
||||
if(name){
|
||||
const func = getFormatter(scope,activeLanguage,name)
|
||||
if(isFunction(func)){
|
||||
results.push((v)=>{
|
||||
return func(v,...formatter[1])
|
||||
})
|
||||
let fn = (value) => {
|
||||
// 每一个格式式化器均支持若干输入参数,但是在使用时,可以支持可选参数
|
||||
// 比currency(value,prefix,suffix, division,precision)支持4个参数,但是在使用时支持可选参数
|
||||
// {value | currency} 或 {value | currency('元')} 或 {value | currency('元',"整")}
|
||||
// 为了让格式化器能比较方便地处理最后一个配置参数,当格式化器函数指定了paramCount参数来声明其支持的参数后
|
||||
// 在此就自动初始参数个数,这样在currency函数中,就可以使用这样的currency(value,prefix,suffix, division,precision,options)
|
||||
// 不管开发者使用时的输入参数数是多少均可以保证在currency函数中总是可以得到有效的options参数
|
||||
if(func.paramCount && args.length < func.paramCount ){
|
||||
args.push(...new Array(parseInt(func.paramCount)-args.length).fill(undefined))
|
||||
}
|
||||
return func.call(scope,value,...args,scope.activeFormatterOptions)
|
||||
}
|
||||
fn.$name = name
|
||||
wrappedFormatters.push(fn)
|
||||
}else{
|
||||
// 格式化器无效或者没有定义时,查看当前值是否具有同名的原型方法,如果有则执行调用
|
||||
// 比如padStart格式化器是String的原型方法,不需要配置就可以直接作为格式化器调用
|
||||
results.push((v)=>{
|
||||
if(isFunction(v[formatter[0]])){
|
||||
return v[formatter[0]].call(v,...formatter[1])
|
||||
wrappedFormatters.push((value)=>{
|
||||
if(isFunction(value[name])){
|
||||
// 最后一个参数是当前作用域的格式化器配置参数
|
||||
return value[name](value,...args)
|
||||
}else{
|
||||
return v
|
||||
return value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
return wrappedFormatters
|
||||
}
|
||||
|
||||
/**
|
||||
@ -353,14 +388,15 @@ function buildFormatters(scope,activeLanguage,formatters){
|
||||
*/
|
||||
function getFormattedValue(scope,activeLanguage,formatters,value){
|
||||
// 1. 取得格式化器函数列表
|
||||
const formatterFuncs = buildFormatters(scope,activeLanguage,formatters)
|
||||
// 2. 查找每种数据类型默认格式化器,并添加到formatters最前面,默认数据类型格式化器优先级最高
|
||||
const defaultFormatter = getDataTypeDefaultFormatter(scope,activeLanguage,getDataTypeName(value))
|
||||
// 默认数据类型的格式化器仅在没有指定其他格式化器时生效
|
||||
if(defaultFormatter && formatterFuncs.length==0){
|
||||
formatterFuncs.splice(0,0,defaultFormatter)
|
||||
}
|
||||
const formatterFuncs = wrapperFormatters(scope,activeLanguage,formatters)
|
||||
// 3. 执行格式化器
|
||||
if(formatterFuncs.length==0){
|
||||
// 当没有格式化器时,查询是否指定了默认数据类型的格式化器,如果有则执行
|
||||
const defaultFormatter = getDataTypeDefaultFormatter(scope,activeLanguage,getDataTypeName(value))
|
||||
if(defaultFormatter){
|
||||
return executeFormatter(value,[defaultFormatter],scope)
|
||||
}
|
||||
}
|
||||
value = executeFormatter(value,formatterFuncs,scope)
|
||||
return value
|
||||
}
|
||||
|
@ -57,49 +57,29 @@ module.exports = class i18nScope {
|
||||
this.register(callback);
|
||||
}
|
||||
// 作用域
|
||||
get id() {
|
||||
return this._id;
|
||||
}
|
||||
get id() {return this._id;}
|
||||
// 调试开关
|
||||
get debug() {
|
||||
return this._debug;
|
||||
}
|
||||
get debug() {return this._debug;}
|
||||
// 默认语言名称
|
||||
get defaultLanguage() {
|
||||
return this._defaultLanguage;
|
||||
}
|
||||
get defaultLanguage() {return this._defaultLanguage;}
|
||||
// 默认语言名称
|
||||
get activeLanguage() {
|
||||
return this._activeLanguage;
|
||||
}
|
||||
get activeLanguage() {return this._activeLanguage;}
|
||||
// 默认语言包
|
||||
get default() {
|
||||
return this._default;
|
||||
}
|
||||
get default() {return this._default;}
|
||||
// 当前语言包
|
||||
get messages() {
|
||||
return this._messages;
|
||||
}
|
||||
get messages() {return this._messages; }
|
||||
// 消息id映射列表
|
||||
get idMap() {
|
||||
return this._idMap;
|
||||
}
|
||||
get idMap() {return this._idMap;}
|
||||
// 当前作用域的格式化器 {<lang>:{$types,$options,[格式化器名称]:()=>{},[格式化器名称]:()=>{}}}
|
||||
get formatters() {
|
||||
return this._formatters;
|
||||
}
|
||||
get formatters() { return this._formatters;}
|
||||
// 当前作用域支持的语言列表[{name,title,fallback}]
|
||||
get languages() {
|
||||
return this._languages;
|
||||
}
|
||||
get languages() {return this._languages;}
|
||||
// 异步加载语言文件的函数列表
|
||||
get loaders() {
|
||||
return this._loaders;
|
||||
}
|
||||
get loaders() { return this._loaders;}
|
||||
// 引用全局VoerkaI18n配置,注册后自动引用
|
||||
get global() {
|
||||
return this._global;
|
||||
}
|
||||
get global() { return this._global;}
|
||||
// 当前格式化器配置参数
|
||||
get activeFormatterOptions(){return this._activeFormatterOptions}
|
||||
/**
|
||||
* 在全局注册作用域
|
||||
* @param {*} callback 注册成功后的回调
|
||||
|
@ -197,16 +197,77 @@ function getByPath(obj,path,defaultValue){
|
||||
return cur
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回value相对rel的相对时间
|
||||
*
|
||||
* 如:12分钟前, 6秒前, 1小时
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
function relativeTime(value, rel){
|
||||
|
||||
// YY 18 年,两位数
|
||||
// YYYY 2018 年,四位数
|
||||
// M 1-12 月,从1开始
|
||||
// MM 01-12 月,两位数字
|
||||
// MMM Jan-Dec 月,英文缩写
|
||||
// D 1-31 日
|
||||
// DD 01-31 日,两位数
|
||||
// H 0-23 24小时
|
||||
// HH 00-23 24小时,两位数
|
||||
// h 1-12 12小时
|
||||
// hh 01-12 12小时,两位数
|
||||
// m 0-59 分钟
|
||||
// mm 00-59 分钟,两位数
|
||||
// s 0-59 秒
|
||||
// ss 00-59 秒,两位数
|
||||
// S 0-9 毫秒(百),一位数
|
||||
// SS 00-99 毫秒(十),两位数
|
||||
// SSS 000-999 毫秒,三位数
|
||||
// Z -05:00 UTC偏移
|
||||
// ZZ -0500 UTC偏移,两位数
|
||||
// A AM / PM 上/下午,大写
|
||||
// a am / pm 上/下午,小写
|
||||
// Do 1st... 31st 月份的日期与序号
|
||||
|
||||
function formatDatetime(value,templ="YYYY/MM/DD HH:mm:ss"){
|
||||
const v = toDate(value)
|
||||
const year =String(v.getFullYear()),month = String(v.getMonth()+1),weekday=String(v.getDay()),day=String(v.getDate())
|
||||
const minute = String(v.getMinutes()),second = String(v.getSeconds()),millisecond=String(v.getMilliseconds()),hour = String(v.getHours())
|
||||
const time = String(v.getTime())
|
||||
let result = templ
|
||||
const vars = [
|
||||
["YYYY", year], // 2018 年,四位数
|
||||
["YY", year.substring(year.length - 2, year.length)], // 18 年,两位数
|
||||
["MMM", ""], // Jan-Dec 月,英文缩写
|
||||
["MM", month.padStart(2, "0")], // 01-12 月,两位数字
|
||||
["M", month], // 1-12 月,从1开始
|
||||
["DD", day.padStart(2, "0")], // 01-31 日,两位数
|
||||
["D", day], // 1-31 日
|
||||
["HH", String(hour).padStart(2, "0")], // 00-23 24小时,两位数
|
||||
["H", String(hour)], // 0-23 24小时
|
||||
["hh", String(hour > 12 ? hour - 12 : hour).padStart(2, "0")], // 01-12 12小时,两位数
|
||||
["h", hour > 12 ? hour - 12 : hour], // 1-12 12小时
|
||||
["mm", minute.padStart(2, "0")], // 00-59 分钟,两位数
|
||||
["m", minute], // 0-59 分钟
|
||||
["ss", second.padStart(2, "0")], // 00-59 秒,两位数
|
||||
["s", second], // 0-59 秒
|
||||
["SSS", millisecond], // 000-999 毫秒,三位数
|
||||
["SS", millisecond.substring(year.length - 2, year.length)], // 00-99 毫秒(十),两位数
|
||||
["S",millisecond[millisecond.length - 1]], // 0-9 毫秒(百),一位数
|
||||
["A", hour > 12 ? "PM" : "AM"], // AM / PM 上/下午,大写
|
||||
["a", hour > 12 ? "pm" : "am"] // am / pm 上/下午,小写
|
||||
]
|
||||
vars.forEach(([key,value])=>result = result.replace(key,value))
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建格式化器
|
||||
*/
|
||||
function createFormatter(fn,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
|
||||
}
|
||||
|
||||
module.exports ={
|
||||
|
Loading…
x
Reference in New Issue
Block a user