add formatters unit test
This commit is contained in:
parent
73794587c3
commit
139e2e2282
@ -70,7 +70,7 @@ export default defineConfig({
|
||||
"/guide/advanced/langpack.md",
|
||||
"/guide/advanced/autotranslate.md",
|
||||
"/guide/advanced/framework.md",
|
||||
"/guide/advanced/remoteLoad.md",
|
||||
"/guide/advanced/dynamic-add.md",
|
||||
"/guide/advanced/lngpatch.md",
|
||||
"/guide/advanced/langedit.md"
|
||||
],
|
||||
|
@ -1,4 +1,4 @@
|
||||
# 远程加载语言包
|
||||
# 动态增加语言支持
|
||||
|
||||
## 前言
|
||||
`voerkaI18n`默认将要翻译的文本内容经编译后保存在当`languages`文件夹下,当打包应用时会与工程一起进行打包进工程源码中。这会带来以下问题:
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
### 准备
|
||||
|
||||
为说明如何从远程加载语言包,我们将假设以下的应用:
|
||||
为说明如何利用远程加载语言包的机制为应用动态增加语言支持,我们将假设以下的应用:
|
||||
应用`chat`,依赖于`user`、`manager`、`log`等三个库,均使用了`voerkiai18n`作为多语言解决方案
|
||||
当执行完`voerkai18n compile`后,项目结构大概如下:
|
||||
```javascript | pure
|
||||
@ -95,14 +95,6 @@ i18nScope.registerDefaultLoader(async (language,scope)=>{
|
||||
}
|
||||
```
|
||||
|
||||
**重点:为什么要向服务器传递`scope.id`参数?**
|
||||
在多包环境下,按照多包/库开发的规范,每一个库或包均具有一个**唯一的id**,默认会使用`package.json`中的`name`字段。
|
||||
**例如**:
|
||||
- 应用`A`,依赖于包/库`X`、`Y`、`Z`,并且`A/X/Y/Z`均使用了`voerkiai18n`作为多语言解决方案
|
||||
- 当应用启动时,`A/X/Y/Z`均会创建一个`i18nScope`实例,其`id`分别是`A/X/Y/Z`,然后这些`i18nScope`实例会注册到全局的`voerkaI18n`实例中(详见多库联动介绍)。
|
||||
- 假如应用`A`配置支持`zh`、`en`两种语言,当应用要切换到`de`语言时,那么不仅是`A`应用本身需要切换到`de`语言,所依赖的库也需要切换到`de`语言。但是库`X`、`Y`、`Z`本身可能支持`de`语言,也可能不支持。如果不支持,则同样需要向服务器请求该库的翻译语言。因此,在向服务器请求时就需要带上`scope.id`,这样服务器就可以分别为应用`A`和依赖库`X`、`Y`、`Z`均准备对应的语言包了。
|
||||
|
||||
|
||||
### 第三步:将语言包文件保存在服务器
|
||||
|
||||
在上一步中,我们通过`fetch(/languages/${scope.id}/${language}.json)`来传递读取语言包(您可以使用任意您喜欢的方式,如`axios`),这意味着我们需要在web服务器上根据此`URL`来组织语言包,以便可以下载到语言包。比如可以这样组织:
|
||||
@ -192,8 +184,8 @@ webroot
|
||||
语言加载器时会传入两个参数:
|
||||
| 参数 | 说明 |
|
||||
| --- | --- |
|
||||
| language | 要切换的此语言|
|
||||
| scope |语言作用域实例,其中`scope.id`值默认等于`package.json`中的`name`字段。详见[参考](../../reference/i18nscope)。 |
|
||||
| **language** | 要切换的此语言|
|
||||
| **scope** |语言作用域实例,其中`scope.id`值默认等于`package.json`中的`name`字段。详见[参考](../../reference/i18nscope)。 |
|
||||
|
||||
- 典型的语言加载器非常简单,如下:
|
||||
```javascript | pure
|
||||
@ -202,8 +194,9 @@ i18nScope.registerDefaultLoader(async (language,scope)=>{
|
||||
return await (await fetch(`/languages/${scope.id}/${language}.json`)).json()
|
||||
})
|
||||
```
|
||||
- 为什么要应用自己编写语言加载器,而不是提供约定开箱即用?
|
||||
主要原因是编写语言加载器很简单,但是如何组织在服务器上的保存,想让应用开发者自行决定。比如,开发者完全可以将语言包保存在数据库中等。 另外考虑安全、兼容性等原因,因此`voerkaI18n`就将此交由开发者自行编写。
|
||||
- 为什么要应用自己编写语言加载器,而不是提供开箱即用的功能?
|
||||
主要原因是编写语言加载器很简单,只是简单地使用HTTP从服务器上读取JSON语言包文件,不存在任何难度,甚至您可以直接使用上面的例子即可。
|
||||
而关键是语言包在服务器上的如何组织与保存,可以让应用开发者自行决定。比如,开发者完全可以将语言包保存在数据库表中,以便能扩展其他功能。另外考虑安全、兼容性等原因,因此`voerkaI18n`就将此交由开发者自行编写。
|
||||
|
||||
|
||||
### 编写语言切换界面
|
||||
@ -242,7 +235,46 @@ i18nScope.registerDefaultLoader(async (language,scope)=>{
|
||||
```
|
||||
通过编写合适的语言切换界面,您可以在后期随时在线增加语种支持。
|
||||
|
||||
### 关于语言包补丁
|
||||
语言包补丁仅对在`settings.json`配置的语言起作用,而动态增加的语种因为其语言包本身就保存在服务器,因此就不存在补丁的问题。
|
||||
语言包补丁会在加载时自动合并到源码中的语言包,并且会自动在本地`localStorage`中缓存。
|
||||
### `scope.id`参数
|
||||
|
||||
**重点:为什么要向服务器传递`scope.id`参数?**
|
||||
在多包环境下,按照多包/库开发的规范,每一个库或包均具有一个**唯一的id**,默认会使用`package.json`中的`name`字段。
|
||||
|
||||
**例如**:
|
||||
- 应用`A`,依赖于包/库`X`、`Y`、`Z`,并且`A/X/Y/Z`均使用了`voerkiai18n`作为多语言解决方案
|
||||
- 当应用启动时,`A/X/Y/Z`均会创建一个`i18nScope`实例,其`id`分别是`A/X/Y/Z`,然后这些`i18nScope`实例会注册到全局的`voerkaI18n`实例中(详见多库联动介绍)。
|
||||
- 假如应用`A`配置支持`zh`、`en`两种语言,当应用要切换到`de`语言时,那么不仅是`A`应用本身需要切换到`de`语言,所依赖的库也需要切换到`de`语言。但是库`X`、`Y`、`Z`本身可能支持`de`语言,也可能不支持。如果不支持,则同样需要向服务器请求该库的翻译语言。因此,在向服务器请求时就需要带上`scope.id`,这样服务器就可以分别为应用`A`和依赖库`X`、`Y`、`Z`均准备对应的语言包了。
|
||||
|
||||
**按此机制,如果您的应用使用了任何第三方库,只要第三方库也是使用voerkai18n作为多语言解决方案,那么不需要原开发者支持,您自已就可以为之`增加语言支持`或者`打语言包补丁`。**
|
||||
|
||||
|
||||
### 缓存语言包
|
||||
|
||||
当切换到动态增加的语言时会从远程服务器加载语言包,取决于语言包的大小,可能会产生延迟,这可能对用户体验造成不良影响。因此,您可以在客户端对语言包进行缓存。
|
||||
|
||||
```javascript | pure
|
||||
import { i18nScope } from "./languages"
|
||||
|
||||
async function loadLanguageMessages(language,scope){
|
||||
let messages = await (await fetch(`/languages/${scope.id}/${language}.json`)).json()
|
||||
localStorage.setItem(`voerkai18n_${scope.id}_${language}_messages`,JSON.stringify(messages));
|
||||
return messages
|
||||
}
|
||||
|
||||
i18nScope.registerDefaultLoader(async (language,scope)=>{
|
||||
let message = localStorage.getItem(`voerkai18n_${scope.id}_${language}_messages`);
|
||||
if(messages){
|
||||
setTimeout(async ()=>{
|
||||
const messages = loadLanguageMessages(language,scope)
|
||||
scope.refresh()
|
||||
},0)
|
||||
}else{
|
||||
messages = loadLanguageMessages(language,scope)
|
||||
}
|
||||
return messages
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
@ -5,4 +5,9 @@
|
||||
|
||||
**基本思路是,应用上线后发现翻译错误时,可以在服务器上约定位置放置语言包补丁,应用会自动进行更新修复,很实用的一个特性。**
|
||||
|
||||
### 关于语言包补丁
|
||||
语言包补丁仅对在`settings.json`配置的语言起作用,而动态增加的语种因为其语言包本身就保存在服务器,因此就不存在补丁的问题。
|
||||
语言包补丁会在加载时自动合并到源码中的语言包,并且会自动在本地`localStorage`中缓存。
|
||||
|
||||
|
||||
使用方法详见`动态加载语言包`介绍。
|
||||
|
@ -25,11 +25,11 @@ console.log(t("中华人民共和国成立于{}",1949))
|
||||
`t`翻译函数是从`myapp/languages/index.js`文件导出的翻译函数,但是现在`myapp/languages`还不存在,后续会使用工具自动生成。`voerkai18n`后续会使用正则表达式对提取要翻译的文本。
|
||||
|
||||
## 第一步:安装命令行工具
|
||||
|
||||
安装`@voerkai18n/cli`到全局。
|
||||
```shell
|
||||
> npm install -g @voerkai18n/cli
|
||||
> yarn global add @voerkai18n/cli
|
||||
>pnpm add -g @voerkai18/cli
|
||||
> pnpm add -g @voerkai18/cli
|
||||
```
|
||||
|
||||
## 第二步:初始化工程
|
||||
@ -72,10 +72,25 @@ console.log(t("中华人民共和国成立于{}",1949))
|
||||
|
||||
- `voerkai18n init`是可选的,`voerkai18n extract`也可以实现相同的功能。
|
||||
- 一般情况下,您可以手工修改`settings.json`,如定义名称空间。
|
||||
- `voerkai18n init`仅仅是创建`languages`文件,并且生成`settings.json`,因此您也可以自己手工创建。
|
||||
|
||||
## 第三步:提取文本
|
||||
## 第三步:标识翻译内容
|
||||
|
||||
接下来在源码文件中,将所有需要翻译的内容使用`t`翻译函数进行包装,例如下:
|
||||
```javascript | pure
|
||||
import { t } from "<myapp>/languages"
|
||||
// 不含插值变量
|
||||
t("中华人民共和国")
|
||||
// 位置插值变量
|
||||
t("中华人民共和国{}","万岁")
|
||||
t("中华人民共和国成立于{}年,首都{}",1949,"北京")
|
||||
```
|
||||
`t`翻译函数只是一个普通函数,您需要为之提供执行环境,关于`t`翻译函数的更多用法见[这里](../use/t.md)
|
||||
|
||||
## 第四步:提取文本
|
||||
|
||||
接下来我们使用`voerkai18n extract`命令来自动扫描工程源码文件中的需要的翻译的文本信息。
|
||||
`voerkai18n extract`命令会使用正则表达式来提取`t("提取文本")`包装的文本。
|
||||
|
||||
```shell
|
||||
myapp>voerkai18n extract
|
||||
@ -83,7 +98,7 @@ myapp>voerkai18n extract
|
||||
|
||||
执行`voerkai18n extract`命令后,就会在`myapp/languages`通过生成`translates/default.json`、`settings.json`等相关文件。
|
||||
|
||||
- **translates/default.json** : 该文件就是需要进行翻译的文本信息。
|
||||
- **translates/default.json** : 该文件就是从当前工程扫描提取出来的需要进行翻译的文本信息。
|
||||
|
||||
- **settings.json**: 语言环境的基本配置信息,可以进行修改。
|
||||
|
||||
@ -100,7 +115,7 @@ myapp
|
||||
|
||||
```
|
||||
|
||||
**如果略过第一步中的`voerkai18n init`,也可以使用以下命令来为创建和更新`settinbgs.json`**
|
||||
**如果略过第一步中的`voerkai18n init`,也可以使用以下命令来为创建和更新`settings.json`**
|
||||
|
||||
```javascript | pure
|
||||
myapp>voerkai18n extract -D -lngs zh en de jp -d zh -a zh
|
||||
@ -112,9 +127,9 @@ myapp>voerkai18n extract -D -lngs zh en de jp -d zh -a zh
|
||||
- 计划支持`zh`、`en`、`de`、`jp`四种语言
|
||||
- 默认语言是中文。(指在源码文件中我们直接使用中文即可)
|
||||
- 激活语言是中文(即默认切换到中文)
|
||||
- `-D`代表显示扫描调试信息
|
||||
- `-D`代表显示扫描调试信息,可以显示从哪些文件提供哪些文本
|
||||
|
||||
## 第四步:翻译文本
|
||||
## 第五步:翻译文本
|
||||
|
||||
接下来就可以分别对`language/translates`文件夹下的所有`JSON`文件进行翻译了。每个`JSON`文件大概如下:
|
||||
|
||||
@ -122,13 +137,13 @@ myapp>voerkai18n extract -D -lngs zh en de jp -d zh -a zh
|
||||
{
|
||||
"中华人民共和国万岁":{
|
||||
"en":"<在此编写对应的英文翻译内容>",
|
||||
"de":"<在此编写对应的德文翻译内容>"
|
||||
"de":"<在此编写对应的德文翻译内容>",
|
||||
"jp":"<在此编写对应的日文翻译内容>",
|
||||
"$files":["index.js"] // 记录了该信息是从哪几个文件中提取的
|
||||
},
|
||||
"中华人民共和国成立于{}":{
|
||||
"en":"<在此编写对应的英文翻译内容>",
|
||||
"de":"<在此编写对应的德文翻译内容>"
|
||||
"de":"<在此编写对应的德文翻译内容>",
|
||||
"jp":"<在此编写对应的日文翻译内容>",
|
||||
"$files":["index.js"]
|
||||
}
|
||||
@ -143,7 +158,7 @@ myapp>voerkai18n extract -D -lngs zh en de jp -d zh -a zh
|
||||
- 如果文本内容在源代码中已修改了,则会视为新增加的内容。
|
||||
- 如果文本内容已经翻译了一部份了,则会保留已翻译的内容。
|
||||
|
||||
因此,反复执行`voerkai18n extract`命令是安全的,不会导致进行了一半的翻译内容丢失,可以放心执行。
|
||||
总之,反复执行`voerkai18n extract`命令是安全的,不会导致进行了一半的翻译内容丢失,可以放心执行。
|
||||
|
||||
大部分国际化解决方案至此就需要交给人工进行翻译了,但是`voerkai18n`除了手动翻译外,通过`voerkai18n translate`命令来实现**调用在线翻译服务**进行自动翻译。
|
||||
|
||||
@ -151,9 +166,9 @@ myapp>voerkai18n extract -D -lngs zh en de jp -d zh -a zh
|
||||
>voerkai18n translate --provider baidu --appkey <在百度翻译上申请的密钥> --appid <在百度翻译上申请的appid>
|
||||
```
|
||||
|
||||
在项目文件夹下执行上面的语句,将会自动调用百度的在线翻译API进行翻译,以现在的翻译水平而言,您只需要进行少量的微调即可。关于`voerkai18n translate`命令的使用请查阅后续介绍。
|
||||
在项目文件夹下执行上面的语句,将会自动调用`百度的在线翻译API`进行翻译,以现在的翻译水平而言,您只需要进行少量的微调即可。关于`voerkai18n translate`命令的使用请查阅后续介绍。
|
||||
|
||||
## 第五步:编译语言包
|
||||
## 第六步:编译语言包
|
||||
|
||||
当我们完成`myapp/languages/translates`下的所有`JSON语言文件`的翻译后(如果配置了名称空间后,每一个名称空间会对应生成一个文件,详见后续`名称空间`介绍),接下来需要对翻译后的文件进行编译。
|
||||
|
||||
@ -180,7 +195,7 @@ myapp> voerkai18n compile
|
||||
|
||||
```
|
||||
|
||||
## 第六步:导入翻译函数
|
||||
## 第七步:导入翻译函数
|
||||
|
||||
第一步中我们在源文件中直接使用了`t`翻译函数包装要翻译的文本信息,该`t`翻译函数就是在编译环节自动生成并声明在`myapp/languages/index.js`中的。
|
||||
|
||||
@ -192,7 +207,7 @@ import { t } from "./languages"
|
||||
|
||||
但是如果源码文件很多,重次重复导入`t`函数也是比较麻烦的,所以我们也提供了一个`babel/vite`等插件来自动导入`t`函数。
|
||||
|
||||
## 第六步:切换语言
|
||||
## 第八步:切换语言
|
||||
|
||||
当需要切换语言时,可以通过调用`change`方法来切换语言。
|
||||
|
||||
@ -201,7 +216,7 @@ import { i18nScope } from "./languages"
|
||||
|
||||
// 切换到英文
|
||||
await i18nScope.change("en")
|
||||
// VoerkaI18n是一个全局单例,可以直接访问
|
||||
// 或者VoerkaI18n是一个全局单例,可以直接访问
|
||||
await VoerkaI18n.change("en")
|
||||
```
|
||||
|
||||
@ -214,15 +229,19 @@ import { i18nScope } from "./languages"
|
||||
|
||||
// 切换到英文
|
||||
i18nScope.on((newLanguage)=>{
|
||||
// 在此重新渲染界面
|
||||
...
|
||||
|
||||
})
|
||||
//
|
||||
VoerkaI18n.on((newLanguage)=>{
|
||||
...
|
||||
// 在此重新渲染界面
|
||||
...
|
||||
})
|
||||
```
|
||||
[@voerkai18n/vue](../tools/vue.md)和[@voerkai18n/react](../use/react.md)提供了相对应的插件和库来简化重新界面更新渲染。
|
||||
|
||||
## 第七步:语言包补丁
|
||||
## 第九步:语言包补丁
|
||||
|
||||
一般情况下,多语言的工程化过程就结束了,`voerkai18n`在多语言实践考虑得更加人性化。有没有经常发现这样的情况,当项目上线后,才发现:
|
||||
- 翻译有误
|
||||
@ -230,10 +249,11 @@ VoerkaI18n.on((newLanguage)=>{
|
||||
- 临时要增加支持一种语言
|
||||
|
||||
一般碰到这种情况,只好重新打包构建工程,重新发布,整个过程繁琐而麻烦。
|
||||
现在`voerkai18n`针对此问题提供了完美的解决方案,可以通过服务器来为应用打语言包补丁和增加语言支持,而不需要重新打包应用和修改应用。
|
||||
方法如下:
|
||||
现在`voerkai18n`针对此问题提供了完美的解决方案,可以通过服务器来为应用`打语言包补丁`和`动态增加语言`支持,而不需要重新打包应用和修改应用。
|
||||
|
||||
1. 注册一个默认的语言包加载器函数,用来从服务器加载语言包文件
|
||||
**方法如下:**
|
||||
|
||||
1. 注册一个默认的语言包加载器函数,用来从服务器加载语言包文件。
|
||||
```javascript | pure
|
||||
import { i18nScope } from "./languages"
|
||||
|
||||
@ -243,7 +263,7 @@ i18nScope.registerDefaultLoader(async (language,scope)=>{
|
||||
```
|
||||
|
||||
2. 将语言包补丁文件保存在服务器上指定的位置`/languages/<应用名称>/<语言名称>.json`即可。
|
||||
3. 当应用启动后会自动从服务器上加载语言补丁包,从而实现动为语言包打补丁的功能。也可以实现动态增加临时支持一种语言的功能
|
||||
3. 当应用启动后会自动从服务器上加载语言补丁包合并,从而实现动为语言包打补丁的功能。也可以实现动态增加临时支持一种语言的功能
|
||||
|
||||
更完整的说明详见[`动态加载语言包`](../advanced/remote-load.md)和[`语言包补丁`](../advanced/lngpatch.md)功能介绍。
|
||||
|
||||
|
@ -5,6 +5,8 @@ hero:
|
||||
actions:
|
||||
- text: 快速入门
|
||||
link: https://zhangfisher.github.io/voerka-i18n/guide/intro/get-started
|
||||
- text: 交流QQ群
|
||||
link: https://qm.qq.com/cgi-bin/qm/qr?k=jKyZR9KupT9Ith5ZsulB-i04OaJDkCwe&jump_from=webapi
|
||||
features:
|
||||
- title: 全流程支持
|
||||
icon: images/flow.png
|
||||
|
@ -5,17 +5,18 @@
|
||||
|
||||
const { toDate,toCurrency,toNumber,formatDatetime,formatTime,Formatter } = require("../utils")
|
||||
|
||||
// 日期格式化器
|
||||
// format取字符串"long","short","local","iso","gmt","utc"或者日期模块字符串
|
||||
// { value | date } == '2022/8/15' 默认
|
||||
// { value | date('long') } == '2022/8/15 12:08:32'
|
||||
// { value | date('short') } == '8/15'
|
||||
// { value | date('GMT') } == 'Mon, 15 Aug 2022 06:39:38 GMT'
|
||||
// { value | date('ISO') } == 'Mon, 15 Aug 2022 06:39:38 ISO'
|
||||
// { value | date('YYYY-MM-DD HH:mm:ss') } == '2022-8-15 12:08:32'
|
||||
|
||||
|
||||
/**
|
||||
* 日期格式化器
|
||||
* format取值:
|
||||
* 0-local,1-long,2-short,3-iso,4-gmt,5-UTC
|
||||
* 或者日期模板字符串
|
||||
* 默认值是local
|
||||
*/
|
||||
const dateFormatter = Formatter((value,format,$config)=>{
|
||||
const optionals = ["long","short","local","iso","gmt","utc"]
|
||||
// 处理参数:支持大小写和数字0-long,1-short,2-local,3-iso,4-gmt,5-utc
|
||||
const optionals = ["local","long","short","iso","gmt","utc"]
|
||||
// 处理参数:同时支持大小写名称和数字
|
||||
const optionIndex = optionals.findIndex((v,i)=>{
|
||||
if(typeof(format)=="string"){
|
||||
return v==format || v== format.toUpperCase()
|
||||
@ -24,12 +25,12 @@ const dateFormatter = Formatter((value,format,$config)=>{
|
||||
}
|
||||
})
|
||||
switch(optionIndex){
|
||||
case 0: // long
|
||||
return formatDatetime(value,$config.long)
|
||||
case 1: // short
|
||||
return formatDatetime(value,$config.short)
|
||||
case 2: // local
|
||||
case 0: // local
|
||||
return value.toLocaleString()
|
||||
case 1: // long
|
||||
return formatDatetime(value,$config.long)
|
||||
case 2: // short
|
||||
return formatDatetime(value,$config.short)
|
||||
case 3: // ISO
|
||||
return value.toISOString()
|
||||
case 4: // GMT
|
||||
@ -45,7 +46,7 @@ const dateFormatter = Formatter((value,format,$config)=>{
|
||||
configKey: "datetime.date"
|
||||
})
|
||||
// 季度格式化器 format= 0=短格式 1=长格式
|
||||
const mquarterFormatter = Formatter((value,format,$config)=>{
|
||||
const quarterFormatter = Formatter((value,format,$config)=>{
|
||||
const month = value.getMonth() + 1
|
||||
const quarter = Math.floor( ( month % 3 == 0 ? ( month / 3 ) : (month / 3 + 1 ) ))
|
||||
if(format<0 && format>1) format = 0
|
||||
@ -70,7 +71,7 @@ const monthFormatter = Formatter((value,format,$config)=>{
|
||||
configKey: "datetime.month"
|
||||
})
|
||||
|
||||
// 周格式化器 format可以取值0,1,2,也可以取字符串long,short,number
|
||||
// 星期x格式化器 format可以取值0,1,2,也可以取字符串long,short,number
|
||||
const weekdayFormatter = Formatter((value,format,$config)=>{
|
||||
const day = value.getDay()
|
||||
if(typeof(format)==='string'){
|
||||
@ -85,10 +86,9 @@ const weekdayFormatter = Formatter((value,format,$config)=>{
|
||||
})
|
||||
|
||||
|
||||
// 时间格式化器 format可以取值0,1,2,也可以取字符串long,short,timestamp,local
|
||||
// 时间格式化器 format可以取值0-local(默认),1-long,2-short,3-timestamp,也可以是一个插值表达式
|
||||
const timeFormatter = Formatter((value,format,$config)=>{
|
||||
const month = value.getMonth()
|
||||
const optionals = ['long','short','timestamp','local'] //toLocaleTimeString
|
||||
const optionals = ['local','long','short','timestamp']
|
||||
const optionIndex = optionals.findIndex((v,i)=>{
|
||||
if(typeof(format)=="string"){
|
||||
return v==format || v== format.toUpperCase()
|
||||
@ -97,21 +97,21 @@ const timeFormatter = Formatter((value,format,$config)=>{
|
||||
}
|
||||
})
|
||||
switch(optionIndex){
|
||||
case 0: // long
|
||||
return formatTime(value,$config.long)
|
||||
case 1: // short
|
||||
return formatTime(value,$config.short)
|
||||
case 2: // timestamp
|
||||
return value.getTime()
|
||||
case 3: // local
|
||||
case 0: // local : toLocaleTimeString
|
||||
return value.toLocaleTimeString()
|
||||
case 1: // long
|
||||
return formatTime(value,$config.long)
|
||||
case 2: // short
|
||||
return formatTime(value,$config.short)
|
||||
case 3: // timestamp
|
||||
return value.getTime()
|
||||
default:
|
||||
return formatTime(value,format)
|
||||
}
|
||||
},{
|
||||
normalize: toDate,
|
||||
params : ['format'],
|
||||
configKey: "datetime.month"
|
||||
configKey: "datetime.time"
|
||||
})
|
||||
|
||||
// 货币格式化器, CNY $13,456.00
|
||||
@ -132,8 +132,8 @@ module.exports = {
|
||||
units : ["Year","Quarter","Month","Week","Day","Hour","Minute","Second","Millisecond","Microsecond"],
|
||||
date :{
|
||||
long : 'YYYY/MM/DD HH:mm:ss',
|
||||
short : "MM/DD",
|
||||
format : "long"
|
||||
short : "YYYY/MM/DD",
|
||||
format : "local"
|
||||
},
|
||||
quarter : {
|
||||
names : ["Q1","Q2","Q3","Q4"],
|
||||
@ -156,11 +156,16 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
currency : {
|
||||
symbol : "$", // 符号
|
||||
prefix : "", // 前缀
|
||||
suffix : "", // 后缀
|
||||
division : 3, // ,分割位
|
||||
precision : 2, // 精度
|
||||
units : ["Thousands","Millions","Billions","Trillions"], //千,百万,十亿,万亿
|
||||
default : "{symbol}{value}",
|
||||
long : "{prefix} {symbol}{value}{suffix}",
|
||||
short : "{symbol}{value}",
|
||||
symbol : "$", // 符号
|
||||
prefix : "", // 前缀
|
||||
suffix : "", // 后缀
|
||||
division : 3, // ,分割位
|
||||
precision : 2, // 精度
|
||||
|
||||
},
|
||||
number : {
|
||||
division : 3,
|
||||
|
@ -13,8 +13,8 @@ module.exports = {
|
||||
units : CN_DATETIME_UNITS,
|
||||
date :{
|
||||
long : 'YYYY年MM月DD日 HH点mm分ss秒',
|
||||
short : "MM/DD",
|
||||
format : 'long'
|
||||
short : "YYYY/MM/DD",
|
||||
format : 'local'
|
||||
},
|
||||
quarter : {
|
||||
names : ["一季度","二季度","三季度","四季度"],
|
||||
@ -34,15 +34,17 @@ module.exports = {
|
||||
time:{
|
||||
long : "HH点mm分ss秒",
|
||||
short : "HH:mm:ss",
|
||||
format : 'local'
|
||||
}
|
||||
},
|
||||
|
||||
currency : {
|
||||
units : ["万","亿","万亿","万万亿"]
|
||||
symbol : "¥",
|
||||
prefix : "",
|
||||
suffix : "元",
|
||||
division : 4,
|
||||
precision : 2
|
||||
precision : 2
|
||||
},
|
||||
number : {
|
||||
division : 3,
|
||||
|
@ -407,22 +407,23 @@ function wrapperFormatters(scope,activeLanguage,formatters){
|
||||
let wrappedFormatters = []
|
||||
addDefaultFormatters(formatters)
|
||||
for(let [name,args] of formatters){
|
||||
if(name){
|
||||
let fn = getFormatter(scope,activeLanguage,name)
|
||||
// 格式化器无效或者没有定义时,查看当前值是否具有同名的原型方法,如果有则执行调用
|
||||
// 比如padStart格式化器是String的原型方法,不需要配置就可以直接作为格式化器调用
|
||||
if(!isFunction(fn)){
|
||||
fn = (value) =>{
|
||||
if(isFunction(value[name])){
|
||||
return value[name](...args)
|
||||
}else{
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
fn.$name = name
|
||||
wrappedFormatters.push(fn)
|
||||
}
|
||||
let fn = getFormatter(scope,activeLanguage,name)
|
||||
let formatter
|
||||
// 格式化器无效或者没有定义时,查看当前值是否具有同名的原型方法,如果有则执行调用
|
||||
// 比如padStart格式化器是String的原型方法,不需要配置就可以直接作为格式化器调用
|
||||
if(isFunction(fn)){
|
||||
formatter = (value,config) => fn.call(scope,value,...args,config)
|
||||
}else{
|
||||
formatter = (value) =>{
|
||||
if(isFunction(value[name])){
|
||||
return value[name](...args)
|
||||
}else{
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
formatter.$name = name
|
||||
wrappedFormatters.push(formatter)
|
||||
}
|
||||
return wrappedFormatters
|
||||
}
|
||||
|
@ -77,12 +77,12 @@ function deepMerge(toObj,formObj,options={}){
|
||||
if(key in results){
|
||||
if(typeof value === "object" && value !== null){
|
||||
if(Array.isArray(value)){
|
||||
if(options.array === 0){//替换
|
||||
results[key] = value
|
||||
}else if(options.array === 1){//合并
|
||||
if(options.array === 1){//合并
|
||||
results[key] = [...results[key],...value]
|
||||
}else if(options.array === 2){//去重合并
|
||||
results[key] = [...new Set([...results[key],...value])]
|
||||
}else{ //默认: 替换
|
||||
results[key] = value
|
||||
}
|
||||
}else{
|
||||
results[key] = deepMerge(results[key],value,options)
|
||||
@ -259,8 +259,6 @@ function formatDatetime(value,templ="YYYY/MM/DD HH:mm:ss"){
|
||||
["ss", second.padStart(2, "0")], // 00-59 秒,两位数
|
||||
["s", second], // 0-59 秒
|
||||
["SSS", millisecond], // 000-999 毫秒,三位数
|
||||
["SS", millisecond.substring(year.length - 2, year.length)], // 00-99 毫秒(十),两位数
|
||||
["S",millisecond[millisecond.length - 1]], // 0-9 毫秒(百),一位数
|
||||
["A", hour > 12 ? "PM" : "AM"], // AM / PM 上/下午,大写
|
||||
["a", hour > 12 ? "pm" : "am"], // am / pm 上/下午,小写
|
||||
]
|
||||
@ -284,8 +282,6 @@ function formatTime(value,templ="HH:mm:ss"){
|
||||
["ss", second.padStart(2, "0")], // 00-59 秒,两位数
|
||||
["s", second], // 0-59 秒
|
||||
["SSS", millisecond], // 000-999 毫秒,三位数
|
||||
["SS", millisecond.substring(year.length - 2, year.length)], // 00-99 毫秒(十),两位数
|
||||
["S",millisecond[millisecond.length - 1]], // 0-9 毫秒(百),一位数
|
||||
["A", hour > 12 ? "PM" : "AM"], // AM / PM 上/下午,大写
|
||||
["a", hour > 12 ? "pm" : "am"] // am / pm 上/下午,小写
|
||||
]
|
||||
@ -381,8 +377,8 @@ function replaceAll(str,findValue,replaceValue){
|
||||
const formatterConfig =Object.assign({},defaultParams,getByPath(activeFormatterConfigs,opts.configKey,{}))
|
||||
let finalArgs = opts.params.map(param=>getByPath(formatterConfig,param,undefined))
|
||||
// 4. 将翻译函数执行格式化器时传入的参数覆盖默认参数
|
||||
for(let i =0; i<finalArgs.length-1;i++){
|
||||
if(i>=args.length-1) break // 最后一参数是配置
|
||||
for(let i =0; i<finalArgs.length;i++){
|
||||
if(i==args.length-1) break // 最后一参数是配置
|
||||
if(args[i]!==undefined) finalArgs[i] = args[i]
|
||||
}
|
||||
return fn(finalValue,...finalArgs,formatterConfig)
|
||||
|
@ -1,18 +1,292 @@
|
||||
const {i18nScope, translate, getInterpolatedVars } = require('../packages/runtime/dist/runtime.cjs')
|
||||
const dayjs = require('dayjs');
|
||||
|
||||
function toLanguageDict(values,startIndex=0){
|
||||
return values.reduce((result,curValue,i)=>{
|
||||
result[i+startIndex] = curValue;
|
||||
return result
|
||||
},{})
|
||||
}
|
||||
function toLanguageIdMap(values,startIndex=0){
|
||||
return values.reduce((result,curValue,i)=>{
|
||||
result[curValue] = i+startIndex
|
||||
return result
|
||||
},{})
|
||||
}
|
||||
function diffArray(arr1,arr2){
|
||||
let diffs = []
|
||||
arr1.forEach((v,i)=>{
|
||||
if(v!=arr2[i]) diffs.push([i,[v,arr2[i]]])
|
||||
})
|
||||
return diffs
|
||||
}
|
||||
|
||||
|
||||
const NOW = new Date("2022/08/12 10:12:36")
|
||||
|
||||
const zhDatetimes =[
|
||||
"现在是{ value }",
|
||||
"现在是{ value | date }",
|
||||
"现在是{ value | date('local') }",
|
||||
"现在是{ value | date('long') }",
|
||||
"现在是{ value | date('short') }",
|
||||
"现在是{ value | date('iso') }",
|
||||
"现在是{ value | date('gmt') }",
|
||||
"现在是{ value | date('utc') }",
|
||||
"现在是{ value | date(0) }", // local
|
||||
"现在是{ value | date(1) }", // long
|
||||
"现在是{ value | date(2) }", // short
|
||||
"现在是{ value | date(3) }", // iso
|
||||
"现在是{ value | date(4) }", // gmt
|
||||
"现在是{ value | date(5) }", // utc
|
||||
"现在是{ value | date('YYYY-MM-DD HH:mm:ss')}",
|
||||
"现在是{ value | date('YYYY-MM-DD')}",
|
||||
"现在是{ value | date('HH:mm:ss')}",
|
||||
"现在是{ value | month }",
|
||||
"现在是{ value | month('long')}",
|
||||
"现在是{ value | month('short')}",
|
||||
"现在是{ value | month('number')}",
|
||||
"现在是{ value | month(0)}",
|
||||
"现在是{ value | month(1)}",
|
||||
"现在是{ value | month(2)}",
|
||||
"现在是{ value | weekday }",
|
||||
"现在是{ value | weekday('long')}",
|
||||
"现在是{ value | weekday('short')}",
|
||||
"现在是{ value | weekday('number')}",
|
||||
"现在是{ value | weekday(0)}",
|
||||
"现在是{ value | weekday(1)}",
|
||||
"现在是{ value | weekday(2)}",
|
||||
// 时间
|
||||
"现在时间 - { value | time }",
|
||||
"现在时间 - { value | time('local') }",
|
||||
"现在时间 - { value | time('long') }",
|
||||
"现在时间 - { value | time('short') }",
|
||||
"现在时间 - { value | time('timestamp') }",
|
||||
"现在时间 - { value | time(0) }",
|
||||
"现在时间 - { value | time(1) }",
|
||||
"现在时间 - { value | time(2) }",
|
||||
"现在时间 - { value | time(3) }",
|
||||
"现在时间 - { value | time('HH:mm:ss') }",
|
||||
"现在时间 - { value | time('mm:ss') }",
|
||||
"现在时间 - { value | time('ss') }"
|
||||
]
|
||||
//
|
||||
|
||||
const expectZhDatetimes =[
|
||||
"现在是2022/8/12 10:12:36", // { value }
|
||||
"现在是2022/8/12 10:12:36", // { value | date }
|
||||
`现在是${NOW.toLocaleString()}`, // { value | date('local') }
|
||||
"现在是2022年08月12日 10点12分36秒", // { value | date('long') }
|
||||
"现在是2022/08/12", // { value | date('short') }
|
||||
`现在是${NOW.toISOString()}`, // { value | date('iso') }
|
||||
`现在是${NOW.toGMTString()}`, // { value | date('gmt') }
|
||||
`现在是${NOW.toUTCString()}`, // { value | date('utc') }
|
||||
`现在是${NOW.toLocaleString()}`, // { value | date(0) } // local
|
||||
"现在是2022年08月12日 10点12分36秒", // { value | date(1) } // long
|
||||
"现在是2022/08/12", // { value | date(2) } // short
|
||||
`现在是${NOW.toISOString()}`, // { value | date(3) } // iso
|
||||
`现在是${NOW.toGMTString()}`, // { value | date(4) } // gmt
|
||||
`现在是${NOW.toUTCString()}`, // { value | date(5) } // utc
|
||||
"现在是2022-08-12 10:12:36", // { value | date('YYYY-MM-DD HH:mm:ss')}
|
||||
"现在是2022-08-12", // { value | date('YYYY-MM-DD')}
|
||||
"现在是10:12:36", // { value | date('HH:mm:ss')}
|
||||
"现在是八月", // { value | month }
|
||||
"现在是八月", // { value | month('long')}
|
||||
"现在是八", // { value | month('short')}
|
||||
"现在是8", // { value | month('number')}
|
||||
"现在是八月", // { value | month(0)}
|
||||
"现在是八", // { value | month(1)}
|
||||
"现在是8", // { value | month(2)}
|
||||
"现在是星期五", // { value | weekday }
|
||||
"现在是星期五", // { value | weekday('long')}
|
||||
"现在是五", // { value | weekday('short')}
|
||||
"现在是5", // { value | weekday('number')}
|
||||
"现在是星期五", // { value | weekday(0)}
|
||||
"现在是五", // { value | weekday(1)}
|
||||
"现在是5", // { value | weekday(2)}
|
||||
// 时间
|
||||
`现在时间 - ${NOW.toLocaleTimeString()}`, // { value | time }
|
||||
`现在时间 - ${NOW.toLocaleTimeString()}`, // { value | time('local') }
|
||||
"现在时间 - 10点12分36秒", // { value | time('long') }
|
||||
"现在时间 - 10:12:36", // { value | time('short') }
|
||||
"现在时间 - 1660270356000", // { value | time('timestamp') }
|
||||
`现在时间 - ${NOW.toLocaleTimeString()}`, // { value | time(0) }
|
||||
"现在时间 - 10点12分36秒", // { value | time(1) }
|
||||
"现在时间 - 10:12:36", // { value | time(2) }
|
||||
"现在时间 - 1660270356000", // { value | time(3) }
|
||||
"现在时间 - 10:12:36", // { value | time('HH:mm:ss') }
|
||||
"现在时间 - 12:36", // { value | time('mm:ss') }
|
||||
"现在时间 - 36", // { value | time('ss') }"
|
||||
]
|
||||
|
||||
|
||||
const enDatetimes =[
|
||||
"Now is { value }",
|
||||
"Now is { value | date }",
|
||||
"Now is { value | date('local') }",
|
||||
"Now is { value | date('long') }",
|
||||
"Now is { value | date('short') }",
|
||||
"Now is { value | date('iso') }",
|
||||
"Now is { value | date('gmt') }",
|
||||
"Now is { value | date('utc') }",
|
||||
"Now is { value | date(0) }", // local
|
||||
"Now is { value | date(1) }", // long
|
||||
"Now is { value | date(2) }", // short
|
||||
"Now is { value | date(3) }", // iso
|
||||
"Now is { value | date(4) }", // gmt
|
||||
"Now is { value | date(5) }", // utc
|
||||
"Now is { value | date('YYYY-MM-DD HH:mm:ss')}",
|
||||
"Now is { value | date('YYYY-MM-DD')}",
|
||||
"Now is { value | date('HH:mm:ss')}",
|
||||
"Now is { value | month }",
|
||||
"Now is { value | month('long')}",
|
||||
"Now is { value | month('short')}",
|
||||
"Now is { value | month('number')}",
|
||||
"Now is { value | month(0)}",
|
||||
"Now is { value | month(1)}",
|
||||
"Now is { value | month(2)}",
|
||||
"Now is { value | weekday }",
|
||||
"Now is { value | weekday('long')}",
|
||||
"Now is { value | weekday('short')}",
|
||||
"Now is { value | weekday('number')}",
|
||||
"Now is { value | weekday(0)}",
|
||||
"Now is { value | weekday(1)}",
|
||||
"Now is { value | weekday(2)}",
|
||||
// 时间
|
||||
"Now time: { value | time }",
|
||||
"Now time: { value | time('local') }",
|
||||
"Now time: { value | time('long') }",
|
||||
"Now time: { value | time('short') }",
|
||||
"Now time: { value | time('timestamp') }",
|
||||
"Now time: { value | time(0) }",
|
||||
"Now time: { value | time(1) }",
|
||||
"Now time: { value | time(2) }",
|
||||
"Now time: { value | time(3) }",
|
||||
"Now time: { value | time('HH:mm:ss') }",
|
||||
"Now time: { value | time('mm:ss') }",
|
||||
"Now time: { value | time('ss') }"
|
||||
]
|
||||
|
||||
const expectEnDatetimes =[
|
||||
"Now is 2022/8/12 10:12:36", // { value }
|
||||
"Now is 2022/8/12 10:12:36", // { value | date }
|
||||
`Now is ${NOW.toLocaleString()}`, // { value | date('local') }
|
||||
"Now is 2022/08/12 10:12:36", // { value | date('long') }
|
||||
"Now is 2022/08/12", // { value | date('short') }
|
||||
`Now is ${NOW.toISOString()}`, // { value | date('iso') }
|
||||
`Now is ${NOW.toGMTString()}`, // { value | date('gmt') }
|
||||
`Now is ${NOW.toUTCString()}`, // { value | date('utc') }
|
||||
`Now is ${NOW.toLocaleString()}`, // { value | date(0) } // local
|
||||
"Now is 2022/08/12 10:12:36", // { value | date(1) } // long
|
||||
"Now is 2022/08/12", // { value | date(2) } // short
|
||||
`Now is ${NOW.toISOString()}`, // { value | date(3) } // iso
|
||||
`Now is ${NOW.toGMTString()}`, // { value | date(4) } // gmt
|
||||
`Now is ${NOW.toUTCString()}`, // { value | date(5) } // utc
|
||||
"Now is 2022-08-12 10:12:36", // { value | date('YYYY-MM-DD HH:mm:ss')}
|
||||
"Now is 2022-08-12", // { value | date('YYYY-MM-DD')}
|
||||
"Now is 10:12:36", // { value | date('HH:mm:ss')}
|
||||
"Now is August", // { value | month }
|
||||
"Now is August", // { value | month('long')}
|
||||
"Now is Aug", // { value | month('short')}
|
||||
"Now is 8", // { value | month('number')}
|
||||
"Now is August", // { value | month(0)}
|
||||
"Now is Aug", // { value | month(1)}
|
||||
"Now is 8", // { value | month(2)}
|
||||
"Now is Friday", // { value | weekday }
|
||||
"Now is Friday", // { value | weekday('long')}
|
||||
"Now is Fri", // { value | weekday('short')}
|
||||
"Now is 5", // { value | weekday('number')}
|
||||
"Now is Friday", // { value | weekday(0)}
|
||||
"Now is Fri", // { value | weekday(1)}
|
||||
"Now is 5", // { value | weekday(2)}
|
||||
// 时间
|
||||
`Now time: ${NOW.toLocaleTimeString()}`, // { value | time }
|
||||
`Now time: ${NOW.toLocaleTimeString()}`, // { value | time('local') }
|
||||
"Now time: 10:12:36", // { value | time('long') }
|
||||
"Now time: 10:12:36", // { value | time('short') }
|
||||
"Now time: 1660270356000", // { value | time('timestamp') }
|
||||
`Now time: ${NOW.toLocaleTimeString()}`, // { value | time(0) }
|
||||
"Now time: 10:12:36", // { value | time(1) }
|
||||
"Now time: 10:12:36", // { value | time(2) }
|
||||
"Now time: 1660270356000", // { value | time(3) }
|
||||
"Now time: 10:12:36", // { value | time('HH:mm:ss') }
|
||||
"Now time: 12:36", // { value | time('mm:ss') }
|
||||
"Now time: 36", // { value | time('ss') }"
|
||||
]
|
||||
|
||||
const MONEY = 123456789.8848
|
||||
const zhMoneys = [
|
||||
"商品价格: { value | currency }", // 默认格式,由语言配置指定,不同的语言不一样
|
||||
"商品价格: { value | currency('long')}", // 长格式
|
||||
"商品价格: { value | currency('short')}", // 短格式 == short(0)
|
||||
"商品价格: { value | currency('long',1)}", // 长格式: 万元
|
||||
"商品价格: { value | currency('long',2)}", // 长格式: 亿
|
||||
"商品价格: { value | currency('long',3)}", // 长格式: 万亿
|
||||
"商品价格: { value | currency('long',4)}", // 长格式: 万万亿
|
||||
|
||||
"商品价格: { value | currency('short')}", // 短格式
|
||||
"商品价格: { value | currency('short',1)}", // 短格式
|
||||
"商品价格: { value | currency('short',2)}", // 短格式
|
||||
"商品价格: { value | currency('short',3)}", // 短格式
|
||||
"商品价格: { value | currency('short',4)}", // 短格式
|
||||
"商品价格: { value | currency('short',5)}", // 短格式
|
||||
|
||||
"商品价格: { value | currency({symbol,prefix ,suffix, division,precision,unit})}", // 自定义货币格式
|
||||
|
||||
"商品价格: { value | currency('¥')}", // 指定货币符号
|
||||
"商品价格: { value | currency('¥','CNY')}", // 指定货币符号+前缀
|
||||
"商品价格: { value | currency('¥','CNY','元')}", // 指定货币符号+前缀+后缀
|
||||
"商品价格: { value | currency('¥','CNY','元',3)}", // 指定货币符号+前缀+后缀+分割位
|
||||
"商品价格: { value | currency('¥','CNY','元',3,3)}", // 指定货币符号+前缀+后缀+分割位+精度
|
||||
]
|
||||
const expectZhMoneys =[
|
||||
"商品价格: ¥1,2345,6789.88", // { value | currency }
|
||||
"商品价格: ¥1,2345,6789.88元", // { value | currency('long')}
|
||||
"商品价格: ¥1,2345,6789.88", // { value | currency('short')}
|
||||
"商品价格: ¥1,2345,6789.88", // { value | currency('¥')}
|
||||
"商品价格: CNY¥1,2345,6789.88", // { value | currency('¥','CNY')}
|
||||
"商品价格: CNY¥1,2345,6789.88元", // { value | currency('¥','CNY','元')}
|
||||
"商品价格: CNY¥123,456,789.885元", // { value | currency('¥','CNY','元',3)}
|
||||
"商品价格: CNY¥123,456,789.885元", //{ value | currency('¥','CNY','元',3,3)}
|
||||
]
|
||||
const enMoneys = [
|
||||
"Price: { value | currency }", // 默认格式,由语言配置指定,不同的语言不一样
|
||||
"Price: { value | currency('long')}", // 长格式
|
||||
"Price: { value | currency('short')}", // 短格式
|
||||
"Price: { value | currency('$')}", // 指定货币符号
|
||||
"Price: { value | currency('$','USD')}", // 指定货币符号+前缀
|
||||
"Price: { value | currency('$','USD','')}", // 指定货币符号+前缀+后缀
|
||||
"Price: { value | currency('$','USD','',3)}", // 指定货币符号+前缀+后缀+分割位
|
||||
"Price: { value | currency('$','USD','',3,3)}", // 指定货币符号+前缀+后缀+分割位+精度
|
||||
]
|
||||
const expectEnMoneys =[
|
||||
"Price: $123,456,789.88", // { value | currency }
|
||||
"Price: $123,456,789.88", // { value | currency('long')}
|
||||
"Price: $123,456,789.88", // { value | currency('short')}
|
||||
"Price: $123,456,789.88", // { value | currency('$')}
|
||||
"Price: USD$1,2345,6789.88", // { value | currency('$','USD')}
|
||||
"Price: USD$1,2345,6789.88", // { value | currency('$','USD','元')}
|
||||
"Price: USD$123,456,789.885", // { value | currency('$','USD','元',3)}
|
||||
"Price: USD$123,456,789.885", //{ value | currency('$','USD','元',3,3)}
|
||||
|
||||
]
|
||||
|
||||
|
||||
|
||||
const loaders = {
|
||||
zh:{
|
||||
1:"你好",
|
||||
2:"现在是{}",
|
||||
2:"现在是{ value | }",
|
||||
3:"我出生于{year}年,今年{age}岁",
|
||||
4:"我有{}个朋友",
|
||||
...toLanguageDict(zhDatetimes,5),
|
||||
},
|
||||
en :{
|
||||
1:"hello",
|
||||
2:"Now is {}",
|
||||
3:"I was born in {year}, now is {age} years old",
|
||||
4:["I have no friends","I have one friends","I have two friends","I have {} friends"]
|
||||
4:["I have no friends","I have one friends","I have two friends","I have {} friends"],
|
||||
...toLanguageDict(enDatetimes,5),
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,9 +306,10 @@ const formatters = {
|
||||
|
||||
const idMap = {
|
||||
"你好":1,
|
||||
"现在是{}":2,
|
||||
"现在是{ value | }":2,
|
||||
"我出生于{year}年,今年{age}岁":3,
|
||||
"我有{}个朋友":4
|
||||
"我有{}个朋友":4,
|
||||
...toLanguageIdMap(zhDatetimes,5)
|
||||
}
|
||||
const languages = [
|
||||
{ name: "zh", title: "中文" },
|
||||
@ -173,7 +448,7 @@ test("切换到其他语言时的自动匹配同名格式化器",async ()=>{
|
||||
test("位置插值翻译文本内容",async ()=>{
|
||||
const now = new Date()
|
||||
expect(t("你好")).toBe("你好");
|
||||
expect(t("现在是{}",now)).toBe(`现在是${dayjs(now).format('YYYY年MM月DD日 HH点mm分ss秒')}`);
|
||||
expect(t("现在是{ value | }",now)).toBe(`现在是${dayjs(now).format('YYYY年MM月DD日 HH点mm分ss秒')}`);
|
||||
|
||||
// 经babel自动码换后,文本内容会根据idMap自动转为id
|
||||
expect(t("1")).toBe("你好");
|
||||
@ -182,7 +457,7 @@ test("位置插值翻译文本内容",async ()=>{
|
||||
await scope.change("en")
|
||||
|
||||
expect(t("你好")).toBe("hello");
|
||||
expect(t("现在是{}",now)).toBe(`Now is ${dayjs(now).format('YYYY/MM/DD HH:mm:ss')}`);
|
||||
expect(t("现在是{ value | }",now)).toBe(`Now is ${dayjs(now).format('YYYY/MM/DD HH:mm:ss')}`);
|
||||
expect(t("1")).toBe("hello");
|
||||
expect(t("2",now)).toBe(`Now is ${dayjs(now).format('YYYY/MM/DD HH:mm:ss')}`);
|
||||
})
|
||||
@ -190,11 +465,11 @@ test("位置插值翻译文本内容",async ()=>{
|
||||
test("命名插值翻译文本内容",async ()=>{
|
||||
const now = new Date()
|
||||
expect(t("你好")).toBe("你好");
|
||||
expect(t("现在是{}",now)).toBe(`现在是${dayjs(now).format('YYYY年MM月DD日 HH点mm分ss秒')}`);
|
||||
expect(t("现在是{ value | }",now)).toBe(`现在是${dayjs(now).format('YYYY年MM月DD日 HH点mm分ss秒')}`);
|
||||
|
||||
await scope.change("en")
|
||||
expect(t("你好")).toBe("hello");
|
||||
expect(t("现在是{}",now)).toBe(`Now is ${dayjs(now).format('YYYY/MM/DD HH:mm:ss')}`);
|
||||
expect(t("现在是{ value | }",now)).toBe(`Now is ${dayjs(now).format('YYYY/MM/DD HH:mm:ss')}`);
|
||||
expect(t("1")).toBe("hello");
|
||||
expect(t("2",now)).toBe(`Now is ${dayjs(now).format('YYYY/MM/DD HH:mm:ss')}`);
|
||||
})
|
||||
@ -222,4 +497,22 @@ test("翻译复数支持",async ()=>{
|
||||
expect(t("我有{}个朋友",3)).toBe("I have 3 friends");
|
||||
expect(t("我有{}个朋友",4)).toBe("I have 4 friends");
|
||||
})
|
||||
|
||||
test("日期时间格式化器",async ()=>{
|
||||
|
||||
let zhTranslatedResults = zhDatetimes.map(v=>t(v,NOW))
|
||||
expect(zhTranslatedResults).toStrictEqual(expectZhDatetimes)
|
||||
await scope.change("en")
|
||||
let enTranslatedResults = zhDatetimes.map(v=>t(v,NOW))
|
||||
expect(enTranslatedResults).toStrictEqual(expectEnDatetimes)
|
||||
})
|
||||
|
||||
|
||||
test("货币格式化器",async ()=>{
|
||||
|
||||
|
||||
let zhTranslatedResults = zhDatetimes.map(v=>t(v,NOW))
|
||||
expect(zhTranslatedResults).toStrictEqual(expectZhDatetimes)
|
||||
await scope.change("en")
|
||||
let enTranslatedResults = zhDatetimes.map(v=>t(v,NOW))
|
||||
expect(enTranslatedResults).toStrictEqual(expectEnDatetimes)
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user