You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every formatter delegates to the @finagent/i18n formatters (spec §52-59) so the active UI locale controls presentation (issue #86): the OS locale no longer participates, and one view can never mix US$1,234.56 and $1,234.56 for the same currency.
本地 helper 同时违反了这句话的两个分句:
toLocaleString(undefined, …) 的 undefined 就是「用 OS locale」,即「the OS locale no longer participates」被重新破坏。同一份 React 代码在英文与中文 Windows 上渲染出不同的千分位与小数点形态。
toLocaleString() 不带 locale 参数 → 跟随 OS locale。用户把应用语言切成 English、而系统是中文 Windows 时,这一行仍然渲染中文格式的日期,与界面其它所有日期(均走 @finagent/i18n)不一致。
复现
新增 packages/ui/src/components/research/ResearchMarketWorkspace.test.tsx(4 个断言)。在未修复的 main 上运行:
(fail) formats the quote price through the unified money layer
Expected: "$812.31" Received: "812.31 USD"
(fail) prices a Hong Kong instrument in HKD, not "<number> HKD"
Expected: "HK$350.00" Received: "350.00 HKD"
(fail) formats the open/high/low/prev-close strip through the unified money layer
Expected: ["HK$352.00","HK$355.00","HK$348.00","HK$351.50"] Received: []
(fail) formats the last-updated line through the i18n date layer
Expected to contain: "Nov 21, 2024, 09:20 AM"
Received: "Updated 2024/11/21 09:20:00"
0 pass / 4 fail
现象
packages/ui/src/components/research/ResearchMarketWorkspace.tsx自带一个本地formatPrice,完全绕过了 issue #86 建立起来的统一金额层packages/ui/src/lib/money.ts→@finagent/i18n:它的输出形如
812.31 USD。问题不在「好不好看」,而在于这个形状恰好是lib/money.ts专门为「无法格式化的未知币种」保留的降级通路:USD 属于
SUPPORTED_CURRENCIES,本应走formatCurrency,却被本地 helper 强行拉到了「未知币种」的降级分支上。涉及 6 个数字(与 #258 的 4 个调用点是同一类漏迁,但文件与 helper 均不同):
ResearchMarketWorkspace.tsx:171812.31 USD$812.31ResearchMarketWorkspace.tsx:245-248352.00 HKDHK$352.00money.ts的 docstring 把这条约束写得非常明确:本地 helper 同时违反了这句话的两个分句:
toLocaleString(undefined, …)的undefined就是「用 OS locale」,即「the OS locale no longer participates」被重新破坏。同一份 React 代码在英文与中文 Windows 上渲染出不同的千分位与小数点形态。SecurityHeader(ui: 行情价格展示层硬编码「$」,港股/A 股/新加坡标的币种显示错误(4 个调用点) #258 已修)渲染$812.31,而研究行情工作台渲染812.31 USD。这正是 docstring 里「one view can never mix」禁止的情形。第 3 处更直接:更新时间用的是平台日期 API,而非 App 内选择的语言。
toLocaleString()不带 locale 参数 → 跟随 OS locale。用户把应用语言切成 English、而系统是中文 Windows 时,这一行仍然渲染中文格式的日期,与界面其它所有日期(均走@finagent/i18n)不一致。复现
新增
packages/ui/src/components/research/ResearchMarketWorkspace.test.tsx(4 个断言)。在未修复的main上运行:第 4 条把 OS-locale 问题完整暴露出来:同一个 epoch 秒,i18n 层给
Nov 21, 2024, 09:20 AM,组件给2024/11/21 09:20:00。根因
已关闭的 issue #86「Unify currency formatting onto the i18n locale policy (lib/money vs @finagent/i18n)」把所有货币格式化收敛到
lib/money.ts→@finagent/i18n,但只迁走了显示「持仓金额」的调用点,漏掉了「只显示行情价格、不显示持仓金额」的一组。这一组各自保留了设计刷新时期的本地 helper ——ResearchMarketWorkspace是其中之一(#258 登记的另外 4 处是SecurityHeader/OverviewView/Watchlist/StockCard)。区别在于:#258 那 4 处是硬编码
$(币种错),本文件是不硬编码$、但自建了第二个格式化实现(格式与 locale 错)。同一个根因(迁移遗漏),不同的表现形式。修复方案
ResearchMarketWorkspace.tsx:35-39的本地formatPrice。import { formatMoney } from '../../lib/money'与import { formatDateTime } from '@finagent/i18n'。formatMoney(value, currency);更新时间改用formatDateTime(quote.timestamp * 1000)。刻意不做的事(避免把 PR 扩大成两类改动,也避免与 #258/#259 抢同一块地盘):
currency的推导(ResearchMarketWorkspace.tsx:90的info?.currency ?? 'USD'保持原样)。本 PR 只解决「格式化通路被绕过」,不解决「币种推导」。currencyForSymbol的符号路由属于 ui: 行情价格展示层硬编码「$」,港股/A 股/新加坡标的币种显示错误(4 个调用点) #258/fix(ui): 行情价格按标的币种格式化,移除 4 处硬编码「$」 #259 的范围,此处不引入。formatMoney与本地 helper 对undefined/ 非有限数的返回值都是'—',行为一致,无回归。影响面
仅「研究行情工作台」这一个组件的价格与更新时间渲染。
quote.timestamp是 epoch 秒,formatDateTime收 epoch 毫秒,故传入时为timestamp * 1000,与仓库既有约定一致。未纳入:
packages/ui/src/lib/screeningMetrics.ts:92-95(筛选「价格」列同样不带币种)——那是另一条数据通路与另一个签名,如需处理建议单独开 issue。验证
TDD 顺序:先写测试 → 在未修复代码上确认
0 pass / 4 fail→ 修复 →4 pass / 0 fail。测试覆盖:
NVDA.US最新价 →$812.31(证明走的是formatCurrency主通路,而非降级通路)0700.HK+ provider 上报HKD→HK$350.00(证明币种来自数据源而非硬编码,且未退回350.00 HKD)HK$352.00 / HK$355.00 / HK$348.00 / HK$351.50(证明 strip 内 4 个货币数字全部迁移;成交量与市场状态两格非货币,不参与匹配)formatDateTime(ts * 1000)(证明不再跟随 OS locale)回归对照:
bun test packages/ui/src/components/research/ --isolatebun run typecheck