update formatters

This commit is contained in:
wxzhang 2022-08-15 22:12:15 +08:00
parent ccad9d45c3
commit ba9f9bfd49
6 changed files with 75 additions and 59 deletions

View File

@ -3,7 +3,7 @@
* *
*/ */
const { toDate,toCurrency,formatDatetime,formatTime,Formatter } = require("../utils") const { toDate,toCurrency,toNumber,formatDatetime,formatTime,Formatter } = require("../utils")
// 日期格式化器 // 日期格式化器
// format取字符串"long","short","local","iso","gmt","utc"或者日期模块字符串 // format取字符串"long","short","local","iso","gmt","utc"或者日期模块字符串
@ -43,7 +43,17 @@ const dateFormatter = Formatter((value,format,$config)=>{
params : ['format'], params : ['format'],
configKey: "datetime.date" configKey: "datetime.date"
}) })
// 季度格式化器 format= 0=短格式 1=长格式
const mquarterFormatter = Formatter((value,format,$config)=>{
const month = value.getMonth() + 1
const quarter = Math.floor( ( month % 3 == 0 ? ( month / 3 ) : (month / 3 + 1 ) ))
if(format<0 && format>1) format = 0
return format==0 ? $config.shortNames[month] : (format==1 ? $config.shortNames[month] : month+1)
},{
normalize: toDate,
params : ['format'],
configKey: "datetime.quarter"
})
// 月份格式化器 format可以取值0,1,2也可以取字符串long,short,number // 月份格式化器 format可以取值0,1,2也可以取字符串long,short,number
const monthFormatter = Formatter((value,format,$config)=>{ const monthFormatter = Formatter((value,format,$config)=>{
@ -104,15 +114,17 @@ const timeFormatter = Formatter((value,format,$config)=>{
}) })
// 货币格式化器, CNY $13,456.00 // 货币格式化器, CNY $13,456.00
const currencyFormatter = Formatter((value, unit,prefix ,suffix, division,precision) =>{ const currencyFormatter = Formatter((value, symbol,prefix ,suffix, division,precision) =>{
return toCurrency(value, { unit,division, prefix, precision,suffix }) return toCurrency(value, { symbol,division, prefix, precision,suffix })
},{ },{
normalize: toNumber, normalize: toNumber,
params:["prefix","suffix", "division","precision"], params:["symbol","prefix","suffix", "division","precision"],
configKey: "currency" configKey: "currency"
}) })
module.exports = {
module.exports = {
// 配置参数 // 配置参数
$config:{ $config:{
datetime : { datetime : {
@ -122,6 +134,10 @@ const currencyFormatter = Formatter((value, unit,prefix ,suffix, division,precis
short : "MM/DD", short : "MM/DD",
format : "local" format : "local"
}, },
quarter : {
names : ["Q1","Q2","Q3","Q4"],
shortNames : ["Q1","Q2","Q3","Q4"]
},
month:{ month:{
names : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], names : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
shortNames : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"], shortNames : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"],
@ -137,9 +153,9 @@ const currencyFormatter = Formatter((value, unit,prefix ,suffix, division,precis
short : "HH:mm:ss", short : "HH:mm:ss",
format : 'local' format : 'local'
}, },
} },
currency : { currency : {
unit : "$", // 单位 symbol : "$", // 符号
prefix : "", // 前缀 prefix : "", // 前缀
suffix : "", // 后缀 suffix : "", // 后缀
division : 3, // ,分割位 division : 3, // ,分割位
@ -150,14 +166,14 @@ const currencyFormatter = Formatter((value, unit,prefix ,suffix, division,precis
precision : 2 precision : 2
}, },
empty:{ empty:{
//values : [], // 可选定义空值如果想让0,''也为空值可以指定values=[0,''] //values : [], // 可选定义空值如果想让0,''也为空值可以指定values=[0,'']
escape : "", // 当空值时显示的备用值 escape : "", // 当空值时显示的备用值
next : 'break' // 当空值时下一步的行为: break=中止;skip=跳过 next : 'break' // 当空值时下一步的行为: break=中止;skip=跳过
}, },
error : { error : {
//当错误时显示的内容支持的插值变量有message=错误信息,error=错误类名,也可以是一个返回上面内容的同步函数 //当错误时显示的内容支持的插值变量有message=错误信息,error=错误类名,也可以是一个返回上面内容的同步函数
escape : null, // 默认当错误时显示空内容 escape : null, // 默认当错误时显示空内容
next : 'break' // 当出错时下一步的行为: break=中止;skip=忽略 next : 'break' // 当出错时下一步的行为: break=中止;skip=忽略
}, },
fileSize:{ fileSize:{
//brief: ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB","NB","DB"], //brief: ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB","NB","DB"],

View File

@ -16,6 +16,11 @@ module.exports = {
short : "MM/DD", short : "MM/DD",
format : 'YYYY年MM月DD日 HH点mm分ss秒' format : 'YYYY年MM月DD日 HH点mm分ss秒'
}, },
quarter : {
names : ["一季度","二季度","三季度","四季度"],
shortNames : ["Q1","Q2","Q3","Q4"],
format : 0 // 0-短格式,1-长格式
},
month:{ month:{
names : CN_MONTH_NAMES, names : CN_MONTH_NAMES,
shortNames : CN_SHORT_MONTH_NAMES, shortNames : CN_SHORT_MONTH_NAMES,
@ -33,7 +38,7 @@ module.exports = {
}, },
currency : { currency : {
unit : "¥", symbol : "¥",
prefix : "", prefix : "",
suffix : "元", suffix : "元",
division : 4, division : 4,
@ -49,9 +54,7 @@ module.exports = {
Boolean : value =>value ? "是":"否" Boolean : value =>value ? "是":"否"
}, },
// 货币 // 中文货币big=true代表大写形式
currency : (value,prefix = "¥",suffix="", division = 4, precision = 2) => toCurrency(value, { division, prefix, precision,suffix }),
// 中文货币big=true代表大写形式
capitalizeCurrency:(value,big,unit="元",prefix,suffix)=>toChineseCurrency(value,{big,prefix,suffix,unit}), capitalizeCurrency:(value,big,unit="元",prefix,suffix)=>toChineseCurrency(value,{big,prefix,suffix,unit}),
// 中文数字,如一千二百三十一 // 中文数字,如一千二百三十一
number:(value,isBig)=>toChineseNumber(value,isBig) number:(value,isBig)=>toChineseNumber(value,isBig)

View File

@ -386,23 +386,17 @@ function addDefaultFormatters(formatters){
* *
*/ */
function wrapperFormatters(scope,activeLanguage,formatters){ function wrapperFormatters(scope,activeLanguage,formatters){
let wrappedFormatters = [] let wrappedFormatters = []
addDefaultFormatters(formatters)
for(let [name,args] of formatters){ for(let [name,args] of formatters){
if(name){ if(name){
const func = getFormatter(scope,activeLanguage,name) const func = getFormatter(scope,activeLanguage,name)
if(isFunction(func)){ if(isFunction(func)){
let fn = (value) => { const fn = (value) => {
// 每一个格式式化器均支持若干输入参数,但是在使用时,可以支持可选参数 if(func.configurable){ // 如果格式化器函数是使用createFormatter创建的
// 比currency(value,prefix,suffix, division,precision)支持4个参数但是在使用时支持可选参数 return func.call(scope,value,...args,scope.activeFormatterConfig)
// {value | currency} 或 {value | currency('元')} 或 {value | currency('元',"整")} }else{ // 不可配置的格式化器不会传入格式化器配置
// 为了让格式化器能比较方便地处理最后一个配置参数当格式化器函数指定了paramCount参数来声明其支持的参数后 return func.call(scope,value,...args)
// 在此就自动初始参数个数这样在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 fn.$name = name
wrappedFormatters.push(fn) wrappedFormatters.push(fn)
@ -441,8 +435,9 @@ function getFormattedValue(scope,activeLanguage,formatters,value,template){
if(defaultFormatter){ if(defaultFormatter){
return executeFormatter(value,[defaultFormatter],scope,template) return executeFormatter(value,[defaultFormatter],scope,template)
} }
} }else{
value = executeFormatter(value,formatterFuncs,scope,template) value = executeFormatter(value,formatterFuncs,scope,template)
}
return value return value
} }

View File

@ -40,10 +40,10 @@ module.exports = class i18nScope {
if (!globalThis.VoerkaI18n) { if (!globalThis.VoerkaI18n) {
const { I18nManager } = require("./"); const { I18nManager } = require("./");
globalThis.VoerkaI18n = new I18nManager({ globalThis.VoerkaI18n = new I18nManager({
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._global = globalThis.VoerkaI18n; this._global = globalThis.VoerkaI18n;
@ -64,7 +64,7 @@ module.exports = class i18nScope {
get global() { return this._global;} // 引用全局VoerkaI18n配置注册后自动引用 get global() { return this._global;} // 引用全局VoerkaI18n配置注册后自动引用
get formatters() { return this._formatters;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}} get formatters() { return this._formatters;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}}
get activeFormatters() {return this._activeFormatters} // 当前作用域激活的格式化器定义 {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () = >{}} get activeFormatters() {return this._activeFormatters} // 当前作用域激活的格式化器定义 {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () = >{}}
get activeFormatterOptions(){return this._activeFormatterOptions} // 当前格式化器合并后的配置参数,参数已经合并了全局格式化器中的参数 get activeFormatterConfig(){return this._activeFormatterConfig} // 当前格式化器合并后的配置参数,参数已经合并了全局格式化器中的参数
/** /**
* 在全局注册作用域当前作用域 * 在全局注册作用域当前作用域
@ -119,12 +119,12 @@ module.exports = class i18nScope {
* @param {*} formatters ={"*",zh:{...},en:{...}} * @param {*} formatters ={"*",zh:{...},en:{...}}
* @returns * @returns
*/ */
registerFormatters(formatters,isGlobal=false) { registerFormatters(formatters,asGlobal=false) {
Object.entries(formatters).forEach(([language,fns]=>{ Object.entries(formatters).forEach(([language,fns])=>{
Object.entries(fns).forEach(([name,formatter])=>{ Object.entries(fns).forEach(([name,formatter])=>{
this.registerFormatter(name,formatter,{language}) this.registerFormatter(name,formatter,{language,global:asGlobal})
}) })
})) })
} }
/** /**
* 注册默认文本信息加载器 * 注册默认文本信息加载器
@ -222,7 +222,7 @@ module.exports = class i18nScope {
if(this.debug) console.error(`Error while generate <${language}> formatter options: `,e) if(this.debug) console.error(`Error while generate <${language}> formatter options: `,e)
if(!options) options = this._activeFormatters.$config || {} if(!options) options = this._activeFormatters.$config || {}
} }
return this._activeFormatterOptions = options return this._activeFormatterConfig = options
} }
/** /**
* 刷新当前语言包 * 刷新当前语言包

View File

@ -138,7 +138,7 @@ function toDate(value) {
function toNumber(value,defualt=0) { function toNumber(value,defualt=0) {
try { try {
if (isNumber(value)) { if (isNumber(value)) {
return parseInt(value) return parseFloat(value)
} else { } else {
return defualt return defualt
} }
@ -156,7 +156,7 @@ function toNumber(value,defualt=0) {
* @param {*} precision 小数点精确到几位0-自动 * @param {*} precision 小数点精确到几位0-自动
* @returns * @returns
*/ */
function toCurrency(value,{unit="",division=3,prefix="",precision=0,suffix=""}={}){ function toCurrency(value,{symbol="",division=3,prefix="",precision=0,suffix=""}={}){
let [wholeValue,decimalValue] = String(value).split(".") let [wholeValue,decimalValue] = String(value).split(".")
let result = [] let result = []
for(let i=0;i<wholeValue.length;i++){ for(let i=0;i<wholeValue.length;i++){
@ -169,7 +169,7 @@ function toNumber(value,defualt=0) {
} }
result.push(`.${decimalValue}`) result.push(`.${decimalValue}`)
} }
return `${prefix}${unit}${result.join("")}${suffix}` return `${prefix}${symbol}${result.join("")}${suffix}`
} }
/** /**
@ -253,6 +253,7 @@ function formatDatetime(value,templ="YYYY/MM/DD HH:mm:ss"){
vars.forEach(([key,value])=>result = replaceAll(result,key,value)) vars.forEach(([key,value])=>result = replaceAll(result,key,value))
return result return result
} }
function formatTime(value,templ="HH:mm:ss"){ function formatTime(value,templ="HH:mm:ss"){
const v = toDate(value) const v = toDate(value)
const hourNum = v.getHours() const hourNum = v.getHours()
@ -348,7 +349,7 @@ function replaceAll(str,findValue,replaceValue){
configKey : null // 声明该格式化器在$config中的路径支持简单的使用.的路径语法 configKey : null // 声明该格式化器在$config中的路径支持简单的使用.的路径语法
}) })
// 最后一个参数是传入activeFormatterOptions参数 // 最后一个参数是传入activeFormatterConfig参数
const wrappedFn = function(value,...args){ const wrappedFn = function(value,...args){
let finalValue = value let finalValue = value
// 1. 输入值规范处理,主要是进行类型转换,确保输入的数据类型及相关格式的正确性,提高数据容错性 // 1. 输入值规范处理,主要是进行类型转换,确保输入的数据类型及相关格式的正确性,提高数据容错性
@ -357,18 +358,18 @@ function replaceAll(str,findValue,replaceValue){
finalValue = opts.normalize(finalValue) finalValue = opts.normalize(finalValue)
}catch{} }catch{}
} }
// 2. 读取activeFormatterOptions // 2. 读取activeFormatterConfig
let activeFormatterOpts = args.length>0 ? args[args.length-1] : {} let activeFormatterConfigs = args.length>0 ? args[args.length-1] : {}
if(!isPlainObject( activeFormatterOpts)) activeFormatterOpts ={} if(!isPlainObject( activeFormatterConfigs)) activeFormatterConfigs ={}
// 3. 从当前语言的激活语言中读取配置参数 // 3. 从当前语言的激活语言中读取配置参数
const activeConfig =Object.assign({},defaultParams,getByPath(activeFormatterOpts,opts.configKey,{})) const formatterConfig =Object.assign({},defaultParams,getByPath(activeFormatterConfigs,opts.configKey,{}))
let finalArgs = opts.params.map(param=>getByPath(activeConfig,param,undefined)) let finalArgs = opts.params.map(param=>getByPath(formatterConfig,param,undefined))
// 4. 将翻译函数执行格式化器时传入的参数覆盖默认参数 // 4. 将翻译函数执行格式化器时传入的参数覆盖默认参数
for(let i =0; i<finalArgs.length-1;i++){ for(let i =0; i<finalArgs.length-1;i++){
if(i>=args.length-1) break // 最后一参数是配置 if(i>=args.length-1) break // 最后一参数是配置
if(args[i]!==undefined) finalArgs[i] = args[i] if(args[i]!==undefined) finalArgs[i] = args[i]
} }
return fn(finalValue,...finalArgs,activeFormatterOpts) return fn(finalValue,...finalArgs,activeFormatterConfigs)
} }
wrappedFn.configurable = true // 当函数是可配置时才在最后一个参数中传入$config wrappedFn.configurable = true // 当函数是可配置时才在最后一个参数中传入$config
return wrappedFn return wrappedFn

View File

@ -1,5 +1,5 @@
const {i18nScope, translate, getInterpolatedVars } = require('../packages/runtime/index')
const dayjs = require('dayjs'); const dayjs = require('dayjs');
const { i18nScope, translate, getInterpolatedVars } = require('../packages/runtime/dist/runtime.cjs')
const loaders = { const loaders = {
zh:{ zh:{
@ -12,7 +12,7 @@ const loaders = {
1:"hello", 1:"hello",
2:"Now is {}", 2:"Now is {}",
3:"I was born in {year}, now is {age} years old", 3:"I was born in {year}, now is {age} years old",
4:["I have no friends","I have one friends","I have two friends","I have {} friends"], 4:["I have no friends","I have one friends","I have two friends","I have {} friends"]
} }
} }
@ -29,10 +29,11 @@ const formatters = {
book:(v)=>`<${v}>`, book:(v)=>`<${v}>`,
}, },
} }
const idMap = { const idMap = {
1:"你好", 1:"你好",
2:"现在是{}", 2:"现在是{}",
3:"我出生于{year}年,今年{age}岁" 3:"我出生于{year}年,今年{age}岁",
4:"我有{}个朋友" 4:"我有{}个朋友"
} }
const languages = [ const languages = [
@ -64,7 +65,7 @@ scope.registerFormatters({
"*":{ "*":{
sum : (v,n=1)=>v+n, sum : (v,n=1)=>v+n,
double: (v)=>v*2, double: (v)=>v*2,
upper : (v)=>v.toUpperCase(), upper : (v)=>v.toUpperCase()
} }
},true) },true)
@ -207,22 +208,22 @@ test("命名插值翻译文本内容",done=>{
test("当没有对应的语言翻译时",done=>{ test("当没有对应的语言翻译时",done=>{
expect(t("我是中国人")).toBe("我是中国人"); expect(t("我是中国人")).toBe("我是中国人");
changeLanguage("en") scope.change("en")
expect(t("我是中国人")).toBe("我是中国人"); expect(t("我是中国人")).toBe("我是中国人");
done() done()
}) })
test("切换到未知语言",done=>{ test("切换到未知语言时回退到默认语言",done=>{
expect(t("我是中国人")).toBe("我是中国人"); expect(t("我是中国人")).toBe("我是中国人");
changeLanguage("en") scope.change("xn")
expect(t("我是中国人")).toBe("我是中国人"); expect(t("我是中国人")).toBe("我是中国人");
done() done()
}) })
test("翻译复数支持",done=>{ test("翻译复数支持",done=>{
changeLanguage("en") scope.change("en")
expect(t("我有{}个朋友",0)).toBe("I have no friends"); expect(t("我有{}个朋友",0)).toBe("I have no friends");
expect(t("我有{}个朋友",1)).toBe("I have one friends"); expect(t("我有{}个朋友",1)).toBe("I have one friends");
expect(t("我有{}个朋友",2)).toBe("I have two friends"); expect(t("我有{}个朋友",2)).toBe("I have two friends");