Intl.NumberFormat 数字展示:舍入只改变文字,分组与单位要按部件读取

10-01 4阅读

设备读数需要在页面显示为两位小数,开发者常把格式化后的字符串又写回数据对象。短期看起来很整齐,后续排序、求平均或重新切换语言时却开始出错。数字的原始值、展示精度和地区格式应分别保存,展示文本只是最后一步的产物。

Intl.NumberFormat 会根据地区约定生成分组符号、小数符号和单位文字。它不会把传入的 Number 改成另一个数字,也不是通用的本地化解析器。想给整数部分加样式,可以读取 formatToParts 的类型,而不是猜测逗号或句点出现在哪里。

同一个数值生成两种地区展示

下面保存为 demo.mjs,用具备相应语言数据的 Node.js 运行。示例显式设置最少和最多两位小数,并把中文地区与德国地区并列。两个格式器处理同一个原值,测试只在文字输出上比较舍入,原始数值另用断言确认。

示例还用百分比说明单位规则:输入是比例零点一二六,输出为百分之十二点六。最后按照部件类型取出整数部分,再检查所有部件拼回去是否等于完整文本。这个步骤对应组件渲染,既保住符号顺序,也不破坏地区约定。

Intl.NumberFormat 数字展示:舍入只改变文字,分组与单位要按部件读取

AI概念示意图:以抽象物件说明本文主题,不代表真实界面或运行结果。

import assert from 'node:assert/strict';
const reading = 12345.678;
const options = {minimumFractionDigits: 2, maximumFractionDigits: 2};
const zh = new Intl.NumberFormat('zh-CN', options);
const de = new Intl.NumberFormat('de-DE', options);
assert.equal(zh.format(reading), '12,345.68');
assert.equal(de.format(reading), '12.345,68');
assert.equal(reading, 12345.678);
console.log(zh.format(reading));
console.log(de.format(reading));

const parts = de.formatToParts(reading);
assert.equal(parts.map(part => part.value).join(''), de.format(reading));
const integers = parts.filter(part => part.type === 'integer');
console.log(integers.map(part => part.value).join('|'));
console.log(parts.find(part => part.type === 'decimal').value);

const percent = new Intl.NumberFormat('en-US', {
  style: 'percent', minimumFractionDigits: 1, maximumFractionDigits: 1
});
assert.equal(percent.format(0.126), '12.6%');
assert.equal(percent.format(12.6), '1,260.0%');
console.log(percent.format(0.126));
const padded = new Intl.NumberFormat('en-US', options);
assert.equal(padded.format(2), '2.00');
console.log(`raw=${reading}, padded=${padded.format(2)}`);

按类型使用部件,不按标点拆分

前两行应为 12,345.68 与 12.345,68,说明分组符和小数符交换了位置。第三行是 12|345,因为整数可能被分组符拆成多个 integer 部件;第四行是一个逗号。不要假定整数只出现一次,也不要直接拿第一项当作完整整数。

在页面组件中,可以遍历全部部件,只给需要强调的类型添加样式,其余部件原样保留。负号、单位、空白或方向控制符都可能属于展示的一部分。把不认识的类型直接丢弃,会让结果在某些语言中失去语义,甚至改变阅读方向。

同时设置 minimumFractionDigits 和 maximumFractionDigits,意味着整数也要补成两位,而较长小数要在显示时舍入。如果只想最多两位,应把最少位数设为零。不要把“固定两位”与“最多两位”混成同一个验收条件。

原值与展示文本各走自己的数据通路

最后一行仍包含 raw=12345.678,说明格式器没有改变源值。计算与排序使用原始数值,屏幕或导出报告再决定展示规则。用户输入的字符串则需要单独规定可接受的格式,不能用删除所有逗号的方式反向解析任意地区输出。

百分比格式会按比例展示,不能先把数值乘一百再交给百分比样式。上面的两个断言正好区分零点一二六和十二点六。接口字段最好注明传递的是比例还是百分数,这比靠页面显示猜测单位更可靠。

小数舍入依然建立在传入数值之上,Number 的二进制表示不等于任意十进制都可精确保存。本例用于展示测量数据,不承担精确十进制运算。需要精确计算时,应先选定数据表示与运算规则,再决定怎样把最终结果交给格式器。

自动化验收要固定地区与选项,并核对运行时是否含有所需语言数据。不同版本的地区数据可能调整符号或空白,因此通用组件应主要检查部件保留与顺序;只有已经约定环境的快照测试,才适合把整串展示文字当成固定答案。

参考资料

  1. ECMA-402:NumberFormat 对象

  2. ECMA-402:数值格式部件

文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。