diff --git a/.umirc.ts b/.umirc.ts index c7f3f84..128f8a2 100644 --- a/.umirc.ts +++ b/.umirc.ts @@ -15,7 +15,12 @@ export default defineConfig({ locales: [['zh-CN', '中文']], theme:{ "@c-heading": "#4569d4" - }, + }, + styles: [` + ul.__dumi-default-layout-toc > li[data-depth=2] { + font-weight: bold; + } + `], scripts:[` var _hmt = _hmt || []; (function() { diff --git a/docs/src/guide/intro/get-started.md b/docs/src/guide/intro/get-started.md index 7571308..d94685f 100644 --- a/docs/src/guide/intro/get-started.md +++ b/docs/src/guide/intro/get-started.md @@ -188,6 +188,11 @@ myapp> voerkai18n compile |-- en.js |-- jp.js |-- de.js + |-- formatters // 自定义扩展格式化器 + |-- zh.js + |-- en.js + |-- jp.js + |-- de.js |-- translates // 此文件夹包含了所有需要翻译的内容 |-- default.json |-- package.json diff --git a/docs/src/guide/use/datetime.md b/docs/src/guide/use/datetime.md index eba27e6..9dfe5d4 100644 --- a/docs/src/guide/use/datetime.md +++ b/docs/src/guide/use/datetime.md @@ -2,26 +2,11 @@ ## 概述 -得益于`voerkai18n`强大的格式化机制,`@voerkai18n/runtime`内置了对强大灵活的日期时间处理机制,可以很轻松实现多语言场景下的灵活多变的日期时间显示。 -`voerkai18n`支持丰富的格式化器用来支持本地化日期时间显示: -- `date` -- `time` -- `year` -- `quarter` -- `month` -- `day` -- `weekday` -- `hour` -- `hour12` -- `minute` -- `second` -- `millisecond` -- `timestamp` +得益于`voerkai18n`强大的格式化机制,`@voerkai18n/runtime`内置了对强大灵活的日期时间处理机制,可以很轻松实现多语言场景下的灵活多变的日期时间显示。 -关于格式化器的更完整说明请[参阅](../advanced/customformatter) +关于格式化器的工作原理和配置说明请[参阅](../advanced/customformatter) -## 指南 -### 基本用法 +## 基本用法 当需要对日期和时间进行本地化显示时,请使用相对应的日期时间格式化器,可以在`t`函数中使用来对日期型变量进行本地化格式输出。 @@ -52,116 +37,6 @@ ``` 根据指定的模板字符串进行插值后输出。模板字符串中可以使用如`YYYY`、`MM`、`DD`等`占位符`来表示日期时间中的年月日等,可用的模板占位符见本文最后。 - -### 配置方法 -`voerkai18n`运行时已经内置了`zh`、`en`两种语言的日期时间相关的的格式化器。为了满足复杂的应用需要求,可以根据需要对日期时间格式化进行配置定制。 - -配置定制日期时间格式化非常简单,当使用`voerkai18n compile`后,项目结构中会生成`formatters`如下: -```javascript | pure - - |--src - | |-- languages - | | |-- - | | |-- formatters - | | | |-- zh.js - | | | |-- en.js - | | | |-- de.js - - .... -``` -`formatters`文件夹中的`zh.js`、`en.js`、`de.js`文件中包括了您自定义的格式化器。 -现在一般您可以选择: -- 修改内置的日期时间格式化 -- 配置指定语言的日期时间格式化 -#### **修改内置的日期时间格式** -由于`@voerkai18n/runtime`中已经内置了`zh`和`en`两种语言的日期时间格式化器,大多数情况下,我们会定时更新确保其有效工作,一般情况下,您是不需要修改`zh.js`、`en.js`这两个文件了。 -但是如果内置的`zh`和`en`两种语言的日期时间格式化器不能满足要求,您可以选择性地修改`zh.js`、`en.js`这两个文件,这些文件会覆盖合并到内置的日期和时间格式化规则。 - -当您第一次打开`languages/formatters/<语言名称>.js`时会发现里面是空的,除了一些注释外。如下: -```javscript | pure -export default { - $config:{ - } -} -``` -现在假设我们需要将`zh`语言的日期时间`long`格式的输出从默认的`YYYY年MM月DD日 HH点mm分ss秒`调整为`北京时间: YYYY年MM月DD日 HH点mm分ss秒`,那么只需要修改 `languages/formatters/zh.js`,如下: -```javscript | pure -export default { - $config:{ - datetime:{ - date:{ - long:"北京时间: YYYY年MM月DD日 HH点mm分ss秒" - } - } - } -} -``` -`languages/formatters/zh.js`中的配置优先级最高,会合并覆盖内置的配置。 - -**为什么可以通过修改`$config.datetime.date`来修改默认的日期时间格式化?** - -因为`date`格式化器是可配置的,当该格式化器会从`$config.datetime.date`读取模板字符串来进行格式化输出。因此,只需要覆盖`$config.datetime.date`参数即可实现自定义格式化。 - -事实上,`date`/`quarter`/`month`/`weekday`/`time`等格式化器均是可配置的,对应的配置位置是: -```javscript | pure -export default { - $config:{ - datetime:{ - date:{ ... }, - quarter:{ ... }, - month:{ ... }, - weekday:{ ... }, - time:{ ... }, - } - } -} -``` -按照这样的机制,我们就可分别配置在不种语言下,对日期时间等显示方式。 -#### **配置指定语言的日期时间格式化** -默认情况下,`en`语言的日期时间格式化器被注册到全局,当任何一种语言的指定格式化器没有定义时,会在全局格式化器中查找,因此`en`语言的日期时间格式化器是适用于所有语言。 - -如果`en`语言的日期时间格式化不符合`de`语言的要求,修改`languages/formatters/de.js`文件。 - -```javscript | pure -export default { - $config:{ - datetime:{ - date:{ - long:"" - short:"" - format:"long" - } - } - } -} -``` -这样,当切换到`de`语言时,date格式化器就会读取`languages/formatters/de.js`文件中的配置,从而实现符合要求的`de`语言的日期时间格式化。 - - -### 格式模板占位符 - - -| 占位符 | 说明 | -| --- | --- | -|YYYY | 2018 年,四位数| -|YY | 18 年,两位数 | -|MMM | Jan-Dec 月,英文缩写| -|MM | 01-12 月,两位数字| -|M | 1-12 月,从1开始| -|DD | 01-31 日,两位数| -|D | 1-31 日| -|HH | 00-23 24小时,两位数| -|H | 0-23 24小时| -|hh | 01-12 12小时,两位数| -|h | 1-12 12小时| -|mm | 00-59 分钟,两位数| -|m | 0-59 分钟| -|ss | 00-59 秒,两位数| -|s | 0-59 秒| -|SSS | 000-999 毫秒,三位数| -|A | AM / PM 上/下午,大写| -|a | am / pm 上/下午,小写| - ## 格式化器 ### 日期 - `date` @@ -209,12 +84,12 @@ t("{ value | date('YYYY-MM-DD') }",1661084229790) | --- | --- | | `t("现在是{ value }",NOW)` | `现在是2022/8/12 10:12:36`| | `t("现在是{ value \| date }",NOW)` | `现在是2022/8/12 10:12:36`| -| `t("现在是{ value \| date('local') }",NOW)` | `` | +| `t("现在是{ value \| date('local') }",NOW)` | `现在是2022/8/12 10:12:36` | | `t("现在是{ value \| date('long') }",NOW)` | `现在是2022年08月12日 10点12分36秒` | | `t("现在是{ value \| date('short') }",NOW)` | `现在是2022/08/12` | -| `t("现在是{ value \| date('iso') }",NOW)` | `` | -| `t("现在是{ value \| date('gmt') }",NOW)` | `` | -| `t("现在是{ value \| date('utc') }",NOW)` | `` | +| `t("现在是{ value \| date('iso') }",NOW)` | `现在是2022-08-12T02:12:36.000Z` | +| `t("现在是{ value \| date('gmt') }",NOW)` | `现在是Fri, 12 Aug 2022 02:12:36 GMT` | +| `t("现在是{ value \| date('utc') }",NOW)` | `现在是Fri, 12 Aug 2022 02:12:36 GMT` | | **自定义格式** | | | `t("现在是{ value \| date('YYYY-MM-DD HH:mm:ss')}",NOW)`| `现在是2022-08-12 10:12:36`| | `t("现在是{ value \| date('YYYY/MM/DD') }",NOW)",NOW)` | `现在是2022-08-12`| @@ -225,12 +100,12 @@ t("{ value | date('YYYY-MM-DD') }",1661084229790) | --- | --- | | `t("现在是 { value }",NOW)` | `Now is 2022/8/12 10:12:36`| | `t("现在是 { value \| date }",NOW)` | `Now is 2022/8/12 10:12:36`| -| `t("现在是 { value \| date('local') }",NOW)` | `Now is ` | +| `t("现在是 { value \| date('local') }",NOW)` | `Now is 2022/8/12 10:12:36` | | `t("现在是 { value \| date('long') }",NOW)` | `Now is 2022/08/12 10:12:36` | | `t("现在是 { value \| date('short') }",NOW)` | `Now is 2022/08/12` | -| `t("现在是 { value \| date('iso') }",NOW)` | `Now is ` | -| `t("现在是 { value \| date('gmt') }",NOW)` | `Now is ` | -| `t("现在是 { value \| date('utc') }",NOW)` | `Now is ` | +| `t("现在是 { value \| date('iso') }",NOW)` | `Now is 2022-08-12T02:12:36.000Z` | +| `t("现在是 { value \| date('gmt') }",NOW)` | `Now is Fri, 12 Aug 2022 02:12:36 GMT` | +| `t("现在是 { value \| date('utc') }",NOW)` | `Now is Fri, 12 Aug 2022 02:12:36 GMT` | | **自定义格式** | | | `t("现在是 { value \| date('YYYY-MM-DD HH:mm:ss') }",NOW`| `Now is 2022-08-12 10:12:36`| | `t("现在是 { value \| date('YYYY/MM/DD')}",NOW"),` | `Now is 2022-08-12`| @@ -238,16 +113,40 @@ t("{ value | date('YYYY-MM-DD') }",1661084229790) #### **配置** -除了`local`,`iso`,`gmt`,`utc`是调用`Date原型方法`外,`long`,`short`两种预设的格式是采用可配置的模板字符串来定义的。默认情况下其预设格式是: +除了`local`,`iso`,`gmt`,`utc`是调用`Date原型方法`外,`date`格式化器的支持以下配置: -| 格式名称 | 中文(zh) | 英文(en) | -| --- | --- | --- | -| `long` | `YYYY年MM月DD日 HH点mm分ss秒` | `YYYY/MM/DD HH:mm:ss` | -| `short` | `YYYY/MM/DD` | `YYYY/MM/DD` | +- **配置文件**:`languages/formatters/<语言名称>.js` +- **配置位置**: `$config.datetime.date` +- **配置参数**: + ```javascript + export default { + $config:{ + datetime:{ + date:{ + long:"<模板字符串>", + short:"<模板字符串>", + format:"" + } + } + } + } + + ``` ### 时间 - `time` `date`格式化器用来对日期类型的变量进行格式化。`date`格式化器支持参数: +#### 用法 + +```javascript | pure +t("{ value | time }",new Date()) +t("{ value | time('local') }","2022/12/9 09:12:36") +t("{ value | timetime('long') }",new Date()) +t("{ value | time('short') }","2022/12/9 09:12:36") +t("{ value | time('timestamp')}",1661084229790) +t("{ value | time('HH点mm分ss秒') }",1661084229790) +t("{ value | time('HH:mms:ss') }",1661084229790) +``` #### 参数 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | @@ -263,11 +162,12 @@ t("{ value | date('YYYY-MM-DD') }",1661084229790) **当`activeLanguage == "zh"`时:** | 翻译 | 输出 | | --- | --- | -| `t("现在时间 - { value \| time }",NOW)` | `现在时间 - ` | -| `t("现在时间 - { value \| time('local') }",NOW)` | `现在时间 - ` | +| `t("现在时间 - { value \| time }",NOW)` | `现在时间 - 10:12:36` | +| `t("现在时间 - { value \| time('local') }",NOW)` | `现在时间 - 10:12:36` | | `t("现在时间 - { value \| time('long') }",NOW)` | `现在时间 - 10点12分36秒` | | `t("现在时间 - { value \| time('short') }",NOW)` | `现在时间 - 10:12:36` | | `t("现在时间 - { value \| time('timestamp') }", ",NOW)` | `现在时间 - 1660270356000` | + | **自定义格式** | `t("现在时间 - { value \| time('HH:mm:ss') }",NOW)` | `现在时间 - 10:12:36` | | `t("现在时间 - { value \| time('mm:ss') }",NOW)` | `现在时间 - 12:36` | @@ -276,8 +176,8 @@ t("{ value | date('YYYY-MM-DD') }",1661084229790) **当`activeLanguage == "en"`时:** | 翻译 | 输出 | | --- | --- | -| `t("现在时间 - { value \| time }",NOW)` | `Now is - ` | -| `t("现在时间 - { value \| time('local') }",NOW)` | `Now is - ` | +| `t("现在时间 - { value \| time }",NOW)` | `Now is - 10:12:36` | +| `t("现在时间 - { value \| time('local') }",NOW)` | `Now is - 10:12:36` | | `t("现在时间 - { value \| time('long') }",NOW)` | `Now is - 10点12分36秒` | | `t("现在时间 - { value \| time('short') }",NOW)` | `Now is - 10:12:36` | | `t("现在时间 - { value \| time('timestamp') }",NOW)` | `Now is - 1660270356000` | @@ -288,12 +188,33 @@ t("{ value | date('YYYY-MM-DD') }",1661084229790) #### 配置 -`time`格式化器的配置方式同`date`格式化器。 +- **配置文件**:`languages/formatters/<语言名称>.js` +- **配置位置**: `$config.datetime.time` +- **配置参数**: + ```javascript + export default { + $config:{ + datetime:{ + time:{ + long:"<模板字符串>", + short:"<模板字符串>", + format:"" + } + } + } + } + ``` -### 年份 - `year` -简单输出年份数值,如`t("现在是{ value | year}",new Date())`输出`现在是2022` ### 季度 - `quarter` 输出一年中第几个季度。 +#### 用法 +```javascript | pure +t("{ value | quarter }",new Date()) +t("{ value | quarter('long') }",new Date()) +t("{ value | quarter('short') }","2022/12/9 09:12:36") +t("{ value | quarter('number') }","2022/12/9 09:12:36") +``` + #### 参数 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | @@ -311,32 +232,43 @@ t("{ value | date('YYYY-MM-DD') }",1661084229790) **当`activeLanguage == "en"`时:** | 文本 | 输出 | | --- | --- | -| `t("今年{ value \| quarter }",NOW)` | `First Quarter of this year` | +| `t("今年{ value \| quarter }",NOW)` | `Q1 of this year` | | `t("今年{ value \| quarter("long") }",NOW)` | `First Quarter of this year` | | `t("今年{ value \| quarter("short") }",NOW)` | `Q1 of this year` | | `t("今年{ value \| quarter("number") }",NOW)` | `1 of this year` | #### 配置 -配置方式同`date`格式化器。可以选择性地修改`languages/formatters/<语言名称>.js` -```javascript | pure -export default { - $config:{ - datetime:{ - quarter:{ - long:["Q1","Q2","Q3","Q4"], - short:["Q1","Q2","Q3","Q4"], - format : "short", + +- **配置文件**:`languages/formatters/<语言名称>.js` +- **配置位置**: `$config.datetime.quarter` +- **配置参数**: + ```javascript + export default { + $config:{ + datetime:{ + quarter:{ + long:"<模板字符串>", + short:"<模板字符串>", + format:"< 0:long | 1:short | 2:number>" + } } } - } -} -``` - + } + ``` ### 月份 - `month` 输出月份 `month`格式化器用来输出月份。 +#### 用法 + +```javascript | pure +t("{ value | month }",new Date()) +t("{ value | month('long') }",new Date()) +t("{ value | month('short') }","2022/12/9 09:12:36") +t("{ value | month('number') }","2022/12/9 09:12:36") +``` + #### 参数 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | @@ -368,26 +300,33 @@ export default { #### 配置 -`month`格式化器的配置方式同`date`格式化器。 -可以选择性地修改`languages/formatters/<语言名称>.js` -```javascript | pure -export default { - $config:{ - datetime:{ - month:{ - long : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], - short : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"], - format : "short", +- **配置文件**:`languages/formatters/<语言名称>.js` +- **配置位置**: `$config.datetime.month` +- **配置参数**: + ```javascript + export default { + $config:{ + datetime:{ + month:{ + long:["一月",...,"十二月"], + short:["1月",...,"12月"], + format:"< 0:long | 1:short | 2:number>" + } } } - } -} -``` + } + ``` -### 天 - `day` -输出每个月的几号。如`t("现在是{ value | day }号",new Date())`输出`现在是28号`。 ### 星期 - `weekday` 输出一星期中的第几天,如星期一、星期二、...星期日。 +#### 用法 +```javascript | pure +t("{ value | weekday }",new Date()) +t("{ value | weekday('long') }",new Date()) +t("{ value | weekday('short') }","2022/12/9 09:12:36") +t("{ value | weekday('number') }","2022/12/9 09:12:36") +``` + #### 参数 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | @@ -400,7 +339,7 @@ export default { | --- | --- | | `t("今天是{ value \| weekday }",NOW)` | `今天是星期一 ` | | `t("今天是{ value \| weekday("long") }",NOW)` | `今天是星期一 ` | -| `t("今天是{ value \| weekday("short") }",NOW)` | `今天是一 ` | +| `t("今天是{ value \| weekday("short") }",NOW)` | `今天是周一 ` | | `t("今天是{ value \| weekday("number") }",NOW)` | `今天是1` | **当`activeLanguage == "en"`时:** @@ -413,24 +352,170 @@ export default { | `t("今天是{ value \| weekday("number") }",NOW)` | `Today is 1` | #### 配置 -配置方式同`date`格式化器。可以选择性地修改`languages/formatters/<语言名称>.js` +- **配置文件**:`languages/formatters/<语言名称>.js` +- **配置位置**: `$config.datetime.weekday` +- **配置参数**: + ```javascript + export default { + $config:{ + datetime:{ + weekday:{ + long:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"], + short:["周日","周一","周二","周三","周四","周五","周六"], + format:"< 0:long | 1:short | 2:number>" + } + } + } + } + ``` -```javascript | pure + + +## 扩展配置 +`voerkai18n`运行时已经内置了`zh`、`en`两种语言的日期时间相关的的格式化器。 +为了满足复杂的应用需要求,日期时间格式化器被设计为可以进行配置。当以上格式化器不能满足要求,或者缺少某种语言的日期时间格式化时,可以非常容易地进行扩展。 + +扩展支持不同语言言的日期时间格式化非常简单,当使用`voerkai18n compile`后,项目结构中会生成`formatters`如下: + +```javascript | pure + + |--src + | |-- languages + | | |-- + | | |-- formatters + | | | |-- zh.js + | | | |-- en.js + | | | |-- de.js + + .... +``` +`formatters`文件夹中的`zh.js`、`en.js`、`de.js`文件中包括了您自定义的格式化器。 + +当您第一次打开这些文件时,会发现里面除了一些注释来引导您如何编写扩展格式化器外,并没有其他有效的内容。 + +如果您对现有的日期格式化器的输出不满意,或者缺少某种语言的日期时间格式化,您可以按下面介绍的方式来进行扩展。 + +**放心**,整个扩展过程非常简单,大部分情况下,只需要配置一些模板字符串即可。 + +以下开始介绍内容: + +- **通过简单的配置修改内置的日期时间格式化规则** +- **为运行时没有支持的语言增加日期时间格式化规则** +- **自定义预设的规则** +- **编写日期时间格式化模板** + +### **修改内置规则** +由于`@voerkai18n/runtime`中已经内置了`zh`和`en`两种语言的日期时间格式化器,大多数情况下,我们会定时更新确保其有效工作,一般情况下,您是不需要修改`zh.js`、`en.js`这两个文件了。 +但是如果内置的`zh`和`en`两种语言的日期时间格式化器不能满足要求,您可以选择性地修改`zh.js`、`en.js`这两个文件,这些文件会覆盖合并到内置的日期和时间格式化规则。 + +当您第一次打开`languages/formatters/<语言名称>.js`时会发现里面是空的(除了一些注释外)。内容大概如下: +```javascript | pure +export default { + $config:{ + } +} +``` +现在假设我们需要将`zh`语言的日期时间`long`格式的输出从默认的`YYYY年MM月DD日 HH点mm分ss秒`调整为`北京时间: YYYY年MM月DD日 HH点mm分ss秒`,那么只需要修改 `languages/formatters/zh.js`,如下: +```javascript | pure export default { $config:{ datetime:{ - long :["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], - short : ["Sun", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat"], - format : 0, // 默认格式: 0:long-长名称,1:short-短名称,2:number-数字 + date:{ + long:"北京时间: YYYY年MM月DD日 HH点mm分ss秒" + } } - } -} + } +} +``` +`languages/formatters/zh.js`中的配置优先级最高,会覆盖内置的格式化规则。 + +**为什么可以通过修改`$config.datetime.date`来修改默认的日期时间格式化?** + +因为`date`格式化器是可配置的,该格式化器函数会从`$config.datetime.date`读取模板字符串来进行格式化输出。因此,只需要修改`$config.datetime.date`中的配置参数即可实现自定义格式化。 + +事实上,`date`/`quarter`/`month`/`weekday`/`time`等格式化器均是可配置的,对应的配置位置是: +```javascript | pure +export default { + $config:{ + datetime:{ + date:{ ... }, + quarter:{ ... }, + month:{ ... }, + weekday:{ ... }, + time:{ ... }, + } + } +} +``` +按照这样的机制,我们就可以很容易分别配置在不种语言下对日期时间等显示方式。并且在多包模下,这种修改只会对当前应用或库生效。 + +### **增加格式化规则** +由于开发者对各语种日期时间格式化知识的缺失。目前,`voerkai18n`只内置了`zh`,`en`两种语言的日期时间规则支持。 +其中,`en`语言的日期时间格式化器被注册到全局。当切换到`zh`,`en`两种语言之外的其他语言时,会使用`en`语言的日期时间格式化规则。 + +很明显,`en`语言的日期时间格式化规则并不能适应所有语言的要求,在官方提供该语言支持前,您可以自行配置语言支持。 + +方法很简单,以`de`语言为例,打开`languages/formatters/de.js`文件。 + +```javascript | pure +export default { + $config:{ + datetime:{ + date:{ + long:"" + short:"" + format:"long" + } + } + } +} +``` +这样,当切换到`de`语言时,date格式化器就会读取`languages/formatters/de.js`文件中的配置,从而实现符合要求的`de`语言的日期时间格式化。 + +最后,欢迎大家贡献其他语种的日期时间格式化规则,贡献方法可以直接提交在[issues](https://gitee.com/zhangfisher/voerka-i18n/issues),也可以通过[源码方式](https://gitee.com/zhangfisher/voerka-i18n)提交PR。 + +### **扩展预设规则** + +除了预设的`long`、`short`、`number`等规则外,您可以通过模板字符串来自定义更加灵活的格式化规则。 +您也可以自己定义一个预设格式化规则。 + +比如可以为`date`格式化器定义一个`full`的规则来显示更加完整的日期时间: +```javascript | pure +t("现在是{ value | date('full') }") // 北京时间:2022年08月12日 10点12分36秒 上午 ``` -### 小时 - `hour` +方法如下: +在`languages/formatters/zh.json`中,增加一个`full`配置项即可。 +```javascript | pure +export default { + $config: { + full: "北京时间:YYYY年MM月DD日 HH点mm分ss秒 a" + } +} +``` +有了自定义的`full`预设规则,应用中就可以直接使用`t("现在是{ value | date('full') }") `进行格式化,而不需要使用自定义模板字符串的形式。 +### 自定义模板 -### 分钟 - `minute` -### 秒 - `second` -### 毫秒 - `millisecond` -### 时间戳 - `timestamp` +日期时间格式化时可以自定义显示格式模板,模板字符串中可以使用以下占位符: + +| 占位符 | 说明 | +| --- | --- | +|YYYY | 2018 年,四位数| +|YY | 18 年,两位数 | +|MMM | Jan-Dec 月,英文缩写| +|MM | 01-12 月,两位数字| +|M | 1-12 月,从1开始| +|DD | 01-31 日,两位数| +|D | 1-31 日| +|HH | 00-23 24小时,两位数| +|H | 0-23 24小时| +|hh | 01-12 12小时,两位数| +|h | 1-12 12小时| +|mm | 00-59 分钟,两位数| +|m | 0-59 分钟| +|ss | 00-59 秒,两位数| +|s | 0-59 秒| +|SSS | 000-999 毫秒,三位数| +|A | AM / PM 上/下午,大写| +|a | am / pm 上/下午,小写| \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 648c205..c7b3506 100644 --- a/jest.config.js +++ b/jest.config.js @@ -17,10 +17,15 @@ module.exports = { // clearMocks: false, // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: false, + // collectCoverage: true, // An array of glob patterns indicating a set of files for which coverage information should be collected - // collectCoverageFrom: undefined, + // collectCoverageFrom: [ + // "./packages/runtime/dist/runtime.cjs", + // "./packages/runtime/**/*.js", + //"!./packages/runtime/dist/**", + //"!./packages/runtime/node_modules/**", + //], // The directory where Jest should output its coverage files // coverageDirectory: undefined, @@ -34,12 +39,12 @@ module.exports = { coverageProvider: "v8", // A list of reporter names that Jest uses when writing coverage reports - // coverageReporters: [ - // "json", - // "text", - // "lcov", - // "clover" - // ], + coverageReporters: [ + //"json", + // "text", + "lcov", + // "clover" + ], // An object that configures minimum threshold enforcement for coverage results // coverageThreshold: undefined, diff --git a/package.json b/package.json index d1a4dad..ea19613 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,11 @@ "scripts": { "test": "cross-env NODE_OPTIONS=--experimental-vm-modules node node_modules/jest/bin/jest.js ", "test:app": "cross-env NODE_OPTIONS=--experimental-vm-modules node node_modules/jest/bin/jest.js -- app", + "test:runtime": "cross-env NODE_OPTIONS=--experimental-vm-modules jest translate --coverage --collectCoverageFrom packages/runtime/dist/runtime.cjs", "list:package": "node ./packages/autopublish/index.js list", "autopublish": "node ./packages/autopublish/index.js -a --no-ask", - "docs:build": "cross-env NODE_OPTIONS=--openssl-legacy-provider && dumi build", - "docs:dev": "cross-env NODE_OPTIONS=--openssl-legacy-provider && dumi dev " + "docs:build": "cross-env NODE_OPTIONS=--openssl-legacy-provider dumi build", + "docs:dev": "cross-env NODE_OPTIONS=--openssl-legacy-provider dumi dev " }, "author": "", "license": "ISC", @@ -23,7 +24,7 @@ "cross-env": "^7.0.3", "dayjs": "^1.11.0", "deepmerge": "^4.2.2", - "dumi": "^1.0.13", + "dumi": "^1.1.47", "fs-extra": "^10.0.1", "gulp": "^4.0.2", "jest": "^27.5.1", diff --git a/packages/runtime/__test__/translate.test.js b/packages/runtime/__test__/translate.test.js deleted file mode 100644 index f83a434..0000000 --- a/packages/runtime/__test__/translate.test.js +++ /dev/null @@ -1,547 +0,0 @@ -const {i18nScope, translate, getInterpolatedVars } = require('../dist/runtime.cjs.js') -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.88 -const zhMoneys = [ - "商品价格: { value | currency}", // 默认格式 - // long - "商品价格: { value | currency('long')}", // 长格式 - "商品价格: { value | currency('long',1)}", // 长格式: 万元 - "商品价格: { value | currency('long',2)}", // 长格式: 亿 - "商品价格: { value | currency('long',3)}", // 长格式: 万亿 - "商品价格: { value | currency('long',4)}", // 长格式: 万万亿 - // short - "商品价格: { value | currency('short')}", // 短格式 - "商品价格: { value | currency('short',1)}", // 短格式 Thousands - "商品价格: { value | currency('short',2)}", // 短格式 Millions - "商品价格: { value | currency('short',3)}", // 短格式 Billions - "商品价格: { value | currency('short',4)}", // 短格式 Trillions - - // 自定义货币格式 - "商品价格: { value | currency({symbol:'¥¥'})}", - "商品价格: { value | currency({symbol:'¥¥',prefix:'人民币:'})}", - "商品价格: { value | currency({symbol:'¥¥',prefix:'人民币:',suffix:'元整'})}", - "商品价格: { value | currency({symbol:'¥¥',prefix:'人民币:',suffix:'元整',unit:2})}", - "商品价格: { value | currency({symbol:'¥¥',prefix:'人民币:',suffix:'元整',unit:2,precision:4})}", - "商品价格: { value | currency({symbol:'¥¥',prefix:'人民币:',suffix:'元整',unit:2,precision:4,format:'{prefix}*{symbol}*{value}*{unit}*{suffix}'})}" - -] - -const expectZhMoneys =[ - "商品价格: ¥1,2345,6789.88", // { value | currency } - // long - "商品价格: RMB ¥1,2345,6789.88元", // { value | currency('long')} - "商品价格: RMB ¥1,2345.678988万元", // { value | currency('long',1)} - "商品价格: RMB ¥1.2345678988亿元", // { value | currency('long',2)} - "商品价格: RMB ¥0.00012345678988万亿元", // { value | currency('long',3)} - "商品价格: RMB ¥0.000000012345678988万万亿元", // { value | currency('long',4)} - // short - "商品价格: ¥1,2345,6789.88", // { value | currency('short')} - "商品价格: ¥1,2345.678988万", // { value | currency('short',1)} - "商品价格: ¥1.2345678988亿", // { value | currency('short',2)} - "商品价格: ¥0.00012345678988万亿", // { value | currency('short',3)} - "商品价格: ¥0.000000012345678988万万亿", // { value | currency('short',4)} - // 自定义货币格式 - "商品价格: RMB ¥¥1,2345,6789.88元", - "商品价格: 人民币: ¥¥1,2345,6789.88元", - "商品价格: 人民币: ¥¥1,2345,6789.88元整", - "商品价格: 人民币: ¥¥1.2345678988亿元整", - "商品价格: 人民币: ¥¥1.2346+亿元整", - "商品价格: 人民币:*¥¥*1.2346+*亿*元整" -] - - - -const enMoneys = [ - "Price: { value | currency }", // 默认格式 - // long - "Price: { value | currency('long') }", // 长格式 - "Price: { value | currency('long',1) }", // 长格式: 万元 - "Price: { value | currency('long',2) }", // 长格式: 亿 - "Price: { value | currency('long',3) }", // 长格式: 万亿 - "Price: { value | currency('long',4) }", // 长格式: 万万亿 - // short - "Price: { value | currency('short') }", // 短格式 - "Price: { value | currency('short',1) }", // 短格式 Thousands - "Price: { value | currency('short',2) }", // 短格式 Millions - "Price: { value | currency('short',3) }", // 短格式 Billions - "Price: { value | currency('short',4) }", // 短格式 Trillions -] -const expectEnMoneys =[ - "Price: $123,456,789.88", // { value | currency } - // long - "Price: USD $123,456,789.88", // { value | currency('long')} - "Price: USD $123,456.78988 thousands", // { value | currency('long',1)} - "Price: USD $123.45678988 millions", // { value | currency('long',2)} - "Price: USD $0.12345678988 billions", // { value | currency('long',3)} - "Price: USD $0.00012345678988 trillions", // { value | currency('long',4)} - // short - "Price: $123,456,789.88", // { value | currency('short')} - "Price: $123,456.78988 thousands", // { value | currency('short',1)} - "Price: $123.45678988 millions", // { value | currency('short',2)} - "Price: $0.12345678988 billions", // { value | currency('short',3)} - "Price: $0.00012345678988 trillions", // { value | currency('short',4)} -] - - - -const loaders = { - zh:{ - 1:"你好", - 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"], - ...toLanguageDict(enDatetimes,5), - } -} - - -const formatters = { - zh:{ - $config:{}, - $types:{}, - book:(v)=>`《${v}》`, - }, - en:{ - $config:{}, - $types:{ }, - book:(v)=>`<${v}>`, - }, -} - -const idMap = { - "你好":1, - "现在是{ value | }":2, - "我出生于{year}年,今年{age}岁":3, - "我有{}个朋友":4, - ...toLanguageIdMap(zhDatetimes,5) -} -const languages = [ - { name: "zh", title: "中文" }, - { name: "en", title: "英文" }, - { name: "de", title: "德语" }, - { name: "jp", title: "日语" } -] - - -const scope = new i18nScope({ - id : "test", - defaultLanguage: "zh", - activeLanguage : "zh", - namespaces : {}, - default : loaders.zh, // 默认语言包 - messages : loaders.zh, // 当前语言包 - languages, // 语言配置 - idMap, // 消息id映射列表 - formatters, // 扩展自定义格式化器 - loaders // 语言包加载器 -}) - - -const t = translate.bind(scope) - -// 适用于所有语言的格式化器,并且注册到全局 -scope.registerFormatters({ - "*":{ - sum : (v,n=1)=>v+n, - double: (v)=>v*2, - upper : (v)=>v.toUpperCase() - } -},true) - - - - -describe("翻译函数",()=>{ - beforeEach(() => { - scope.change("zh") - }); -test("获取翻译内容中的插值变量",done=>{ - const results = getInterpolatedVars("中华人民共和国成立于{date | year(1,2) | time('a','b') | rel }年,首都是{city}市"); - expect(results.length).toEqual(2); - // - expect(results[0].name).toEqual("date"); - expect(results[0].formatters.length).toEqual(3); - // year(1,2) - expect(results[0].formatters[0].name).toEqual("year"); - expect(results[0].formatters[0].args).toEqual([1,2]); - // time('a','b') - expect(results[0].formatters[1].name).toEqual("time"); - expect(results[0].formatters[1].args).toEqual(["a","b"]); - // rel - expect(results[0].formatters[2].name).toEqual("rel"); - expect(results[0].formatters[2].args).toEqual([]); - - - expect(results[1].name).toEqual("city"); - expect(results[1].formatters.length).toEqual(0); - - done() -}) - -test("获取翻译内容中定义了重复的插值变量",done=>{ - const results = getInterpolatedVars("{a}{a}{a|x}{a|x}{a|x|y}{a|x|y}"); - expect(results.length).toEqual(3); - expect(results[0].name).toEqual("a"); - expect(results[0].formatters.length).toEqual(0); - - expect(results[1].name).toEqual("a"); - expect(results[1].formatters.length).toEqual(1); - expect(results[1].formatters[0].name).toEqual("x"); - expect(results[1].formatters[0].args).toEqual([]); - - expect(results[2].name).toEqual("a"); - expect(results[2].formatters.length).toEqual(2); - expect(results[2].formatters[0].name).toEqual("x"); - expect(results[2].formatters[0].args).toEqual([]); - expect(results[2].formatters[1].name).toEqual("y"); - expect(results[2].formatters[1].args).toEqual([]); - - done() -}) - -test("替换翻译内容的位置插值变量",done=>{ - expect(t("{}{}{}",1,2,3)).toBe("123"); - expect(t("{a}{b}{c}",1,2,3)).toBe("123"); - // 定义了一些无效的格式化器,直接忽略 - expect(t("{a|xxx}{b|dd}{c|}",1,2,3)).toBe("123"); - expect(t("{a|xxx}{b|dd}{c|}",1,2,3,4,5,6)).toBe("123"); - expect(t("{ a|}{b|dd}{c|}{}",1,2,3)).toBe("123{}"); - // 中文状态下true和false被转换成中文的"是"和"否" - expect(t("{}{}{}",1,"2",true)).toBe("12是"); - expect(t("{|double}{}{}",1,"2",true)).toBe("22是"); - done() -}) - -test("替换翻译内容的命名插值变量",done=>{ - expect(t("{a}{b}{c}",{a:11,b:22,c:33})).toBe("112233"); - expect(t("{a}{b}{c}{a}{b}{c}",{a:1,b:"2",c:3})).toBe("123123"); - done() -}) - -test("命名插值变量使用格式化器",done=>{ - // 提供无效的格式化器,直接忽略 - expect(t("{a|x}{b|x|y}{c|}",{a:1,b:2,c:3})).toBe("123"); - expect(t("{a|x}{b|x|y}{c|double}",{a:1,b:2,c:3})).toBe("126"); - // padStart和trim格式化器只是字符串的原型方法,不需要额外定义可以直接使用 - expect(t("{a|padStart(10)}",{a:"123"})).toBe(" 123"); - expect(t("{a|padStart(10)|trim}",{a:"123"})).toBe("123"); - done() -}) - - -test("命名插值变量使用格式化器",done=>{ - // 提供无效的格式化器,直接忽略 - expect(t("{a|x}{b|x|y}{c|}",{a:1,b:2,c:3})).toBe("123"); - expect(t("{a|x}{b|x|y}{c|double}",{a:1,b:2,c:3})).toBe("126"); - // 默认的字符串格式化器,不需要定义使用字符串方法 - expect(t("{a|x}{b|x|y}{c|double}",{a:1,b:2,c:3})).toBe("126"); - expect(t("{a|padStart(10)}",{a:"123"})).toBe(" 123"); - expect(t("{a|padStart(10)|trim}",{a:"123"})).toBe("123"); - done() - }) - - - -test("切换到其他语言时的自动匹配同名格式化器",async ()=>{ - expect(t("{a}",{a:true})).toBe("是"); - expect(t("{name|book}是毛泽东思想的重要载体","毛泽东选集")).toBe("《毛泽东选集》是毛泽东思想的重要载体"); - await scope.change("en") - expect(t("{a}",{a:false})).toBe("False"); - expect(t("{name|book}是毛泽东思想的重要载体","毛泽东选集")).toBe("<毛泽东选集>是毛泽东思想的重要载体"); -}) - - -test("位置插值翻译文本内容",async ()=>{ - const now = new Date() - expect(t("你好")).toBe("你好"); - expect(t("现在是{ value | }",now)).toBe(`现在是${dayjs(now).format('YYYY/M/D HH:mm:ss')}`); - - // 经babel自动码换后,文本内容会根据idMap自动转为id - expect(t("1")).toBe("你好"); - expect(t("2",now)).toBe(`现在是${dayjs(now).format('YYYY/M/D HH:mm:ss')}`); - - await scope.change("en") - - expect(t("你好")).toBe("hello"); - expect(t("现在是{ value | }",now)).toBe(`Now is ${dayjs(now).format('YYYY/M/D HH:mm:ss')}`); - expect(t("1")).toBe("hello"); - expect(t("2",now)).toBe(`Now is ${dayjs(now).format('YYYY/M/D HH:mm:ss')}`); -}) - -test("命名插值翻译文本内容",async ()=>{ - const now = new Date() - expect(t("你好")).toBe("你好"); - expect(t("现在是{ value | }",now)).toBe(`现在是${dayjs(now).format('YYYY/M/D HH:mm:ss')}`); - - await scope.change("en") - expect(t("你好")).toBe("hello"); - expect(t("现在是{ value | }",now)).toBe(`Now is ${dayjs(now).format('YYYY/M/D HH:mm:ss')}`); - // 使用idMap - expect(t("1")).toBe("hello"); - expect(t("2",now)).toBe(`Now is ${dayjs(now).format('YYYY/M/D HH:mm:ss')}`); -}) - - -test("当没有对应的语言翻译时,保持原始输出",async ()=>{ - expect(t("我是中国人")).toBe("我是中国人"); - await scope.change("en") - expect(t("我是中国人")).toBe("我是中国人"); -}) - - -test("切换到未知语言时回退到默认语言",async ()=>{ - expect(t("我是中国人")).toBe("我是中国人"); - expect(async ()=>await scope.change("xn")).rejects.toThrow(Error); - expect(t("我是中国人")).toBe("我是中国人"); -}) - - -test("翻译复数支持",async ()=>{ - await scope.change("en") - expect(t("我有{}个朋友",0)).toBe("I have no friends"); - expect(t("我有{}个朋友",1)).toBe("I have one friends"); - expect(t("我有{}个朋友",2)).toBe("I have two friends"); - 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 zhMoneysResults = zhMoneys.map(v=>t(v,MONEY)) - expect(zhMoneysResults).toStrictEqual(expectZhMoneys) - await scope.change("en") - let enMoneysResults = enMoneys.map(v=>t(v,MONEY)) - expect(enMoneysResults).toStrictEqual(expectEnMoneys) - }) - -}) diff --git a/packages/runtime/cnutils.js b/packages/runtime/cnutils.js index f6816ad..18a92cf 100644 --- a/packages/runtime/cnutils.js +++ b/packages/runtime/cnutils.js @@ -7,9 +7,9 @@ const { isNumber } = require('./utils') const CN_DATETIME_UNITS = ["年","季度","月","周","日","小时","分钟","秒","毫秒","微秒"] const CN_WEEK_DAYS = ["星期日","星期一","星期二","星期三","星期四","星期五","星期六"] -const CN_SHORT_WEEK_DAYS =["日","一","二","三","四","五","六"] +const CN_SHORT_WEEK_DAYS =["周日","周一","周二","周三","周四","周五","周六"] const CN_MONTH_NAMES= ["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"] -const CN_SHORT_MONTH_NAMES = ["一","二","三","四","五","六","七","八","九","十","十一","十二"] +const CN_SHORT_MONTH_NAMES = ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"] const CN_NUMBER_DIGITS = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"] const CN_NUMBER_UNITS = ['', '十', '百', '千', '万', '十', '百', '千', '亿', '十', '百', '千', '兆', '十', '百', '千', '京', '十', '百', '千', '垓'] diff --git a/packages/runtime/formatters/default.js b/packages/runtime/formatters/default.js index 7b9f472..f88493d 100644 --- a/packages/runtime/formatters/default.js +++ b/packages/runtime/formatters/default.js @@ -83,6 +83,8 @@ empty.paramCount = 2 { value | error('ERROR:{ error}',) } == 显示error.constructor.name + + * @param {*} value * @param {*} escapeValue * @param {*} next 下一步的行为,取值,break,ignore diff --git a/packages/runtime/formatters/en.js b/packages/runtime/formatters/en.js index 0f546f6..73c21d8 100644 --- a/packages/runtime/formatters/en.js +++ b/packages/runtime/formatters/en.js @@ -3,118 +3,221 @@ * */ - const { toDate,toCurrency,toNumber,isPlainObject,formatDatetime,formatTime } = require("../utils") + const { toDate,toCurrency,toNumber,isFunction,isPlainObject,formatDatetime,formatTime } = require("../utils") const { Formatter } = require("../formatter") + +/** + * 该类型的格式化器具有以下特点: + * + * 1. 接受一个format参数, + * 2. format参数取值可以是若干预设的值,如long,short等,也可能是一个模板字符串 + * 3. 当format值时,如果定义在$config[configKey]里面,代表了$config[configKey][format]是一个模板字符串 + * 4. 如果!(format in $config[configKey]),则代表format值是一个模板字符串 + * 5. 如果format in presets, 则要求presets[format ]是一个(value)=>{....},直接返回 + * + **/ +function createDateTimeFormatter(options={},transformer){ + let opts = Object.assign({presets:{}},options) + return Formatter((value,format,$config)=>{ + if((format in opts.presets) && isFunction(opts.presets[format])){ + return opts.presets[format](value) + }else if((format in $config)){ + format = $config[format] + }else if(format == "number"){ + return value + } + try{ + return format==null ? value : transformer(value,format) + }catch(e){ + return value + } + },opts) +} + + /** * 日期格式化器 - * format取值: - * 0-local,1-long,2-short,3-iso,4-gmt,5-UTC - * 或者日期模板字符串 - * 默认值是local + * - format取值:local,long,short,iso,gmt,utc,<模板字符串> + * - 默认值由$config.datetime.date.format指定 */ -const dateFormatter = Formatter((value,format,$config)=>{ - const optionals = ["local","long","short","iso","gmt","utc"] - // 处理参数:同时支持大小写名称和数字 - const optionIndex = optionals.findIndex((v,i)=>{ - if(typeof(format)=="string"){ - return v==format || v== format.toUpperCase() - }else if(typeof(format)=="number"){ - return format === i - } - }) - switch(optionIndex){ - 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 - return value.toGMTString() - case 5: // UTC - return value.toUTCString() - default: - return formatDatetime(value,format) - } -},{ - normalize: toDate, // 转换输入为Date类型 - params : ['format'], - configKey: "datetime.date" -}) -// 季度格式化器 format= 0=短格式 1=长格式 1=数字 -const quarterFormatter = Formatter((value,format,$config)=>{ - const month = value.getMonth() + 1 - const quarter = Math.floor( ( month % 3 == 0 ? ( month / 3 ) : (month / 3 + 1 ) )) - if(typeof(format)==='string'){ - format = ['short','long','number'].indexOf(format) +const dateFormatter = createDateTimeFormatter({ + normalize: toDate, + params : ["format"], + configKey: "datetime.date", + presets : { + local: value=>value.toLocaleString(), + iso : value=>value.toISOString(), + utc : value=>value.toUTCString(), + gmt : value=>value.toGMTString() } - if(format<0 && format>2) format = 0 - return format==0 ? $config.short[quarter] : (format==1 ? $config.long[quarter] : quarter) -},{ - normalize: toDate, - params : ['format'], +},formatDatetime) + + +/** + * 季度格式化器 + * - format: long,short,number + * - 默认值是 short + */ +const quarterFormatter = createDateTimeFormatter({ + normalize : value=>{ + const month = value.getMonth() + 1 + return Math.floor( ( month % 3 == 0 ? ( month / 3 ) : (month / 3 + 1 ) )) + }, + params : ["format"], configKey: "datetime.quarter" -}) +},(quarter,format)=>format[quarter-1]) -// 月份格式化器 format可以取值0,1,2,也可以取字符串long,short,number -const monthFormatter = Formatter((value,format,$config)=>{ - const month = value.getMonth() - if(typeof(format)==='string'){ - format = ['long','short','number'].indexOf(format) - } - if(format<0 && format>2) format = 0 - return format==0 ? $config.long[month] : (format==1 ? $config.short[month] : month+1) -},{ - normalize: toDate, - params : ['format'], +/** + * 月份格式化器 + * - format: long,short,number + * - 默认值是 short + */ +const monthFormatter = createDateTimeFormatter({ + normalize: value=> value.getMonth() + 1, + params : ["format"], configKey: "datetime.month" -}) +},(month,format)=>format[month-1]) -// 星期x格式化器 format可以取值0,1,2,也可以取字符串long,short,number -const weekdayFormatter = Formatter((value,format,$config)=>{ - const day = value.getDay() - if(typeof(format)==='string'){ - format = ['long','short','number'].indexOf(format) - } - if(format<0 && format>2) format = 0 - return format==0 ? $config.long[day] : (format==1 ? $config.short[day] : day) -},{ - normalize: toDate, - params : ['format'], +/** + * 周格式化器 + * - format: long,short,number + * - 默认值是 long + */ +const weekdayFormatter = createDateTimeFormatter({ + normalize: value=> value.getDay(), + params : ["format"], configKey: "datetime.weekday" -}) +},(day,format)=>format[day]) + +/** + * 时间格式化器 + * - format取值:local,long,short,timestamp,<模板字符串> + * - 默认值由$config.datetime.time.format指定 + */ + const timeFormatter = createDateTimeFormatter({ + normalize : toDate, + params : ["format"], + configKey : "datetime.time", + presets : { + local : value=>value.toLocaleTimeString(), + timestamp: value=>value.getTime() + } +},formatTime) -// 时间格式化器 format可以取值0-local(默认),1-long,2-short,3-timestamp,也可以是一个插值表达式 -const timeFormatter = Formatter((value,format,$config)=>{ - const optionals = ['local','long','short','timestamp'] - const optionIndex = optionals.findIndex((v,i)=>{ - if(typeof(format)=="string"){ - return v==format || v== format.toUpperCase() - }else if(typeof(format)=="number"){ - return format === i - } - }) - switch(optionIndex){ - 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.time" -}) +// const dateFormatter = Formatter((value,format,$config)=>{ +// const optionals = ["local","long","short","iso","gmt","utc"] +// // 处理参数:同时支持大小写名称和数字 +// const optionIndex = optionals.findIndex((v,i)=>{ +// if(typeof(format)=="string"){ +// return v==format || v== format.toUpperCase() +// }else if(typeof(format)=="number"){ +// return format === i +// } +// }) +// // format名称不是optionals中的一个,并且被配置在$config,则视为扩展预设值 +// if(optionIndex==-1 && typeof(format)=="string" && (format in $config)){ +// format = $config[format] +// } + +// switch(optionIndex){ +// 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 +// return value.toGMTString() +// case 5: // UTC +// return value.toUTCString() +// default: +// return formatDatetime(value,format) +// } +// },{ +// normalize: toDate, // 转换输入为Date类型 +// params : ['format'], +// configKey: "datetime.date" +// }) +// // 季度格式化器 format= 0=短格式 1=长格式 1=数字 +// const quarterFormatter = Formatter((value,format,$config)=>{ +// const month = value.getMonth() + 1 +// const quarter = Math.floor( ( month % 3 == 0 ? ( month / 3 ) : (month / 3 + 1 ) )) +// if(typeof(format)==='string'){ +// format = ['short','long','number'].indexOf(format) +// } +// if(format<0 && format>2) format = 0 +// return format==0 ? $config.short[quarter] : (format==1 ? $config.long[quarter] : quarter) +// },{ +// normalize: toDate, +// params : ['format'], +// configKey: "datetime.quarter" +// }) + +// // 月份格式化器 format可以取值0,1,2,也可以取字符串long,short,number +// const monthFormatter = Formatter((value,format,$config)=>{ +// const month = value.getMonth() +// if(typeof(format)==='string'){ +// format = ['long','short','number'].indexOf(format) +// } +// if(format<0 && format>2) format = 0 +// return format==0 ? $config.long[month] : (format==1 ? $config.short[month] : month+1) +// },{ +// normalize: toDate, +// params : ['format'], +// configKey: "datetime.month" +// }) + +// // 星期x格式化器 format可以取值0,1,2,也可以取字符串long,short,number +// const weekdayFormatter = Formatter((value,format,$config)=>{ +// const day = value.getDay() +// if(typeof(format)==='string'){ +// format = ['long','short','number'].indexOf(format) +// } +// if(format<0 && format>2) format = 0 +// return format==0 ? $config.long[day] : (format==1 ? $config.short[day] : day) +// },{ +// normalize: toDate, +// params : ['format'], +// configKey: "datetime.weekday" +// }) + + +// // 时间格式化器 format可以取值0-local(默认),1-long,2-short,3-timestamp,也可以是一个插值表达式 +// const timeFormatter = Formatter((value,format,$config)=>{ +// const optionals = ['local','long','short','timestamp'] +// const optionIndex = optionals.findIndex((v,i)=>{ +// if(typeof(format)=="string"){ +// return v==format || v== format.toUpperCase() +// }else if(typeof(format)=="number"){ +// return format === i +// } +// }) +// // format名称不是optionals中的一个,并且被配置在$config,则视为扩展预设值 +// if(optionIndex==-1 && typeof(format)=="string" && (format in $config)){ +// format = $config[format] +// } + +// switch(optionIndex){ +// 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.time" +// }) // 货币格式化器, CNY $13,456.00 /** @@ -175,7 +278,6 @@ const currencyFormatter = Formatter((value,...args) =>{ }) - module.exports = { // 配置参数 $config:{ @@ -194,18 +296,23 @@ module.exports = { month:{ long : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], short : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"], - format : 0 // 0-长名称,1-短名称,2-数字 + format : "long" // 0-长名称,1-短名称,2-数字 }, weekday:{ long : ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], short : ["Sun", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat"], - format : 0, // 0-长名称,1-短名称,2-数字 + format : "long", // 0-长名称,1-短名称,2-数字 }, time : { long : "HH:mm:ss", short : "HH:mm:ss", format : 'local' - }, + }, + timeslots : { + slots : [12], + lowercase : ["AM","PM"] + uppercase : ["AM","PM"] + } }, currency : { default : "{symbol}{value}{unit}", @@ -254,19 +361,12 @@ module.exports = { // 以下是格式化定义 // ******************* 日期 ******************* date : dateFormatter, - time : timeFormatter, year : value => toDate(value).getFullYear(), quarter : quarterFormatter, month : monthFormatter, weekday : weekdayFormatter, - day : value => toDate(value).getDate(), // ******************* 时间 ******************* - hour : value => toDate(value).getHours(), - hour12 : value => {const hour = toDate(value).getHours(); return hour > 12 ? hour - 12 : thour}, - minute : value => toDate(value).getMinutes(), - second : value => toDate(value).getSeconds(), - millisecond : value => toDate(value).getMilliseconds(), - timestamp : value => toDate(value).getTime(), + time : timeFormatter, // ******************* 货币 ******************* currency : currencyFormatter, // 数字,如,使用分割符 diff --git a/packages/runtime/formatters/zh.js b/packages/runtime/formatters/zh.js index 343bf81..930c40a 100644 --- a/packages/runtime/formatters/zh.js +++ b/packages/runtime/formatters/zh.js @@ -22,19 +22,23 @@ module.exports = { format : "short" // 0-短格式,1-长格式,2-数字 }, month:{ - long : CN_MONTH_NAMES, + long : CN_MONTH_NAMES, short : CN_SHORT_MONTH_NAMES, - format : 0, // 0-长名称,1-短名称,2-数字 + format : "long", // 0-长名称,1-短名称,2-数字 }, weekday:{ - short : CN_WEEK_DAYS, - long : CN_SHORT_WEEK_DAYS, - format : 0, // 0-长名称,1-短名称,2-数字 + long : CN_WEEK_DAYS, + short : CN_SHORT_WEEK_DAYS, + format : "long", // 0-长名称,1-短名称,2-数字 }, time:{ long : "HH点mm分ss秒", short : "HH:mm:ss", format : 'local' + }, + timeslots : { + slots : [6,9,11,13,18], + long : ["凌晨","早上","上午","中午","下午","晚上"] } }, diff --git a/packages/runtime/index.js b/packages/runtime/index.js index 409cbd6..4e21537 100644 --- a/packages/runtime/index.js +++ b/packages/runtime/index.js @@ -1,4 +1,4 @@ -const {getDataTypeName,isNumber,isPlainObject,isFunction,isNothing,deepMerge,deepMixin} = require("./utils") +const {DataTypes,getDataTypeName,isNumber,isPlainObject,isFunction,isNothing,deepMerge,deepMixin} = require("./utils") const {getInterpolatedVars,replaceInterpolatedVars} = require("./interpolate") const {createFormatter,Formatter} = require("./formatter") const EventEmitter = require("./eventemitter") @@ -7,8 +7,6 @@ const i18nScope = require("./scope") const { translate } = require("./translate") -const DataTypes = ["String","Number","Boolean","Object","Array","Function","Error","Symbol","RegExp","Date","Null","Undefined","Set","Map","WeakSet","WeakMap"] - // 默认语言配置 const defaultLanguageSettings = { debug : true, diff --git a/packages/runtime/scope.js b/packages/runtime/scope.js index 2246797..8c3b895 100644 --- a/packages/runtime/scope.js +++ b/packages/runtime/scope.js @@ -1,19 +1,4 @@ -const { isPlainObject, isFunction, getByPath, deepMixin,deepClone } = require("./utils"); - -const DataTypes = [ - "String", - "Number", - "Boolean", - "Object", - "Array", - "Function", - "Null", - "Undefined", - "Symbol", - "Date", - "RegExp", - "Error", -]; +const { DataTypes,isPlainObject, isFunction, getByPath, deepMixin,deepClone } = require("./utils"); module.exports = class i18nScope { constructor(options = {}, callback) { diff --git a/packages/runtime/utils.js b/packages/runtime/utils.js index edefa46..a568050 100644 --- a/packages/runtime/utils.js +++ b/packages/runtime/utils.js @@ -128,7 +128,7 @@ function toDate(value) { try { return value instanceof Date ? value : new Date(value) } catch { - return value + return parseInt(value) } } /** @@ -281,10 +281,10 @@ function formatDatetime(value,templ="YYYY/MM/DD HH:mm:ss"){ ["M", month], // 1-12 月,从1开始 ["DD", day.padStart(2, "0")], // 01-31 日,两位数 ["D", day], // 1-31 日 - ["HH", hour.padStart(2, "0")], // 00-23 24小时,两位数 - ["H", hour], // 0-23 24小时 + ["HH", hour.padStart(2, "0")], // 00-23 24小时,两位数 + ["H", hour], // 0-23 24小时 ["hh", String(hourNum > 12 ? hourNum - 12 : hourNum).padStart(2, "0")], // 01-12 12小时,两位数 - ["h", String(hourNum > 12 ? hourNum - 12 : hourNum)], // 1-12 12小时 + ["h", String(hourNum > 12 ? hourNum - 12 : hourNum)], // 1-12 12小时 ["mm", minute.padStart(2, "0")], // 00-59 分钟,两位数 ["m", minute], // 0-59 分钟 ["ss", second.padStart(2, "0")], // 00-59 秒,两位数 @@ -304,8 +304,8 @@ function formatTime(value,templ="HH:mm:ss"){ const hour = String(hourNum),minute = String(v.getMinutes()),second = String(v.getSeconds()),millisecond=String(v.getMilliseconds()) let result = templ const vars = [ - ["HH", hour.padStart(2, "0")], // 00-23 24小时,两位数 - ["H", hour], // 0-23 24小时 + ["HH", hour.padStart(2, "0")], // 00-23 24小时,两位数 + ["H", hour], // 0-23 24小时 ["hh", String(hour > 12 ? hour - 12 : hour).padStart(2, "0")], // 01-12 12小时,两位数 ["h", String(hour > 12 ? hour - 12 : hour)], // 1-12 12小时 ["mm", minute.padStart(2, "0")], // 00-59 分钟,两位数 @@ -322,21 +322,16 @@ function formatTime(value,templ="HH:mm:ss"){ /** * 替换所有字符串 * 低版本ES未提供replaceAll,此函数用来替代 - * - * * @param {*} str * @param {*} findValue * @param {*} replaceValue */ function replaceAll(str,findValue,replaceValue){ if(typeof(str)!=="string" || findValue=="" || findValue==replaceValue) return str - let result = str try{ - while(result.includes(findValue)){ - result = result.replace(findValue,replaceValue) - } + return str.replace(new RegExp(escapeRegexpStr(findValue),"g"),replaceValue) }catch{} - return result + return str } /** * 使用正则表达式解析非标JOSN @@ -387,8 +382,10 @@ function safeParseJson(str){ } return JSON.parse(str) } +const DataTypes = ["String","Number","Boolean","Object","Array","Function","Error","Symbol","RegExp","Date","Null","Undefined","Set","Map","WeakSet","WeakMap"] module.exports ={ + DataTypes, isPlainObject, isFunction, isNumber, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c2cbdbd..674a135 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: cross-env: ^7.0.3 dayjs: ^1.11.0 deepmerge: ^4.2.2 - dumi: ^1.0.13 + dumi: ^1.1.47 fs-extra: ^10.0.1 gulp: ^4.0.2 inquirer: ^8.2.2 diff --git a/test/translate.test.js b/test/translate.test.js index 976d239..57b5fd6 100644 --- a/test/translate.test.js +++ b/test/translate.test.js @@ -34,12 +34,6 @@ const zhDatetimes =[ "现在是{ 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')}", @@ -47,26 +41,21 @@ const zhDatetimes =[ "现在是{ 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 | quarter }", + "现在是{ value | quarter('long')}", + "现在是{ value | quarter('short')}", + "现在是{ value | quarter('number')}", + // 时间 "现在时间 - { 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') }" @@ -82,39 +71,28 @@ const expectZhDatetimes =[ `现在是${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('short')} "现在是8", // { value | month('number')} - "现在是八月", // { value | month(0)} - "现在是八", // { value | month(1)} - "现在是8", // { value | month(2)} "现在是星期五", // { value | weekday } "现在是星期五", // { value | weekday('long')} - "现在是五", // { value | weekday('short')} + "现在是周五", // { value | weekday('short')} "现在是5", // { value | weekday('number')} - "现在是星期五", // { value | weekday(0)} - "现在是五", // { value | weekday(1)} - "现在是5", // { value | weekday(2)} + "现在是Q3", // { value | quarter } + "现在是三季度", // { value | quarter('long')} + "现在是Q3", // { value | quarter('short')} + "现在是3", // { value | quarter('number')} + // 时间 `现在时间 - ${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') }" @@ -130,12 +108,6 @@ const enDatetimes =[ "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')}", @@ -143,26 +115,20 @@ const enDatetimes =[ "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 is { value | quarter }", + "Now is { value | quarter('long')}", + "Now is { value | quarter('short')}", + "Now is { value | quarter('number')}", // 时间 "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') }" @@ -177,12 +143,6 @@ const expectEnDatetimes =[ `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')} @@ -190,26 +150,22 @@ const expectEnDatetimes =[ "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 is Q3", // { value | quarter } + "Now is Third Quarter", // { value | quarter('long')} + "Now is Q3", // { value | quarter('short')} + "Now is 3", // { value | quarter('number')} + + // 时间 `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') }" @@ -393,7 +349,6 @@ test("获取翻译内容中的插值变量",done=>{ expect(results[0].formatters[2].name).toEqual("rel"); expect(results[0].formatters[2].args).toEqual([]); - expect(results[1].name).toEqual("city"); expect(results[1].formatters.length).toEqual(0); @@ -526,8 +481,7 @@ test("翻译复数支持",async ()=>{ expect(t("我有{}个朋友",3)).toBe("I have 3 friends"); expect(t("我有{}个朋友",4)).toBe("I have 4 friends"); }) -test("日期时间格式化器",async ()=>{ - +test("日期时间格式化器",async ()=>{ let zhTranslatedResults = zhDatetimes.map(v=>t(v,NOW)) expect(zhTranslatedResults).toStrictEqual(expectZhDatetimes) await scope.change("en")