update formatters
This commit is contained in:
parent
6be922d6fe
commit
af148a30cd
@ -7,7 +7,7 @@
|
||||
- 以下定义了一些格式化器,在中文场景下,会启用这些格式化器。
|
||||
import dayjs from "dayjs";
|
||||
export default {
|
||||
$options:{...},
|
||||
$config:{...},
|
||||
$types:{
|
||||
Date:(value)=>dayjs(value).format("YYYY年MM月DD日 HH:mm:ss"),
|
||||
},
|
||||
@ -32,7 +32,7 @@
|
||||
{{if moduleType === "esm"}}
|
||||
export default{{else}}module.exports = {{/if}}{
|
||||
// 格式化器参数
|
||||
$options:{
|
||||
$config:{
|
||||
|
||||
},
|
||||
// 指定数据类型的默认格式化器
|
||||
|
@ -3,27 +3,147 @@
|
||||
*
|
||||
*/
|
||||
|
||||
const { toDate,toCurrency } = require("../utils")
|
||||
|
||||
const { toDate,toCurrency,formatDatetime,formatTime,Formatter } = require("../utils")
|
||||
|
||||
// 日期格式化器
|
||||
// format取字符串"long","short","local","iso","gmt","utc"或者日期模块字符串
|
||||
// { value | date } == '2022/8/15'
|
||||
// { value | date('long') } == '2022/8/15 12:08:32'
|
||||
// { value | date('short') } == '8/15'
|
||||
// { value | date('GMT') } == 'Mon, 15 Aug 2022 06:39:38 GMT'
|
||||
// { value | date('ISO') } == 'Mon, 15 Aug 2022 06:39:38 ISO'
|
||||
// { value | date('YYYY-MM-DD HH:mm:ss') } == '2022-8-15 12:08:32'
|
||||
const dateFormatter = Formatter((value,format,$config)=>{
|
||||
const optionals = ["long","short","local","iso","gmt","utc"]
|
||||
const optionIndex = optionals.findIndex((v,i)=>{
|
||||
if(typeof(format)=="string"){
|
||||
return v==format || v== format.toUpperCase()
|
||||
}else if(typeof(format)=="number"){
|
||||
return format === i
|
||||
}
|
||||
})
|
||||
switch(optionIndex){
|
||||
case 0: // long
|
||||
return formatDatetime(value,$config.long)
|
||||
case 1: // short
|
||||
return formatDatetime(value,$config.short)
|
||||
case 2: // local
|
||||
return value.toLocaleString()
|
||||
case 3: // ISO
|
||||
return value.toISOString()
|
||||
case 4: // GMT
|
||||
return value.toGMTString()
|
||||
case 5: // UTC
|
||||
return value.toUTCString()
|
||||
default:
|
||||
return formatDatetime(value,format)
|
||||
}
|
||||
},{
|
||||
normalize: toDate, // 转换输入为Date类型
|
||||
params : ['format'],
|
||||
configKey: "datetime.date"
|
||||
})
|
||||
|
||||
|
||||
// 月份格式化器 format可以取值0,1,2,也可以取字符串long,short,number
|
||||
const monthFormatter = Formatter((value,format,$config)=>{
|
||||
const month = value.getMonth()
|
||||
if(typeof(format)==='string'){
|
||||
format = ['long','short','number'].indexOf(format)
|
||||
}
|
||||
if(format<0 && format>2) format = 0
|
||||
return format==0 ? $config.names[month] : (format==1 ? $config.shortNames[month] : month+1)
|
||||
},{
|
||||
normalize: toDate,
|
||||
params : ['format'],
|
||||
configKey: "datetime.month"
|
||||
})
|
||||
|
||||
// 周格式化器 format可以取值0,1,2,也可以取字符串long,short,number
|
||||
const weekdayFormatter = Formatter((value,format,$config)=>{
|
||||
const day = value.getDay()
|
||||
if(typeof(format)==='string'){
|
||||
format = ['long','short','number'].indexOf(format)
|
||||
}
|
||||
if(format<0 && format>2) format = 0
|
||||
return format==0 ? $config.names[day] : (format==1 ? $config.shortNames[day] : day)
|
||||
},{
|
||||
normalize: toDate,
|
||||
params : ['format'],
|
||||
configKey: "datetime.weekday"
|
||||
})
|
||||
|
||||
|
||||
// 时间格式化器 format可以取值0,1,2,也可以取字符串long,short,timestamp,local
|
||||
const timeFormatter = Formatter((value,format,$config)=>{
|
||||
const month = value.getMonth()
|
||||
const optionals = ['long','short','timestamp','local'] //toLocaleTimeString
|
||||
const optionIndex = optionals.findIndex((v,i)=>{
|
||||
if(typeof(format)=="string"){
|
||||
return v==format || v== format.toUpperCase()
|
||||
}else if(typeof(format)=="number"){
|
||||
return format === i
|
||||
}
|
||||
})
|
||||
switch(optionIndex){
|
||||
case 0: // long
|
||||
return formatTime(value,$config.long)
|
||||
case 1: // short
|
||||
return formatTime(value,$config.short)
|
||||
case 2: // timestamp
|
||||
return value.getTime()
|
||||
case 3: // local
|
||||
return value.toLocaleTimeString()
|
||||
default:
|
||||
return formatTime(value,format)
|
||||
}
|
||||
},{
|
||||
normalize: toDate,
|
||||
params : ['format'],
|
||||
configKey: "datetime.month"
|
||||
})
|
||||
|
||||
// 货币格式化器, CNY $13,456.00
|
||||
const currencyFormatter = Formatter((value, unit,prefix ,suffix, division,precision) =>{
|
||||
return toCurrency(value, { unit,division, prefix, precision,suffix })
|
||||
},{
|
||||
normalize: toNumber,
|
||||
params:["prefix","suffix", "division","precision"],
|
||||
configKey: "currency"
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
// 配置参数: 格式化器函数的最后一个参数就是该配置参数
|
||||
$options:{
|
||||
datetime : {
|
||||
units : ["Year","Quarter","Month","Week","Day","Hour","Minute","Second","Millisecond","Microsecond"],
|
||||
weekday : ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
|
||||
shortWeekdays : ["Sun", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat"],
|
||||
monthNames : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
|
||||
shorMonthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"]
|
||||
},
|
||||
// 配置参数
|
||||
$config:{
|
||||
datetime : {
|
||||
units : ["Year","Quarter","Month","Week","Day","Hour","Minute","Second","Millisecond","Microsecond"],
|
||||
date :{
|
||||
long : 'YYYY/MM/DD HH:mm:ss',
|
||||
short : "MM/DD",
|
||||
format : "local"
|
||||
},
|
||||
month:{
|
||||
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"],
|
||||
format : 0 // 0-长名称,1-短名称,2-数字
|
||||
},
|
||||
weekday:{
|
||||
names :["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
|
||||
shortNames : ["Sun", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat"],
|
||||
format : 0, // 0-长名称,1-短名称,2-数字
|
||||
},
|
||||
time : {
|
||||
long : "HH:mm:ss",
|
||||
short : "HH:mm:ss",
|
||||
format : 'local'
|
||||
},
|
||||
}
|
||||
currency : {
|
||||
unit : "$",
|
||||
prefix : "",
|
||||
suffix : "",
|
||||
division : 3,
|
||||
precision : 2
|
||||
unit : "$", // 单位
|
||||
prefix : "", // 前缀
|
||||
suffix : "", // 后缀
|
||||
division : 3, // ,分割位
|
||||
precision : 2, // 精度
|
||||
},
|
||||
number : {
|
||||
division : 3,
|
||||
@ -50,32 +170,27 @@
|
||||
Date : value => { const d = toDate(value); return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()} ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}` },
|
||||
Null : value =>"",
|
||||
Undefined: value =>"",
|
||||
Error : value => "ERROR"
|
||||
Error : value => "ERROR",
|
||||
Boolean : value =>value ? "True":"False"
|
||||
},
|
||||
// 以下是格式化定义
|
||||
// 日期
|
||||
date : value => { const d = toDate(value); return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()}` },
|
||||
shortdate : value => { const d = toDate(value); return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()}` },
|
||||
time : value => { const d = toDate(value); return `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}` },
|
||||
shorttime : value => { const d = toDate(value); return `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}` },
|
||||
// ******************* 日期 *******************
|
||||
date : dateFormatter,
|
||||
time : timeFormatter,
|
||||
year : value => toDate(value).getFullYear(),
|
||||
month : value => toDate(value).getMonth() + 1,
|
||||
day : value => toDate(value).getDate(),
|
||||
weekdayValue : value => toDate(value).getDay(),
|
||||
weekday : value => ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][toDate(value).getDay()],
|
||||
shortWeekday : value => ["Sun", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat"][toDate(value).getDay()],
|
||||
monthName : value => ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][toDate(value).getMonth()],
|
||||
shorMonthName : value => ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"][toDate(value).getMonth()],
|
||||
// 时间
|
||||
weekday : weekdayFormatter,
|
||||
month : monthFormatter,
|
||||
// ******************* 时间 *******************
|
||||
hour : value => toDate(value).getHours(),
|
||||
hour12 : value => {const hour = toDate(value).getHours(); return hour > 12 ? hour - 12 : thour},
|
||||
minute : value => toDate(value).getMinutes(),
|
||||
second : value => toDate(value).getSeconds(),
|
||||
millisecond : value => toDate(value).getMilliseconds(),
|
||||
timestamp : value => toDate(value).getTime(),
|
||||
// 货币
|
||||
// 常规货币形式 $111,233.33
|
||||
currency : (value, prefix = "$",suffix="", division = 3,precision = 2) => toCurrency(value, { division, prefix, precision,suffix }),
|
||||
// ******************* 货币 *******************
|
||||
currency : currencyFormatter,
|
||||
// 数字,如,使用分割符
|
||||
number : (value, division = 3,precision = 0) => toCurrency(value, { division, precision})
|
||||
}
|
@ -8,19 +8,35 @@
|
||||
|
||||
module.exports = {
|
||||
// 配置参数: 格式化器函数的最后一个参数就是该配置参数
|
||||
$options:{
|
||||
$config:{
|
||||
datetime : {
|
||||
units : CN_DATETIME_UNITS,
|
||||
weekdays : CN_WEEK_DAYS,
|
||||
shortWeekdays : CN_SHORT_WEEK_DAYS,
|
||||
monthNames : CN_MONTH_NAMES,
|
||||
shorMonthNames: CN_SHORT_MONTH_NAMES
|
||||
date :{
|
||||
long : 'YYYY年MM月DD日 HH点mm分ss秒',
|
||||
short : "MM/DD",
|
||||
format : 'YYYY年MM月DD日 HH点mm分ss秒'
|
||||
},
|
||||
month:{
|
||||
names : CN_MONTH_NAMES,
|
||||
shortNames : CN_SHORT_MONTH_NAMES,
|
||||
format : 0, // 0-长名称,1-短名称,2-数字
|
||||
},
|
||||
weekday:{
|
||||
names : CN_WEEK_DAYS,
|
||||
shortNames : CN_SHORT_WEEK_DAYS,
|
||||
format : 0, // 0-长名称,1-短名称,2-数字
|
||||
},
|
||||
time:{
|
||||
long : "HH点mm分ss秒",
|
||||
short : "HH:mm:ss",
|
||||
}
|
||||
},
|
||||
|
||||
currency : {
|
||||
unit : "¥",
|
||||
prefix : "",
|
||||
suffix : "",
|
||||
division : 3,
|
||||
suffix : "元",
|
||||
division : 4,
|
||||
precision : 2
|
||||
},
|
||||
number : {
|
||||
@ -29,16 +45,10 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
$types: {
|
||||
Date: value => {const d = toDate(value);return `${d.getFullYear()}年${d.getMonth() + 1}月${d.getDate()}日 ${d.getHours()}点${d.getMinutes()}分${d.getSeconds()}秒`}
|
||||
Date: value => {const d = toDate(value);return `${d.getFullYear()}年${d.getMonth() + 1}月${d.getDate()}日 ${d.getHours()}点${d.getMinutes()}分${d.getSeconds()}秒`},
|
||||
Boolean : value =>value ? "是":"否"
|
||||
|
||||
},
|
||||
// 日期
|
||||
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()],
|
||||
shorMonthName: value => CN_SHORT_MONTH_NAMES[toDate(value).getMonth()],
|
||||
// 时间
|
||||
time : value =>{const d = toDate(value);return `${d.getHours()}点${d.getMinutes()}分${d.getSeconds()}秒`},
|
||||
// 货币
|
||||
currency : (value,prefix = "¥",suffix="", division = 4, precision = 2) => toCurrency(value, { division, prefix, precision,suffix }),
|
||||
// 中文货币,big=true代表大写形式
|
||||
|
@ -271,7 +271,7 @@ function getFormatter(scope,activeLanguage,name){
|
||||
resetScopeCache(scope,activeLanguage)
|
||||
}
|
||||
const fallbackLanguage = scope.getLanguage(activeLanguage).fallback
|
||||
// 2. 先在当前作用域中查找,再在全局查找 formatters={$types,$options,[格式化器名称]:()=>{},[格式化器名称]:()=>{}}
|
||||
// 2. 先在当前作用域中查找,再在全局查找 formatters={$types,$config,[格式化器名称]:()=>{},[格式化器名称]:()=>{}}
|
||||
const range = [
|
||||
scope.activeFormatters,
|
||||
scope.formatters[fallbackLanguage], // 如果指定了回退语言时,也在该回退语言中查找
|
||||
@ -305,9 +305,9 @@ function executeChecker(checker,value){
|
||||
/**
|
||||
* 执行格式化器并返回结果
|
||||
*
|
||||
* 格式化器this指向当前scope,并且最后一个参数是当前scope格式化器的$options
|
||||
* 格式化器this指向当前scope,并且最后一个参数是当前scope格式化器的$config
|
||||
*
|
||||
* 这样格式化器可以读取$options
|
||||
* 这样格式化器可以读取$config
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {Array[Function]} formatters 多个格式化器函数(经过包装过的)顺序执行,前一个输出作为下一个格式化器的输入
|
||||
@ -655,7 +655,7 @@ function translate(message) {
|
||||
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 formatters(){ return this._settings.formatters } // 内置格式化器{*:{$config,$types,...},zh:{$config,$types,...},en:{$config,$types,...}}
|
||||
get defaultMessageLoader(){ return this._defaultMessageLoader} // 默认语言包加载器
|
||||
|
||||
// 通过默认加载器加载文件
|
||||
|
@ -25,7 +25,7 @@ module.exports = class i18nScope {
|
||||
this._default = options.default; // 默认语言包
|
||||
this._messages = options.messages; // 当前语言包
|
||||
this._idMap = options.idMap; // 消息id映射列表
|
||||
this._formatters = options.formatters; // 当前作用域的格式化函数列表{<lang>: {$types,$options,[格式化器名称]: () => {},[格式化器名称]: () => {}}}
|
||||
this._formatters = options.formatters; // 当前作用域的格式化函数列表{<lang>: {$types,$config,[格式化器名称]: () => {},[格式化器名称]: () => {}}}
|
||||
this._loaders = options.loaders; // 异步加载语言文件的函数列表
|
||||
this._global = null; // 引用全局VoerkaI18n配置,注册后自动引用
|
||||
this._patchMessages = {}; // 语言包补丁信息{<language>: {....},<language>:{....}}
|
||||
@ -62,8 +62,8 @@ module.exports = class i18nScope {
|
||||
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 formatters() { return this._formatters;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}}
|
||||
get activeFormatters() {return this._activeFormatters} // 当前作用域激活的格式化器定义 {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () = >{}}
|
||||
get activeFormatterOptions(){return this._activeFormatterOptions} // 当前格式化器合并后的配置参数,参数已经合并了全局格式化器中的参数
|
||||
|
||||
/**
|
||||
@ -86,10 +86,12 @@ module.exports = class i18nScope {
|
||||
registerFormatter(name,value=>{...},{langauge:["zh","cht"]}) // 注册到zh和cht语言
|
||||
registerFormatter(name,value=>{...},{langauge:"zh,cht"})
|
||||
* @param {*} formatter 格式化器
|
||||
language : 声明该格式化器适用语言
|
||||
language : 字符串或数组,声明该格式化器适用语言
|
||||
*代表适用于所有语言
|
||||
语言名称,语言名称数组,或者使用,分割的语言名称字符串
|
||||
asGlobal : 注册到全局
|
||||
*/
|
||||
registerFormatter(name, formatter, { language = "*", asGlobal } = {}) {
|
||||
registerFormatter(name, formatter, { language = "*", global : asGlobal } = {}) {
|
||||
if (!isFunction(formatter) || typeof name !== "string") {
|
||||
throw new TypeError("Formatter must be a function");
|
||||
}
|
||||
@ -110,6 +112,20 @@ module.exports = class i18nScope {
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 注册多种格式化器
|
||||
* registerFormatters(={"*",zh:{...},en:{...}})
|
||||
* registerFormatters(={"*",zh:{...},en:{...}},true) 在全局注册
|
||||
* @param {*} formatters ={"*",zh:{...},en:{...}}
|
||||
* @returns
|
||||
*/
|
||||
registerFormatters(formatters,isGlobal=false) {
|
||||
Object.entries(formatters).forEach(([language,fns]=>{
|
||||
Object.entries(fns).forEach(([name,formatter])=>{
|
||||
this.registerFormatter(name,formatter,{language})
|
||||
})
|
||||
}))
|
||||
}
|
||||
/**
|
||||
* 注册默认文本信息加载器
|
||||
* @param {Function} 必须是异步函数或者是返回Promise
|
||||
@ -144,7 +160,7 @@ module.exports = class i18nScope {
|
||||
/**
|
||||
* 初始化格式化器
|
||||
* 激活和默认语言的格式化器采用静态导入的形式,而没有采用异步块的形式,这是为了确保首次加载时的能马上读取,而减少延迟加载
|
||||
* _activeFormatters={$options:{...},$types:{...},[格式化器名称]:()=>{...},[格式化器名称]:()=>{...},...}}
|
||||
* _activeFormatters={$config:{...},$types:{...},[格式化器名称]:()=>{...},[格式化器名称]:()=>{...},...}}
|
||||
*/
|
||||
_initFormatters(newLanguage){
|
||||
this._activeFormatters = {}
|
||||
@ -167,7 +183,7 @@ module.exports = class i18nScope {
|
||||
* 当切换语言时,格式化器应该切换到对应语言的格式化器
|
||||
*
|
||||
* 重要需要处理:
|
||||
* $options参数采用合并继承机制
|
||||
* $config参数采用合并继承机制
|
||||
*
|
||||
*
|
||||
* @param {*} language
|
||||
@ -192,19 +208,19 @@ module.exports = class i18nScope {
|
||||
}
|
||||
/**
|
||||
* 生成格式化器的配置参数,该参数由以下合并而成:
|
||||
* - global.formatters[*].$options
|
||||
* - global.formatters[language].$options
|
||||
* - scope.activeFormatters.$options 当前优先
|
||||
* - global.formatters[*].$config
|
||||
* - global.formatters[language].$config
|
||||
* - scope.activeFormatters.$config 当前优先
|
||||
*/
|
||||
_generateFormatterOptions(language){
|
||||
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",{}))
|
||||
options = Object.assign({},getByPath(this._global.formatters,`*.$config`,{}))
|
||||
deepMixin(options,getByPath(this._global.formatters,`${language}.$config`,{}))
|
||||
deepMixin(options,getByPath(this._activeFormatters,"$config",{}))
|
||||
}catch(e){
|
||||
if(this.debug) console.error(`Error while generate <${language}> formatter options: `,e)
|
||||
if(!options) options = this._activeFormatters.$options || {}
|
||||
if(!options) options = this._activeFormatters.$config || {}
|
||||
}
|
||||
return this._activeFormatterOptions = options
|
||||
}
|
||||
|
@ -151,12 +151,12 @@ function toNumber(value,defualt=0) {
|
||||
*
|
||||
* @param {*} value 可以是数字也可以是字符串
|
||||
* @param {*} division 分割符号位数,3代表每3个数字添加一个,号
|
||||
* @param {*} prefix 前缀,货币单位
|
||||
* @param {*} suffix 前缀,货币单位
|
||||
* @param {*} prefix 前缀
|
||||
* @param {*} suffix 后缀
|
||||
* @param {*} precision 小数点精确到几位,0-自动
|
||||
* @returns
|
||||
*/
|
||||
function toCurrency(value,{division=3,prefix="",precision=0,suffix=""}={}){
|
||||
function toCurrency(value,{unit="",division=3,prefix="",precision=0,suffix=""}={}){
|
||||
let [wholeValue,decimalValue] = String(value).split(".")
|
||||
let result = []
|
||||
for(let i=0;i<wholeValue.length;i++){
|
||||
@ -169,7 +169,7 @@ function toNumber(value,defualt=0) {
|
||||
}
|
||||
result.push(`.${decimalValue}`)
|
||||
}
|
||||
return prefix + result.join("") + suffix
|
||||
return `${prefix}${unit}${result.join("")}${suffix}`
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,9 +225,9 @@ function getByPath(obj,path,defaultValue){
|
||||
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 hourNum = v.getHours()
|
||||
const hour = String(hourNum).minute = String(v.getMinutes()),second = String(v.getSeconds()),millisecond=String(v.getMilliseconds())
|
||||
const timezone = v.getTimezoneOffset()
|
||||
const vars = [
|
||||
["YYYY", year], // 2018 年,四位数
|
||||
["YY", year.substring(year.length - 2, year.length)], // 18 年,两位数
|
||||
@ -236,10 +236,33 @@ function formatDatetime(value,templ="YYYY/MM/DD HH:mm:ss"){
|
||||
["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", hour.padStart(2, "0")], // 00-23 24小时,两位数
|
||||
["H", hour], // 0-23 24小时
|
||||
["hh", String(hourNum > 12 ? hourNum - 12 : hourNum).padStart(2, "0")], // 01-12 12小时,两位数
|
||||
["h", String(hourNum > 12 ? hourNum - 12 : hourNum)], // 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 = replaceAll(result,key,value))
|
||||
return result
|
||||
}
|
||||
function formatTime(value,templ="HH:mm:ss"){
|
||||
const v = toDate(value)
|
||||
const hourNum = v.getHours()
|
||||
const hour = String(hourNum).minute = String(v.getMinutes()),second = String(v.getSeconds()),millisecond=String(v.getMilliseconds())
|
||||
let result = templ
|
||||
const vars = [
|
||||
["HH", hour.padStart(2, "0")], // 00-23 24小时,两位数
|
||||
["H", 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小时
|
||||
["h", String(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 秒,两位数
|
||||
@ -250,10 +273,9 @@ function formatDatetime(value,templ="YYYY/MM/DD HH:mm:ss"){
|
||||
["A", hour > 12 ? "PM" : "AM"], // AM / PM 上/下午,大写
|
||||
["a", hour > 12 ? "pm" : "am"] // am / pm 上/下午,小写
|
||||
]
|
||||
vars.forEach(([key,value])=>result = result.replace(key,value))
|
||||
vars.forEach(([key,value])=>result = replaceAll(result,key,value))
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换所有字符串
|
||||
* 低版本ES未提供replaceAll,此函数用来替代
|
||||
@ -282,10 +304,23 @@ function replaceAll(str,findValue,replaceValue){
|
||||
*
|
||||
* - 函数第一个参数是上一上格式化器的输出
|
||||
* - 支持0-N个简单类型的入参
|
||||
* - 格式化器可以在格式化器的$options参数指定一个键值来配置不同语言时的参数
|
||||
* - 格式化器可以在格式化器的$config参数指定一个键值来配置不同语言时的参数
|
||||
*
|
||||
* createFormatter((value,prefix,suffix, division ,precision,options)=>{
|
||||
*
|
||||
* "currency":createFormatter((value,prefix,suffix, division ,precision,options)=>{
|
||||
* // 无论在格式化入参数是多少个,经过处理后在此得到prefix,suffix, division ,precision参数已经是经过处理后的参数
|
||||
* 依次读取格式化器的参数合并:
|
||||
* - 从当前激活格式化器的$config中读取配置参数
|
||||
* - 在t函数后传入参数
|
||||
* 比如currency格式化器支持4参数,其入参顺序是prefix,suffix, division ,precision
|
||||
* 那么在t函数中可以使用以下五种入参数方式
|
||||
* {value | currency } //prefix=undefined,suffix=undefined, division=undefined ,precision=undefined
|
||||
* {value | currency(prefix) }
|
||||
* {value | currency(prefix,suffix) }
|
||||
* {value | currency(prefix,suffix,division) }
|
||||
* {value | currency(prefix,suffix,division,precision)}
|
||||
*
|
||||
* 经过createFormatter处理后,会从当前激活格式化器的$config中读取prefix,suffix, division ,precision参数作为默认参数
|
||||
* 然后t函数中的参数会覆盖默认参数,优先级更高
|
||||
* },
|
||||
* {
|
||||
* unit:"$",
|
||||
@ -295,8 +330,9 @@ function replaceAll(str,findValue,replaceValue){
|
||||
* precision
|
||||
* },
|
||||
* {
|
||||
* normalize:value=>{...},
|
||||
* params:["prefix","suffix", "division" ,"precision"] // 声明参数顺序
|
||||
* optionKey:"currency" // 声明特定语言下的配置在$options.currency
|
||||
* configKey:"currency" // 声明特定语言下的配置在$config.currency
|
||||
* }
|
||||
* )
|
||||
*
|
||||
@ -309,13 +345,13 @@ function replaceAll(str,findValue,replaceValue){
|
||||
let opts = Object.assign({
|
||||
normalize : null, // 对输入值进行规范化处理,如进行时间格式化时,为了提高更好的兼容性,支持数字时间戳/字符串/Date等,需要对输入值进行处理,如强制类型转换等
|
||||
params : [], // 声明参数顺序
|
||||
optionKeyPath: null // 声明该格式化器在$options中的路径,支持简单的使用.的路径语法
|
||||
configKey : null // 声明该格式化器在$config中的路径,支持简单的使用.的路径语法
|
||||
})
|
||||
|
||||
// 最后一个参数是传入activeFormatterOptions参数
|
||||
const wrappedFn = function(value,...args){
|
||||
let finalValue = value
|
||||
// 1. 输入值规范处理,主要的类型转换等
|
||||
// 1. 输入值规范处理,主要是进行类型转换,确保输入的数据类型及相关格式的正确性,提高数据容错性
|
||||
if(isFunction(opts.normalize)){
|
||||
try{
|
||||
finalValue = opts.normalize(finalValue)
|
||||
@ -325,16 +361,16 @@ function replaceAll(str,findValue,replaceValue){
|
||||
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. 将翻译函数执行格式化器时传入的参数具有高优先级
|
||||
const activeConfig =Object.assign({},defaultParams,getByPath(activeFormatterOpts,opts.configKey,{}))
|
||||
let finalArgs = opts.params.map(param=>getByPath(activeConfig,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
|
||||
wrappedFn.configurable = true // 当函数是可配置时才在最后一个参数中传入$config
|
||||
return wrappedFn
|
||||
}
|
||||
|
||||
@ -352,6 +388,8 @@ module.exports ={
|
||||
replaceAll,
|
||||
getByPath,
|
||||
getDataTypeName,
|
||||
formatDatetime,
|
||||
formatTime,
|
||||
toDate,
|
||||
toNumber,
|
||||
toCurrency,
|
||||
|
@ -1,7 +1,7 @@
|
||||
const dayjs = require('dayjs');
|
||||
const { getInterpolatedVars, replaceInterpolatedVars , translate} = require('../packages/runtime/index.js')
|
||||
|
||||
const messages = {
|
||||
const { i18nScope, translate, getInterpolatedVars } = require('../packages/runtime/dist/runtime.cjs')
|
||||
|
||||
const loaders = {
|
||||
zh:{
|
||||
1:"你好",
|
||||
2:"现在是{}",
|
||||
@ -16,88 +16,60 @@ const messages = {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const formatters = {
|
||||
zh:{
|
||||
$config:{},
|
||||
$types:{},
|
||||
book:(v)=>`《${v}》`,
|
||||
},
|
||||
en:{
|
||||
$config:{},
|
||||
$types:{ },
|
||||
book:(v)=>`<${v}>`,
|
||||
},
|
||||
}
|
||||
const idMap = {
|
||||
"你好":1,
|
||||
"现在是{}":2,
|
||||
"我出生于{year}年,今年{age}岁":3,
|
||||
"我有{}个朋友":4
|
||||
1:"你好",
|
||||
2:"现在是{}",
|
||||
3:"我出生于{year}年,今年{age}岁"
|
||||
4:"我有{}个朋友"
|
||||
}
|
||||
const languages = [
|
||||
{ name: "zh", title: "中文" },
|
||||
{ name: "en", title: "英文" },
|
||||
{ name: "de", title: "德语" },
|
||||
{ name: "jp", title: "日语" }
|
||||
]
|
||||
|
||||
|
||||
const scope = new i18nScope({
|
||||
id : "test",
|
||||
defaultLanguage: "zh",
|
||||
activeLanguage : "zh",
|
||||
namespaces : {},
|
||||
default : loaders.zh, // 默认语言包
|
||||
messages : loaders.zh, // 当前语言包
|
||||
languages, // 语言配置
|
||||
idMap, // 消息id映射列表
|
||||
formatters, // 扩展自定义格式化器
|
||||
loaders // 语言包加载器
|
||||
})
|
||||
|
||||
|
||||
let scope1 ={
|
||||
defaultLanguage: "zh", // 默认语言名称
|
||||
default: messages.zh,
|
||||
messages :messages.zh,
|
||||
idMap,
|
||||
formatters:{ // 当前作用域的格式化函数列表
|
||||
"*":{
|
||||
$types:{
|
||||
Date:(v)=>dayjs(v).format('YYYY-MM-DD HH:mm:ss'),
|
||||
Boolean:(v)=>v?"True":"False",
|
||||
},
|
||||
sum:(v,n=1)=>v+n,
|
||||
double:(v)=>v*2,
|
||||
upper:(v)=>v.toUpperCase(),
|
||||
lower:(v)=>v.toLowerCase()
|
||||
},
|
||||
zh:{
|
||||
$types:{
|
||||
Date:(v)=>dayjs(v).format('YYYY年MM月DD日 HH点mm分ss秒'),
|
||||
Boolean:(v)=>v?"是":"否",
|
||||
},
|
||||
book:(v)=>`《${v}》`,
|
||||
},
|
||||
en:{
|
||||
$types:{
|
||||
const t = translate.bind(scope)
|
||||
|
||||
},
|
||||
book:(v)=>`<${v}>`,
|
||||
},
|
||||
},
|
||||
loaders:{}, // 异步加载语言文件的函数列表
|
||||
global:{// 引用全局VoerkaI18n配置
|
||||
defaultLanguage: "zh",
|
||||
activeLanguage: "zh",
|
||||
languages:[
|
||||
{name:"zh",title:"中文",default:true},
|
||||
{name:"en",title:"英文"},
|
||||
{name:"de",title:"德语"},
|
||||
{name:"jp",title:"日语"}
|
||||
],
|
||||
formatters:{ // 当前作用域的格式化函数列表
|
||||
"*":{
|
||||
$types:{
|
||||
|
||||
}
|
||||
},
|
||||
zh:{
|
||||
$types:{
|
||||
|
||||
}
|
||||
},
|
||||
en:{
|
||||
$types:{
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const replaceVars = replaceInterpolatedVars.bind(scope1)
|
||||
const t = translate.bind(scope1)
|
||||
|
||||
|
||||
function changeLanguage(language){
|
||||
scope1.global.activeLanguage = language
|
||||
scope1.messages = messages[language]
|
||||
|
||||
}
|
||||
// 适用于所有语言的格式化器,并且注册到全局
|
||||
scope.registerFormatters({
|
||||
"*":{
|
||||
sum : (v,n=1)=>v+n,
|
||||
double: (v)=>v*2,
|
||||
upper : (v)=>v.toUpperCase(),
|
||||
}
|
||||
},true)
|
||||
|
||||
beforeEach(() => {
|
||||
scope1.global.activeLanguage = "zh" // 切换到中文
|
||||
scope.change("zh")
|
||||
});
|
||||
|
||||
|
||||
@ -121,7 +93,6 @@ test("获取翻译内容中的插值变量",done=>{
|
||||
expect(results[1].name).toEqual("city");
|
||||
expect(results[1].formatters.length).toEqual(0);
|
||||
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
@ -136,7 +107,6 @@ test("获取翻译内容中定义了重复的插值变量",done=>{
|
||||
expect(results[1].formatters[0].name).toEqual("x");
|
||||
expect(results[1].formatters[0].args).toEqual([]);
|
||||
|
||||
|
||||
expect(results[2].name).toEqual("a");
|
||||
expect(results[2].formatters.length).toEqual(2);
|
||||
expect(results[2].formatters[0].name).toEqual("x");
|
||||
@ -148,45 +118,45 @@ test("获取翻译内容中定义了重复的插值变量",done=>{
|
||||
})
|
||||
|
||||
test("替换翻译内容的位置插值变量",done=>{
|
||||
expect(replaceVars("{}{}{}",1,2,3)).toBe("123");
|
||||
expect(replaceVars("{a}{b}{c}",1,2,3)).toBe("123");
|
||||
expect(t("{}{}{}",1,2,3)).toBe("123");
|
||||
expect(t("{a}{b}{c}",1,2,3)).toBe("123");
|
||||
// 定义了一些无效的格式化器,直接忽略
|
||||
expect(replaceVars("{a|xxx}{b|dd}{c|}",1,2,3)).toBe("123");
|
||||
expect(replaceVars("{a|xxx}{b|dd}{c|}",1,2,3,4,5,6)).toBe("123");
|
||||
expect(replaceVars("{ a|}{b|dd}{c|}{}",1,2,3)).toBe("123{}");
|
||||
expect(t("{a|xxx}{b|dd}{c|}",1,2,3)).toBe("123");
|
||||
expect(t("{a|xxx}{b|dd}{c|}",1,2,3,4,5,6)).toBe("123");
|
||||
expect(t("{ a|}{b|dd}{c|}{}",1,2,3)).toBe("123{}");
|
||||
// 中文状态下true和false被转换成中文的"是"和"否"
|
||||
expect(replaceVars("{}{}{}",1,"2",true)).toBe("12是");
|
||||
expect(replaceVars("{|double}{}{}",1,"2",true)).toBe("22是");
|
||||
expect(t("{}{}{}",1,"2",true)).toBe("12是");
|
||||
expect(trim("{|double}{}{}",1,"2",true)).toBe("22是");
|
||||
done()
|
||||
})
|
||||
|
||||
test("替换翻译内容的命名插值变量",done=>{
|
||||
expect(replaceVars("{a}{b}{c}",{a:11,b:22,c:33})).toBe("112233");
|
||||
expect(replaceVars("{a}{b}{c}{a}{b}{c}",{a:1,b:"2",c:3})).toBe("123123");
|
||||
expect(t("{a}{b}{c}",{a:11,b:22,c:33})).toBe("112233");
|
||||
expect(t("{a}{b}{c}{a}{b}{c}",{a:1,b:"2",c:3})).toBe("123123");
|
||||
done()
|
||||
})
|
||||
|
||||
test("命名插值变量使用格式化器",done=>{
|
||||
// 提供无效的格式化器,直接忽略
|
||||
expect(replaceVars("{a|x}{b|x|y}{c|}",{a:1,b:2,c:3})).toBe("123");
|
||||
expect(replaceVars("{a|x}{b|x|y}{c|double}",{a:1,b:2,c:3})).toBe("126");
|
||||
expect(t("{a|x}{b|x|y}{c|}",{a:1,b:2,c:3})).toBe("123");
|
||||
expect(t("{a|x}{b|x|y}{c|double}",{a:1,b:2,c:3})).toBe("126");
|
||||
// 默认的字符串格式化器,不需要定义使用字符串方法
|
||||
expect(replaceVars("{a|x}{b|x|y}{c|double}",{a:1,b:2,c:3})).toBe("126");
|
||||
expect(t("{a|x}{b|x|y}{c|double}",{a:1,b:2,c:3})).toBe("126");
|
||||
// padStart格式化器是字符串的方法,不需要额外定义可以直接使用
|
||||
expect(replaceVars("{a|padStart(10)}",{a:"123"})).toBe(" 123");
|
||||
expect(replaceVars("{a|padStart(10)|trim}",{a:"123"})).toBe("123");
|
||||
expect(t("{a|padStart(10)}",{a:"123"})).toBe(" 123");
|
||||
expect(t("{a|padStart(10)|trim}",{a:"123"})).toBe("123");
|
||||
done()
|
||||
})
|
||||
|
||||
|
||||
test("命名插值变量使用格式化器",done=>{
|
||||
// 提供无效的格式化器,直接忽略
|
||||
expect(replaceVars("{a|x}{b|x|y}{c|}",{a:1,b:2,c:3})).toBe("123");
|
||||
expect(replaceVars("{a|x}{b|x|y}{c|double}",{a:1,b:2,c:3})).toBe("126");
|
||||
expect(t("{a|x}{b|x|y}{c|}",{a:1,b:2,c:3})).toBe("123");
|
||||
expect(t("{a|x}{b|x|y}{c|double}",{a:1,b:2,c:3})).toBe("126");
|
||||
// 默认的字符串格式化器,不需要定义使用字符串方法
|
||||
expect(replaceVars("{a|x}{b|x|y}{c|double}",{a:1,b:2,c:3})).toBe("126");
|
||||
expect(replaceVars("{a|padStart(10)}",{a:"123"})).toBe(" 123");
|
||||
expect(replaceVars("{a|padStart(10)|trim}",{a:"123"})).toBe("123");
|
||||
expect(t("{a|x}{b|x|y}{c|double}",{a:1,b:2,c:3})).toBe("126");
|
||||
expect(t("{a|padStart(10)}",{a:"123"})).toBe(" 123");
|
||||
expect(t("{a|padStart(10)|trim}",{a:"123"})).toBe("123");
|
||||
done()
|
||||
})
|
||||
|
||||
@ -194,11 +164,11 @@ test("命名插值变量使用格式化器",done=>{
|
||||
|
||||
test("切换到其他语言时的自动匹配同名格式化器",done=>{
|
||||
// 默认的字符串类型的格式化器
|
||||
expect(replaceVars("{a}",{a:true})).toBe("是");
|
||||
expect(replaceVars("{name|book}是毛泽东思想的重要载体","毛泽东选集")).toBe("《毛泽东选集》是毛泽东思想的重要载体");
|
||||
expect(t("{a}",{a:true})).toBe("是");
|
||||
expect(t("{name|book}是毛泽东思想的重要载体","毛泽东选集")).toBe("《毛泽东选集》是毛泽东思想的重要载体");
|
||||
changeLanguage("en")
|
||||
expect(replaceVars("{a}",{a:false})).toBe("False");
|
||||
expect(replaceVars("{name|book}是毛泽东思想的重要载体","毛泽东选集")).toBe("<毛泽东选集>是毛泽东思想的重要载体");
|
||||
expect(t("{a}",{a:false})).toBe("False");
|
||||
expect(t("{name|book}是毛泽东思想的重要载体","毛泽东选集")).toBe("<毛泽东选集>是毛泽东思想的重要载体");
|
||||
done()
|
||||
})
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user