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
Found while censusing formatPercent consumers for #4553 phase 2 (PR #4565). Filed rather than fixed — different package, different function, outside that card's ruled surface.
The gap
packages/core/src/utils/dataset-format.ts formats dashboard measure values with a bare undefined locale tag at three sites:
Line 130 is formatMeasure's main body (numbers, currency-ish and percent measures alike); 111 is its no-format-hint branch; 141 is formatDimensionValue. undefined is not "the user's locale", it is the MACHINE's — neither of the repo's two locale channels, exactly as useDisplayLocale's own doc puts it.
It is easy to assume this was already fixed, and the census showed it was not. formatMeasure is a parallel implementation, not a consumer: it never calls formatPercent, formatNumber or formatDisplayNumber. It reimplements the percent scaling by sharing percentDisplayValue with @object-ui/fields and then formats independently.
So #4553's producer fix reached every formatPercent consumer and moved nothing here. DatasetWidget, ObjectMetric and the pivot matrix — the surfaces formatMeasure actually feeds — still render in the machine's locale on every session.
Worth noting it is the milder half of the same family: toLocaleString does group by default, so this is a locale defect only, not the locale-plus-grouping defect formatPercent had.
Why it can be observed
A German session on a dashboard reads a KPI as 1,234.5 where the convention is 1.234,5, next to a grid or gantt tooltip that (after #4541 / #4544 / #4557 / #4565) renders the same number as 1.234,5. Inverted separators read as a different number, not as an unstyled one.
The part that needs a decision, not just a patch
formatMeasure is a pure function in @object-ui/core, called from React components. It cannot read useDisplayLocale() itself, so the locale has to arrive as a parameter and every caller has to thread it — the same shape renderFieldValue needed in PR #4565, but with more callers and across a package boundary.
Whoever picks this up should decide deliberately between:
B. Route these through formatDisplayNumber in @object-ui/i18n so the grouping policy and locale handling stop being duplicated at all — larger, but it retires the parallel implementation rather than localizing it in place.
B is worth real consideration precisely because this function exists to keep dashboard measures agreeing with list cells (its own doc comment says so), and it currently achieves that agreement by duplicating logic that has since grown a policy layer the duplicate does not have.
Found while censusing
formatPercentconsumers for #4553 phase 2 (PR #4565). Filed rather than fixed — different package, different function, outside that card's ruled surface.The gap
packages/core/src/utils/dataset-format.tsformats dashboard measure values with a bareundefinedlocale tag at three sites:Line 130 is
formatMeasure's main body (numbers, currency-ish and percent measures alike); 111 is its no-format-hint branch; 141 isformatDimensionValue.undefinedis not "the user's locale", it is the MACHINE's — neither of the repo's two locale channels, exactly asuseDisplayLocale's own doc puts it.Why this was not covered by the #4553 family
It is easy to assume this was already fixed, and the census showed it was not.
formatMeasureis a parallel implementation, not a consumer: it never callsformatPercent,formatNumberorformatDisplayNumber. It reimplements the percent scaling by sharingpercentDisplayValuewith@object-ui/fieldsand then formats independently.So #4553's producer fix reached every
formatPercentconsumer and moved nothing here.DatasetWidget,ObjectMetricand the pivot matrix — the surfacesformatMeasureactually feeds — still render in the machine's locale on every session.Worth noting it is the milder half of the same family:
toLocaleStringdoes group by default, so this is a locale defect only, not the locale-plus-grouping defectformatPercenthad.Why it can be observed
A German session on a dashboard reads a KPI as
1,234.5where the convention is1.234,5, next to a grid or gantt tooltip that (after #4541 / #4544 / #4557 / #4565) renders the same number as1.234,5. Inverted separators read as a different number, not as an unstyled one.The part that needs a decision, not just a patch
formatMeasureis a pure function in@object-ui/core, called from React components. It cannot readuseDisplayLocale()itself, so the locale has to arrive as a parameter and every caller has to thread it — the same shaperenderFieldValueneeded in PR #4565, but with more callers and across a package boundary.Whoever picks this up should decide deliberately between:
formatMeasure/formatDimensionValuean optional locale parameter and thread it from each widget (theformatDateTimeplaybook from Date formatter is half-localized: future relative forms and absolute timestamps stay en-US on a non-English locale #4272: parameter lands WITH its consumers).formatDisplayNumberin@object-ui/i18nso the grouping policy and locale handling stop being duplicated at all — larger, but it retires the parallel implementation rather than localizing it in place.B is worth real consideration precisely because this function exists to keep dashboard measures agreeing with list cells (its own doc comment says so), and it currently achieves that agreement by duplicating logic that has since grown a policy layer the duplicate does not have.
Relations
Intl.NumberFormat('en-US')— an ordinalField.number(a year) always renders2,026, and no field property can turn it off #4033, Date formatter is half-localized: future relative forms and absolute timestamps stay en-US on a non-English locale #4272 / PR fix(fields): the date formatter's last three en-US channels thread the display locale (#4272) #4544, [plugin-grid] ObjectGrid's record-detail date fallback renders the MACHINE locale #4541 / PR fix(plugin-grid): the record-detail date fallback threads the display locale (#4541) #4552, plugin-timeline hardcodes 'en-US' at four date sites, so timeline headers stay English on every non-English session #4513, and [plugin-gantt] tooltip number / currency / percent rows render in the MACHINE locale, next to date rows that follow the display locale #4553 / PRs fix(plugin-gantt): tooltip numbers and currency follow the display locale (#4553) #4557 + fix(fields): formatPercent groups and follows the display locale — the last tooltip channel (#4553) #4565.formatPercentand its consumers; this is a different function in a different package that [plugin-gantt] tooltip number / currency / percent rows render in the MACHINE locale, next to date rows that follow the display locale #4553's fix provably does not reach.Generated by Claude Code