更新格式化器

This commit is contained in:
wxzhang 2022-08-09 18:30:39 +08:00
parent 7d0feb00eb
commit 7b75e57195
12 changed files with 301 additions and 80 deletions

View File

@ -7,7 +7,7 @@ export default defineConfig({
base:"/voerka-i18n/",
publicPath:"/voerka-i18n/",
mode: 'site',
logo: "images/i18n.png",
logo: "/images/i18n.png",
outputPath:"docs/dist",
resolve:{
includes:["docs/src"]

View File

@ -1,19 +1,27 @@
---
title: 日期时间
---
# 日期时间
`@voerkai18n/runtime`内置了对日期时间进行处理的格式化器,可以直接使用,不需要额外的安装。
```javascript | pure
// 切换到中文
t("现在是{d | date}",new Date()) // == 现在是2022年3月12日
t("现在是{d | time}",new Date()) // == 现在是18点28分12秒
t("现在是{d | shorttime}",new Date()) // == 现在是18:28:12
t("现在是{}",new Date()) // == 现在是2022年3月12日 18点28分12秒
t("现在是{ value }",new Date()) // == 现在是2022年3月12日 18点28分12秒
t("现在是{ value | date }",new Date()) // == 现在是2022年3月12日
t("现在是{ value | shortdate }",new Date()) // == 现在是2022/3/12
t("现在是{ value | time }",new Date()) // == 现在是18点28分12秒
t("现在是{ value | shorttime }",new Date()) // == 现在是18:28:12
t("现在是{}",new Date()) // == 现在是2022年3月12日 18点28分12秒
// 切换到英文
t("现在是{d | date}",new Date()) // == Now is 2022/3/12
t("现在是{d | time}",new Date()) // == Now is 18:28:12
t("现在是{ value }",new Date()) // == 现在是2022年3月12日 18点28分12秒
t("现在是{ value | date }",new Date()) // == Now is 2022/3/12
t("现在是{ value | shortdate }",new Date()) // == Now is 2022/3/12
t("现在是{ value | time }",new Date()) // == Now is 18:28:12
t("现在是{ value | shorttime }",new Date()) // == Now is 18:28:12
t("现在是{}",new Date()) // == Now is 2022/3/20 19:17:24'
t("现在是{ | shorttime | empty() }",new Date()) // == Now is 2022/3/20 19:17:24'
```

View File

@ -4,14 +4,14 @@ module.exports = {
"@babel/preset-env"
],
plugins: [
// [
// i18nPlugin,
// {
// // 可选,指定语言文件存放的目录,即保存编译后的语言文件的文件夹
// // 可以指定相对路径,也可以指定绝对路径
// // location:"",
// autoImport:"/src/languages/index.js"
// }
// ]
[
i18nPlugin,
{
// 可选,指定语言文件存放的目录,即保存编译后的语言文件的文件夹
// 可以指定相对路径,也可以指定绝对路径
// location:"",
autoImport:"/src/languages/index.js"
}
]
]
}

View File

@ -11,7 +11,7 @@
"@voerkai18n/cli": "workspace:^1.0.11",
"@voerkai18n/vite": "workspace:^1.0.7",
"@voerkai18n/vue": "workspace:^1.0.0",
"vue": "^3.2.31"
"vue": "^3.2.37"
},
"devDependencies": {
"@babel/cli": "^7.17.6",
@ -25,7 +25,7 @@
"@voerkai18n/babel": "workspace:^1.0.0",
"babel-preset-env": "^1.7.0",
"core-js": "^3.21.1",
"vite": "^2.8.6",
"vite": "^2.9.14",
"vite-plugin-inspect": "^0.4.3"
}
}

View File

