从数字到版面:得物数据产品里数字格式化的那些事


一、前 言

做数据前端,你会很快建立一个共识:

 

怎样把枯燥的数字用合适的方式展示出来,是我们的第一要务,但这只是起点。

 

如果说规范的数字排版是中后台系统的“地基”,保证了信息的准确传达;那么可视化图表就是地基之上的“建筑”。地基稳固,建筑才能发挥其功能——让用户从微观的读数中解放出来,更快速地识别趋势、定位异常,从而真正从数据中获取规律。

 

但这篇主要想聊的,不是那座“建筑”,而是这块往往被忽视,却决定了整个系统专业度的“地基”——数字格式化。

 

请看得物各业务线里的这些日常场景:

  • 商品详情页:券后价、折扣、分期单价等;
  • 智珠(得物社区运营平台)、得数(自助取数平台)、智能运营(得物交易运营平台)里的 GMV、转化率、留存率、PVCTR等;
  • 社区里帖子阅读量、点赞数、粉丝数。

 

这些数字表面上只是 number,一旦出现在屏幕上,就自动变成版面的一部分:

对齐方式、位数长短、小数几位、有没有“万 / 亿”、单位怎么放——都会影响到整个页面的节奏和专业感。

 

排版本质上是在管理信息和秩序:层级、节奏、对齐、留白。而数字不是排版的“附属品”,恰恰是这些原则最密集的载体。

 

本文不发散去谈数据可视化,只专注于“数字格式化”这一件事:

  1. 什么是数字格式化?它背后有哪些鲜为人知的文化差异和技术细节?
  2. 如果没有统一方案,失控的数字会给产品决策、UI 排版和工程维护带来什么麻烦?
  3. 得物数据前端在得数 / 智珠 / 智能运营里,是怎么把这件事从“工具函数”做成“基础设施”的?
  4. 我们基于Intl.NumberFormat和@dfx/number-format 的方案,架构是怎样的,带来了哪些实际收益?

二、什么是“数字格式化”?不只是toFixed(2)

一提到提到数字格式化,第一反应是:toFixed(2)、拼个%、加个¥就完事了或者通过正则来拼接千分符。但在真实世界里,“同一个数长成什么样”远比想象复杂。

 

2.1 数字写法本身就是“文化差异”

https://zh.wikipedia.org/wiki/小数点

小数分隔符的符号演变史

 

据Florian Cajori 1928年的著作《数学符号史》记载,小数分隔符(旧称 separatrix)的演变经历了一个漫长的标准化过程。

 

在早期数学文献中,不同地区对小数的处理方式各异。以数值 34.65 为例,中世纪文献常采用在整数与小数间加横线或在个位数字上方加注标记的方式。英国早期曾采用竖线“|”作为分隔,后在印刷中逐渐简化为逗号或点,这与当时阿拉伯数学家主要使用逗号的习惯相呼应。

 

“点”与“逗号”分流的历史成因

17 世纪末至 18 世纪初是符号标准化的关键时期,也是英美体系与欧洲大陆体系产生分歧的起点。这一差异在很大程度上受到了微积分发明者及其符号体系的影响:

  1. 欧洲大陆(莱布尼茨的影响):德国数学家莱布尼茨提议使用“点”作为乘法符号。这一提议经由克里斯蒂安·沃尔夫等学者的推广,在欧洲大陆教科书中广泛普及。为了避免符号含义的冲突,欧洲大陆数学家普遍采用了“逗号”作为小数分隔符。

  2. 英国(牛顿体系的延续):英国数学界未采纳莱布尼茨的乘法符号,而是沿用“X”表示乘法。因此,“点”在英国并未被乘法占用,得以继续作为小数分隔符使用。据统计,18 世纪初的英国教科书中,约 60% 使用点,40% 使用逗号;而到了 18 世纪末,点已成为英国的绝对主流。

 

标准的确立

 

尽管比利时和意大利等国曾长期坚持使用点作为小数分隔符,但最终均向欧洲大陆的主流标准靠拢,改用了逗号。至此,英美使用“小数点”、欧洲大陆使用“小数逗号”的格局基本定型。

 

值得注意的是,直至20世纪初,符号的统一仍未完全完成,各类文献中仍可见等非标准写法。

  • 英语系 / 中国常见写法:1,234,567.89
  • 很多欧洲国家:1.234.567,89(点是千分位,逗号是小数)
  • 瑞士习惯:1’234.56或1’234,56,用撇号 ‘ 做分组。

 

这些规则都已经被整理进Unicode CLDR和ICU数据库,现代浏览器的Intl.NumberFormat就是建立在这套数据之上,能根据 locale 自适应这些写法。

 

所以,一个简单的1,000:

  • 在美国人或中国人眼里是“ one thousand / 一千”;
  • 在某些欧洲语境下可能被读成“保留三位小数的一点零”。

 

一旦你做电商、跨境、数据产品,这种“写法”的差异,就不再是小问题,而是直接影响决策和合规的东西。

 

