在这里插入图片描述
在这里插入图片描述

一、它解决什么场景问题

旅行中记账的最大麻烦是币种混乱:钱包里同时有欧元硬币、日元纸币、刷卡消费记的是美元。旅行者想知道"我到底花了多少"——但没人愿意拿计算器把 6 种币种手动换算。本应用用旅行记账本承载货币本地化能力,解决:

  1. 原币记账(保持真实性)+ 本币折算(统一视角);
  2. 货币符号随语言习惯显示(中英日韩各有正确姿势);
  3. 汇率换算器随手查任意金额。

二、典型用户故事

故事 A:背包客小林(zh_CN)
小林在欧洲玩了两周,记账本里躺着 €4.5 的咖啡、£180 的酒店、$28 的门票。打开 App,每笔账下面自动跟着"折算 ¥××"(人民币),底部一行"旅行总花费 ¥4,823.65"——不用再翻汇率 App 心算了。

故事 B:留美学生 Kevin(en_US)
Kevin 把 App 切英文,账单金额变成 $1,299.00 格式(美元符号在前、千分位逗号)。他注意到汇率换算器里 JPY 显示 ¥860(日元无小数位),而 EUR 显示 €4.50——每个币种都按自己的规则显示,一看就是"做给懂行的人用的"。

故事 C:商务旅客 Tanaka(ja_JP)
Tanaka 常飞东京-伦敦。他用日元视角看账:£180 下面跟着 約 ¥32,000。他感叹汇率工具不少,但"自动把每笔账都折算好、还能按币种分类小计"的只有这个。

故事 D:自由译者 Maria(de_DE,未内置语言)
Maria 的系统语言是德语,本应用没有内置德语。此时界面文案降级到默认中文,但金额格式化仍按她选择的语言(如 en_US)正确输出 €4.50。她发现:货币格式与界面语言可以解耦——即使文案没有翻译,数字、金额、日期的本地化也应该独立工作,这是 i18n 分层设计的体现(文案层与格式化层各自降级)。

三、适用使用环境

环境类型 适配说明
旅行记账 多币种消费统一汇总
差旅报销 外币票据折算本币
跨境电商比价 同商品多币种价格对比
多币种钱包 资产分布查看
财务审计 多币种流水核对

四、目标用户画像

  1. 环球旅行者:多国多币种消费,需要统一视角看开销;
  2. 差旅商务人士:外币票据报销需要折算凭证;
  3. 跨境消费者:海外购、代购需要比价;
  4. 出海产品团队:验证货币格式化的多语言覆盖。

五、关键设计决策

1. 原币记账,本币折算

BILLS{ currency, amount } 原币金额,折算在展示时计算。理由:汇率会变,存折算结果是存死数据;展示时算,汇率更新后历史账目自动重算。

2. 参考汇率透明化

页面明确标注"汇率 1 EUR = 7.85 CNY"(换算器下方小字),用户知道这是参考值——真实产品接实时汇率,但演示场景透明比精确更重要

3. 语言即币种视角

切换语言时,不仅符号变化,折算目标(本币)也固定在 CNY——旅行者的"家"是固定概念,不随界面语言变(与 13 应用"币种随订单走"一致的生产实践)。

五·五、货币格式化的三个维度(场景视角)

用户在不同语言下看到的货币差异,本质上来自三个独立维度,理解它们才能设计出正确的产品:

维度 由谁决定 例子(¥ 金额) 错误做法
币种 数据(账单的 currency) 日元账单显示 JPY 把币种写死在 locale 里
符号样式 locale(用户阅读习惯) zh_CN 看美元是 US$,en_US 看是 $ 自己拼 $ + 数字
精度 币种(JPY 0 位,CNY 2 位) ¥860 vs ¥860.00 统一 toFixed(2)

举一个真实反例:某 App 的英文界面显示 US$1,299.00,中文界面显示 US$1,299.00——两个界面一样,因为开发者把 US$ 写死在了模板里。用户虽然能看懂,但德语用户看到的是 1.299,00 US$(点号千分位、逗号小数位),如果模板写死 US$1,299.00 就会完全错误。正确的做法是三个维度都交给 NumberFormat 组合输出

// 一句话输出:币种 + 符号样式 + 精度 全部正确
new intl.NumberFormat('de_DE', { style: 'currency', currency: 'USD' }).format(1299)
// → "1.299,00 $"(德语习惯)

这是"场景视角"最重要的一课:用户看到的每一分钱格式,都是"数据(币种)× 环境(locale)× 规则(精度)"三者相乘的结果,缺一个维度就错一个。

六、边界与降级

  • 未知币种CURRENCIES 找不到时汇率兜底 1(按 1:1 折算)并继续展示;
  • 格式化失败:try/catch 降级 toFixed(2) 裸数字;
  • 汇率精度:演示值 2-3 位小数,真实场景取银行中间价并标注时效;
  • 金额精度:本应用是展示型记账,浮点可直接用;涉及支付的金额必须整数分(见 13)。

七、竞品对比

方案 优点 缺点 本应用选择
全本币记账(入账即折算) 展示简单 汇率漂移不可追溯 拒绝
原币记账 + 展示折算 真实可追溯 需维护汇率
多币种分账本 结构清晰 无法跨账本汇总 不适用
服务端统一记账 数据一致 离线不可用 混合

