告别杂乱数字:用 Intl.NumberFormat 打造全球友好的前端体验

大家好,我是CC,在这里欢迎大家的到来~

开场

书接上文,Intl 下的 Segmenter 对象可以实现对文本的分割,Collator 对象可以处理字符串的比较,除此之外,还有数字格式化、日期格式化等其他功能。

这篇文章先来看看数字格式化,现在来理论加实践一下。

数字格式化

Intl.NumberFormat使数字在特定语言环境下格式化。

配置项

为了方便阅读,属性列表根据用途划分为多个部分,包括区域选项、样式选项、数字选项和其他选项。

区域选项

  • localeMatcher
    • 使用的区域匹配算法,可能的值包括:
    • 默认值为 best fit,还有 lookup
  • numberingSystem
    • 数字格式化的数字系统,像阿拉伯数字 arab、简体中文数字 hans、无衬线数字 mathsans
    • 默认值取决于区域
    • 同 locales 的 Unicode 扩展键 nu 设置,但优先级高于他

样式选项

  • style
    • 使用的格式化样式,可选的值包括:
    • decimal: 普通数字格式化
    • currency: 货币格式化
    • percent: 百分比格式化
    • unit: 单位格式化
    • 默认值是 decimal
  • currency
    • 货币格式化中使用的货币,像美元 USD、欧元 EUR 和人民币 CNY。
    • 没有默认值,style 为 currency 时必须提供,内容会被转换为大写。
  • currencyDisplay
    • 货币格式化中如何显示货币,可选的值包括:
    • code: 使用 ISO 货币代码
    • symbol: 使用本地化货币符号
    • narrowSymbol: 使用窄格式符号,像 <math xmlns="http://www.w3.org/1998/Math/MathML"> 100 而不是 U S 100 而不是 US </math>100而不是US100
    • name: 使用本地化货币名称,像 dollar
  • currencySign
    • 使用括号将数字括起来,而不是添加负号,可选的值包括:
    • standard: 默认值
    • accounting: 会计
  • unit
    • 格式化的单位
    • style 为 unit 时必填
  • unitDisplay
    • unit 格式化时使用的格式化风格,可选的值包括:
    • short: 默认值,例如 16 l
    • narrow: 例如 16l
    • long: 例如 16 litres

数字选项,由 Intl.PluralRules 支持

  • minimumIntegerDigits
    • 最小整数位数,默认值为 1,范围是 1~21
    • 若实际整数位数不足会在左侧用 0 补足,比如对于数字 5 该值设置为 3 则显示为"005"
  • minimumFractionDigits
    • 小数部分的最小位数,范围是 0~100
    • 若小数位数不足时会在右侧补 0,超过时会按四舍五入截断
    • 默认值对于普通数字和百分比是 0,对于 currency 是 2(ISO 4217 标准小数位数)
  • maximumFractionDigits
    • 小数部分的最大位数,范围是 0~100
    • 若小数位数不足时会在右侧补 0,超过时会按四舍五入截断
    • 默认值对于普通数字和百分比是 3,对于 currency 是 2(ISO 4217 标准小数位数)
  • minimumSignificanntDigits
    • 最小有效数字,默认值为 1。范围是 1~21。
    • 优先级高于 minimumFractionDigits
  • maximumSignificanntDigits
    • 最大有效数字,默认值为 21。范围是 1~21。
    • 优先级高于 maximumFractionDigits
  • roundingPriority
    • 当同时使用 FractionDigits 和 SignificantDigits 时指定如何解决四舍五入冲突,可选的值包括:
    • auto: 默认值,使用有效数字属性
    • morePrecision: 使用精度更高的属性
    • lessPrecision: 使用精度更低的属性
    • auto 属性会在 natation 为 compact 时且未设置任何四个 FractionDigits/SignificantDigits 时会被设置为 morePrecision
    • 除 auto 属性以外的值会根据 maximumSignificanntDigits 和 maximumFractionDigits 计算出更高精度,忽略最小小数位和有效数字位
  • roundingIncrement
    • 相对于计算出的舍入单位的舍入增量
    • 默认值为 1,其他值包括 1、2、5、10、20、25、50、100、200、250、500、1000、2000、5000
    • 不能与有效数字位舍入或任何 roundingPriority(除了 auto) 混合使用
  • roundingMode
    • 对小数进行舍入,可选的值包括:
    • ceil: 向正无穷舍入,正数向上,负数"向正"
    • floor: 向负无穷舍入,正数向下,负数"向负"
    • expand: 四舍五入远离 0,绝对值增大
    • trunc: 四舍五入朝向 0,绝对值减小
    • halfCeil: 趋向于正无穷舍入,包括半值
    • halfFloor: 趋向于负无穷舍入,包括半值
    • halfExpand: 默认值,半值远离 0 舍入
    • halfTrunc: 向 0 取整,包括半值
    • halfEven: 半值向最接近的偶数整数舍入,常用于统计,减少片差
  • trailingZeroDisplay
    • 整数末尾 0 的显示策略,可选的值包括:
    • auto: 默认值,根据 minimumFractionDigits 和 minimumSignificanntDigits 保持末尾 0
    • stripIfInteger: 如果小数部分全为 0 则删除小数部分,如果小数部分有任何非零数则与 auto 相同

