更新fortmatters
This commit is contained in:
parent
b8f59c43bc
commit
6be922d6fe
@ -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
|
* 空值: null,undefined
|
||||||
*
|
*
|
||||||
* 当输入空值时的行为
|
* 当输入空值时的处理逻辑
|
||||||
*
|
*
|
||||||
* { value | empty } == 转换显示为''
|
* { value | empty } == 转换显示为'',并且忽略
|
||||||
* { value | empty('无') } == 无
|
* { value | empty('无') } == 无
|
||||||
* { value | unit('KB') | empty('0') } == 0KB
|
* { value | unit('KB') | empty('0') } == 0KB
|
||||||
*
|
*
|
||||||
@ -54,14 +54,16 @@ import { toNumber,isFunction } from "../utils"
|
|||||||
*
|
*
|
||||||
* @param {*} value
|
* @param {*} value
|
||||||
* @param {String} escapeValue
|
* @param {String} escapeValue
|
||||||
|
* @paran {String} next 下一步行为,取值true/false,break,skip,默认是break
|
||||||
* @param {*} options
|
* @param {*} options
|
||||||
*/
|
*/
|
||||||
function empty(value,escapeValue,next) {
|
function empty(value,escapeValue,next,options) {
|
||||||
if(next===true) next = 'break'
|
if(next===false) next = 'break'
|
||||||
let opts = Object.assign({escape:"",next:'ignore'},options.empty || {})
|
if(next===true) next = 'skip'
|
||||||
if(!escapeValue) opts.escape = escapeValue
|
let opts = Object.assign({escape:"",next:'break',values:[]},options.empty || {})
|
||||||
|
if(escapeValue!=undefined) opts.escape = escapeValue
|
||||||
let emptyValues = [undefined,null]
|
let emptyValues = [undefined,null]
|
||||||
if(Array.isArray(opts.values)) emptyValues.push(...opts.values)
|
if(Array.isArray(opts.values)) emptyValues.push(...opts.values)
|
||||||
if(emptyValues.includes(value)){
|
if(emptyValues.includes(value)){
|
||||||
return {value:opts.escape,next: opts.next}
|
return {value:opts.escape,next: opts.next}
|
||||||
}else{
|
}else{
|
||||||
@ -73,13 +75,13 @@ empty.paramCount = 2
|
|||||||
/**
|
/**
|
||||||
* 当执行格式化器出错时的显示内容.
|
* 当执行格式化器出错时的显示内容.
|
||||||
|
|
||||||
{ value | error } ==
|
{ value | error } == 默认
|
||||||
{ value | error('') } == 显示空字符串
|
{ value | error('') } == 显示空字符串
|
||||||
{ value | error('ERROR') } == 显示ERROR字样
|
{ value | error('ERROR') } == 显示ERROR字样
|
||||||
{ value | error('ERROR:{ message}') } == 显示error.message
|
{ value | error('ERROR:{ message}') } == 显示error.message
|
||||||
{ value | error('ERROR:{ error}') } == 显示error.constructor.name
|
{ value | error('ERROR:{ error}') } == 显示error.constructor.name
|
||||||
|
{ value | error('ERROR:{ error}',) } == 显示error.constructor.name
|
||||||
|
|
||||||
this--> scope实例
|
|
||||||
|
|
||||||
* @param {*} value
|
* @param {*} value
|
||||||
* @param {*} escapeValue
|
* @param {*} escapeValue
|
||||||
@ -87,21 +89,22 @@ this--> scope实例
|
|||||||
* @param {*} options 格式化器的全局配置参数
|
* @param {*} options 格式化器的全局配置参数
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function error(value,escapeValue,next,options) {
|
function error(value,escapeValue,next,options) {
|
||||||
|
|
||||||
if(value instanceof Error){
|
if(value instanceof Error){
|
||||||
|
if(scope.debug) console.error(`Error while execute formatter<${value.formatter}>:`,e)
|
||||||
|
const scope = this
|
||||||
try{
|
try{
|
||||||
let opts = Object.assign({escape:"",next:'break'},options.error || {})
|
let opts = Object.assign({escape:null,next:'break'},options.error || {})
|
||||||
if(!escapeValue) opts.escape = escapeValue
|
if(escapeValue!=undefined) opts.escape = escapeValue
|
||||||
if(!next) opts.next = next
|
if(next!=undefined) opts.next = next
|
||||||
return {
|
return {
|
||||||
value : String(opts.escape).replace(/\{\s*message\s*\}/g,value.message)
|
value : opts.escape ? String(opts.escape).replace(/\{\s*message\s*\}/g,value.message).replace(/\{\s*error\s*\}/g,value.constructor.name) : null,
|
||||||
.replace(/\{\s*error\s*\}/g,value.constructor.name)
|
|
||||||
next : opts.next
|
next : opts.next
|
||||||
}
|
}
|
||||||
}cache(e){
|
}catch(e){
|
||||||
if(this.debug) console.error(`Error while execute formatter: ${e.message}`)
|
if(scope.debug) console.error(`Error while execute formatter:`,e.message)
|
||||||
}
|
}
|
||||||
|
return value
|
||||||
}else{
|
}else{
|
||||||
return value
|
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_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 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 {*} brief
|
||||||
* @param {*} options
|
* @param {*} options
|
||||||
*/
|
*/
|
||||||
function fileSize(value,unit,brief=true,options={}){
|
function filesize(value,unit,brief=true,options={}){
|
||||||
let opts = Object.assign({
|
let opts = Object.assign({
|
||||||
precision: 2,
|
precision: 2,
|
||||||
brief : FILE_SIZE_BRIEF_UNITS,
|
brief : FILE_SIZE_BRIEF_UNITS,
|
||||||
@ -179,14 +181,17 @@ function fileSize(value,unit,brief=true,options={}){
|
|||||||
}
|
}
|
||||||
return brief ? `${result} ${opts.brief[unitIndex]}` : `${result} ${opts.brief[whole]}`
|
return brief ? `${result} ${opts.brief[unitIndex]}` : `${result} ${opts.brief[whole]}`
|
||||||
}
|
}
|
||||||
fileSize.paramCount = 2
|
filesize.paramCount = 2
|
||||||
fileSize.escape = 0
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
dict,
|
dict,
|
||||||
prefix,
|
prefix,
|
||||||
suffix,
|
suffix,
|
||||||
filesize,
|
filesize,
|
||||||
error,
|
error,
|
||||||
empty
|
empty
|
||||||
}
|
}
|
||||||
|
|
@ -32,12 +32,12 @@
|
|||||||
empty:{
|
empty:{
|
||||||
//values : [], // 可选,定义空值,如果想让0,''也为空值,可以指定values=[0,'']
|
//values : [], // 可选,定义空值,如果想让0,''也为空值,可以指定values=[0,'']
|
||||||
escape : "", // 当空值时显示的备用值
|
escape : "", // 当空值时显示的备用值
|
||||||
next : null // 当空值时下一步的行为: break=中止;ignore=忽略
|
next : 'break' // 当空值时下一步的行为: break=中止;skip=跳过
|
||||||
},
|
},
|
||||||
error : {
|
error : {
|
||||||
//当错误时显示的内容,支持的插值变量有message=错误信息,error=错误类名,也可以是一个返回上面内容的同步函数
|
//当错误时显示的内容,支持的插值变量有message=错误信息,error=错误类名,也可以是一个返回上面内容的同步函数
|
||||||
escape : "", // 默认当错误时显示空内容
|
escape : null, // 默认当错误时显示空内容
|
||||||
next : null // 当出错时下一步的行为: break=中止;ignore=忽略
|
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"],
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
|
|
||||||
const enFormatters = require("./en")
|
const enFormatters = require("./en")
|
||||||
const zhFormatters = require("./zh")
|
const zhFormatters = require("./zh")
|
||||||
const commonFormatters = require("./common")
|
const defaultFormatters = require("./default")
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"*":{
|
"*":{
|
||||||
...enFormatters,
|
...enFormatters,
|
||||||
...commonFormatters
|
...defaultFormatters
|
||||||
},
|
},
|
||||||
zh:zhFormatters
|
zh:zhFormatters
|
||||||
}
|
}
|
@ -17,7 +17,7 @@ module.exports = {
|
|||||||
shorMonthNames: CN_SHORT_MONTH_NAMES
|
shorMonthNames: CN_SHORT_MONTH_NAMES
|
||||||
},
|
},
|
||||||
currency : {
|
currency : {
|
||||||
unit : "$",
|
unit : "¥",
|
||||||
prefix : "",
|
prefix : "",
|
||||||
suffix : "",
|
suffix : "",
|
||||||
division : 3,
|
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 => {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()],
|
weekday : value => CN_WEEK_DAYS[toDate(value).getDay()],
|
||||||
shortWeekday : value => CN_SHORT_WEEK_DAYS[toDate(value).getDay()],
|
shortWeekday : value => CN_SHORT_WEEK_DAYS[toDate(value).getDay()],
|
||||||
monthName : value => CN_MONTH_NAMES[toDate(value).getMonth()],
|
monthName : value => CN_MONTH_NAMES[toDate(value).getMonth()],
|
||||||
|
@ -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 EventEmitter = require("./eventemitter")
|
||||||
const i18nScope = require("./scope.js")
|
const inlineFormatters = require("./formatters")
|
||||||
let inlineFormatters = require("./formatters")
|
const i18nScope = require("./scope")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 用来提取字符里面的插值变量参数 , 支持管道符 { var | formatter | formatter }
|
// 用来提取字符里面的插值变量参数 , 支持管道符 { var | formatter | formatter }
|
||||||
@ -150,10 +149,11 @@ function forEachInterpolatedVars(str,callback,options={}){
|
|||||||
const formatters = parseFormatters(match.groups.formatters)
|
const formatters = parseFormatters(match.groups.formatters)
|
||||||
if(isFunction(callback)){
|
if(isFunction(callback)){
|
||||||
try{
|
try{
|
||||||
|
const finalValue = callback(varname,formatters,match[0])
|
||||||
if(opts.replaceAll){ // 在某此版本上可能没有
|
if(opts.replaceAll){ // 在某此版本上可能没有
|
||||||
result=result.replaceAll(match[0],callback(varname,formatters,match[0]))
|
result=result.replaceAll(match[0],finalValue)
|
||||||
}else{
|
}else{
|
||||||
result=result.replace(new RegExp(match[0],"gm"),callback(varname,formatters,match[0]))
|
result=replaceAll(result,match[0],finalValue)
|
||||||
}
|
}
|
||||||
}catch{// callback函数可能会抛出异常,如果抛出异常,则中断匹配过程
|
}catch{// callback函数可能会抛出异常,如果抛出异常,则中断匹配过程
|
||||||
break
|
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 {*} value
|
||||||
* @param {Array[Function]} formatters 多个格式化器函数(经过包装过的)顺序执行,前一个输出作为下一个格式化器的输入
|
* @param {Array[Function]} formatters 多个格式化器函数(经过包装过的)顺序执行,前一个输出作为下一个格式化器的输入
|
||||||
*/
|
*/
|
||||||
function executeFormatter(value,formatters,scope){
|
function executeFormatter(value,formatters,scope,template){
|
||||||
if(formatters.length===0) return value
|
if(formatters.length===0) return value
|
||||||
let result = value
|
let result = value
|
||||||
|
|
||||||
const emptyChecker = formatters.find(func=>func.$name==='empty')
|
const emptyChecker = formatters.find(func=>func.$name==='empty')
|
||||||
const errorChecker = formatters.find(func=>func.$name==='error')
|
const errorChecker = formatters.find(func=>func.$name==='error')
|
||||||
|
|
||||||
// 当输入是一个空值时
|
// 1. 空值检查
|
||||||
if(emptyChecker){
|
if(emptyChecker){
|
||||||
const { value,next } = emptyChecker(result)
|
const { value,next } = executeChecker(emptyChecker,result)
|
||||||
if(next == 'break') return value
|
if(next == 'break') {
|
||||||
}
|
return value
|
||||||
if(result instanceof Error && errorChecker){
|
}else{
|
||||||
const { value,next } = errorChecker(result)
|
result = value
|
||||||
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){
|
for(let formatter of formatters){
|
||||||
try{
|
try{
|
||||||
result = formatter(result)
|
result = formatter(result)
|
||||||
}catch(e){
|
}catch(e){
|
||||||
if(scope.debug) console.error(`Error while execute i18n formatter<${formatter.$name}> for ${value}: ${e.message} ` )
|
e.formatter = formatter.$name
|
||||||
const { value,next } = errorChecker(e)
|
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(next=="break"){
|
||||||
if(value!==undefined) result = value
|
if(value!==undefined) result = value
|
||||||
break
|
break
|
||||||
}else if(next=="ignore"){
|
}else if(next=="skip"){
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if(value!==undefined) result = value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
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",[]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* 将[[格式化器名称,[参数,参数,...]],[格式化器名称,[参数,参数,...]]]格式化器包装转化为
|
* 将[[格式化器名称,[参数,参数,...]],[格式化器名称,[参数,参数,...]]]格式化器包装转化为
|
||||||
@ -342,7 +386,8 @@ function executeFormatter(value,formatters,scope){
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
@ -386,7 +431,7 @@ function wrapperFormatters(scope,activeLanguage,formatters){
|
|||||||
* @param {*} value
|
* @param {*} value
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function getFormattedValue(scope,activeLanguage,formatters,value){
|
function getFormattedValue(scope,activeLanguage,formatters,value,template){
|
||||||
// 1. 取得格式化器函数列表
|
// 1. 取得格式化器函数列表
|
||||||
const formatterFuncs = wrapperFormatters(scope,activeLanguage,formatters)
|
const formatterFuncs = wrapperFormatters(scope,activeLanguage,formatters)
|
||||||
// 3. 执行格式化器
|
// 3. 执行格式化器
|
||||||
@ -394,10 +439,10 @@ function getFormattedValue(scope,activeLanguage,formatters,value){
|
|||||||
// 当没有格式化器时,查询是否指定了默认数据类型的格式化器,如果有则执行
|
// 当没有格式化器时,查询是否指定了默认数据类型的格式化器,如果有则执行
|
||||||
const defaultFormatter = getDataTypeDefaultFormatter(scope,activeLanguage,getDataTypeName(value))
|
const defaultFormatter = getDataTypeDefaultFormatter(scope,activeLanguage,getDataTypeName(value))
|
||||||
if(defaultFormatter){
|
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
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,9 +477,9 @@ function replaceInterpolatedVars(template,...args) {
|
|||||||
// 读取模板字符串中的插值变量列表
|
// 读取模板字符串中的插值变量列表
|
||||||
// [[var1,[formatter,formatter,...],match],[var2,[formatter,formatter,...],match],...}
|
// [[var1,[formatter,formatter,...],match],[var2,[formatter,formatter,...],match],...}
|
||||||
let varValues = args[0]
|
let varValues = args[0]
|
||||||
return forEachInterpolatedVars(template,(varname,formatters)=>{
|
return forEachInterpolatedVars(template,(varname,formatters,match)=>{
|
||||||
let value = (varname in varValues) ? varValues[varname] : ''
|
let value = (varname in varValues) ? varValues[varname] : ''
|
||||||
return getFormattedValue(scope,activeLanguage,formatters,value)
|
return getFormattedValue(scope,activeLanguage,formatters,value,template)
|
||||||
})
|
})
|
||||||
}else{
|
}else{
|
||||||
// ****************************位置插值****************************
|
// ****************************位置插值****************************
|
||||||
@ -442,9 +487,9 @@ function replaceInterpolatedVars(template,...args) {
|
|||||||
const params=(args.length===1 && Array.isArray(args[0])) ? [...args[0]] : args
|
const params=(args.length===1 && Array.isArray(args[0])) ? [...args[0]] : args
|
||||||
if(params.length===0) return template // 没有变量则不需要进行插值处理,返回原字符串
|
if(params.length===0) return template // 没有变量则不需要进行插值处理,返回原字符串
|
||||||
let i = 0
|
let i = 0
|
||||||
return forEachInterpolatedVars(template,(varname,formatters)=>{
|
return forEachInterpolatedVars(template,(varname,formatters,match)=>{
|
||||||
if(params.length>i){
|
if(params.length>i){
|
||||||
return getFormattedValue(scope,activeLanguage,formatters,params[i++])
|
return getFormattedValue(scope,activeLanguage,formatters,params[i++],template)
|
||||||
}else{
|
}else{
|
||||||
throw new Error() // 抛出异常,停止插值处理
|
throw new Error() // 抛出异常,停止插值处理
|
||||||
}
|
}
|
||||||
@ -603,19 +648,16 @@ function translate(message) {
|
|||||||
I18nManager.instance = this;
|
I18nManager.instance = this;
|
||||||
this._settings = deepMerge(defaultLanguageSettings,settings)
|
this._settings = deepMerge(defaultLanguageSettings,settings)
|
||||||
this._scopes=[] // 保存i18nScope实例
|
this._scopes=[] // 保存i18nScope实例
|
||||||
this._defaultMessageLoader = null // 默认文本加载器
|
this._defaultMessageLoader = null // 默认语言包加载器
|
||||||
}
|
}
|
||||||
get settings(){ return this._settings }
|
get settings(){ return this._settings } // 配置参数
|
||||||
get scopes(){ return this._scopes }
|
get scopes(){ return this._scopes } // 注册的报有i18nScope实例q
|
||||||
// 当前激活语言
|
get activeLanguage(){ return this._settings.activeLanguage} // 当前激活语言 名称
|
||||||
get activeLanguage(){ return this._settings.activeLanguage}
|
get defaultLanguage(){ return this._settings.defaultLanguage} // 默认语言名称
|
||||||
// 默认语言
|
get languages(){ return this._settings.languages} // 支持的语言列表
|
||||||
get defaultLanguage(){ return this._settings.defaultLanguage}
|
get formatters(){ return this._settings.formatters } // 内置格式化器{*:{$options,$types,...},zh:{$options,$types,...},en:{$options,$types,...}}
|
||||||
// 支持的语言列表
|
get defaultMessageLoader(){ return this._defaultMessageLoader} // 默认语言包加载器
|
||||||
get languages(){ return this._settings.languages}
|
|
||||||
// 内置格式化器
|
|
||||||
get formatters(){ return inlineFormatters }
|
|
||||||
get defaultMessageLoader(){ return this._defaultMessageLoader}
|
|
||||||
// 通过默认加载器加载文件
|
// 通过默认加载器加载文件
|
||||||
async loadMessagesFromDefaultLoader(newLanguage,scope){
|
async loadMessagesFromDefaultLoader(newLanguage,scope){
|
||||||
if(!isFunction(this._defaultMessageLoader)) return //throw new Error("No default message loader specified")
|
if(!isFunction(this._defaultMessageLoader)) return //throw new Error("No default message loader specified")
|
||||||
@ -627,10 +669,9 @@ function translate(message) {
|
|||||||
async change(value){
|
async change(value){
|
||||||
value=value.trim()
|
value=value.trim()
|
||||||
if(this.languages.findIndex(lang=>lang.name === value)!==-1 || isFunction(this._defaultMessageLoader)){
|
if(this.languages.findIndex(lang=>lang.name === value)!==-1 || isFunction(this._defaultMessageLoader)){
|
||||||
// 通知所有作用域刷新到对应的语言包
|
await this._refreshScopes(value) // 通知所有作用域刷新到对应的语言包
|
||||||
await this._refreshScopes(value)
|
|
||||||
this._settings.activeLanguage = value
|
this._settings.activeLanguage = value
|
||||||
await this.emit(value) /// 触发语言切换事件
|
await this.emit(value) // 触发语言切换事件
|
||||||
}else{
|
}else{
|
||||||
throw new Error("Not supported language:"+value)
|
throw new Error("Not supported language:"+value)
|
||||||
}
|
}
|
||||||
@ -640,7 +681,6 @@ function translate(message) {
|
|||||||
* @param {*} newLanguage
|
* @param {*} newLanguage
|
||||||
*/
|
*/
|
||||||
async _refreshScopes(newLanguage){
|
async _refreshScopes(newLanguage){
|
||||||
// 并发执行所有作用域语言包的加载
|
|
||||||
try{
|
try{
|
||||||
const scopeRefreshers = this._scopes.map(scope=>{
|
const scopeRefreshers = this._scopes.map(scope=>{
|
||||||
return scope.refresh(newLanguage)
|
return scope.refresh(newLanguage)
|
||||||
@ -728,8 +768,13 @@ module.exports ={
|
|||||||
I18nManager,
|
I18nManager,
|
||||||
translate,
|
translate,
|
||||||
i18nScope,
|
i18nScope,
|
||||||
|
createFormatter,
|
||||||
defaultLanguageSettings,
|
defaultLanguageSettings,
|
||||||
getDataTypeName,
|
getDataTypeName,
|
||||||
isNumber,
|
isNumber,
|
||||||
isPlainObject
|
isNothing,
|
||||||
|
isPlainObject,
|
||||||
|
isFunction,
|
||||||
|
deepMerge,
|
||||||
|
deepMixin
|
||||||
}
|
}
|
@ -17,25 +17,24 @@ const DataTypes = [
|
|||||||
|
|
||||||
module.exports = class i18nScope {
|
module.exports = class i18nScope {
|
||||||
constructor(options = {}, callback) {
|
constructor(options = {}, callback) {
|
||||||
this._id = options.id || Date.now().toString() + parseInt(Math.random() * 1000);
|
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._debug = options.debug == undefined ? process && process.env && process.env.NODE_ENV === "development" : options.debug;
|
this._languages = options.languages; // 当前作用域支持的语言列表
|
||||||
this._languages = options.languages; // 当前作用域的语言列表
|
this._defaultLanguage = options.defaultLanguage || "zh"; // 默认语言名称
|
||||||
this._defaultLanguage = options.defaultLanguage || "zh"; // 默认语言名称
|
this._activeLanguage = options.activeLanguage; // 当前语言名称
|
||||||
this._activeLanguage = options.activeLanguage; // 当前语言名称
|
this._default = options.default; // 默认语言包
|
||||||
this._default = options.default; // 默认语言包
|
this._messages = options.messages; // 当前语言包
|
||||||
this._messages = options.messages; // 当前语言包
|
this._idMap = options.idMap; // 消息id映射列表
|
||||||
this._idMap = options.idMap; // 消息id映射列表
|
this._formatters = options.formatters; // 当前作用域的格式化函数列表{<lang>: {$types,$options,[格式化器名称]: () => {},[格式化器名称]: () => {}}}
|
||||||
this._formatters = options.formatters; // 当前作用域的格式化函数列表{<lang>:{$types,$options,[格式化器名称]:()=>{},[格式化器名称]:()=>{}}}
|
this._loaders = options.loaders; // 异步加载语言文件的函数列表
|
||||||
this._loaders = options.loaders; // 异步加载语言文件的函数列表
|
this._global = null; // 引用全局VoerkaI18n配置,注册后自动引用
|
||||||
this._global = null; // 引用全局VoerkaI18n配置,注册后自动引用
|
this._patchMessages = {}; // 语言包补丁信息{<language>: {....},<language>:{....}}
|
||||||
this._activeFormatters = options.formatters[options.activeLanguage]; // 激活使用的格式化器,查找格式化器时在此查找
|
this._refreshing = false; // 正在加载语言包标识
|
||||||
this._patchMessages = {}; // 语言包补丁信息 {<language>:{....},<language>:{....}}
|
// 用来缓存格式化器的引用,当使用格式化器时可以直接引用,减少检索遍历
|
||||||
// 用来缓存格式化器的引用,当使用格式化器时可以直接引用,减少检索遍历
|
|
||||||
this.$cache = {
|
this.$cache = {
|
||||||
activeLanguage: null,
|
activeLanguage : null,
|
||||||
typedFormatters: {},
|
typedFormatters: {},
|
||||||
formatters: {},
|
formatters : {},
|
||||||
};
|
};
|
||||||
// 如果不存在全局VoerkaI18n实例,说明当前Scope是唯一或第一个加载的作用域,则自动创建全局VoerkaI18n实例
|
// 如果不存在全局VoerkaI18n实例,说明当前Scope是唯一或第一个加载的作用域,则自动创建全局VoerkaI18n实例
|
||||||
if (!globalThis.VoerkaI18n) {
|
if (!globalThis.VoerkaI18n) {
|
||||||
@ -47,41 +46,28 @@ module.exports = class i18nScope {
|
|||||||
languages: options.languages,
|
languages: options.languages,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this._global = globalThis.VoerkaI18n;
|
this._global = globalThis.VoerkaI18n;
|
||||||
// 合并补丁语言包
|
this._initFormatters(this.activeLanguage) // 初始化活动的格式化器
|
||||||
this._mergePatchedMessages();
|
this._mergePatchedMessages(); // 从本地缓存中读取并合并补丁语言包
|
||||||
this._patch(this._messages, this.activeLanguage);
|
this._patch(this._messages, this.activeLanguage); // 延后执行补丁命令,该命令会向远程下载补丁包
|
||||||
// 正在加载语言包标识
|
this.register(callback); // 在全局注册作用域
|
||||||
this._refreshing = false;
|
}
|
||||||
// 在全局注册作用域
|
get id() {return this._id;} // 作用域唯一id
|
||||||
this.register(callback);
|
get debug() {return this._debug;} // 调试开关
|
||||||
}
|
get defaultLanguage() {return this._defaultLanguage;} // 默认语言名称
|
||||||
// 作用域
|
get activeLanguage() {return this._activeLanguage;} // 默认语言名称
|
||||||
get id() {return this._id;}
|
get default() {return this._default;} // 默认语言包
|
||||||
// 调试开关
|
get messages() {return this._messages; } // 当前语言包
|
||||||
get debug() {return this._debug;}
|
get idMap() {return this._idMap;} // 消息id映射列表
|
||||||
// 默认语言名称
|
get languages() {return this._languages;} // 当前作用域支持的语言列表[{name,title,fallback}]
|
||||||
get defaultLanguage() {return this._defaultLanguage;}
|
get loaders() { return this._loaders;} // 异步加载语言文件的函数列表
|
||||||
// 默认语言名称
|
get global() { return this._global;} // 引用全局VoerkaI18n配置,注册后自动引用
|
||||||
get activeLanguage() {return this._activeLanguage;}
|
get formatters() { return this._formatters;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$options,[格式化器名称]: () = >{},[格式化器名称]: () => {}}}
|
||||||
// 默认语言包
|
get activeFormatters() {return this._activeFormatters} // 当前作用域激活的格式化器定义 {$types,$options,[格式化器名称]: () = >{},[格式化器名称]: () = >{}}
|
||||||
get default() {return this._default;}
|
get activeFormatterOptions(){return this._activeFormatterOptions} // 当前格式化器合并后的配置参数,参数已经合并了全局格式化器中的参数
|
||||||
// 当前语言包
|
|
||||||
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}
|
|
||||||
/**
|
/**
|
||||||
* 在全局注册作用域
|
* 在全局注册作用域当前作用域
|
||||||
* @param {*} callback 注册成功后的回调
|
* @param {*} callback 注册成功后的回调
|
||||||
*/
|
*/
|
||||||
register(callback) {
|
register(callback) {
|
||||||
@ -155,8 +141,30 @@ module.exports = class i18nScope {
|
|||||||
this._messages = this._default;
|
this._messages = this._default;
|
||||||
this._activeLanguage = this.defaultLanguage;
|
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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当切换语言时,格式化器应该切换到对应语言的格式化器
|
*
|
||||||
|
* 切换到对应语言的格式化器
|
||||||
|
*
|
||||||
|
* 当切换语言时,格式化器应该切换到对应语言的格式化器
|
||||||
*
|
*
|
||||||
* 重要需要处理:
|
* 重要需要处理:
|
||||||
* $options参数采用合并继承机制
|
* $options参数采用合并继承机制
|
||||||
@ -186,12 +194,18 @@ module.exports = class i18nScope {
|
|||||||
* 生成格式化器的配置参数,该参数由以下合并而成:
|
* 生成格式化器的配置参数,该参数由以下合并而成:
|
||||||
* - global.formatters[*].$options
|
* - global.formatters[*].$options
|
||||||
* - global.formatters[language].$options
|
* - global.formatters[language].$options
|
||||||
* - scope.activeFormattes.$options 优先
|
* - scope.activeFormatters.$options 当前优先
|
||||||
*/
|
*/
|
||||||
_generateFormatterOptions(language){
|
_generateFormatterOptions(language){
|
||||||
let options = Object.assign({},getByPath(global.formatters,`*.$options`,{}))
|
let options
|
||||||
deepMixin(options,getByPath(global.formatters,`${language}.$options`,{}))
|
try{
|
||||||
deepMixin(options,getByPath(scope.activeFormattes,"$options",{}))
|
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
|
return this._activeFormatterOptions = options
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -256,17 +270,13 @@ module.exports = class i18nScope {
|
|||||||
async _patch(messages, newLanguage) {
|
async _patch(messages, newLanguage) {
|
||||||
if (!isFunction(this.global.loadMessagesFromDefaultLoader)) return;
|
if (!isFunction(this.global.loadMessagesFromDefaultLoader)) return;
|
||||||
try {
|
try {
|
||||||
let pachedMessages =
|
let pachedMessages = await this.global.loadMessagesFromDefaultLoader(newLanguage,this);
|
||||||
await this.global.loadMessagesFromDefaultLoader(
|
|
||||||
newLanguage,
|
|
||||||
this
|
|
||||||
);
|
|
||||||
if (isPlainObject(pachedMessages)) {
|
if (isPlainObject(pachedMessages)) {
|
||||||
Object.assign(messages, pachedMessages);
|
Object.assign(messages, pachedMessages);
|
||||||
this._savePatchedMessages(pachedMessages, newLanguage);
|
this._savePatchedMessages(pachedMessages, newLanguage);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} 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) {
|
_savePatchedMessages(messages, language) {
|
||||||
try {
|
try {
|
||||||
if (globalThis.localStorage) {
|
if (globalThis.localStorage) {
|
||||||
globalThis.localStorage.setItem(
|
globalThis.localStorage.setItem(`voerkai18n_${this.id}_${language}_patched_messages`,JSON.stringify(messages));
|
||||||
`voerkai18n_${this.id}_${language}_patched_messages`,
|
|
||||||
JSON.stringify(messages)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (this.$cache._debug)
|
if (this.$cache._debug) console.error("Error while save voerkai18n patched messages:",e);
|
||||||
console.error(
|
|
||||||
"Error while save voerkai18n patched messages:",
|
|
||||||
e.message
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -319,11 +322,7 @@ module.exports = class i18nScope {
|
|||||||
*/
|
*/
|
||||||
_getPatchedMessages(language) {
|
_getPatchedMessages(language) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(
|
return JSON.parse(localStorage.getItem(`voerkai18n_${this.id}_${language}_patched_messages`));
|
||||||
localStorage.getItem(
|
|
||||||
`voerkai18n_${this.id}_${language}_patched_messages`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -254,22 +254,92 @@ function formatDatetime(value,templ="YYYY/MM/DD HH:mm:ss"){
|
|||||||
return result
|
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({
|
let opts = Object.assign({
|
||||||
checker : false, // true时代表该格式化器是作为校验器存在,会在执行每一次格式化器函数后调用进行检查
|
normalize : null, // 对输入值进行规范化处理,如进行时间格式化时,为了提高更好的兼容性,支持数字时间戳/字符串/Date等,需要对输入值进行处理,如强制类型转换等
|
||||||
paramCount : 0, // 声明该该格式化器具有几个参数,0代表未知
|
params : [], // 声明参数顺序
|
||||||
error : 0, // 当执行格式化化器函数出错时的行为,取值0-break,1-skip,2-default
|
optionKeyPath: null // 声明该格式化器在$options中的路径,支持简单的使用.的路径语法
|
||||||
empty : false, // 当输入为空时的输出
|
})
|
||||||
default : null // 当出错默认输出
|
|
||||||
},meta)
|
// 最后一个参数是传入activeFormatterOptions参数
|
||||||
Object.entries(opts).forEach(([key,value])=>fn[key] = value)
|
const wrappedFn = function(value,...args){
|
||||||
return fn
|
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 ={
|
module.exports ={
|
||||||
isPlainObject,
|
isPlainObject,
|
||||||
isFunction,
|
isFunction,
|
||||||
@ -277,9 +347,13 @@ module.exports ={
|
|||||||
isNothing,
|
isNothing,
|
||||||
deepMerge,
|
deepMerge,
|
||||||
deepMixin,
|
deepMixin,
|
||||||
|
Formatter,
|
||||||
|
createFormatter,
|
||||||
|
replaceAll,
|
||||||
getByPath,
|
getByPath,
|
||||||
getDataTypeName,
|
getDataTypeName,
|
||||||
toDate,
|
toDate,
|
||||||
toNumber,
|
toNumber,
|
||||||
toCurrency
|
toCurrency,
|
||||||
|
createFormatter
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user