七·五、一次语言切换的完整链路(场景复盘)

以"账单从中文切到英文"为例,拆解货币格式化的响应式传导:

用户点击「🇺🇸 EN」胶囊
   │ 1. onClick 赋值
   ▼
this.currentLocale = 'en_US'        // @StorageLink 全局状态
   │ 2. AppStorage 广播 + PersistentStorage 落盘
   ▼
T(key) 重新执行                     // 文案:标题/按钮/卡片标签全变英文
fmtMoney(...) 重新执行              // 账单金额: €4.50 → EUR €4.50 / $1,299.00
fmtConverted(...) 重新执行          // 折算: 折算 ¥35 → CNY 35.33
fmtTotal() 重新执行                 // 汇总: ¥4,823.65 → CNY 4,823.65
fmtRate(...) 重新执行               // 汇率: 1 EUR = 7.85 → 1 EUR = 7.85
   │ 3. ArkUI 增量渲染
   ▼
页面整体刷新,无闪烁、无空白

这个链路揭示了货币 i18n 的核心认知:符号变化不是翻译,是格式化$1,299.00US$1,299.00 不是两个翻译版本,而是同一个数字 1299en_USzh_CN 两种 locale 下的格式化结果。所以开发者永远不要自己拼 $ + 数字——交给 intl.NumberFormat 一处输出,符号位置、千分位、小数位一次全部正确。

八、场景工作流:一次环球消费记账

以小林的两周欧洲游为例,走一遍完整操作:

1. 巴黎左岸咖啡 €4.5 → 记账(原币 EUR)
2. 伦敦酒店两晚 £180 → 记账
3. 纽约博物馆门票 $28 → 记账
4. 打开汇总卡:"旅行总花费 ¥4,823.65"
   → 每笔账下方都有折算金额,无需手动换算
5. 用汇率换算器查:100 EUR 是多少人民币?
   → 选中 EUR,+100 步进,显示 ≈ ¥785
6. 切英文复查汇率显示:"1 EUR = 7.85 CNY" 格式不变

每一步都体现货币 i18n 的要点:原币真实(事实层)、折算统一(视角层)、符号随语言(表达层)。

九、与相邻应用的关系(场景视角)

  • 应用 02 日期时间:时间要存 UTC 投影本地——与"原币记账、本币折算"是同一思想:存事实,展示时投影
  • 本应用 03 货币:币种格式化三要素(币种/符号/精度)各归其位;
  • 应用 13 订单:把"展示型折算"升级为"支付级金额"——整数分存储、税费分离、发票格式化,是 03 的生产级延伸;
  • 应用 05 复数:订单里"3 items / 1 item"的单复数由复数规则决定,与货币格式化组合使用。

十、扩展方向

  • 实时汇率:接入行情 API,缓存 + 过期刷新;
  • 历史汇率快照:每笔账记入账时汇率,供发票追溯;
  • 预算预警:按币种设预算,超支提醒;
  • 分享账单:本地化格式导出(与 13 的发票导出互通)。

十一、生产级注意事项

  1. 汇率时效标注:展示"更新于 10:00"之类的时间戳,避免用户把参考汇率当实时牌价;
  2. 精度策略分级:展示金额用 NumberFormat(小数位随币种),计算金额用整数分(如 amount * 100 存储);
  3. 汇率来源统一:所有币种用同一来源的同一时刻汇率,避免混合牌价造成汇总偏差;
  4. 负数金额:退款/取消场景的负数,NumberFormat 能正确处理(-$5.00 / -¥5.00),UI 不要手动加负号;
  5. 超大金额:超过 2^53 的金额(如外币债券)必须用字符串或 Decimal 类型,普通记账场景 number 足够。

十一·五、场景陷阱:汇率写死的代价

真实产品最常见的返工,是把汇率"写死"在代码里。举两个场景:

陷阱 1:入账即折算
开发者图省事,在记账时就把 €4.5 × 7.85 = ¥35.33 存进数据库。三个月后汇率变成 7.4,历史账单还是按 7.85 折算——总花费虚高,用户对账时发现"我明明花了 ¥4800,App 却显示 ¥5000"。本应用的做法(存原币、展示时折算)让历史数据永远跟着最新汇率重算,不会失真。

陷阱 2:符号写死
开发者写 '$' + amount.toFixed(2)。中文用户看到 $4.50,德语用户看到 $4.50——都错了。正确做法是 intl.NumberFormat(locale, { style: 'currency', currency: 'USD' }).format(4.5),德语用户自动得到 4,50 $(逗号小数位、符号在后)。

陷阱 3:精度一刀切
对 JPY 用 toFixed(2) 会显示 ¥860.00,违背日元"无角分"的习惯;对 USD 用 toFixed(0) 又会丢掉 0.50。精度必须由币种决定,交给 NumberFormat 自动处理。

十二、结语

旅行记账展示的是货币 i18n 的完整闭环:原币是事实,本币是视角,汇率是桥梁,格式是语言。四者各司其职,用户才能在全球任何角落清楚地知道"我花了多少"。这个应用也为 13 的订单发票打好了货币格式化基础。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