update for ts
This commit is contained in:
parent
e02835fa50
commit
105a75bc59
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import {test,vi,describe,expect,afterAll,beforeAll} from 'vitest'
|
||||||
|
import { VoerkaI18nScope } from '../scope'
|
||||||
|
import zhFormatters from '../formatters/zh';
|
||||||
|
import enFormatters from '../formatters/en';
|
||||||
|
|
||||||
|
|
||||||
|
const zhMessages={
|
||||||
|
"1": "你好",
|
||||||
|
"2": "你好,{name}",
|
||||||
|
"3": "中国",
|
||||||
|
"4": ["我有一部车","我有很多部车"]
|
||||||
|
}
|
||||||
|
const enMessages={
|
||||||
|
"1": "hello",
|
||||||
|
"2": "hello,{name}",
|
||||||
|
"3": "china",
|
||||||
|
"4": "I have {} cars"
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = {
|
||||||
|
zh: zhMessages,
|
||||||
|
en: enMessages
|
||||||
|
}
|
||||||
|
|
||||||
|
const idMap={
|
||||||
|
"你好":1,
|
||||||
|
"你好,{name}":2,
|
||||||
|
"中国":3,
|
||||||
|
"我有{}部车":4
|
||||||
|
}
|
||||||
|
const languages = [
|
||||||
|
{ name: "zh",default:true,active:true},
|
||||||
|
{ name: "en"}
|
||||||
|
]
|
||||||
|
|
||||||
|
const formatters ={
|
||||||
|
zh:zhFormatters,
|
||||||
|
en:enFormatters
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("VoerkaI18nScope", () => {
|
||||||
|
let scope = new VoerkaI18nScope({
|
||||||
|
id: "test",
|
||||||
|
languages,
|
||||||
|
idMap,
|
||||||
|
messages,
|
||||||
|
formatters
|
||||||
|
})
|
||||||
|
test("成功创建实例", () => {
|
||||||
|
expect(scope).toBeInstanceOf(VoerkaI18nScope)
|
||||||
|
expect(scope.activeLanguage).toBe("zh")
|
||||||
|
expect(scope.defaultLanguage).toBe("zh")
|
||||||
|
expect(scope.messages).toEqual(messages)
|
||||||
|
expect(scope.default).toEqual(zhMessages)
|
||||||
|
expect(scope.current).toEqual(zhMessages)
|
||||||
|
expect(scope.idMap).toEqual(idMap)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
test('translate', () => {})
|
||||||
|
|
@ -10,14 +10,14 @@ import { toChineseNumber } from "flex-tools/chinese/toChineseNumber"
|
|||||||
import { toChineseCurrency } from "flex-tools/chinese/toChineseCurrency"
|
import { toChineseCurrency } from "flex-tools/chinese/toChineseCurrency"
|
||||||
|
|
||||||
|
|
||||||
export const chineseNumberFormatter = Formatter((value: number, isBig: boolean, $config: any) => {
|
export const chineseNumberFormatter = Formatter((value: any, [isBig]:[isBig: boolean], $config) => {
|
||||||
return toChineseNumber(value, isBig)
|
return toChineseNumber(value, isBig) as string
|
||||||
}, {
|
}, {
|
||||||
params: ["isBig"]
|
params: ["isBig"]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
export const rmbFormater = FlexFormatter((value:number | string, params : any, $config:any) => {
|
export const rmbFormater = FlexFormatter((value:number | string, [params] : any[], $config:any) => {
|
||||||
return toChineseCurrency(value, params, $config)
|
return toChineseCurrency(value, params, $config)
|
||||||
}, {
|
}, {
|
||||||
params: ["big", "prefix", "unit", "suffix"],
|
params: ["big", "prefix", "unit", "suffix"],
|
||||||
|
@ -98,7 +98,7 @@ export const currencyFormatter = FlexFormatter((value:string | number,params:Rec
|
|||||||
}
|
}
|
||||||
return toCurrency(value,params,$config)
|
return toCurrency(value,params,$config)
|
||||||
},{
|
},{
|
||||||
normalize: toNumber,
|
normalize: (value:string)=>toNumber(value),
|
||||||
params : ["format","unit","precision","prefix","suffix","division","symbol","radix"],
|
params : ["format","unit","precision","prefix","suffix","division","symbol","radix"],
|
||||||
configKey: "currency"
|
configKey: "currency"
|
||||||
},{
|
},{
|
||||||
|
@ -3,18 +3,16 @@
|
|||||||
处理日期时间相关
|
处理日期时间相关
|
||||||
|
|
||||||
*/
|
*/
|
||||||
const { isFunction,replaceAll,toDate } = require('../utils')
|
import { toDate } from '../utils'
|
||||||
const { Formatter } = require('../formatter');
|
import { Formatter } from '../formatter';
|
||||||
import { formatDateTime } from "flex-tools/misc/formatDateTime"
|
import { formatDateTime } from "flex-tools/misc/formatDateTime"
|
||||||
import { relativeTime } from "flex-tools/misc/relativeTime"
|
import { relativeTime } from "flex-tools/misc/relativeTime"
|
||||||
import { assignObject } from "flex-tools/object/assignObject"
|
import { assignObject } from "flex-tools/object/assignObject"
|
||||||
|
import { isFunction } from "flex-tools/typecheck/isFunction"
|
||||||
|
|
||||||
|
|
||||||
function formatTime(value:number ,template="HH:mm:ss"){
|
function formatTime(value:number ,template="HH:mm:ss"){
|
||||||
return formatDateTime(value,template,{
|
return formatDateTime(value,template,{})
|
||||||
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -29,9 +27,10 @@ function formatTime(value:number ,template="HH:mm:ss"){
|
|||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
export type FormatterTransformer = (value:any,format:string)=>string
|
export type FormatterTransformer = (value:any,format:string)=>string
|
||||||
|
|
||||||
export function createDateTimeFormatter(options={},transformer:FormatterTransformer){
|
export function createDateTimeFormatter(options={},transformer:FormatterTransformer){
|
||||||
let opts = assignObject({presets:{}},options)
|
let opts = assignObject({presets:{}},options)
|
||||||
return Formatter(function(value,format,$config){
|
return Formatter(function(this:any,value:any,[format]:any[],$config:Record<string,any>){
|
||||||
if((format in opts.presets) && isFunction(opts.presets[format])){
|
if((format in opts.presets) && isFunction(opts.presets[format])){
|
||||||
return opts.presets[format](value)
|
return opts.presets[format](value)
|
||||||
}else if((format in $config)){
|
}else if((format in $config)){
|
||||||
@ -53,17 +52,17 @@ export function createDateTimeFormatter(options={},transformer:FormatterTransfor
|
|||||||
* - format取值:local,long,short,iso,gmt,utc,<模板字符串>
|
* - format取值:local,long,short,iso,gmt,utc,<模板字符串>
|
||||||
* - 默认值由$config.datetime.date.format指定
|
* - 默认值由$config.datetime.date.format指定
|
||||||
*/
|
*/
|
||||||
const dateFormatter = createDateTimeFormatter({
|
export const dateFormatter = createDateTimeFormatter({
|
||||||
normalize: toDate,
|
normalize: toDate,
|
||||||
params : ["format"],
|
params : ["format"],
|
||||||
configKey: "datetime.date",
|
configKey: "datetime.date",
|
||||||
presets : {
|
presets : {
|
||||||
local: value=>value.toLocaleString(),
|
local: (value:any)=>value.toLocaleString(),
|
||||||
iso : value=>value.toISOString(),
|
iso : (value:any)=>value.toISOString(),
|
||||||
utc : value=>value.toUTCString(),
|
utc : (value:any)=>value.toUTCString(),
|
||||||
gmt : value=>value.toGMTString()
|
gmt : (value:any)=>value.toGMTString()
|
||||||
}
|
}
|
||||||
},formatDatetime)
|
},formatDateTime)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,8 +70,8 @@ export function createDateTimeFormatter(options={},transformer:FormatterTransfor
|
|||||||
* - format: long,short,number
|
* - format: long,short,number
|
||||||
* - 默认值是 short
|
* - 默认值是 short
|
||||||
*/
|
*/
|
||||||
const quarterFormatter = createDateTimeFormatter({
|
export const quarterFormatter = createDateTimeFormatter({
|
||||||
normalize : value=>{
|
normalize : (value:any)=>{
|
||||||
const month = value.getMonth() + 1
|
const month = value.getMonth() + 1
|
||||||
return Math.floor( ( month % 3 == 0 ? ( month / 3 ) : (month / 3 + 1 ) ))
|
return Math.floor( ( month % 3 == 0 ? ( month / 3 ) : (month / 3 + 1 ) ))
|
||||||
},
|
},
|
||||||
@ -85,7 +84,7 @@ const quarterFormatter = createDateTimeFormatter({
|
|||||||
* - format: long,short,number
|
* - format: long,short,number
|
||||||
* - 默认值是 short
|
* - 默认值是 short
|
||||||
*/
|
*/
|
||||||
const monthFormatter = createDateTimeFormatter({
|
export const monthFormatter = createDateTimeFormatter({
|
||||||
normalize: (value:Date)=> value.getMonth() + 1,
|
normalize: (value:Date)=> value.getMonth() + 1,
|
||||||
params : ["format"],
|
params : ["format"],
|
||||||
configKey: "datetime.month"
|
configKey: "datetime.month"
|
||||||
@ -96,7 +95,7 @@ const monthFormatter = createDateTimeFormatter({
|
|||||||
* - format: long,short,number
|
* - format: long,short,number
|
||||||
* - 默认值是 long
|
* - 默认值是 long
|
||||||
*/
|
*/
|
||||||
const weekdayFormatter = createDateTimeFormatter({
|
export const weekdayFormatter = createDateTimeFormatter({
|
||||||
normalize: (value:Date)=> value.getDay(),
|
normalize: (value:Date)=> value.getDay(),
|
||||||
params : ["format"],
|
params : ["format"],
|
||||||
configKey: "datetime.weekday"
|
configKey: "datetime.weekday"
|
||||||
@ -107,7 +106,7 @@ const weekdayFormatter = createDateTimeFormatter({
|
|||||||
* - format取值:local,long,short,timestamp,<模板字符串>
|
* - format取值:local,long,short,timestamp,<模板字符串>
|
||||||
* - 默认值由$config.datetime.time.format指定
|
* - 默认值由$config.datetime.time.format指定
|
||||||
*/
|
*/
|
||||||
const timeFormatter = createDateTimeFormatter({
|
export const timeFormatter = createDateTimeFormatter({
|
||||||
normalize : toDate,
|
normalize : toDate,
|
||||||
params : ["format"],
|
params : ["format"],
|
||||||
configKey : "datetime.time",
|
configKey : "datetime.time",
|
||||||
@ -122,7 +121,7 @@ const weekdayFormatter = createDateTimeFormatter({
|
|||||||
* @param {*} value
|
* @param {*} value
|
||||||
* @param {*} baseTime 基准时间,默认是相对现在
|
* @param {*} baseTime 基准时间,默认是相对现在
|
||||||
*/
|
*/
|
||||||
const relativeTimeFormatter = Formatter((value:Date,baseTime:Date,$config:any)=>{
|
export const relativeTimeFormatter = Formatter((value:any,[baseTime]:[Date],$config:any)=>{
|
||||||
//const { units,now,before,base = Date.now() , after } = $config
|
//const { units,now,before,base = Date.now() , after } = $config
|
||||||
return relativeTime(value, $config)
|
return relativeTime(value, $config)
|
||||||
},{
|
},{
|
||||||
|
@ -8,16 +8,17 @@
|
|||||||
* { value | number('big') }
|
* { value | number('big') }
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
const { isNumber,toNumber } = require("../utils")
|
import { toNumber } from "../utils"
|
||||||
const { Formatter } = require("../formatter")
|
import { Formatter } from "../formatter"
|
||||||
const { toCurrency } = require("./currency")
|
import { toCurrency } from "./currency"
|
||||||
|
import { VoerkaI18nFormatter } from '../types';
|
||||||
|
|
||||||
const numberFormartter = Formatter(function(value,precision,division,$config){
|
export const numberFormartter = Formatter(function(value:any,[precision,division]:[number,number],$config:any){
|
||||||
return toCurrency(value, { division, precision},$config)
|
return toCurrency(value, { division, precision},$config)
|
||||||
},{
|
},{
|
||||||
normalize: toNumber,
|
normalize: toNumber,
|
||||||
params:["precision","division"],
|
params:["precision","division"],
|
||||||
configKey: "number"
|
configKey: "number"
|
||||||
})
|
}) as VoerkaI18nFormatter
|
||||||
|
|
||||||
|
|
@ -271,8 +271,8 @@ function parseFormaterParams(strParams: string): any[] {
|
|||||||
*/
|
*/
|
||||||
export type Formatter = (this: any, value: string, ...args: any[]) => string
|
export type Formatter = (this: any, value: string, ...args: any[]) => string
|
||||||
export interface FormatterOptions {
|
export interface FormatterOptions {
|
||||||
normalize?: (value: string) => string // 对输入值进行规范化处理,如进行时间格式化时,为了提高更好的兼容性,支持数字时间戳/字符串/Date等,需要对输入值进行处理,如强制类型转换等
|
normalize?: (value: string) => any // 对输入值进行规范化处理,如进行时间格式化时,为了提高更好的兼容性,支持数字时间戳/字符串/Date等,需要对输入值进行处理,如强制类型转换等
|
||||||
params?: Record<string, any>, // 可选的,声明参数顺序,如果是变参的,则需要传入null
|
params?: Record<string, any> | null, // 可选的,声明参数顺序,如果是变参的,则需要传入null
|
||||||
configKey?: string // 声明该格式化器在$config中的路径,支持简单的使用.的路径语法
|
configKey?: string // 声明该格式化器在$config中的路径,支持简单的使用.的路径语法
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +285,7 @@ export function createFormatter(fn: Formatter, options?: FormatterOptions, defau
|
|||||||
|
|
||||||
// 最后一个参数是传入activeFormatterConfig参数
|
// 最后一个参数是传入activeFormatterConfig参数
|
||||||
// 并且格式化器的this指向的是activeFormatterConfig
|
// 并且格式化器的this指向的是activeFormatterConfig
|
||||||
const $formatter = function (this: any, value: string, ...args: any[]) {
|
const $formatter = function (this: any, value: string, args: any[],$config:Record<string,any>) {
|
||||||
let finalValue = value
|
let finalValue = value
|
||||||
// 1. 输入值规范处理,主要是进行类型转换,确保输入的数据类型及相关格式的正确性,提高数据容错性
|
// 1. 输入值规范处理,主要是进行类型转换,确保输入的数据类型及相关格式的正确性,提高数据容错性
|
||||||
if (isFunction(opts.normalize)) {
|
if (isFunction(opts.normalize)) {
|
||||||
@ -312,7 +312,7 @@ export function createFormatter(fn: Formatter, options?: FormatterOptions, defau
|
|||||||
if (args[i] !== undefined) finalArgs[i] = args[i]
|
if (args[i] !== undefined) finalArgs[i] = args[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fn.call(this, finalValue, ...finalArgs, formatterConfig)
|
return fn.call(this, finalValue, finalArgs, formatterConfig)
|
||||||
}
|
}
|
||||||
$formatter.configurable = true
|
$formatter.configurable = true
|
||||||
return $formatter
|
return $formatter
|
||||||
@ -339,9 +339,7 @@ export const createFlexFormatter = function (fn: Formatter, options: FormatterOp
|
|||||||
const opts = assignObject({
|
const opts = assignObject({
|
||||||
params: {}
|
params: {}
|
||||||
}, options)
|
}, options)
|
||||||
const $flexFormatter = Formatter(function (this: any, value: string, ...args: string[]) {
|
const $flexFormatter = Formatter(function (this: any, value: string,args: string[],$config:Record<string,any> ) {
|
||||||
// 1. 最后一个参数总是代表格式化器的参数,不同语言不一样
|
|
||||||
let $config = args[args.length - 1] as unknown as Record<string, any>
|
|
||||||
// 2. 从语言配置中读取默认参数
|
// 2. 从语言配置中读取默认参数
|
||||||
let finalParams = (options.params || {}).reduce((r: Record<string, any>, name: string) => {
|
let finalParams = (options.params || {}).reduce((r: Record<string, any>, name: string) => {
|
||||||
r[name] = $config[name] == undefined ? (defaultParams || {})[name] : $config[name]
|
r[name] = $config[name] == undefined ? (defaultParams || {})[name] : $config[name]
|
||||||
@ -361,6 +359,7 @@ export const createFlexFormatter = function (fn: Formatter, options: FormatterOp
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const Formatter = createFormatter
|
||||||
|
|
||||||
export const FlexFormatter = createFlexFormatter
|
export const FlexFormatter = createFlexFormatter
|
||||||
|
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { VoerkaI18nFormatter, VoerkaI18nFormatters, VoerkaI18nFormattersLoader, VoerkaI18nLanguageFormatters, SupportedDateTypes, VoerkaI18nFormatterConfigs, Formatter } from './types';
|
import { VoerkaI18nFormatter, VoerkaI18nFormatters, VoerkaI18nFormattersLoader, VoerkaI18nLanguageFormatters, SupportedDateTypes, VoerkaI18nFormatterConfigs } from './types';
|
||||||
import { DataTypes } from './utils';
|
import { DataTypes } from './utils';
|
||||||
import { get as getByPath } from "flex-tools/object/get"
|
import { get as getByPath } from "flex-tools/object/get"
|
||||||
|
import { isFunction } from 'flex-tools/typecheck/isFunction';
|
||||||
|
|
||||||
|
|
||||||
export interface VoerkaI18nScopeCache{
|
export interface VoerkaI18nScopeCache{
|
||||||
@ -41,7 +42,7 @@ export class VoerkaI18nFormatterRegistry{
|
|||||||
set language(language:string){
|
set language(language:string){
|
||||||
this.#language = language
|
this.#language = language
|
||||||
if(!(language in this.formatters)){
|
if(!(language in this.formatters)){
|
||||||
(this.formatters[language] as any) = Object.assign({},EmptyFormatters)
|
(this.#formatters[language] as any) = Object.assign({},EmptyFormatters)
|
||||||
}
|
}
|
||||||
this.#ready = typeof(this.#formatters[language]) != 'function'
|
this.#ready = typeof(this.#formatters[language]) != 'function'
|
||||||
}
|
}
|
||||||
@ -63,7 +64,7 @@ export class VoerkaI18nFormatterRegistry{
|
|||||||
*代表适用于所有语言
|
*代表适用于所有语言
|
||||||
语言名称,语言名称数组,或者使用,分割的语言名称字符串
|
语言名称,语言名称数组,或者使用,分割的语言名称字符串
|
||||||
*/
|
*/
|
||||||
register(name:string, formatter:VoerkaI18nFormatter, {language = "*"}:{ language: string | string[] } ) {
|
register(name:string | SupportedDateTypes, formatter:VoerkaI18nFormatter, {language = "*"}:{ language: string | string[] } ) {
|
||||||
if (!isFunction(formatter) || typeof name !== "string") {
|
if (!isFunction(formatter) || typeof name !== "string") {
|
||||||
throw new TypeError("Formatter must be a function");
|
throw new TypeError("Formatter must be a function");
|
||||||
}
|
}
|
||||||
@ -73,7 +74,7 @@ export class VoerkaI18nFormatterRegistry{
|
|||||||
this.#formatters[lngName] = { }
|
this.#formatters[lngName] = { }
|
||||||
}
|
}
|
||||||
if(typeof(this.#formatters[lngName])!="function"){
|
if(typeof(this.#formatters[lngName])!="function"){
|
||||||
let lngFormatters = this.#formatters[lngName] as VoerkaI18nFormatters
|
let lngFormatters = this.#formatters[lngName] as any
|
||||||
if (DataTypes.includes(name)) {
|
if (DataTypes.includes(name)) {
|
||||||
if(!lngFormatters.$types){
|
if(!lngFormatters.$types){
|
||||||
lngFormatters.$types = {}
|
lngFormatters.$types = {}
|
||||||
@ -168,7 +169,7 @@ export class VoerkaI18nFormatterRegistry{
|
|||||||
*/
|
*/
|
||||||
get(name:string,dataType?:SupportedDateTypes):VoerkaI18nFormatter | undefined{
|
get(name:string,dataType?:SupportedDateTypes):VoerkaI18nFormatter | undefined{
|
||||||
if(!this.#ready) throw new FormattersNotLoadedError(this.#language)
|
if(!this.#ready) throw new FormattersNotLoadedError(this.#language)
|
||||||
const lngFormatters = this.#formatters[this.#language] as VoerkaI18nFormatters
|
const lngFormatters = this.#formatters[this.#language] as any
|
||||||
if(dataType && (dataType in lngFormatters.$types!)){
|
if(dataType && (dataType in lngFormatters.$types!)){
|
||||||
return lngFormatters.$types![dataType]
|
return lngFormatters.$types![dataType]
|
||||||
}else if(name in lngFormatters){
|
}else if(name in lngFormatters){
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
const { toNumber,isFunction } = require("../utils")
|
import { assignObject } from 'flex-tools/object/assignObject';
|
||||||
const { Formatter } = require("../formatter")
|
import { VoerkaI18nFormatterConfigs } from '../types';
|
||||||
|
import { toNumber } from "../utils"
|
||||||
|
import { Formatter } from "../formatter"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字典格式化器
|
* 字典格式化器
|
||||||
@ -10,11 +12,11 @@ const { Formatter } = require("../formatter")
|
|||||||
* @param {...any} args
|
* @param {...any} args
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function dict(key, values) {
|
function dict(key:string, values:any) {
|
||||||
if(key in values){
|
if(key in values){
|
||||||
return values[key]
|
return values[key]
|
||||||
}else{
|
}else{
|
||||||
return ("default" in values) ? values[key] : value
|
return ("default" in values) ? values[key] : values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,10 +39,8 @@ const { Formatter } = require("../formatter")
|
|||||||
* @paran {String} next 下一步行为,取值true/false,break,skip,默认是break
|
* @paran {String} next 下一步行为,取值true/false,break,skip,默认是break
|
||||||
* @param {*} config
|
* @param {*} config
|
||||||
*/
|
*/
|
||||||
const empty = Formatter(function(value,escapeValue,next,$config){
|
const empty = Formatter(function(value:any,escapeValue:any,next: 'break' | 'ignore',$config:VoerkaI18nFormatterConfigs){
|
||||||
if(next===false) next = 'break'
|
let opts = assignObject({escape:"",next:'break',values:[]},$config)
|
||||||
if(next===true) next = 'skip'
|
|
||||||
let opts = Object.assign({escape:"",next:'break',values:[]},$config)
|
|
||||||
if(escapeValue!=undefined) opts.escape = escapeValue
|
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)
|
||||||
@ -72,10 +72,10 @@ const empty = Formatter(function(value,escapeValue,next,$config){
|
|||||||
* @param {*} config 格式化器的全局配置参数
|
* @param {*} config 格式化器的全局配置参数
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const error = Formatter(function(value,escapeValue,next,$config){
|
const error = Formatter(function(value:any,escapeValue:any,next:'break' | 'ignore',$config:VoerkaI18nFormatterConfigs){
|
||||||
if(value instanceof Error){
|
if(value instanceof Error){
|
||||||
try{
|
try{
|
||||||
let opts = Object.assign({escape:null,next:'break'},$config)
|
let opts = assignObject({escape:null,next:'break'},$config)
|
||||||
if(escapeValue!=undefined) opts.escape = escapeValue
|
if(escapeValue!=undefined) opts.escape = escapeValue
|
||||||
if(next!=undefined) opts.next = next
|
if(next!=undefined) opts.next = next
|
||||||
return {
|
return {
|
||||||
@ -100,7 +100,7 @@ const error = Formatter(function(value,escapeValue,next,$config){
|
|||||||
* @param {*} prefix
|
* @param {*} prefix
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function prefix(value,prefix="") {
|
export function prefix(value:any,prefix:string="") {
|
||||||
return prefix ? `${prefix}${value}` : value
|
return prefix ? `${prefix}${value}` : value
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -109,7 +109,7 @@ function prefix(value,prefix="") {
|
|||||||
* @param {*} suffix
|
* @param {*} suffix
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function suffix(value,suffix="") {
|
export function suffix(value:any,suffix:string="") {
|
||||||
return suffix ? `${value}${suffix}` : value
|
return suffix ? `${value}${suffix}` : value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ const FILE_SIZE_WHOLE_UNITS = ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "
|
|||||||
* @param {*} brief 是否采用简称单位
|
* @param {*} brief 是否采用简称单位
|
||||||
* @param {*} options
|
* @param {*} options
|
||||||
*/
|
*/
|
||||||
const filesize= Formatter((value,unit,brief=true,$config)=>{
|
export const filesize= Formatter((value:any,unit:string,brief:boolean=true,$config:VoerkaI18nFormatterConfigs)=>{
|
||||||
let v = toNumber(value)
|
let v = toNumber(value)
|
||||||
let unitIndex
|
let unitIndex
|
||||||
if(unit==undefined || unit=="auto"){
|
if(unit==undefined || unit=="auto"){
|
||||||
@ -164,13 +164,11 @@ const FILE_SIZE_WHOLE_UNITS = ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
module.exports = {
|
|
||||||
dict,
|
dict,
|
||||||
|
empty,
|
||||||
|
error,
|
||||||
prefix,
|
prefix,
|
||||||
suffix,
|
suffix,
|
||||||
filesize,
|
filesize
|
||||||
error,
|
|
||||||
empty
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import { Formatter } fom "../formatter"
|
|
||||||
import { dateFormatter,quarterFormatter,monthFormatter,weekdayFormatter,timeFormatter,relativeTimeFormatter } from "../datatypes/datetime"
|
import { dateFormatter,quarterFormatter,monthFormatter,weekdayFormatter,timeFormatter,relativeTimeFormatter } from "../datatypes/datetime"
|
||||||
import { numberFormartter } from "../datatypes/numeric"
|
import { numberFormartter } from "../datatypes/numeric"
|
||||||
import { currencyFormatter } from "../datatypes/currency"
|
import { currencyFormatter } from "../datatypes/currency"
|
||||||
@ -149,11 +148,11 @@ export default {
|
|||||||
},
|
},
|
||||||
// 默认数据类型的格式化器
|
// 默认数据类型的格式化器
|
||||||
$types: {
|
$types: {
|
||||||
Date : dateFormatter,
|
// Date : dateFormatter,
|
||||||
Null : value =>"",
|
Null : (value: any) =>"",
|
||||||
Undefined: value =>"",
|
Undefined: (value: any) =>"",
|
||||||
Error : value => "ERROR",
|
Error : (value: any) => "ERROR",
|
||||||
Boolean : value =>value ? "True":"False",
|
Boolean : (value: any) =>value ? "True":"False",
|
||||||
Number : numberFormartter
|
Number : numberFormartter
|
||||||
},
|
},
|
||||||
// 以下是格式化定义
|
// 以下是格式化定义
|
||||||
|
@ -5,9 +5,10 @@
|
|||||||
|
|
||||||
import { chineseNumberFormatter,rmbFormater } from "../datatypes/chinese"
|
import { chineseNumberFormatter,rmbFormater } from "../datatypes/chinese"
|
||||||
|
|
||||||
|
import {CN_DATETIME_UNITS, CN_MONTH_NAMES, CN_SHORT_MONTH_NAMES, CN_SHORT_WEEK_DAYS, CN_WEEK_DAYS } from "flex-tools/chinese/consts"
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
export default {
|
||||||
// 配置参数: 格式化器函数的最后一个参数就是该配置参数
|
// 配置参数: 格式化器函数的最后一个参数就是该配置参数
|
||||||
$config:{
|
$config:{
|
||||||
datetime : {
|
datetime : {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// @ts-ignore
|
||||||
import replaceAll from "string.prototype.replaceall"
|
import replaceAll from "string.prototype.replaceall"
|
||||||
replaceAll.shim()
|
replaceAll.shim()
|
||||||
|
|
||||||
|
@ -203,7 +203,9 @@ function getFormatter(scope:VoerkaI18nScope, activeLanguage:string, name:string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export type FormatterChecker = (value:any,config?:VoerkaI18nFormatterConfigs)=>any;
|
export type FormatterChecker = ((value:any,config?:VoerkaI18nFormatterConfigs)=>any) & {
|
||||||
|
$name:string
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checker是一种特殊的格式化器,会在特定的时间执行
|
* Checker是一种特殊的格式化器,会在特定的时间执行
|
||||||
@ -242,7 +244,7 @@ function executeChecker(checker:FormatterChecker, value:any,scope:VoerkaI18nScop
|
|||||||
* @param {FormatterDefineChain} formatters 经过解析过的格式化器参数链 ,多个格式化器函数(经过包装过的)顺序执行,前一个输出作为下一个格式化器的输入
|
* @param {FormatterDefineChain} formatters 经过解析过的格式化器参数链 ,多个格式化器函数(经过包装过的)顺序执行,前一个输出作为下一个格式化器的输入
|
||||||
* formatters [ [<格式化器名称>,[<参数>,<参数>,...],[<格式化器名称>,[<参数>,<参数>,...]],...]
|
* formatters [ [<格式化器名称>,[<参数>,<参数>,...],[<格式化器名称>,[<参数>,<参数>,...]],...]
|
||||||
*/
|
*/
|
||||||
function executeFormatter(value:any, formatters:FormatterDefineChain, scope:VoerkaI18nScope, template:string) {
|
function executeFormatter(value:any, formatters:VoerkaI18nFormatter[], scope:VoerkaI18nScope, template:string) {
|
||||||
if (formatters.length === 0) return value;
|
if (formatters.length === 0) return value;
|
||||||
let result = value;
|
let result = value;
|
||||||
// 1. 空值检查
|
// 1. 空值检查
|
||||||
@ -258,11 +260,11 @@ function executeFormatter(value:any, formatters:FormatterDefineChain, scope:Voer
|
|||||||
}
|
}
|
||||||
// 2. 错误检查
|
// 2. 错误检查
|
||||||
const errorCheckerIndex = formatters.findIndex((func) => (func as any).$name === "error" );
|
const errorCheckerIndex = formatters.findIndex((func) => (func as any).$name === "error" );
|
||||||
let errorChecker;
|
let errorChecker:FormatterChecker;
|
||||||
if (errorCheckerIndex != -1) {
|
if (errorCheckerIndex != -1) {
|
||||||
errorChecker = formatters.splice(errorCheckerIndex, 1)[0] as unknown as FormatterChecker
|
errorChecker = formatters.splice(errorCheckerIndex, 1)[0] as unknown as FormatterChecker
|
||||||
if (result instanceof Error) {
|
if (result instanceof Error) {
|
||||||
result.formatter = formatter.$name;
|
(result as any).formatter = errorChecker.$name;
|
||||||
const { value, next } = executeChecker(errorChecker, result,scope);
|
const { value, next } = executeChecker(errorChecker, result,scope);
|
||||||
if (next == "break") {
|
if (next == "break") {
|
||||||
return value;
|
return value;
|
||||||
@ -275,13 +277,13 @@ function executeFormatter(value:any, formatters:FormatterDefineChain, scope:Voer
|
|||||||
// 3. 分别执行格式化器函数
|
// 3. 分别执行格式化器函数
|
||||||
for (let formatter of formatters) {
|
for (let formatter of formatters) {
|
||||||
try {
|
try {
|
||||||
result = formatter(result, scope.activeFormatterConfig);
|
result = formatter(result, [result],scope.activeFormatterConfig);
|
||||||
} catch (e:any) {
|
} catch (e:any) {
|
||||||
e.formatter = formatter.$name;
|
e.formatter = (formatter as any).$name;
|
||||||
if (scope.debug)
|
if (scope.debug)
|
||||||
console.error(`Error while execute i18n formatter<${formatter.$name}> for ${template}: ${e.message} `);
|
console.error(`Error while execute i18n formatter<${(formatter as any).$name}> for ${template}: ${e.message} `);
|
||||||
if (isFunction(errorChecker)) {
|
if (isFunction(errorChecker!)) {
|
||||||
const { value, next } = executeChecker(errorChecker, result);
|
const { value, next } = executeChecker(errorChecker!, result,scope);
|
||||||
if (next == "break") {
|
if (next == "break") {
|
||||||
if (value !== undefined) result = value;
|
if (value !== undefined) result = value;
|
||||||
break;
|
break;
|
||||||
@ -329,13 +331,13 @@ function addDefaultFormatters(formatters:FormatterDefineChain) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function wrapperFormatters(scope:VoerkaI18nScope, activeLanguage:string, formatters:FormatterDefineChain) {
|
function wrapperFormatters(scope:VoerkaI18nScope, activeLanguage:string, formatters:FormatterDefineChain) {
|
||||||
let wrappedFormatters = [];
|
let wrappedFormatters:VoerkaI18nFormatter[] = [];
|
||||||
addDefaultFormatters(formatters);
|
addDefaultFormatters(formatters);
|
||||||
for (let [name, args] of formatters) {
|
for (let [name, args] of formatters) {
|
||||||
let fn = getFormatter(scope, activeLanguage, name);
|
let fn = getFormatter(scope, activeLanguage, name);
|
||||||
let formatter;
|
let formatter;
|
||||||
if (isFunction(fn)) {
|
if (isFunction(fn)) {
|
||||||
formatter = (value:any, config:VoerkaI18nFormatterConfigs) =>fn.call(scope.activeFormatterConfig, value, args, config);
|
formatter = (value:any, args?:any[],config?:VoerkaI18nFormatterConfigs) =>fn.call(scope.activeFormatterConfig, value, args, config);
|
||||||
} else {
|
} else {
|
||||||
// 格式化器无效或者没有定义时,查看当前值是否具有同名的原型方法,如果有则执行调用
|
// 格式化器无效或者没有定义时,查看当前值是否具有同名的原型方法,如果有则执行调用
|
||||||
// 比如padStart格式化器是String的原型方法,不需要配置就可以直接作为格式化器调用
|
// 比如padStart格式化器是String的原型方法,不需要配置就可以直接作为格式化器调用
|
||||||
@ -362,7 +364,7 @@ function wrapperFormatters(scope:VoerkaI18nScope, activeLanguage:string, formatt
|
|||||||
* @param {*} value
|
* @param {*} value
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function getFormattedValue(scope:VoerkaI18nScope, activeLanguage:string, formatters:FormatterDefineChain, value, template) {
|
function getFormattedValue(scope:VoerkaI18nScope, activeLanguage:string, formatters:FormatterDefineChain, value:any, template:string) {
|
||||||
// 1. 取得格式化器函数列表,然后经过包装以传入当前格式化器的配置参数
|
// 1. 取得格式化器函数列表,然后经过包装以传入当前格式化器的配置参数
|
||||||
const formatterFuncs = wrapperFormatters(scope, activeLanguage, formatters);
|
const formatterFuncs = wrapperFormatters(scope, activeLanguage, formatters);
|
||||||
// 3. 执行格式化器
|
// 3. 执行格式化器
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { isFunction } from "flex-tools/typecheck/isFunction"
|
import { isFunction } from "flex-tools/typecheck/isFunction"
|
||||||
import { deepMerge } from "flex-tools/object/deepMerge"
|
import { deepMerge } from "flex-tools/object/deepMerge"
|
||||||
import {DataTypes} from "./utils"
|
|
||||||
import { EventEmitter } from "./eventemitter"
|
import { EventEmitter } from "./eventemitter"
|
||||||
import inlineFormatters from "./formatters"
|
import inlineFormatters from "./formatters"
|
||||||
import { VoerkaI18nScope } from "./scope"
|
import type { VoerkaI18nScope } from "./scope"
|
||||||
import type { VoerkaI18nLanguageDefine, VoerkaI18nLanguageFormatters, VoerkaI18nDefaultMessageLoader, VoerkaI18nFormatter, VoerkaI18nTypesFormatters } from "./types"
|
import type { VoerkaI18nLanguageDefine, VoerkaI18nLanguageFormatters, VoerkaI18nDefaultMessageLoader, VoerkaI18nFormatter, VoerkaI18nTypesFormatters } from "./types"
|
||||||
import { SupportedDateTypes } from './types';
|
|
||||||
import { VoerkaI18nFormatterRegistry } from "./formatterRegistry"
|
import { VoerkaI18nFormatterRegistry } from "./formatterRegistry"
|
||||||
|
|
||||||
// 默认语言配置
|
// 默认语言配置
|
||||||
@ -24,7 +22,7 @@ export interface VoerkaI18nManagerOptions {
|
|||||||
debug?: boolean
|
debug?: boolean
|
||||||
defaultLanguage: string
|
defaultLanguage: string
|
||||||
activeLanguage: string
|
activeLanguage: string
|
||||||
formatters: VoerkaI18nLanguageFormatters
|
formatters?: VoerkaI18nLanguageFormatters
|
||||||
languages: VoerkaI18nLanguageDefine[]
|
languages: VoerkaI18nLanguageDefine[]
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -126,9 +124,9 @@ export class VoerkaI18nManager extends EventEmitter{
|
|||||||
* @param {*} scope
|
* @param {*} scope
|
||||||
*/
|
*/
|
||||||
async register(scope:VoerkaI18nScope){
|
async register(scope:VoerkaI18nScope){
|
||||||
if(!(scope instanceof VoerkaI18nScope)){
|
// if(!(scope instanceof VoerkaI18nScope)){
|
||||||
throw new TypeError("Scope must be an instance of VoerkaI18nScope")
|
// throw new TypeError("Scope must be an instance of VoerkaI18nScope")
|
||||||
}
|
// }
|
||||||
this.#scopes.push(scope)
|
this.#scopes.push(scope)
|
||||||
return await scope.refresh(this.activeLanguage)
|
return await scope.refresh(this.activeLanguage)
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import { DataTypes } from "./utils"
|
|
||||||
import { isPlainObject } from "flex-tools/typecheck/isPlainObject"
|
import { isPlainObject } from "flex-tools/typecheck/isPlainObject"
|
||||||
import { isFunction } from "flex-tools/typecheck/isFunction"
|
import { isFunction } from "flex-tools/typecheck/isFunction"
|
||||||
import { deepClone } from "flex-tools/object/deepClone"
|
|
||||||
import { get as getByPath } from "flex-tools/object/get"
|
|
||||||
import { translate } from "./translate"
|
import { translate } from "./translate"
|
||||||
import { deepMerge } from "flex-tools/object/deepMerge"
|
import { deepMerge } from "flex-tools/object/deepMerge"
|
||||||
import { assignObject } from "flex-tools/object/assignObject"
|
import { assignObject } from "flex-tools/object/assignObject"
|
||||||
import type {VoerkaI18nManager } from "./manager"
|
import {VoerkaI18nManager } from "./manager"
|
||||||
import type {
|
import type {
|
||||||
VoerkaI18nFormatterConfigs,
|
VoerkaI18nFormatterConfigs,
|
||||||
VoerkaI18nDefaultMessageLoader,
|
VoerkaI18nDefaultMessageLoader,
|
||||||
@ -18,10 +15,12 @@ import type {
|
|||||||
VoerkaI18nLanguagePack,
|
VoerkaI18nLanguagePack,
|
||||||
VoerkaI18nScopeCache,
|
VoerkaI18nScopeCache,
|
||||||
VoerkaI18nTranslate,
|
VoerkaI18nTranslate,
|
||||||
VoerkaI18nLoaders,
|
VoerkaI18nMessageLoaders,
|
||||||
VoerkaI18nTypesFormatters,
|
VoerkaI18nTypesFormatters,
|
||||||
VoerkaI18nFormatters,
|
VoerkaI18nFormatters,
|
||||||
VoerkaI18nDynamicLanguageMessages,
|
VoerkaI18nDynamicLanguageMessages,
|
||||||
|
VoerkaI18nLanguageMessagePack,
|
||||||
|
VoerkaI18nMessageLoader,
|
||||||
} from "./types"
|
} from "./types"
|
||||||
import { VoerkaI18nFormatterRegistry } from './formatterRegistry';
|
import { VoerkaI18nFormatterRegistry } from './formatterRegistry';
|
||||||
import { InvalidLanguageError } from "./errors"
|
import { InvalidLanguageError } from "./errors"
|
||||||
@ -30,13 +29,11 @@ export interface VoerkaI18nScopeOptions {
|
|||||||
id?: string
|
id?: string
|
||||||
debug?: boolean
|
debug?: boolean
|
||||||
languages: VoerkaI18nLanguageDefine[] // 当前作用域支持的语言列表
|
languages: VoerkaI18nLanguageDefine[] // 当前作用域支持的语言列表
|
||||||
defaultLanguage: string // 默认语言名称
|
defaultLanguage?: string // 默认语言名称
|
||||||
activeLanguage: string // 当前语言名称
|
activeLanguage?: string // 当前语言名称
|
||||||
default: VoerkaI18nLanguageMessages // 默认语言包
|
messages: VoerkaI18nLanguageMessagePack // 当前语言包
|
||||||
messages: VoerkaI18nLanguageMessages // 当前语言包
|
|
||||||
idMap: Voerkai18nIdMap // 消息id映射列表
|
idMap: Voerkai18nIdMap // 消息id映射列表
|
||||||
formatters: VoerkaI18nLanguageFormatters // 当前作用域的格式化函数列表{<lang>: {$types,$config,[格式化器名称]: () => {},[格式化器名称]: () => {}}}
|
formatters: VoerkaI18nLanguageFormatters // 当前作用域的格式化函数列表{<lang>: {$types,$config,[格式化器名称]: () => {},[格式化器名称]: () => {}}}
|
||||||
loaders: VoerkaI18nLoaders; // 异步加载语言文件的函数列表
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VoerkaI18nScope {
|
export class VoerkaI18nScope {
|
||||||
@ -48,58 +45,53 @@ export class VoerkaI18nScope {
|
|||||||
#activeFormatters:VoerkaI18nFormatters = {}
|
#activeFormatters:VoerkaI18nFormatters = {}
|
||||||
#activeFormatterConfig: VoerkaI18nFormatterConfigs={}
|
#activeFormatterConfig: VoerkaI18nFormatterConfigs={}
|
||||||
#cache:VoerkaI18nScopeCache
|
#cache:VoerkaI18nScopeCache
|
||||||
#messages:VoerkaI18nLanguageMessages = {}
|
|
||||||
#formatterRegistry:VoerkaI18nFormatterRegistry
|
#formatterRegistry:VoerkaI18nFormatterRegistry
|
||||||
constructor(options:VoerkaI18nScopeOptions, callback:(e?:Error)=>void) {
|
#defaultLanguage:string ='zh'
|
||||||
|
#activeLanguage:string='zh'
|
||||||
|
#currentMessages:VoerkaI18nLanguageMessages = {} // 当前语言包
|
||||||
|
constructor(options:VoerkaI18nScopeOptions, callback?:(e?:Error)=>void) {
|
||||||
this.#options = assignObject({
|
this.#options = assignObject({
|
||||||
id : Date.now().toString() + parseInt(String(Math.random() * 1000)),
|
id : Date.now().toString() + parseInt(String(Math.random() * 1000)),
|
||||||
debug : false,
|
debug : false,
|
||||||
languages : {}, // 当前作用域支持的语言列表
|
languages : {}, // 当前作用域支持的语言列表
|
||||||
defaultLanguage: "zh", // 默认语言名称
|
messages : {}, // 所有语言包={[language]:VoerkaI18nLanguageMessages}
|
||||||
activeLanguage : "zh", // 当前语言名称
|
|
||||||
default : {}, // 默认语言包
|
|
||||||
messages : {}, // 当前语言包
|
|
||||||
idMap : {}, // 消息id映射列表
|
idMap : {}, // 消息id映射列表
|
||||||
formatters : {}, // 当前作用域的格式化函数列表{<lang>: {$types,$config,[格式化器名称]: () => {},[格式化器名称]: () => {}}}
|
formatters : {}, // 当前作用域的格式化函数列表{<lang>: {$types,$config,[格式化器名称]: () => {},[格式化器名称]: () => {}}}
|
||||||
loaders : {} // 异步加载语言文件的函数列表
|
|
||||||
},options) as Required<VoerkaI18nScopeOptions>
|
},options) as Required<VoerkaI18nScopeOptions>
|
||||||
|
|
||||||
this.#patchMessages = {}; // 语言包补丁信息{<language>: {....},<language>:{....}}
|
|
||||||
this.#refreshing = false; // 正在加载语言包标识
|
|
||||||
// 用来缓存格式化器的引用,当使用格式化器时可以直接引用,减少检索遍历
|
// 用来缓存格式化器的引用,当使用格式化器时可以直接引用,减少检索遍历
|
||||||
this.#cache = {
|
this.#cache = {
|
||||||
activeLanguage : this.#options.activeLanguage,
|
activeLanguage : this.#options.activeLanguage,
|
||||||
typedFormatters: {},
|
typedFormatters: {},
|
||||||
formatters : {},
|
formatters : {},
|
||||||
};
|
};
|
||||||
|
// 初始化
|
||||||
this._initiLanguages()
|
this._initiLanguages()
|
||||||
// 如果不存在全局VoerkaI18n实例,说明当前Scope是唯一或第一个加载的作用域,则自动创建全局VoerkaI18n实例
|
// 如果不存在全局VoerkaI18n实例,说明当前Scope是唯一或第一个加载的作用域,则自动创建全局VoerkaI18n实例
|
||||||
if (!globalThis.VoerkaI18n) {
|
if (!globalThis.VoerkaI18n) {
|
||||||
const { VoerkaI18nManager } = require("./");
|
|
||||||
globalThis.VoerkaI18n = new VoerkaI18nManager({
|
globalThis.VoerkaI18n = new VoerkaI18nManager({
|
||||||
debug : this.debug,
|
debug : this.debug,
|
||||||
defaultLanguage: this.defaultLanguage,
|
defaultLanguage: this.#defaultLanguage,
|
||||||
activeLanguage : this.activeLanguage,
|
activeLanguage : this.#activeLanguage,
|
||||||
languages : options.languages,
|
languages : options.languages,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.#t = translate.bind(this)
|
this.#t = translate.bind(this)
|
||||||
this.#global = globalThis.VoerkaI18n as unknown as VoerkaI18nManager;
|
this.#global = globalThis.VoerkaI18n as unknown as VoerkaI18nManager;
|
||||||
this.#formatterRegistry = new VoerkaI18nFormatterRegistry()
|
this.#formatterRegistry = new VoerkaI18nFormatterRegistry()
|
||||||
this.loadFormatters(this.activeLanguage) // 初始化活动的格式化器
|
|
||||||
this._mergePatchedMessages(); // 从本地缓存中读取并合并补丁语言包
|
this._mergePatchedMessages(); // 从本地缓存中读取并合并补丁语言包
|
||||||
this._patch(this.messages, this.activeLanguage); // 延后执行补丁命令,该命令会向远程下载补丁包
|
this._patch(this.#currentMessages, this.activeLanguage); // 延后执行补丁命令,该命令会向远程下载补丁包
|
||||||
this.register(callback); // 在全局注册作用域
|
if(callback) this.register(callback); // 在全局注册作用域
|
||||||
}
|
}
|
||||||
get id() {return this.#options.id;} // 作用域唯一id
|
get id() {return this.#options.id;} // 作用域唯一id
|
||||||
get debug() {return this.#options.debug;} // 调试开关
|
get debug() {return this.#options.debug;} // 调试开关
|
||||||
get defaultLanguage() {return this.#options.defaultLanguage;} // 默认语言名称
|
get defaultLanguage() {return this.#global.defaultLanguage;} // 默认语言名称
|
||||||
get activeLanguage() {return this.#global.activeLanguage;} // 默认语言名称
|
get activeLanguage() {return this.#global.activeLanguage;} // 默认语言名称
|
||||||
get default() {return this.#options.default;} // 默认语言包
|
// 默认语言包,只能静态语言包,不能是动态语言包
|
||||||
get messages() {return this.#options.messages; } // 当前语言包
|
get default() {return this.#options.messages[this.#defaultLanguage] as VoerkaI18nLanguageMessages;}
|
||||||
|
get current() {return this.#currentMessages;} // 当前语言包
|
||||||
|
get messages() {return this.#options.messages; } // 所有语言包
|
||||||
get idMap() {return this.#options.idMap;} // 消息id映射列表
|
get idMap() {return this.#options.idMap;} // 消息id映射列表
|
||||||
get languages() {return this.#options.languages;} // 当前作用域支持的语言列表[{name,title,fallback}]
|
get languages() {return this.#options.languages;} // 当前作用域支持的语言列表[{name,title,fallback}]
|
||||||
get loaders() { return this.#options.loaders;} // 异步加载语言文件的函数列表
|
|
||||||
get global() { return this.#global;} // 引用全局VoerkaI18n配置,注册后自动引用
|
get global() { return this.#global;} // 引用全局VoerkaI18n配置,注册后自动引用
|
||||||
get formatters() { return this.#formatterRegistry;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}}
|
get formatters() { return this.#formatterRegistry;} // 当前作用域的所有格式化器定义 {<语言名称>: {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () => {}}}
|
||||||
get activeFormatters() {return this.#formatterRegistry.formatters} // 当前作用域激活的格式化器定义 {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () = >{}}
|
get activeFormatters() {return this.#formatterRegistry.formatters} // 当前作用域激活的格式化器定义 {$types,$config,[格式化器名称]: () = >{},[格式化器名称]: () = >{}}
|
||||||
@ -112,19 +104,40 @@ export class VoerkaI18nScope {
|
|||||||
/**
|
/**
|
||||||
* 对输入的语言配置进行处理
|
* 对输入的语言配置进行处理
|
||||||
* - 将en配置为默认回退语言
|
* - 将en配置为默认回退语言
|
||||||
|
* - 确保提供了有效的默认语言和活动语言
|
||||||
*/
|
*/
|
||||||
_initiLanguages(){
|
_initiLanguages(){
|
||||||
if(!Array.isArray(this.languages)){
|
if(!Array.isArray(this.languages)){
|
||||||
console.warn("[VoerkaI18n] invalid languages config,use default languages config instead.")
|
console.warn("[VoerkaI18n] invalid languages config,use default languages config instead.")
|
||||||
this.#options.languages = [
|
this.#options.languages = [
|
||||||
{name: "zh",title: "中文"},
|
{name: "zh",title: "中文",default:true,active:true},
|
||||||
{name: "en",title: "英文"}
|
{name: "en",title: "英文"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
// 将en配置为默认回退语言
|
|
||||||
this.languages.forEach(language=>{
|
this.languages.forEach(language=>{
|
||||||
if(!language.fallback) language.fallback = "en"
|
if(!language.fallback) language.fallback = "en"
|
||||||
|
if(language.default) this.#defaultLanguage = language.name
|
||||||
|
if(language.active) this.#activeLanguage = language.name
|
||||||
})
|
})
|
||||||
|
// 确保提供了有效的默认语言和活动语言
|
||||||
|
const lanMessages = this.#options.messages
|
||||||
|
if(!(this.#defaultLanguage in lanMessages)) {
|
||||||
|
this.#defaultLanguage = Object.keys(lanMessages)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(this.#activeLanguage in lanMessages)){
|
||||||
|
this.#activeLanguage = this.#defaultLanguage
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(this.#defaultLanguage in lanMessages)){
|
||||||
|
throw new Error("[VoerkaI18n] invalid <defaultLanguage> config, messages not specified.")
|
||||||
|
}
|
||||||
|
// 初始化时,默认和激活的语言包只能是静态语言包,不能是动态语言包
|
||||||
|
// 因为初始化时激活语言需要马上显示,如果是异步语言包,会导致显示延迟
|
||||||
|
if(isFunction(this.messages[this.#defaultLanguage])){
|
||||||
|
throw new Error("[VoerkaI18n] invalid <defaultLanguage> config, messages must be static.")
|
||||||
|
}
|
||||||
|
this.#currentMessages = this.messages[this.#activeLanguage] as VoerkaI18nLanguageMessages
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,9 +200,9 @@ export class VoerkaI18nScope {
|
|||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
private loadFormatters(newLanguage:string){
|
private loadFormatters(newLanguage:string){
|
||||||
this.#formatterRegistry.language = newLanguage
|
|
||||||
// 初始化格式化器
|
// 初始化格式化器
|
||||||
this.formatters.loadInitials(this.#options.formatters)
|
this.formatters.loadInitials(this.#options.formatters)
|
||||||
|
this.#formatterRegistry.language = newLanguage
|
||||||
if(this.#options.formatters)
|
if(this.#options.formatters)
|
||||||
// 将配置中的指定为全局的格式化器注册到全局
|
// 将配置中的指定为全局的格式化器注册到全局
|
||||||
Object.entries(this.#options.formatters).forEach(([langName,formatters])=>{
|
Object.entries(this.#options.formatters).forEach(([langName,formatters])=>{
|
||||||
@ -308,29 +321,31 @@ export class VoerkaI18nScope {
|
|||||||
* 回退到默认语言
|
* 回退到默认语言
|
||||||
*/
|
*/
|
||||||
private _fallback() {
|
private _fallback() {
|
||||||
this.#options.messages = this.default;
|
this.#currentMessages = this.default;
|
||||||
this.#options.activeLanguage = this.defaultLanguage;
|
this.#activeLanguage = this.defaultLanguage;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 语言信息包可以是:
|
*
|
||||||
|
* 加载指定语言的语言包
|
||||||
|
*
|
||||||
* - 简单的对象{}
|
* - 简单的对象{}
|
||||||
* - 或者是一个返回Promise<VoerkaI18nLanguageMessages>的异步函数
|
* - 或者是一个返回Promise<VoerkaI18nLanguageMessages>的异步函数
|
||||||
*
|
* - 或者是全局的默认加载器
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param language 语言名称
|
* @param language 语言名称
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
private async loadLanguageMessages(language:string):Promise<VoerkaI18nLanguageMessages>{
|
private async loadLanguageMessages(language:string):Promise<VoerkaI18nLanguageMessages>{
|
||||||
if(!this.hasLanguage(language)) throw new InvalidLanguageError(`Not found language <${language}>`)
|
// if(!this.hasLanguage(language)) throw new InvalidLanguageError(`Not found language <${language}>`)
|
||||||
// 非默认语言可以是:语言包对象,也可以是一个异步加载语言包文件,加载器是一个异步函数
|
// 非默认语言可以是:语言包对象,也可以是一个异步加载语言包文件,加载器是一个异步函数
|
||||||
// 如果没有加载器,则无法加载语言包,因此回退到默认语言
|
// 如果没有加载器,则无法加载语言包,因此回退到默认语言
|
||||||
let loader = this.loaders[language];
|
let loader = this.messages[language];
|
||||||
let messages:VoerkaI18nLanguageMessages = {}
|
let messages:VoerkaI18nLanguageMessages = {}
|
||||||
if (isPlainObject(loader)) { // 静态语言包
|
if (isPlainObject(loader)) { // 静态语言包
|
||||||
messages = loader as unknown as VoerkaI18nLanguageMessages;
|
messages = loader as unknown as VoerkaI18nLanguageMessages;
|
||||||
} else if (isFunction(loader)) { // 语言包异步chunk
|
} else if (isFunction(loader)) { // 语言包异步chunk
|
||||||
messages = (await loader()).default;
|
messages = (await (loader as VoerkaI18nMessageLoader))().default;
|
||||||
} else if (isFunction(this.global.defaultMessageLoader)) {
|
} else if (isFunction(this.global.defaultMessageLoader)) {
|
||||||
// 从远程加载语言包:如果该语言没有指定加载器,则使用全局配置的默认加载器
|
// 从远程加载语言包:如果该语言没有指定加载器,则使用全局配置的默认加载器
|
||||||
const loadedMessages = (await this.global.loadMessagesFromDefaultLoader(language,this)) as unknown as VoerkaI18nDynamicLanguageMessages;
|
const loadedMessages = (await this.global.loadMessagesFromDefaultLoader(language,this)) as unknown as VoerkaI18nDynamicLanguageMessages;
|
||||||
@ -344,6 +359,8 @@ export class VoerkaI18nScope {
|
|||||||
$remote : true // 添加一个标识,表示该语言包是从远程加载的
|
$remote : true // 添加一个标识,表示该语言包是从远程加载的
|
||||||
},this.default,loadedMessages); // 合并默认语言包和动态语言包,这样就可以局部覆盖默认语言包
|
},this.default,loadedMessages); // 合并默认语言包和动态语言包,这样就可以局部覆盖默认语言包
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
throw new Error(`Not found loader for language <${language}>`)
|
||||||
}
|
}
|
||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
@ -356,8 +373,8 @@ export class VoerkaI18nScope {
|
|||||||
if (!newLanguage) newLanguage = this.activeLanguage;
|
if (!newLanguage) newLanguage = this.activeLanguage;
|
||||||
// 默认语言:由于默认语言采用静态加载方式而不是异步块,因此只需要简单的替换即可
|
// 默认语言:由于默认语言采用静态加载方式而不是异步块,因此只需要简单的替换即可
|
||||||
if (newLanguage === this.defaultLanguage) {
|
if (newLanguage === this.defaultLanguage) {
|
||||||
this.#options.messages = this.default;
|
this.#currentMessages = this.default;
|
||||||
await this._patch(this.#options.messages, newLanguage); // 异步补丁
|
await this._patch(this.#currentMessages, newLanguage); // 异步补丁
|
||||||
await this._changeFormatters(newLanguage);
|
await this._changeFormatters(newLanguage);
|
||||||
this.#refreshing = false
|
this.#refreshing = false
|
||||||
return;
|
return;
|
||||||
@ -365,12 +382,12 @@ export class VoerkaI18nScope {
|
|||||||
try{
|
try{
|
||||||
let messages = await this.loadLanguageMessages(newLanguage)
|
let messages = await this.loadLanguageMessages(newLanguage)
|
||||||
if(messages){
|
if(messages){
|
||||||
this.#options.messages = messages
|
this.#currentMessages = messages
|
||||||
this.#options.activeLanguage = newLanguage;
|
this.#activeLanguage = newLanguage;
|
||||||
// 打语言包补丁, 如果是从远程加载语言包则不需要再打补丁了
|
// 打语言包补丁, 如果是从远程加载语言包则不需要再打补丁了
|
||||||
// 因为远程加载的语言包已经是补丁过的了
|
// 因为远程加载的语言包已经是补丁过的了
|
||||||
if(!messages.$remote) {
|
if(!messages.$remote) {
|
||||||
await this._patch(this.#options.messages, newLanguage);
|
await this._patch(this.#currentMessages, newLanguage);
|
||||||
}
|
}
|
||||||
// 切换到对应语言的格式化器
|
// 切换到对应语言的格式化器
|
||||||
await this._changeFormatters(newLanguage);
|
await this._changeFormatters(newLanguage);
|
||||||
@ -458,6 +475,7 @@ export class VoerkaI18nScope {
|
|||||||
on(callback:Function) {return this.#global.on(callback); }
|
on(callback:Function) {return this.#global.on(callback); }
|
||||||
off(callback:Function) {return this.#global.off(callback); }
|
off(callback:Function) {return this.#global.off(callback); }
|
||||||
offAll() {return this.#global.offAll();}
|
offAll() {return this.#global.offAll();}
|
||||||
|
|
||||||
async change(language:string) {
|
async change(language:string) {
|
||||||
await this.#global.change(language);
|
await this.#global.change(language);
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ export function translate(this:VoerkaI18nScope,message:string,...args:any[]):str
|
|||||||
// 2.2 从当前语言包中取得翻译文本模板字符串
|
// 2.2 从当前语言包中取得翻译文本模板字符串
|
||||||
// 如果没有启用babel插件将源文本转换为msgId,需要先将文本内容转换为msgId
|
// 如果没有启用babel插件将源文本转换为msgId,需要先将文本内容转换为msgId
|
||||||
let msgId = isMessageId(result) ? result : scope.idMap[result]
|
let msgId = isMessageId(result) ? result : scope.idMap[result]
|
||||||
result = scope.messages[msgId] || result
|
result = scope.current[msgId] || result
|
||||||
}
|
}
|
||||||
// 2. 处理复数
|
// 2. 处理复数
|
||||||
// 经过上面的处理,content可能是字符串或者数组
|
// 经过上面的处理,content可能是字符串或者数组
|
||||||
|
@ -8,9 +8,13 @@ declare global {
|
|||||||
|
|
||||||
export type SupportedDateTypes = "String" | "Number" | "Boolean" | "Object" | "Array" | "Function" | "Error" | "Symbol" | "RegExp" | "Date" | "Null" | "Undefined" | "Set" | "Map" | "WeakSet" | "WeakMap"
|
export type SupportedDateTypes = "String" | "Number" | "Boolean" | "Object" | "Array" | "Function" | "Error" | "Symbol" | "RegExp" | "Date" | "Null" | "Undefined" | "Set" | "Map" | "WeakSet" | "WeakMap"
|
||||||
|
|
||||||
|
// 语言包
|
||||||
export type VoerkaI18nLanguageMessages = Record<string, string | string[]> & {
|
export type VoerkaI18nLanguageMessages = Record<string, string | string[]> & {
|
||||||
$config?: VoerkaI18nTypesFormatterConfigs
|
$config?: VoerkaI18nTypesFormatterConfigs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type VoerkaI18nLanguageMessagePack = Record<string, VoerkaI18nLanguageMessages | VoerkaI18nMessageLoader>
|
||||||
|
|
||||||
export type VoerkaI18nDynamicLanguageMessages = Record<string, string | string[]> & {
|
export type VoerkaI18nDynamicLanguageMessages = Record<string, string | string[]> & {
|
||||||
$config?: VoerkaI18nTypesFormatterConfigs
|
$config?: VoerkaI18nTypesFormatterConfigs
|
||||||
}
|
}
|
||||||
@ -23,38 +27,36 @@ export type Voerkai18nIdMap = Record<string, number>
|
|||||||
export interface VoerkaI18nLanguageDefine {
|
export interface VoerkaI18nLanguageDefine {
|
||||||
name: string
|
name: string
|
||||||
title?: string
|
title?: string
|
||||||
default?: boolean
|
default?: boolean // 是否默认语言
|
||||||
|
active?: boolean
|
||||||
fallback?: string
|
fallback?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type VoerkaI18nFormatterConfigs = Record<string, any>
|
export type VoerkaI18nFormatterConfigs = Record<string, any>
|
||||||
export type VoerkaI18nFormatter = (value: string,args: any[],$config:VoerkI18nFormatterConfigs) => string
|
export type VoerkaI18nFormatter = ((value: string,args?: any[],$config?:VoerkI18nFormatterConfigs) => string)
|
||||||
export type VoerkaI18nTypesFormatters=Record<SupportedDateTypes | string, VoerkaI18nFormatter>
|
export type VoerkaI18nTypesFormatters=Partial<Record<SupportedDateTypes, VoerkaI18nFormatter>>
|
||||||
export type VoerkaI18nTypesFormatterConfigs= Record<SupportedDateTypes | string, Record<string,any>>
|
export type VoerkaI18nTypesFormatterConfig= Partial<Record<string, any>>
|
||||||
|
export type VoerkaI18nTypesFormatterConfigs= Partial<Record<SupportedDateTypes | string, Record<string,any>>>
|
||||||
export type VoerkaI18nFormattersLoader = (()=>Promise<VoerkaI18nFormatters>)
|
export type VoerkaI18nFormattersLoader = (()=>Promise<VoerkaI18nFormatters>)
|
||||||
// 每一个语言的格式化器定义={$types:{...},$config:{...},[格式化器名称]: () => {},[格式化器名称]: () => {}
|
// 每一个语言的格式化器定义={$types:{...},$config:{...},[格式化器名称]: () => {},[格式化器名称]: () => {}
|
||||||
// 在formatters/xxxx.ts里面进行配置
|
// 在formatters/xxxx.ts里面进行配置
|
||||||
export type VoerkaI18nFormatters = ({
|
export type VoerkaI18nFormatters = {
|
||||||
global?: boolean | Omit<VoerkaI18nFormatters,'global'> // 是否全局格式化器
|
global?: boolean | Omit<VoerkaI18nFormatters,'global'> // 是否全局格式化器
|
||||||
$types?:VoerkaI18nTypesFormatters
|
$types?:VoerkaI18nTypesFormatters
|
||||||
$config?: VoerkaI18nTypesFormatterConfigs
|
$config?: VoerkaI18nTypesFormatterConfigs
|
||||||
} & {
|
} | {
|
||||||
[DataTypeName in SupportedDateTypes]?: VoerkaI18nFormatter
|
|
||||||
} & {
|
|
||||||
[key: string]: VoerkaI18nFormatter
|
[key: string]: VoerkaI18nFormatter
|
||||||
})
|
}
|
||||||
|
|
||||||
// 包括语言的{"*":{...},zh:{...},en:{...}}
|
// 包括语言的{"*":{...},zh:{...},en:{...}}
|
||||||
// 声明格式化器
|
// 声明格式化器
|
||||||
export type VoerkaI18nLanguageFormatters = Record<string,VoerkaI18nFormatters | VoerkaI18nFormattersLoader>
|
export type VoerkaI18nLanguageFormatters = Record<string,VoerkaI18nFormatters | VoerkaI18nMessageLoader>
|
||||||
|
|
||||||
|
|
||||||
|
export type VoerkaI18nMessageLoader = () => Awaited<Promise<any>>
|
||||||
//
|
export interface VoerkaI18nMessageLoaders {
|
||||||
export type VoerkaI18nLoader = () => Awaited<Promise<any>>
|
[key: string]: VoerkaI18nMessageLoader
|
||||||
export interface VoerkaI18nLoaders {
|
|
||||||
[key: string]: VoerkaI18nLoader
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VoerkaI18nDefaultMessageLoader = (this:VoerkaI18nScope,newLanguage:string,scope:VoerkaI18nScope)=>Promise<VoerkaI18nLanguageMessages>
|
export type VoerkaI18nDefaultMessageLoader = (this:VoerkaI18nScope,newLanguage:string,scope:VoerkaI18nScope)=>Promise<VoerkaI18nLanguageMessages>
|
||||||
@ -145,14 +147,13 @@ export interface FormatterDefine {
|
|||||||
}
|
}
|
||||||
// 创建格式化器
|
// 创建格式化器
|
||||||
export type CreateFormatterType = (fn: Function, options: CreateFormatterOptions, defaultParams: Record<string, any>) => FormatterDefine
|
export type CreateFormatterType = (fn: Function, options: CreateFormatterOptions, defaultParams: Record<string, any>) => FormatterDefine
|
||||||
export var createFormatter: CreateFormatterType
|
// export var createFormatter: CreateFormatterType
|
||||||
export var Formatter: CreateFormatterType
|
// export var Formatter: CreateFormatterType
|
||||||
|
|
||||||
export type CreateFlexFormatterType = (fn: Function, options: CreateFormatterOptions, defaultParams: Record<string, any>) => FormatterDefine
|
export type CreateFlexFormatterType = (fn: Function, options: CreateFormatterOptions, defaultParams: Record<string, any>) => FormatterDefine
|
||||||
export var createFlexFormatter: CreateFlexFormatterType
|
// export var createFlexFormatter: CreateFlexFormatterType
|
||||||
export var FlexFormatter: CreateFlexFormatterType
|
// export var FlexFormatter: CreateFlexFormatterType
|
||||||
export var getDataTypeName: (value: any) => string
|
// export var getDataTypeName: (value: any) => string
|
||||||
export var toDate: (value: any) => Date | number
|
// export var toDate: (value: any) => Date | number
|
||||||
export var toNumber: (value: any, defaultValue: number) => number
|
// export var toBoolean: (value: any) => boolean
|
||||||
export var toBoolean: (value: any) => boolean
|
|
||||||
|
|
||||||
|
@ -44,12 +44,12 @@ export const DataTypes = ["String","Number","Boolean","Object","Array","Functio
|
|||||||
/**
|
/**
|
||||||
* 转换为数字类型
|
* 转换为数字类型
|
||||||
*/
|
*/
|
||||||
export function toNumber(value:any,defualt=0):number {
|
export function toNumber(value:any):number {
|
||||||
try {
|
try {
|
||||||
if (isNumber(value)) {
|
if (isNumber(value)) {
|
||||||
return parseFloat(value)
|
return parseFloat(value)
|
||||||
} else {
|
} else {
|
||||||
return defualt
|
return 0
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
return value
|
return value
|
||||||
|
Loading…
x
Reference in New Issue
Block a user