2.2 数字不只是“大小”还有语义和语气

在 UI 里,我们其实经常在表达“数字的语气”:

  • +12.3%:不是纯数学“加号”,而是一种“上涨”的信号,排版上常常配合红/绿颜色;
  • 1.2M / 120 万:为了节省空间和降低认知负担,用缩写表示量级;
  • < 0.01:极小数值,让用户知道“接近 0”,而不是盯着一串 0.000032 的数字尾巴发呆;
  • — /N/A:告诉用户“这里是没数据 / 异常”,而不是“就是 0”。

 

这些都属于“数字的表达”而非“运算结果”。

 

如果我们只用toFixed和拼字符串,很难让这些语气在整个系统内保持一致。

 

2.3浏览器和 Node 已经给了我们一个“引擎”

ECMAScript 402标准中提供的 Intl.NumberFormat,是一个专门做本地化数字格式化的构造器。

 

它支持:

  • 根据 locale 切换小数点、分组规则、数字符号;
  • 货币格式:style: “currency” + currency: “CNY” | “JPY” | …;
  • 百分比:style: “percent”,自动乘 100 并加 %;
  • 紧凑表示:notation: “compact”,输出 9.9M、9.9亿等缩写;
  • formatToParts():把数字拆成整数、小数点、小数、货币符号等片段,方便做更精细的排版。

 

所以,数字格式化的“引擎问题”其实已经有人帮我们解决了——真正难的是:

  • 怎么结合业务语义;
  • 怎么结合排版规范;
  • 怎么在多个系统之间做到一致;
  • 怎么治理“不乱写”的工程实践。

 

这就回到我们自己的故事。

 

三、如果没有统一方案:三个数据产品的日常

下面这几个场景,相信你在得物或类似电商/数据平台里一定见过。

 

想象一张典型后台页面:

  • 顶部是活动看板 KPI 卡片;
  • 中间是按品类/渠道拆开的表格;
  • 下方是创作者表现列表。

 

3.1 价格:同一张订单,不同系统“长得不同”

那么,以前我们是怎么做的?

 

在没有统一规范的“蛮荒时代”,面对一个数字,我们的第一反应往往是:“这还不简单?拼个字符串不就完事了?”

这种代码,你一定写过,或者在项目的某个角落实实在在地见过:

 

场景一:简单粗暴的拼接一把梭

// "我管你什么场景,先拼上去再说"
function formatPrice(value: number) {
  // 隐患埋雷:这里是全角¥还是半角¥?null 会变成 "¥null" 吗?
  return '¥' + value.toFixed(2);
}

 

场景二:为了千分位,手写正则“炫技”

// "网上一搜一大把的正则,看着能跑就行"
export const formatNumberWithCommas = (number: number | string) => {
  const parts = number.toString().split('.');
  
  // 经典的正则替换,但这真的覆盖了所有负数、极大值场景吗?
  parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  
  // 手动补零逻辑,不仅难维护,还容易在边界情况(如 undefined)下报错
  if (parts.length > 1 && parts[1]?.length === 1) {
    parts[1] = ${parts[1]}0;
  }
  return parts.join('.');
};

 

这看起来似乎“能用”,并且用起来貌似也没啥问题。但当业务复杂度稍微上来一点或者需要做国际化的时候,那接手这个需求的同学只能发出一句『哦吼』。

 

  1. 视觉上的“各自为政”
    1. 符号打架: 商品卡片用半角 ⁠¥,详情页用全角 ⁠¥,甚至有的地方混用了 ⁠CNY。
    2. 精度随心: 有的开发觉得 ⁠toFixed(2) 严谨,有的觉得 ⁠。00 太多余直接砍掉,导致同一个页面里,导致同一个页面里,数字像锯齿一样参差不齐。
  2. 排版上的“秩序崩塌”
    1. 试想一个表格列:⁠1299、⁠1,299.00、⁠1299.0 混在一起。
    2. 对齐基准线完全错乱,尤其在表格里,就和『狗啃过一样』,犬牙交错,参差不齐,用户的眼睛需要在不同的小数位之间来回跳跃,阅读体验极差。
  3. 国际化上的“死胡同”
    1. 这种硬编码逻辑(Hardcode),完全堵死了国际化的路。
    2. 一旦业务要支持 USD(美元)、JPY(日元,默认无小数)、EUR(欧元,部分国家用逗号做小数点)。
    3. 比如 ⁠1,234,567.89(英美)和 ⁠1.234.567,89(德意),这完全不是改个符号就能解决的,而是整个数字书写逻辑的根本差异。
未经允许不得转载:紫竹林-程序员中文网 » 从数字到版面:得物数据产品里数字格式化的那些事

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
关于我们 免责申明 意见反馈 隐私政策
程序员中文网:公益在线网站,帮助学习者快速成长!
关注微信 技术交流
推荐文章
每天精选资源文章推送
推荐文章
随时随地碎片化学习
推荐文章
发现有趣的