其他选项

  • notation
    • 数字的显示格式,可选的值包括:
    • standard: 默认值,纯数字格式
    • scientific: 返回格式化数字的数量级
    • engineering: 返回能被 3 整除的 10 的指数
    • compact: 表示指数的字符串,默认使用 short 形式
  • compactDisplay
    • 仅当 notation 为 compact 时使用,可选的值包括:
    • short: 默认值
    • long
  • useGrouping
    • 是否使用分组分隔符,像千位分隔符或者千/十万/千万分隔符,可选的值包括:
    • always: 即使 locale 偏好不同也展示分组分隔符
    • auto: 根据 locale 偏好显示分组分隔符,也取决于货币
    • min2: 当一组数字至少有 2 位数字时显示分组分隔符
    • true: 同 always
    • false: 不展示分组分隔符
    • 当 notation 为 compact 时默认值为 min2,否则默认值为 auto
    • 字符串 true 和 false 会被转化为默认值
  • signDisplay
    • 何时显示数字符号,可选的值包括:
    • auto: 默认值
    • always: 总是显示
    • exceptZero: 正数和负数显示符号,但 0 不显示
    • negative: 仅显示负数的符号,不包括负零
    • never: 从不展示

格式化

format()方法会基于区域和格式化选项进行数字格式化。支持数字、大数和字符串。

数字可能因为太大或太小而丢失精度

javascript 复制代码
const numberFormat = new Intl.NumberFormat("en-US");
console.log(numberFormat.format(987654321987654321));
// 987,654,321,987,654,300

但是使用大数就不会有问题

javascript 复制代码
const numberFormat = new Intl.NumberFormat("en-US");
console.log(numberFormat.format(987654321987654321n));
// 987,654,321,987,654,321

字符串也不会丢失精度

javascript 复制代码
const numberFormat = new Intl.NumberFormat("en-US");
console.log(numberFormat.format("987654321987654321"));
// 987,654,321,987,654,321

使用指数表示

javascript 复制代码
const numberFormat = new Intl.NumberFormat("en-US");
const bigNum = 987654321987654321n;
console.log(numberFormat.format(`${bigNum}E-6`));
// 987,654,321,987.654

格式化分割成多部分

formatToParts()将会返回一个对象数组,包含格式化后的每一部分,适合用来自定义字符串格式化。

javascript 复制代码
const number = 3500;

const formatter = new Intl.NumberFormat("de-DE", {
  style: "currency",
  currency: "EUR",
});

console.log(formatter.format(number));
// "3.500,00 €"

console.log(formatter.formatToParts(number));
// [
//   { type: "integer", value: "3" },
//   { type: "group", value: "." },
//   { type: "integer", value: "500" },
//   { type: "decimal", value: "," },
//   { type: "fraction", value: "00" },
//   { type: "literal", value: " " },
//   { type: "currency", value: "€" },
// ];

格式化数字范围

formatRange()返回一个字符串表示数字范围格式化后的内容。

javascript 复制代码
const nf = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0,
});

console.log(nf.formatRange(3, 5));
// "$3 -- $5"

如果开始值和结束值四舍五入值相同或者完全相同 时则会添加近似等于符号。

javascript 复制代码
console.log(nf.formatRange(2.9, 3.1));
// "~$3"

格式化数字范围分割成多部分

formatRangeToParts()返回一个对象数组,包含格式化后的每一部分,适合用来自定义数字字符串的格式化范围。

