前段时间,项目遇到一个需求,将金额的阿拉伯数字转成大写,而且是西班牙语,网上搜索未果,于是自己撸了一个 cardinal-number。目前已支持三种语言,汉语、西班牙语、英语。
先上示例
import convert, { locale } from 'cardinal-number';
console.log(convert(90009)) // 玖万零玖元整
console.log(convert(90009.23)) // 玖万零玖元贰角叁分
locale('es')
console.log(convert(199)) // ciento noventa y nueve peso 00/100 MIN
locale('en')
console.log(convert(73.02)) // seventy-three and cents two only
思路历程
每种语言特性不太一样,我们汉语是万分制12345678 => 1234,5678, 英语和西班牙语是千分制12345678 => 12,345,678,即按4位和3位为一组。
因此,只需实现一个基础函数baseFunction处理4位和3位数字,然后将原数字按4和3划分为组,循环调用baseFunction即可。
后来又发现,转成汉语如果按位处理仿佛更简便一点。 按照我们人为思维方式,从左向右,分别为仟亿、佰亿、拾亿、亿、仟万、佰万、拾万......
实现过程
1. 按位处理
将数字按位拆分成数组,如: 789123456789 => [7,8,9,1,2,3,4,5,6,7,8,9]。
定义好基础数字object baseMap和位数单位object indexMap
const baseMap = {
0: '零',
1: '壹',
...
8: '捌',
9: '玖'
}
const indexMap = {
1: '拾', // 10^1 => 10
2: '佰', // 10^2 => 100
...
8: '亿', // 10^8 => 100000000
9: '拾', // 10^9 => 1000000000
10: '佰', // 10^10 => 10000000000
11: '仟', // 10^11 => 100000000000
}
特殊说明一下,我们描述789100000000成 柒仟捌佰玖拾壹亿 而不是柒仟亿捌捌亿玖拾亿壹亿,因此只需数字第8位的亿就可以了, 同理万位也一样。所以仟亿、佰亿、拾亿、仟万、佰万、拾万特殊定义成仟、佰、拾。
对于0的特殊处理。
- 如果当前位为0,只记录
零,不会记录当前位的单位。 - 0的相邻元素也为0时,只需保留一个
零。如:3001 => 叁仟零壹, 而不是叁仟零零壹 - 如果万位,要在对应的拾万、佰万、仟万位补上
万。如:300001=>叁拾万零壹 - 亿位为0时同上。
2.分组处理(以英语为例)
将数字分组,如: 23456789 => [23, 456, 789]
只需实现一个baseFunction实现3位数的转换,然后循环调用再拼上对应的千(thousand)、百万(million)、十亿(billion)单位即可。
英语、西班牙语还有一点特殊,英语20以内的数不同,西班牙语30以内的数不同, 而且西班牙语在一些特殊情况下,还有复数和词的转换。下面专门列出部分特性。
2.1 en(英语)
- 20以内不一样。
1 one 11 eleven
2 two 12 twelve
3 three 13 thirteen
4 four 14 fourteen
5 five 15 fifteen
6 six 16 sixteen
7 seven 17 seventeen
8 eight 18 eighteen
9 nine 19 nineteen
10 ten 20 twenty
- 21~99, 可以十位个位拼接, 如:
23 => 20 + 3 => twenty-three - 百位和十位之间用
and拼接, 如:123 => one hundred and twenty-three
2.2 es(西班牙语)
特性
- 30以内不一样。
1 uno 11 once 21 veintiuno
2 dos 12 doce 22 veintidós
3 tres 13 trece 23 veintitrés
4 cuatro 14 catorce 24 veinticuatro
5 cinco 15 quince 25 veinticinco
6 seis 16 dieciséis 26 veintiséis
7 siete 17 diecisiete 27 veintisiete
8 ocho 18 dieciocho 28 veintiocho
9 nueve 19 diecinueve 29 veintinueve
10 diez 20 veinte 30 treinta
- 31~99, 可以拆成十位和个位相加,
32 => 30 + 2 => treinta y dos - 100用cien, 超过100用ciento,如
100 => cien,102 => ciento dos - 1百万用millón, 百万位数大于1时用millones。
补充
twenty-three、treinta y dos像这样的数字描述,一般称为基数词。但是这种描述大多情况也是用来描述金额的数值, 因此汉语转成壹、贰、叁...描述金额的数字不是一、二、三...。
同时,可能多个国家用同一语种,但是金钱单位描述可能不同, 因此也支持只做基数转换或者自定义转换金钱单位的函数。如下
convert(73.02) => seventy-three and cents two only
convert(73.02, false) => seventy-three // 如果仅转换基数,会忽略小数位
const formatPoints = (num: number): string[] => {
return ['aaaakekekekekeke'];
};
convert(73.02, formatPoints) => seventy-three aaaakekekekekeke
如有需要欢迎使用, 目前也做了单元测试
| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
|---|---|---|---|---|---|
| zh.ts | 100 | 96.88 | 100 | 100 | 45 |
| en.ts | 100 | 96.88 | 100 | 100 | 61 |
| es.ts | 100 | 93.75 | 100 | 100 | 61,77 |
最后
欢迎各位大佬指正。项目地址 cardinal-number, 欢迎star、issue、pr。