shuzi
Version:
Chinese Number Format | 中文数字格式
145 lines (144 loc) • 4.82 kB
JavaScript
const SCRIPTS = {
'zh-CN-small': '零一二三四五六七八九十百千万亿点负元整角分厘毫',
'zh-CN-big': '零壹贰叁肆伍陆柒捌玖拾佰仟万亿点负元整角分厘毫',
'zh-HK-small': '零一二三四五六七八九十百千萬億點負元整角分厘毫',
'zh-HK-big': '零壹貳參肆伍陸柒捌玖拾佰仟萬億點負圓整角分厘毫',
'zh-TW-small': '零一二三四五六七八九十百千萬億點負元整角分厘毫',
'zh-TW-big': '零壹貳參肆伍陸柒捌玖拾佰仟萬億點負圓整角分厘毫',
};
export class NumberFormat {
constructor(locale, options) {
this.locale = locale;
this.options = options;
this.script = SCRIPTS[locale];
this.intl = new Intl.NumberFormat('en-US', {
style: 'decimal',
maximumFractionDigits: options?.maximumFractionDigits || (options?.style === 'currency' ? 2 : 3),
useGrouping: false,
});
}
format(value) {
if (!this._validate(value)) {
return 'InvalidNumber';
}
const num = Number(value);
const [significant, fragment] = this.intl.format(Math.abs(num)).split('.');
let output = this._formatSignificant(significant);
if (this.options?.style === 'currency') {
if (significant === '0' && fragment) {
// 大于零元,小于一元的情况
output = '';
}
else {
// 「元」
output += this.script[17];
}
if (fragment) {
// 「角、分、厘、毫」
output += this._formatFragmentCurrency(fragment, significant);
}
else {
// 「整」
output += this.script[18];
}
}
else if (fragment) {
// 「点」
output += this.script[15] + this._formatFragment(fragment);
}
// 「负」
if (num < 0) {
output = this.script[16] + output;
}
return output;
}
/**
* 整数部分格式化
*/
_formatSignificant(value) {
// 「零」简化处理
if (value === '0') {
return this.script[0];
}
// 四位以内简化处理
if (value.length <= 4) {
return this._formatSignificantGroup(value);
}
let output = '';
for (let i = 0; i < value.length / 4; i++) {
switch (i) {
// 「万」
case 3:
case 1:
output = this.script[13] + output;
break;
// 「亿」
case 2:
output = this.script[14] + output;
break;
default:
break;
}
output =
this._formatSignificantGroup(value.substring(value.length - (i + 1) * 4, value.length - i * 4)) + output;
}
return output;
}
/**
* 整数部分四位一组格式化
*/
_formatSignificantGroup(value) {
let output = '';
for (let i = 0; i < value.length; i++) {
const digit = Number(value[value.length - 1 - i]);
// 合并连续的「零」,移除末尾的「零」
if (digit === 0 && (!output || output[0] === this.script[0])) {
continue;
}
// 「十、百、千」
if (i > 0 && digit !== 0) {
output = this.script[i + 9] + output;
}
output = this.script[digit] + output;
}
return output;
}
/**
* 小数部分格式化
*/
_formatFragment(value) {
let output = '';
for (let i = 0; i < value.length; i++) {
const digit = Number(value[i]);
output += this.script[digit];
}
return output;
}
/**
* 金额小数部分格式化
*/
_formatFragmentCurrency(value, significant) {
let output = '';
for (let i = 0; i < value.length; i++) {
const digit = Number(value[i]);
if (digit === 0) {
// 合并连续的「零」,移除开头的「零」
if (output.endsWith(this.script[0]) || (significant === '0' && !output)) {
continue;
}
output += this.script[0];
}
else {
output += this.script[digit] + this.script[19 + i];
}
}
return output;
}
/**
* 校验数字是否有效
*/
_validate(value) {
const num = Number(value);
return !Number.isNaN(num) && Number.isFinite(num);
}
}