javascript 复制代码
const startRange = 3500;
const endRange = 9500;

const formatter = new Intl.NumberFormat("de-DE", {
  style: "currency",
  currency: "EUR",
});

console.log(formatter.formatRange(startRange, endRange));
// "3.500,00--9.500,00 €"

console.log(formatter.formatRangeToParts(startRange, endRange));
// [
//   { type: "integer", value: "3", source: "startRange" },
//   { type: "group", value: ".", source: "startRange" },
//   { type: "integer", value: "500", source: "startRange" },
//   { type: "decimal", value: ",", source: "startRange" },
//   { type: "fraction", value: "00", source: "startRange" },
//   { type: "literal", value: "--", source: "shared" },
//   { type: "integer", value: "9", source: "endRange" },
//   { type: "group", value: ".", source: "endRange" },
//   { type: "integer", value: "500", source: "endRange" },
//   { type: "decimal", value: ",", source: "endRange" },
//   { type: "fraction", value: "00", source: "endRange" },
//   { type: "literal", value: " ", source: "shared" },
//   { type: "currency", value: "€", source: "shared" },
// ]

获取配置项

javascript 复制代码
const de = new Intl.NumberFormat("de-DE", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 2,
  roundingIncrement: 5,
  roundingMode: "halfCeil",
});

const usedOptions = de.resolvedOptions();
console.log(usedOptions.locale); // "de-DE"
console.log(usedOptions.numberingSystem); // "latn"
console.log(usedOptions.compactDisplay); // undefined ("notation" not set to "compact")
console.log(usedOptions.currency); // "USD"
console.log(usedOptions.currencyDisplay); // "symbol"
console.log(usedOptions.currencySign); // "standard"
console.log(usedOptions.minimumIntegerDigits); // 1
console.log(usedOptions.minimumFractionDigits); // 2
console.log(usedOptions.maximumFractionDigits); // 2
console.log(usedOptions.minimumSignificantDigits); // undefined (maximumFractionDigits is set)
console.log(usedOptions.maximumSignificantDigits); // undefined (maximumFractionDigits is set)
console.log(usedOptions.notation); // "standard"
console.log(usedOptions.roundingIncrement); // 5
console.log(usedOptions.roundingMode); // halfCeil
console.log(usedOptions.roundingPriority); // auto
console.log(usedOptions.signDisplay); // "auto"
console.log(usedOptions.style); // "currency"
console.log(usedOptions.trailingZeroDisplay); // auto
console.log(usedOptions.useGrouping); // auto

判断返回支持的 locale

在给定的 locales 数组中判断出 NumberFormat 支持的 locales。但是可能每个浏览器支持的不大一样。

javascript 复制代码
const locales = ["ban", "id-u-co-pinyin", "de-ID"];
const options = { localeMatcher: "lookup" };

console.log(Intl.NumberFormat.supportedLocalesOf(locales, options));
// ["id-u-co-pinyin", "de-ID"]

总结

Intl.NumberFormat用于根据语言和地区格式化数字内容,像把数字格式化为货币、百分比或带单位的本地化字符串,精确控制数字的小数位数、有效数字和整数部分的最小位数,设置丰富的舍入模式像四舍五入、向零舍入或银行家舍入法这些场景下都十分适用。

相关推荐
姓蔡小朋友2 小时前
后端面试八股文
面试·职场和发展
kandee2 小时前
vscode混淆js文件的插件用法(jshaman)
javascript·ide·vscode
妮妮喔妮2 小时前
Webpack和Vite优化的区别
前端·webpack·node.js
广州华水科技3 小时前
单北斗GNSS在大坝形变监测中的应用与性能分析
前端
等风来不如迎风去3 小时前
【web】页面透明、插入图片
前端
谢尔登3 小时前
a 标签的跳转机制
前端·javascript·webpack·node.js
狂炫冰美式3 小时前
当硅基神明撞上人类的“叹息之墙”:距离证明哥德巴赫猜想,AI还有多远?
前端·算法·架构
毕设源码-邱学长3 小时前
【开题答辩全过程】以 基于Vue的爱心公益募捐平台的设计与实现为例,包含答辩的问题和答案
前端·javascript·vue.js
IT_陈寒3 小时前
Redis实战精要:5种高频使用场景与性能优化全解析|得物技术
前端·人工智能·后端