@ -7,7 +7,9 @@
</script>
<script>
import China from './china.vue'
export default {
components:{China},
data() {
return {
date1: new Date(),
@ -23,6 +25,7 @@ export default {
<p>{{ t("现在是{ value | date }",date1)}} </p>
<p>{{ t("现在是{ value | date }",date2)}} </p>
<p>{{ t("现在是{ value | date }",date3)}} </p>
<China></China>
</div>
</template>

View File

@ -5,7 +5,7 @@ import { babel } from '@rollup/plugin-babel';
import Inspect from 'vite-plugin-inspect'
import Voerkai18nPlugin from "@voerkai18n/vite"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
@ -17,10 +17,7 @@ export default defineConfig({
],
resolve:{
alias:{
//"voerkai18n":"./languages/index.js"
"voerkai18n":"./languages/index.js"
}
},
define:{
'process.env':{...process.env, ...loadEnv("dev", process.cwd())}
}
}
})

View File

@ -3,6 +3,7 @@
*
*/
const { isNumber } = require("./utils")
/**
* 字典格式化器
@ -26,51 +27,195 @@
* @param {...any} args
* @returns
*/
function dict(value,...args){
for(let i=0;i<args.length;i+=2){
if(args[i]===value){
return args[i+1]
function dict(value, ...args) {
for (let i = 0; i < args.length; i += 2) {
if (args[i] === value) {
return args[i + 1]
}
}
if(args.length >0 && (args.length % 2!==0)) return args[args.length-1]
if (args.length > 0 && (args.length % 2 !== 0)) return args[args.length - 1]
return value
}
function formatCurrency(value,symbol,retainDots){
/**
* 格式化货币
* formatCurrency("123456789") == "123,456,789"
* formatCurrency("123456789",4) == "1,2345,6789"
* @param {*} value
* @param {*} bit 逗号分割位置
*/
function formatCurrency(value, bit = 3) {
if (!isNumber(value)) return value
}
module.exports = {
"*":{
$types:{
Date:(value)=>value.toLocaleString()
},
// 日期
date: (value)=> value.toLocaleDateString(),
shortdate: (value)=> `${value.getFullYear()}-${value.getMonth()+1}-${value.getDate()}`,
// 时间
time:(value)=> value.toLocaleTimeString(),
shorttime:(value)=> value.toLocaleTimeString(),
dict, //字典格式化器
},
zh:{
$types:{
Date:(value)=> `${value.getFullYear()}${value.getMonth()+1}${value.getDate()}${value.getHours()}${value.getMinutes()}${value.getSeconds()}`
/**
* 格式化日期
* 将值转换为Date类型
* @param {*} value
*/
function toDate(value) {
try {
return value instanceof Date ? value : new Date(value)
} catch {
return value == undefined ? "" : value
}
}
function toNumber(value,defualt=0) {
try {
if (isNumber(value)) {
return parseInt(value)
} else {
return defualt
}
} catch {
return value == undefined ? "" : value
}
}
const CHINESE_DIGITS = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"]
const CHINESE_UNITS = ['', '十', '百', '千', '万', '十', '百', '千', '亿', '十', '百', '千', '兆', '十', '百', '千', '京', '十', '百', '千', '垓']
const CHINESE_BIG_DIGITS = ["零", '壹', '貳', '參', '肆', '伍', '陸', '柒', '捌', '玖']
const CHINESE_BIG_UNITS = ['', '拾', '佰', '仟', '萬', '拾', '佰', '仟', '億', '拾', '佰', '仟', '兆', '拾', '佰', '仟', '京', '拾', '佰', '仟', '垓']
function toChineseNumber(value,isBig=false) {
if(typeof(value)!=="number") return value;
let [wholeValue,decimalValue] = String(value).split(".") // 处理小数点
const DIGITS = isBig ? CHINESE_BIG_DIGITS : CHINESE_DIGITS
const UNITS = isBig ? CHINESE_BIG_UNITS : CHINESE_UNITS
// 整数部份
let result = ''
for(let i=wholeValue.length-1; i>=0; i--){
let bit = parseInt(wholeValue[i])
let digit = DIGITS[bit]
let unit = UNITS[wholeValue.length-i-1]
if(bit==0){
let preBit =i< wholeValue.length ? parseInt(wholeValue[i+1]) : null// 上一位
let isKeyBits = ((wholeValue.length-i-1) % 4)==0
//if(preBit && preBit!=0) result = "零" + result
if(preBit && preBit!=0 && !isKeyBits) result = "零" + result
if(isKeyBits) result = UNITS[wholeValue.length-i-1] + result
}else{
result=`${digit}${unit}` + result
}
}
if(isBig){
result = result.replace("垓京","垓")
.replace("京兆","京")
.replace("兆億","兆")
.replace("億萬","億")
.replace("萬仟","萬")
}else{
result = result.replace("垓京","垓")
.replace("京兆","京")
.replace("兆亿","兆")
.replace("亿万","亿")
.replace("万千","万")
if(result.startsWith("一十")) result=result.substring(1)
}
// 中文数字忽略小数部分
return result
}
function toChineseBigNumber(value) {
return toChineseNumber(value,true)
}
/**
* 转换为货币
* @param {*} value
* @param {*} division 分割符号位数,3代表每3个数字添加一个,
* @param {*} unit 货币单位
* @param {*} precision 小数点精确到几位
* @returns
*/
function toCurrency(value,{division=3,unit="",precision=2}={}){
let [wholeValue,decimalValue] = String(value).split(".")
let result = [unit]
for(let i=0;i<wholeValue.length;i++){
if(((wholeValue.length - i) % division)==0 && i>0) result.push(",")
result.push(wholeValue[i])
}
if(decimalValue){
result.push(`.${decimalValue}`)
}
return result.join("")
}
/**
* 转换为中文大写货币
* @param {*} value
* @param {*} division 分割符号位数,3代表每3个数字添加一个,
* @param {*} unit 货币单位
* @param {*} precision 小数点精确到几位
*/
function toChineseCurrency(value,division=3,unit="¥"){
let result = []
let v = String(value)
for(let i=0;i<v.length;i++){
if(((v.length - i) % division)==0 && i<v.length-1) result.push(",")
result.push(v[i])
}
return unit+result.join("")
}
module.exports = {
"*": {
$types: {
Date: value => {value=toDate(value);return `${value.getFullYear()}/${value.getMonth() + 1}/${value.getDate()} ${value.getHours()}:${value.getMinutes()}:${value.getSeconds()}`}
},
// 日期
date: (value)=> `${value.getFullYear()}${value.getMonth()+1}${value.getDate()}`,
shortdate: (value)=> `${value.getFullYear()}-${value.getMonth()+1}-${value.getDate()}`,
date : value => {value=toDate(value);return `${value.getFullYear()}/${value.getMonth() + 1}/${value.getDate()}`},
shortdate : value => {value=toDate(value);return `${value.getFullYear()}/${value.getMonth() + 1}/${value.getDate()}`},
time : value => {value=toDate(value);return `${value.getHours()}:${value.getMinutes()}:${value.getSeconds()}`},
shorttime : value => {value=toDate(value);return `${value.getHours()}:${value.getMinutes()}:${value.getSeconds()}`},
year : value => toDate(value).getFullYear(),
month : value => toDate(value).getMonth() + 1,
day : value => toDate(value).getDate(),
weekdayValue : value => toDate(value).getDay(),
weekday : value => ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][toDate(value).getDay()],
shortWeekday : value => ["Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur"][toDate(value).getDay()],
monthName : value => ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][toDate(value).getMonth()]
shorMonthName: value => ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"][toDate(value).getMonth()]
// 时间
shortime:(value)=> value.toLocaleTimeString(),
time:(value)=>`${value.getHours()}${value.getMinutes()}${value.getSeconds()}`,
// 货币
currency:(value)=>`${value}`,
hour : value => toDate(value).getHours(),
hour12 : value => toDate(value).getHours()>12 : toDate(value).getHours()-12 : toDate(value).getHours(),
minute : value => toDate(value).getMinutes(),
second : value => toDate(value).getSeconds(),
millisecond : value => toDate(value).getMilliseconds(),
timestamp : value => toDate(value).getTime(),
// 数字
number : (value) => toNumber(value),
error : (value, tips = 'ERROR') => value instanceof Error ? tips : value,
empty : (value) => value,
capital: value=>value,
dict,
},
en:{
currency:(value)=>{
return `$${value}`
}
zh: {
$types: {
Date: value => `${value.getFullYear()}${value.getMonth() + 1}${value.getDate()}${value.getHours()}${value.getMinutes()}${value.getSeconds()}`
},
// 日期
date : value => `${value.getFullYear()}${value.getMonth() + 1}${value.getDate()}`,
weekday : value => ["星期日","星期一","星期二","星期三","星期四","星期五","星期六"][value.getDay()],
shortWeekDay : value => ["日","一","二","三","四","五","六"][value.getDay()]
monthName : value => ["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"][value.getMonth()]
shorMonthName: value => ["一","二","三","四","五","六","七","八","九","十","十一","十二"][value.getMonth()]
// 时间
time : value => `${value.getHours()}${value.getMinutes()}${value.getSeconds()}`
// 数字
number : (value) => toNumber(value),
// 货币
currency: (value) => `${value}`,
},
en: {
currency: (value) => {
return `$${value}`
}
}
}

View File

@ -46,7 +46,7 @@ const DataTypes = ["String","Number","Boolean","Object","Array","Function","Err
formatters="| aaa(1,1,"dddd") | bbb "
目前对参数采用简单的split(",")来解析为无法正确解析aaa(1,1,"dd,,dd")形式的参数
目前对参数采用简单的split(",")来解析此如果参数中包括了,则无法正确解析例如aaa(1,1,"dd,,dd")形式的参数
在此场景下基本够用了如果需要支持更复杂的参数解析可以后续考虑使用正则表达式来解析
@returns [[<formatterName>,[<arg>,<arg>,...]]]
@ -190,14 +190,20 @@ function resetScopeCache(scope,activeLanguage=null){
*
* 可以为每一个数据类型指定一个默认的格式化器,当传入插值变量时
* 会自动调用该格式化器来对值进行格式化转换
*
*
*
const formatters = {
"*":{
$types:{...} // 在所有语言下只作用于特定数据类型的格式化器
}, // 在所有语言下生效的格式化器
}, // 在所有语言下生效的格式化器
zh:{
$types:{
[数据类型]:(value)=>{...},
[数据类型]:{
default:(value)=>{...} // 默认
},
},
[格式化器名称]:(value)=>{...},
[格式化器名称]:(value)=>{...},
@ -216,19 +222,18 @@ function getDataTypeDefaultFormatter(scope,activeLanguage,dataType){
}else{// 当语言切换时清空缓存
resetScopeCache(scope,activeLanguage)
}
// 先在当前作用域中查找,再在全局查找
const targets = [scope.formatters,scope.global.formatters]
for(const target of targets){
if(!target) continue
// 优先在当前语言的$types中查找
// 1. 优先在当前语言的$types中查找
if((activeLanguage in target) && isPlainObject(target[activeLanguage].$types)){
let formatters = target[activeLanguage].$types
if(dataType in formatters && typeof(formatters[dataType])==="function"){
return scope.$cache.typedFormatters[dataType] = formatters[dataType]
}
}
// 在所有语言的$types中查找
// 2. 在全局$types中查找
if(("*" in target) && isPlainObject(target["*"].$types)){
let formatters = target["*"].$types
if(dataType in formatters && typeof(formatters[dataType])==="function"){
@ -240,10 +245,17 @@ function getDataTypeDefaultFormatter(scope,activeLanguage,dataType){
/**
* 获取指定名称的格式化器函数
*
* 查找逻辑
* - 在当前作用域中查找
*
* - 在全局作用域中查找
*
*
* @param {*} scope
* @param {*} activeLanguage
* @param {*} name 格式化器名称
* @returns {Function} 格式化函数
* @param {*} name 格式化器名称
* @returns {Function} 格式化函数
*/
function getFormatter(scope,activeLanguage,name){
// 缓存格式化器引用,避免重复检索
@ -256,12 +268,12 @@ function getFormatter(scope,activeLanguage,name){
// 先在当前作用域中查找,再在全局查找
const targets = [scope.formatters,scope.global.formatters]
for(const target of targets){
// 优先在当前语言查找
// 1. 优先在当前语言查找
if(activeLanguage in target){
let formatters = target[activeLanguage] || {}
if((name in formatters) && typeof(formatters[name])==="function") return scope.$cache.formatters[name] = formatters[name]
}
// 在所有语言的$types中查找
// 2. 全局作用域中查找
let formatters = target["*"] || {}
if((name in formatters) && typeof(formatters[name])==="function") return scope.$cache.formatters[name] = formatters[name]
}
@ -289,13 +301,23 @@ function executeFormatter(value,formatters){
return result
}
/**
*
*
*
* [[格式化器名称,[参数,参数,...]][格式化器名称,[参数,参数,...]]]格式化器转化为
* 格式化器的调用函数链
*
*
*
*
*
*
* @param {*} scope
* @param {*} activeLanguage
* @param {*} formatters
* @returns {Array} [(v)=>{...},(v)=>{...},(v)=>{...}]
*
*
*/
function buildFormatters(scope,activeLanguage,formatters){
let results = []
@ -335,7 +357,8 @@ function getFormattedValue(scope,activeLanguage,formatters,value){
const formatterFuncs = buildFormatters(scope,activeLanguage,formatters)
// 2. 查找每种数据类型默认格式化器,并添加到formatters最前面默认数据类型格式化器优先级最高
const defaultFormatter = getDataTypeDefaultFormatter(scope,activeLanguage,getDataTypeName(value))
if(defaultFormatter){
// 默认数据类型的格式化器仅在没有指定其他格式化器时生效
if(defaultFormatter && formatterFuncs.length==0){
formatterFuncs.splice(0,0,defaultFormatter)
}
// 3. 执行格式化器

View File

@ -16,9 +16,32 @@
return proto === baseProto;
}
function isNumber(value){
return !isNaN(parseInt(value))
/**
* 判断值是否是一个数字
* @param {*} value
* @returns
*/
function isNumber(value){
if(!value) return false
if(typeof(value)=='number') return true
if(typeof(value)!='string') return false
try{
if(value.includes(".")){
let v = parseFloat(value)
if(value.endsWith(".")){
return !isNaN(v) && String(v).length===value.length-1
}else{
return !isNaN(v) && String(v).length===value.length
}
}else{
let v = parseInt(value)
return !isNaN(v) && String(v).length===value.length
}
}catch{
return false
}
}
/**
* 简单进行对象合并

View File

@ -277,10 +277,33 @@ function isPlainObject(obj){
}
return proto === baseProto;
}
function isNumber(value){
return !isNaN(parseInt(value))
/**
* 判断值是否是一个数字
* @param {*} value
* @returns
*/
function isNumber(value){
if(!value) return false
if(typeof(value)=='number') return true
if(typeof(value)!='string') return false
try{
if(value.includes(".")){
let v = parseFloat(value)
if(value.endsWith(".")){
return !isNaN(v) && String(v).length===value.length-1
}else{
return !isNaN(v) && String(v).length===value.length
}
}else{
let v = parseInt(value)
return !isNaN(v) && String(v).length===value.length
}
}catch{
return false
}
}
/**
* 检测当前工程是否是git工程

View File

@ -11,7 +11,6 @@
*/
import { cat } from "shelljs"
import { computed,reactive,ref } from "vue"
function forceUpdate(app){
@ -24,7 +23,7 @@ function forceUpdate(app){
}
}
try{
renderComponent(app._instance.root)
updateComponent(app._instance.root)
}catch(e){
console.warn("forceUpdate error: ",e.message)
}

4
pnpm-lock.yaml generated
View File

@ -96,9 +96,9 @@ importers:
'@voerkai18n/vue': workspace:^1.0.0
babel-preset-env: ^1.7.0
core-js: ^3.21.1
vite: ^2.8.6
vite: ^2.9.14
vite-plugin-inspect: ^0.4.3
vue: ^3.2.31
vue: ^3.2.37
dependencies:
'@voerkai18n/cli': link:../../cli
'@voerkai18n/vite': link:../../vite