diff --git a/docs/src/guide/advanced/customformatter.md b/docs/src/guide/advanced/customformatter.md index 1b69e75..93c5355 100644 --- a/docs/src/guide/advanced/customformatter.md +++ b/docs/src/guide/advanced/customformatter.md @@ -9,7 +9,8 @@ - 本质上是一个普通的同步函数 - 支持有参和无参两种调用方式 - 支持通过管道符`|`进行链式调用 -- 可以通过`$config`为格式化器传入配置参数 +- 格式化器函数最后一个参数是当前语言的格式化器配置参数`$config` +- 格式化器函数`this`指向`scope`实例 ## 指南 @@ -20,17 +21,15 @@ - 无参数的格式化器:`(value,$config)=>{....返回格式化的结果...}`。 - 带参数的格式化器:`(value,arg1,...,$config)=>{....返回格式化的结果...}`,其中`value`是上一个格式化器的输出结果。 - -格式化器函数的第一个参数是上一个格式化器的输出,最后一个参数是定义在`$config`中的配置参数。 +格式化器函数的第一个参数是上一个格式化器的输出,最后一个参数是当前语言的格式化器配置参数`$config`。 **调用方式:** ```javascript | pure - -t("商品价格:{ value | currency }") // 参数调用 -t("商品价格:{ value | currency('long') }") /// 有参调用 -t("商品价格:{ value | currency('long') | prefix('人民币') }") /// 有参调用且链式调用 - +t("商品价格:{ value | currency }",1234.88) // 参数调用 +t("商品价格:{ value | currency('long') }",1234.88) /// 有参调用 +t("商品价格:{ value | currency('long') | prefix('人民币') }",1234.88) /// 有参调用且链式调用 +t("商品价格:{ value | currency({ symbol:'¥¥',prefix:'人民币:',suffix:'元整',unit:2 }) } ") /// 对象参数 ``` ### 类型格式化器 @@ -76,10 +75,10 @@ t("灯状态:{status}",false) // === 灯状态:OFF **说明:** -- 在匹配应用格式化时会先在当前语言的`$types`中查找匹配的格式化器。 - 类型格式化器是**默认执行的,不需要指定名称**。 - 当前作用域的格式化器优先于全局的格式化器。 -- 当指定了其他格式化器时,类型格式化器就失败,比如`t("灯状态:{status | xxx}",true)`时,上面定义的`$types.Boolean`就无效。 +- 类型格式化器仅在无参调用时才生效,当指定了其他格式化器时,类型格式化器无效,比如`t("灯状态:{status | xxx}",true)`时,上面定义的`$types.Boolean`就无效。 + ### 通用的格式化器 @@ -93,30 +92,220 @@ t("灯状态:{status}",false) // === 灯状态:OFF export default { $config:{...}, $types:{...}, - [格式化名称]:(value)=>{.....}, + [格式化名称]:(value,$config)=>{.....} } // languages/formatters/en.js export default { $config:{...}, $types:{....}, - [格式化名称]:(value)=>{.....}, - [格式化名称]:(value,arg)=>{.....}, + [格式化名称]:(value,$config)=>{.....}, + [格式化名称]:(value,arg,$config)=>{.....} } ``` -如果要注册在所有语言中均生效的,可以声明在`languages/formatters/global.js` +如果要注册在所有语言中均生效的,可以声明在`languages/formatters/en.js`中,因为所有语言均的默认`fallback`语言均是`en` +### 可配置 + +#### 引入配置机制 + +格式化器是一个普通的的同步函数,我们只需要针对不同的语言编写相对应的格式化器函数,当切换语言时,就会自动调用相应的格式化器函数进行格式化输出。 +例如:我们开发一个转换数字的格式化器,在中文下显示`一、二、三`,在英文下显示`One、Two、Three`。 ```javascript | pure -// languages/formatters/global.js -export default { - $config:{...}, - $types:{....}, - [格式化名称]:(value)=>{.....}, - [格式化名称]:(value,arg)=>{.....}, +// languages/formatters/zh.js +export default { + uppercase:(value)=> ["一","二","三"][value-1] +} +// languages/formatters/en.js +export default { + uppercase:(value)=> ["One","Two","Three"][value-1] } ``` +上面这个格式化器非常简单,以至于为不同语言编写对应的语言格式化也是非常容易的,比如可以增加`de`语言。 +```javascript | pure +// languages/formatters/de.js +export default { + uppercase:(value)=> ["Eins", "zwei", "drei"][value-1] +} +``` +但是明显上面的格式化器还存在一些瑕疵,比如没有做类型检查,这样容易出错。因此,我们来增加相应的类型检查。 +```javascript | pure +// languages/formatters/zh.js +export default { + uppercase:(value)=>{ + let index = parentInt(value) + if(index<0 || index>3 || isNaN(index)) index = 0 + return ["一","二","三"][index] + } +} +// languages/formatters/en.js +export default { + uppercase:(value)=>{ + let index = parentInt(value) + if(index<0 || index>3 || isNaN(index)) index = 0 + return ["One","Two","Three"][index] + } +} +``` +问题开始显现,`zh`和`en`两种语言中存在了同样的类型检查逻辑,如果要支持10种语言,就得重复同样的逻辑。 +所以问题关键就是无法复用逻辑,而引入**格式化可配置机制**就是为了解决多语言场景下,格式化器的逻辑复用问题。 + +#### 实现机制 + +**格式化可配置机制**事实上很简单,就是两个关键步骤: + +- **第一步:在每个语言格式化器文件,指定`$config`用来保存配置参数** +```javascript | pure + +// languages/formatters/zh.js +export default { + $config:{ .... } +} +// languages/formatters/en.js +export default { + $config:{ .... } +} +``` + +- **第二步:在执行格式化函数时传入$config** ```javascript | pure + +// languages/formatters/zh.js +export default { + $config:{ .... }, + [格式化名称]:(value,...args,$config)=>{...} +} +// languages/formatters/en.js +export default { + $config:{ .... } + [格式化名称]:(value,...args,$config)=>{...} +} +``` +当执行格式化器函数时,总是**将当前语言激活的格式化器声明中的$config作为最后一个参数传递给执行格式化器函数**。 +这样格式化器函数就可以读取配置。 + +下面我们来重写`uppercase`格式化器: + +```javascript | pure +// languages/formatters/en.js +export default { + $config:{ + values:["One","Two","Three"] + }, + uppercase:(value,$config)=>{ + let index = parentInt(value) + if(index<0 || index>3 || isNaN(index)) index = 0 + return $config.values[index] + } +} +// languages/formatters/zh.js +export default { + $config:{ + values:["一","二","三"] + } +} +// languages/formatters/de.js +export default { + $config:{ + values:["Eins", "zwei", "drei"] + } +} +``` +可以看到,引入配置机制后,只需要在`en`语言中定义格式化器,让该格式化器中与语言相关的参数从配置中读取,然后在其他语言中只需要声明$config就可以实现不同语言下的输出,逻辑得到重用。 + +#### **合并配置** + +格式化器配置机制的重点在于: +**当切换语言时,`voerkai18n`会将当前`scope.activeFormaters`切换到对应的`languages/formatters/<语言名称>.js`,这样在执行格式化器函数时,就总是可以得到当前语言的格式化器配置。** + +格式化器配置机制还支持配置的合并机制,在上例中,当我们切换到`de`语言时,`voerkai18n`会依次合并: +```javascript | pure +const finalConfig = deepMerge( + scope.global.formatters["en"].$config, // 优先级最低 + scope.global.formatters["*"].$config, + scope.formatters["en"].$config, + scope.formatters["*"].$config, + scope.global.formatters["de"].$config, + scope.formatters["de"].$config // 优先级最高 +) +``` + +按照这样的合并机制,在扩展配置时就不需要指定所有配置项,只需要按需配置即可。 + +例如:`currency`格式化器的完整配置是: + +```javascript | pure +export default { + currency:{ + default : "{symbol}{value}{unit}", + long : "{prefix} {symbol}{value}{unit}{suffix}", + short : "{symbol}{value}{unit}", + custom : "{prefix} {symbol}{value}{unit}{suffix}", + format : "default", + //-- + units : [""," thousands"," millions"," billions"," trillions"], //千,百万,十亿,万亿 + radix : 3, // 进制,即三位一进,中文是4位一进 + symbol : "$", // 符号 + prefix : "USD", // 前缀 + suffix : "", // 后缀 + division : 3, // ,分割位 + precision : 2, // 精度 + } +} +``` +如果我们要增加`de`语言,则只需要配置`untis`,`symbol`,`prefix`等需要的参数即可、 + +### 查找格式化器 + +上节中,我们说只需要在`en`语言中定义格式化器,其他语言只需要配置就可以,原因就在于`en`语言是所有语言的默认回退语言(fallback)。 + +`voerkai18n`按以下顺序查找指定名称的格式化器: + +- `scope.activeFormatters` :当前语言中声明的,即`languages/formatters/<语言名称>.js` +- `scope.formatters[fallbackLanguage]` : 当前语言的回退语言 +- `scope.formatters["*"]` : 声明在`languages/formatters/<语言名称>.js`中的`{"*":{....}}` +- `scope.global.formatters[activeLanguage]` : 注册在Voerkai18n实例中的当前语言格式化器 +- `scope.global.formatters[fallbackLanguage]` : 注册在Voerkai18n实例中的当前语言回退语言的格式化器 +- `scope.global.formatters["*"]` : 注册在Voerkai18n实例中的所有语言的格式化器 + +按照这样的搜索顺序,大部份语言的格式化器均只需要声明在`languages/formatters/en.js`中,然后为格式化增加可配置支持,然后只需要在其他语言的`languages/formatters/<语言名称>.js`声明一下配置就可以复用格式化器。 + + +### 格式化器作用域 + +定义在`languages/formatters/<语言名称>.js`里面的格式化器仅在当前`i18nScope`实例中生效,也就是仅在当前作用域生效。 + +### 全局格式化器 + +定义在`@voerkai18n/runtime`里面的格式化器则全局有效,在所有场合均可以使用,但是其优先级低于`i18nScope`作用域内的同名格式化器。 + +目前内置的全局格式化器请参阅API参考 + +### 扩展格式化器 + +除了可以在当前项目`languages/formatters/<语言名称>.js`自定义格式化器和`@voerkai18n/runtime`里面的全局格式化器外,计划单列了`@voerkai18n/formatters`项目用来包含了一些不常用的格式化器。 + +目前`@voerkai18n/formatters`还是空项目,作为开源项目,欢迎大家提交贡献更多的格式化器。 + +## 开发格式化器 + +### 普通格式化器 + +普通格式化器就是一个很简单的同步函数,其输入是插值变量值或者上一个格式化器的输出。 + +**例:** 开发一个转换数字的格式化器,在中文下显示`一、二、三`,在英文下显示`One、Two、Three` + +```javascript | pure +// languages/formatters/zh.js +export default { + uppercase:(value)=> ["一","二","三"][value-1] +} +// languages/formatters/en.js +export default { + uppercase:(value)=> ["One","Two","Three"][value-1] +} + // 当切换到中文时 t("{value | uppercase}",1) // == 一 t("{value | uppercase}",2) // == 二 @@ -127,14 +316,176 @@ t("{value | uppercase}",1) // == One t("{value | uppercase}",2) // == Two t("{value | uppercase}",3) // == Three -// 当切换到日文时,由于在该语言下没有定义uppercase,因此到global中查找 -t("{value | uppercase}",1) // == 1 -t("{value | uppercase}",2) // == 2 -t("{value | uppercase}",3) // == 3 +// 当切换到日文时,由于在该语言下没有定义uppercase,因此到`fallback`语言中查找 +t("{value | uppercase}",1) // == One +t("{value | uppercase}",2) // == Two +t("{value | uppercase}",3) // == Three ``` +**注意:** + - 普通格式化器是同步函数,不支持异步函数 -### 自定义格式化器 +### 变参格式化器 + +如果需要格式化器支持变参调用,像这样: +```javascript | pure +t("{value | uppercase}",1) +t("{value | uppercase(a)}",2) +t("{value | uppercase(a,b,c)}",3) +``` +那么就需要自己在格式化函数中处理,一般可以像这样: + + +```javascript | pure +// languages/formatters/zh.js +export default { + uppercase:(value,...args)=>{ + // 最后后一个参数总是$config + let $config = args[args.length-1] + let params = args.slice(0,args.length-1) + //.... + } +} +``` + +### Formatter + +为简化编写格式化器函数时对输入值进行值检查、变参处理、默认值处理等通用逻辑,`voerkai18n`提供了`createFormater`和`Formatter`函数帮助创建格式化函数。 + +```javascript | pure +// languages/formatters/en.js +import { Formatter } from "../runtime" +export default { + $config{ + sum:{ + a:0, + b:0, + c:0, + format:"Sum={result}" + }, + }, + sum: Formatter((value,a,b,c,$config)=>{ + // $config指向的是当前配置的sum + return $config.format.replace("{result}",value + a + b + c) + },{ + normalize:(value)=>parentInt(value), + params:["a","b","c"], + configKey:"sum" // 声明该配置化器的配置参数位置 + }) +} +``` +以上创建了一个名叫`sum`的格式化器,该格式化器支持3个参数,然后会计算`value`+`a`+`b`+`c`,然后输出`Sum={result}`。 + +```javascript | pure +t("{value | sum }",1) // a=0,b=0,c=0 +t("{value | sum(1)}",1) // a=1,b=0,c=0 +t("{value | sum(1,2)}",1) // a=1,b=2,c=0 +t("{value | sum(1,2,3)}",1) // a=1,b=2,c=3 +``` + +`Formatter`是`createFormater`的别名,其参数说明如下: + +- **normalize** + + `Function`,可选,提供一个函数来对输入值进行规范化处理,并返回规范化后的值。如上例中需要输入参数是一个数字,因为就需要转换为数字。 +- **params** + + `Array`,可选,用来声明该格式化器支持的参数列表。 + `params`的作用除了是用来声明该格式化器支持多少个参数外,`Formatter`还支持从配置中读取这些参数的默认值。 + 如上例中在`$config.sum`中配置了`a`,`b`,`c`三个参数的默认值。 + + **当指定`params`参数时**: + - 格式化器函数总是可以接收到与`params`参数匹配的参数,并且最后一个参数是`$config`,格式化函数就不需要处理变参的情况了。 + - 接收到的参数已经自动填入了从`$config[configKey]`中读取到的默认值。 + + **当没有指定`params`参数时**: + - 格式化器函数接收到的是`(value,...args,$config)`,这就意味着,你需要自己处理变参的情况了。 + + + 利用这个特性,我们就可以为不同语言下格式化器的默认参数。例如`currency`格式化器的`symbol`参数在不同语言下的值。如下: + + ```javascript | pure + // languages/formatters/zh.js + export default { + $config:{ + currency:{ + symbol : "¥" + } + } + } + // languages/formatters/en.js + export default { + $config:{ + currency:{ + symbol : "$" + } + } + } + ``` +- **configKey** + +`configKey`用来指定当前格式化器函数的配置参数位置,上例中`configKey="sum"`,则`(value,a,b,c,$config)=>{....}`中传入的`$config`就是`scope.activeFormatterConfig.sum`,也就是`languages/formatters/en.js`中的`$config.sum`。 + +如果没有指定`configKey`,则传入`$config`值就是`scope.activeFormatterConfig`,此时就无法为`a`,`b`,`c`指定默认参数了。 + +### FlexFormatter + +大部份情况下,使用`Formatter`就可以比较方便地创建格式化器了。 +当某个格式化器的可配置参数很多时,如`currency`格式化器,支持`format`,`unit`,`precision`,`prefix`,`suffix`,`division`,`symbol`,`radix`共8个位置参数,用户可以通过以下方式调用: + + ```javascript | pure + t("{ value | currency('long',1)}",1234.56) // 长格式: 万元 + t("{ value | currency('long',1,2,'人民币')}",1234.56) + t("{ value | currency('long',1,2,'人民币','元',3,'')}",1234.56) +``` +如果我们只需要变更`symbol`参数,由于`symbol`参数是第七个参数,使用位置参数就不得不这样调用。 + +```javascript | pure + t("{ value | currency(,,,,,,,'¥')}",1234.56) +``` +很丑陋的,因此,我们希望支持像这样: +```javascript | pure + t("{ value | currency({symbol:'¥')}",1234.56) +``` +也就是说,格式化器即支持可选位置传参,也支持`{...}`传参,基于上述已经介绍的格式化机制,您完全可以自己来处理这样的参数处理功能。 + +而`FlexFormatter`则封装了这样的功能,其即支持可选位置传参,也支持`{...}`传参。用法如下: + +```javascript | pure +// languages/formatters/en.js +import { FlexFormatter } from "../runtime" +export default { + $config{ + sum:{ + a:0, + b:0, + c:0, + format:"Sum={result}" + }, + }, + sum: FlexFormatter((value,params,$config)=>{ + return $config.format.replace("{result}",value + params.a + params.b + params.c) + },{ + normalize:(value)=>parentInt(value), + params:["a","b","c"], + configKey:"sum" // 声明该配置化器的配置参数位置 + }) +} +``` +经过上述改造过,上例中的`sum`格式化器就可以支持以下方式进行调用: +```javascript | pure +t("{value | sum }",1) // a=0,b=0,c=0 +t("{value | sum(1)}",1) // a=1,b=0,c=0 +t("{value | sum(1,2)}",1) // a=1,b=2,c=0 +t("{value | sum(1,2,3)}",1) // a=1,b=2,c=3 +t("{value | sum({a:1})}",1) // a=1,b=0,c=0 +t("{value | sum({a:1,b:2})}",1) // a=1,b=2,c=0 +t("{value | sum({a:1,b:2,c:3})}",1) // a=1,b=2,c=3 +``` + +## 部署格式化器 + +上面我们已经介绍了如何开发格式化器和配置相关,一般我们需要为特定语言的格式化器的配置和函数声明在`languages/formatters/<语言名称>.js`中,下面进一步介绍一下格式化器应该在哪里进行声明。 当使用`voerkai18n compile`后,项目结构中会生成`formatters`如下: @@ -142,59 +493,64 @@ t("{value | uppercase}",3) // == 3 |--src | |-- languages - | | |-- formatters - | | | |-- zh.js - | | | |-- en.js - | | | |-- de.js - - .... + |-- formatters + | |-- zh.js + | |-- en.js + | |-- de.js + ...... ``` -您可以在`formatters`文件夹中的`zh.js`、`en.js`、`de.js`文件中配置或者增加自己的自定义的格式化器。 - -`languages/formatters/<语言名称>.js`内容大概如下: +您可以在`formatters`文件夹中的`zh.js`、`en.js`、`de.js`文件中**配置**或者**新增**自己的自定义格式化器。 + +当切换到指定的语言时,就会`voerkai18n`会切换到对应的`languages/formatters/<语言名称>.js`,从而该语言的格式化器生效,并且会按一下的顺序进行查找格式化器。 + +打开`languages/formatters/<语言名称>.js`内容大概如下: ```javascript | pure export default { $config:{ // 在此配置各格式化器的参数 }, - // 在所有语言下只作用于特定数据类型的格式化器 + // 只作用于特定数据类型的格式化器 $types:{ // [数据类型名称]:(value)=>{...}, // [数据类型名称]:(value)=>{...}, - }, + }, // 自定义的格式化器 - [格式化名称]:(value,$config)=>{.....}, - [格式化名称]:(value,$config)=>{.....}, - [格式化名称]:(value,$config)=>{.....}, + [格式化名称]:(value,...args,$config)=>{.....}, + [格式化名称]:(value,...args,$config)=>{.....}, + [格式化名称]:(value,...args,$config)=>{.....}, //.....更多的格式化器..... - } ``` -### 配置机制 +`languages/formatters/<语言名称>.js`中定义的格式化器会被注册到当有`i18nScope`实例中。如此,在多包工程中,其他包/库就无法共享当前应用的格式式化器。 + +因此,您想注册一个格式化器可以在所有库/包中均可以使用,则需要将格式器注册到全局`VoerkaI18n`实例中。方法有两种是: + +- 指定`global=true`将该文件声明的所有格式化器均注册到全局中 +```javascript | pure +export default { + $config:{...}, + $types:{... }, + [格式化名称]:(value,...args,$config)=>{.....}, + //......... +} +``` + +- 在`global`对象明中声明的所有全局格式化器 + +```javascript | pure +export default { + $config:{...}, + $types:{... }, + [格式化名称]:(value,...args,$config)=>{.....}, + global:{ + $config:{...}, + $types:{... }, + [格式化名称]:(value,...args,$config)=>{.....}, + } + //......... +} +``` - - -### 格式化器作用域 - -定义在`languages/formatters/<语言名称>.js`里面的格式化器仅在当前工程生效,也就是仅在当前作用域生效。 - - -### 全局格式化器 - -定义在`@voerkai18n/runtime`里面的格式化器则全局有效,在所有场合均可以使用,但是其优先级低于作用域内的同名格式化器。 - -目前内置的全局格式化器请参阅API参考 - -### 扩展格式化器 - -除了可以在当前项目`languages/formatters/<语言名称>.js`自定义格式化器和`@voerkai18n/runtime`里面的全局格式化器外,计划单列了`@voerkai18n/formatters`项目用来包含了一些不常用的格式化器。 - -目前`@voerkai18n/formatters`还是空项目,作为开源项目,欢迎大家提交贡献更多的格式化器。 - - - -## 开发格式化器 - diff --git a/docs/src/guide/intro/versions.md b/docs/src/guide/intro/versions.md index 117d910..7248303 100644 --- a/docs/src/guide/intro/versions.md +++ b/docs/src/guide/intro/versions.md @@ -2,10 +2,10 @@ | 包| 版本号| 最后更新|说明| | --- | :---:| --- |---| |**@voerkai18n/utils**|1.0.13|2022/08/20|公共工具库| -|**@voerkai18n/runtime**|1.1.5|2022/08/25|核心运行时| +|**@voerkai18n/runtime**|1.1.6|2022/08/26|核心运行时| |**@voerkai18n/formatters**|1.0.6|2022/04/15|格式化器,提供对要翻译文本的转换功能| |**@voerkai18n/react**|1.0.4|2022/04/16|React支持,提供语言切换等功能| -|**@voerkai18n/cli**|1.0.38|2022/08/25|命令行工具,用来初始化/提取/编译/自动翻译等工具链| +|**@voerkai18n/cli**|1.0.39|2022/08/26|命令行工具,用来初始化/提取/编译/自动翻译等工具链| |**@voerkai18n/babel**|1.0.24|2022/08/20|Babel插件,实现自动导入t函数和自动文本映射| |**@voerkai18n/vite**|1.0.13|2022/08/20|Vite插件,提供自动插入翻译函数和文本映射等功能| |**@voerkai18n/vue**|1.0.6|2022/08/20|Vue3插件,提供自动插件翻译函数和语言切换功能| \ No newline at end of file diff --git a/packages/cli/languages/runtime.js b/packages/cli/languages/runtime.js index 40a5dce..1b76218 100644 --- a/packages/cli/languages/runtime.js +++ b/packages/cli/languages/runtime.js @@ -576,7 +576,7 @@ const formatterNestingParamsRegex = String.raw`((([\'\"])(.*?)\3))|__TAG_REGEXP_ } return fn.call(this,finalValue,...finalArgs,formatterConfig) }; - $formatter.configurable = true; // 当函数是可配置时才在最后一个参数中传入$config + $formatter.configurable = true; return $formatter } @@ -618,10 +618,6 @@ const createFlexFormatter$1 = function(fn,options={},defaultParams={}){ if(args[i]!==undefined) finalParams[options.params[i]] = args[i]; } } - if(finalParams.format in $config){ - finalParams.format = $config[finalParams.format]; - } - return fn.call(this,value,finalParams,$config) },{...options,params:null}); // 变参工式化器需要指定params=null return $flexFormatter @@ -850,6 +846,7 @@ function getFormatter(scope, activeLanguage, name) { const range = [ scope.activeFormatters, scope.formatters[fallbackLanguage], // 如果指定了回退语言时,也在该回退语言中查找 + scope.formatters["*"], scope.global.formatters[activeLanguage], // 适用于activeLanguage全局格式化器 scope.global.formatters[fallbackLanguage], scope.global.formatters["*"], // 适用于所有语言的格式化器 @@ -1432,6 +1429,10 @@ function addSplitChars(str,bits=3){ .replace("{unit}",unitName) } const currencyFormatter$1 = FlexFormatter$2((value,params={},$config)=>{ + // format可以取预设值的名称,如long,short等 + if(params.format in $config){ + params.format = $config[params.format]; + } params.unit = parseInt(params.unit) || 0; if(params.unit>$config.units.length-1) params.unit = $config.units.length-1; if(params.unit<0) params.unit = 0; @@ -1644,8 +1645,7 @@ var en = { prefix : "USD", // 前缀 suffix : "", // 后缀 division : 3, // ,分割位 - precision : 2, // 精度 - + precision : 2, // 精度 }, number : { division : 3, // , 分割位,3代表每3位添加一个, @@ -2190,6 +2190,7 @@ var scope = class i18nScope { this.global.registerFormatter(name, formatter, { language }); } else { language.forEach((lng) => { + if(!(lng in this._formatters)) this._formatters[lng] = {}; if (DataTypes$1.includes(name)) { this._formatters[lng].$types[name] = formatter; } else { @@ -2276,16 +2277,25 @@ var scope = class i18nScope { * - scope.activeFormatters.$config 当前优先 */ _generateFormatterConfig(language){ - let options; try{ - options = deepClone(getByPath(this._global.formatters,`*.$config`,{})); - deepMixin$1(options,getByPath(this._global.formatters,`${language}.$config`,{})); - deepMixin$1(options,getByPath(this._activeFormatters,"$config",{})); + const fallbackLanguage = this.getLanguage(language).fallback; + let configSources = [ + getByPath(this._global.formatters,`${fallbackLanguage}.$config`,{}), + getByPath(this._global.formatters,"*.$config",{}), + getByPath(this._formatters,`${fallbackLanguage}.$config`,{}), + getByPath(this._formatter,"*.$config",{}), + getByPath(this._global.formatters,`${language}.$config`,{}), + getByPath(this._activeFormatters,"$config",{}) + ]; + return this._activeFormatterConfig = configSources.reduce((finalConfig, config)=>{ + if(isPlainObject$2(config)) deepMixin$1(finalConfig,config); + return finalConfig + },deepClone(getByPath(this._global.formatters,`*.$config`,{}))) + }catch(e){ if(this.debug) console.error(`Error while generate <${language}> formatter options: `,e); - if(!options) options = this._activeFormatters.$config || {}; + return this._activeFormatters.$config || {} } - return this._activeFormatterConfig = options } /** diff --git a/packages/cli/package.json b/packages/cli/package.json index cea39cc..57d1494 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@voerkai18n/cli", - "version": "1.0.38", + "version": "1.0.39", "description": "命令行工具,用来初始化/提取/编译/自动翻译等工具链", "main": "index.js", "homepage": "https://gitee.com/zhangfisher/voerka-i18n", @@ -50,5 +50,5 @@ "devDependencies": { "@voerkai18n/autopublish": "workspace:^1.0.2" }, - "lastPublish": "2022-08-25T22:07:13+08:00" + "lastPublish": "2022-08-26T17:39:52+08:00" } \ No newline at end of file diff --git a/test/runtime.test.js b/test/runtime.test.js index 27b5df1..d1c857e 100644 --- a/test/runtime.test.js +++ b/test/runtime.test.js @@ -280,7 +280,7 @@ const formatters = { book:(v)=>`《${v}》`, }, en:{ - $config:{}, + $config:{a:1}, $types:{ }, book:(v)=>`<${v}>`, }, @@ -483,7 +483,7 @@ describe("翻译函数",()=>{ }) test("日期时间格式化",async ()=>{ let zhTranslatedResults = zhDatetimes.map(v=>t(v,NOW)) - let p = diffArray(zhTranslatedResults,expectZhDatetimes) + // let p = diffArray(zhTranslatedResults,expectZhDatetimes) expect(zhTranslatedResults).toStrictEqual(expectZhDatetimes) await scope.change("en") let enTranslatedResults = zhDatetimes.map(v=>t(v,NOW)) @@ -649,4 +649,26 @@ describe("翻译函数",()=>{ }) + test("注册普通格式化器",async ()=>{ + const f1 = (value,$config) => value + 1 + scope.registerFormatter("f1",f1) + expect(t("{value | f1}",1)).toBe("2") + expect(t("{value | f1}","1")).toBe("11") + + const f2 = (value,arg,$config) => value + arg + scope.registerFormatter("f2",f2) + expect(t("{value | f2('*')}","1")).toBe("1*") + + const f3 = (value,args,$config) => value + args.name + + scope.registerFormatter("f3",f3) + expect(t("{value | f3({name:'x'})}","1")).toBe("1x") + + const f4 = (value,$config) => value + 1 + scope.registerFormatter("f4",f4,{languages:"en"}) + expect(t("{value | f4(1)}",1)).toBe("2") + + }) + + })