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