@automattic/number-formatters 1.1.8 → 1.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
6
|
|
|
7
|
+
## [1.1.10] - 2026-05-21
|
|
8
|
+
### Changed
|
|
9
|
+
- Update package dependencies. [#49012]
|
|
10
|
+
|
|
11
|
+
## [1.1.9] - 2026-05-19
|
|
12
|
+
### Fixed
|
|
13
|
+
- Currency formatting: use ISO 4217 minor-unit exponent for smallest-unit conversion to correctly format IDR and other currencies where browser ICU disagrees with ISO 4217. [#48967]
|
|
14
|
+
|
|
7
15
|
## [1.1.8] - 2026-05-19
|
|
8
16
|
### Changed
|
|
9
17
|
- Internal updates.
|
|
@@ -144,6 +152,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
144
152
|
- Initial release
|
|
145
153
|
- Basic number formatting functionality
|
|
146
154
|
|
|
155
|
+
[1.1.10]: https://github.com/Automattic/number-formatters/compare/1.1.9...1.1.10
|
|
156
|
+
[1.1.9]: https://github.com/Automattic/number-formatters/compare/1.1.8...1.1.9
|
|
147
157
|
[1.1.8]: https://github.com/Automattic/number-formatters/compare/1.1.7...1.1.8
|
|
148
158
|
[1.1.7]: https://github.com/Automattic/number-formatters/compare/1.1.6...1.1.7
|
|
149
159
|
[1.1.6]: https://github.com/Automattic/number-formatters/compare/1.1.5...1.1.6
|
|
@@ -84,6 +84,35 @@ function getCurrencyFormatter({ number, currency, browserSafeLocale, forceLatin
|
|
|
84
84
|
options: numberFormatOptions,
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Smallest-unit exponent overrides for currencies where browser ICU's
|
|
89
|
+
* `maximumFractionDigits` disagrees with the API's smallest-unit encoding.
|
|
90
|
+
*
|
|
91
|
+
* Keep this list minimal — the backend is the source of truth for the API's
|
|
92
|
+
* smallest-unit encoding, so adding speculative entries here risks silent
|
|
93
|
+
* drift. Only add a currency once we've verified that browsers report a
|
|
94
|
+
* value the API does not use.
|
|
95
|
+
*
|
|
96
|
+
* - IDR: modern Chrome / Node 24+ ICU reports 0; the API encodes with exponent 2.
|
|
97
|
+
* - HUF: same browser/API divergence as IDR.
|
|
98
|
+
*/
|
|
99
|
+
const SMALLEST_UNIT_EXPONENT_OVERRIDES = {
|
|
100
|
+
IDR: 2,
|
|
101
|
+
HUF: 2,
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Returns the smallest unit exponent for a currency.
|
|
105
|
+
*
|
|
106
|
+
* Falls back to the browser-derived display precision for any currency not in
|
|
107
|
+
* the override map — i.e. existing behavior is preserved for everything except
|
|
108
|
+
* the explicitly listed currencies.
|
|
109
|
+
* @param currency - The currency code (ISO 4217)
|
|
110
|
+
* @param fallback - The browser-derived precision to use when no override applies
|
|
111
|
+
* @return number - The smallest unit exponent
|
|
112
|
+
*/
|
|
113
|
+
function getSmallestUnitExponent(currency, fallback) {
|
|
114
|
+
return SMALLEST_UNIT_EXPONENT_OVERRIDES[currency] ?? fallback;
|
|
115
|
+
}
|
|
87
116
|
/**
|
|
88
117
|
* Returns the precision for a given locale and currency.
|
|
89
118
|
* @param browserSafeLocale - The browser safe locale.
|
|
@@ -119,14 +148,12 @@ function scaleNumberForPrecision(number, currencyPrecision) {
|
|
|
119
148
|
/**
|
|
120
149
|
* Prepares a number for formatting.
|
|
121
150
|
* @param number - The number to prepare.
|
|
122
|
-
* @param currencyPrecision - The precision to
|
|
151
|
+
* @param currencyPrecision - The display precision (from the browser) to round the result to.
|
|
152
|
+
* @param currency - The currency code, used to look up any smallest-unit exponent override.
|
|
123
153
|
* @param isSmallestUnit - Whether the number is the smallest unit of a currency.
|
|
124
154
|
* @return {number} The prepared number.
|
|
125
155
|
*/
|
|
126
|
-
function prepareNumberForFormatting(number,
|
|
127
|
-
// currencyPrecision here must be the precision of the currency, regardless
|
|
128
|
-
// of what precision is requested for display!
|
|
129
|
-
currencyPrecision, isSmallestUnit) {
|
|
156
|
+
function prepareNumberForFormatting(number, currencyPrecision, currency, isSmallestUnit) {
|
|
130
157
|
if (isNaN(number)) {
|
|
131
158
|
debug('formatCurrency was called with NaN');
|
|
132
159
|
return 0;
|
|
@@ -135,7 +162,7 @@ currencyPrecision, isSmallestUnit) {
|
|
|
135
162
|
if (!Number.isInteger(number)) {
|
|
136
163
|
debug('formatCurrency was called with isSmallestUnit and a float which will be rounded', number);
|
|
137
164
|
}
|
|
138
|
-
const smallestUnitDivisor = 10 ** currencyPrecision;
|
|
165
|
+
const smallestUnitDivisor = 10 ** getSmallestUnitExponent(currency, currencyPrecision);
|
|
139
166
|
return scaleNumberForPrecision(Math.round(number) / smallestUnitDivisor, currencyPrecision);
|
|
140
167
|
}
|
|
141
168
|
return scaleNumberForPrecision(number, currencyPrecision);
|
|
@@ -186,7 +213,7 @@ const numberFormatCurrency = ({ number, browserSafeLocale, currency, stripZeros,
|
|
|
186
213
|
if (isSmallestUnit && typeof currencyPrecision === 'undefined') {
|
|
187
214
|
throw new Error(`Could not determine currency precision for ${validCurrency} in ${browserSafeLocale}`);
|
|
188
215
|
}
|
|
189
|
-
const numberAsFloat = prepareNumberForFormatting(number, currencyPrecision ?? 0, isSmallestUnit);
|
|
216
|
+
const numberAsFloat = prepareNumberForFormatting(number, currencyPrecision ?? 0, validCurrency, isSmallestUnit);
|
|
190
217
|
const formatter = getCurrencyFormatter({
|
|
191
218
|
number: numberAsFloat,
|
|
192
219
|
currency: validCurrency,
|
|
@@ -259,7 +286,7 @@ const getCurrencyObject = ({ number, browserSafeLocale, currency, stripZeros, is
|
|
|
259
286
|
const validCurrency = getValidCurrency(currency, geoLocation);
|
|
260
287
|
const currencyOverride = getCurrencyOverride(validCurrency, geoLocation);
|
|
261
288
|
const currencyPrecision = getPrecisionForLocaleAndCurrency(browserSafeLocale, validCurrency, forceLatin);
|
|
262
|
-
const numberAsFloat = prepareNumberForFormatting(number, currencyPrecision ?? 0, isSmallestUnit);
|
|
289
|
+
const numberAsFloat = prepareNumberForFormatting(number, currencyPrecision ?? 0, validCurrency, isSmallestUnit);
|
|
263
290
|
const formatter = getCurrencyFormatter({
|
|
264
291
|
number: numberAsFloat,
|
|
265
292
|
currency: validCurrency,
|
|
@@ -80,6 +80,35 @@ function getCurrencyFormatter({ number, currency, browserSafeLocale, forceLatin
|
|
|
80
80
|
options: numberFormatOptions,
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Smallest-unit exponent overrides for currencies where browser ICU's
|
|
85
|
+
* `maximumFractionDigits` disagrees with the API's smallest-unit encoding.
|
|
86
|
+
*
|
|
87
|
+
* Keep this list minimal — the backend is the source of truth for the API's
|
|
88
|
+
* smallest-unit encoding, so adding speculative entries here risks silent
|
|
89
|
+
* drift. Only add a currency once we've verified that browsers report a
|
|
90
|
+
* value the API does not use.
|
|
91
|
+
*
|
|
92
|
+
* - IDR: modern Chrome / Node 24+ ICU reports 0; the API encodes with exponent 2.
|
|
93
|
+
* - HUF: same browser/API divergence as IDR.
|
|
94
|
+
*/
|
|
95
|
+
const SMALLEST_UNIT_EXPONENT_OVERRIDES = {
|
|
96
|
+
IDR: 2,
|
|
97
|
+
HUF: 2,
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Returns the smallest unit exponent for a currency.
|
|
101
|
+
*
|
|
102
|
+
* Falls back to the browser-derived display precision for any currency not in
|
|
103
|
+
* the override map — i.e. existing behavior is preserved for everything except
|
|
104
|
+
* the explicitly listed currencies.
|
|
105
|
+
* @param currency - The currency code (ISO 4217)
|
|
106
|
+
* @param fallback - The browser-derived precision to use when no override applies
|
|
107
|
+
* @return number - The smallest unit exponent
|
|
108
|
+
*/
|
|
109
|
+
function getSmallestUnitExponent(currency, fallback) {
|
|
110
|
+
return SMALLEST_UNIT_EXPONENT_OVERRIDES[currency] ?? fallback;
|
|
111
|
+
}
|
|
83
112
|
/**
|
|
84
113
|
* Returns the precision for a given locale and currency.
|
|
85
114
|
* @param browserSafeLocale - The browser safe locale.
|
|
@@ -115,14 +144,12 @@ function scaleNumberForPrecision(number, currencyPrecision) {
|
|
|
115
144
|
/**
|
|
116
145
|
* Prepares a number for formatting.
|
|
117
146
|
* @param number - The number to prepare.
|
|
118
|
-
* @param currencyPrecision - The precision to
|
|
147
|
+
* @param currencyPrecision - The display precision (from the browser) to round the result to.
|
|
148
|
+
* @param currency - The currency code, used to look up any smallest-unit exponent override.
|
|
119
149
|
* @param isSmallestUnit - Whether the number is the smallest unit of a currency.
|
|
120
150
|
* @return {number} The prepared number.
|
|
121
151
|
*/
|
|
122
|
-
function prepareNumberForFormatting(number,
|
|
123
|
-
// currencyPrecision here must be the precision of the currency, regardless
|
|
124
|
-
// of what precision is requested for display!
|
|
125
|
-
currencyPrecision, isSmallestUnit) {
|
|
152
|
+
function prepareNumberForFormatting(number, currencyPrecision, currency, isSmallestUnit) {
|
|
126
153
|
if (isNaN(number)) {
|
|
127
154
|
debug('formatCurrency was called with NaN');
|
|
128
155
|
return 0;
|
|
@@ -131,7 +158,7 @@ currencyPrecision, isSmallestUnit) {
|
|
|
131
158
|
if (!Number.isInteger(number)) {
|
|
132
159
|
debug('formatCurrency was called with isSmallestUnit and a float which will be rounded', number);
|
|
133
160
|
}
|
|
134
|
-
const smallestUnitDivisor = 10 ** currencyPrecision;
|
|
161
|
+
const smallestUnitDivisor = 10 ** getSmallestUnitExponent(currency, currencyPrecision);
|
|
135
162
|
return scaleNumberForPrecision(Math.round(number) / smallestUnitDivisor, currencyPrecision);
|
|
136
163
|
}
|
|
137
164
|
return scaleNumberForPrecision(number, currencyPrecision);
|
|
@@ -182,7 +209,7 @@ const numberFormatCurrency = ({ number, browserSafeLocale, currency, stripZeros,
|
|
|
182
209
|
if (isSmallestUnit && typeof currencyPrecision === 'undefined') {
|
|
183
210
|
throw new Error(`Could not determine currency precision for ${validCurrency} in ${browserSafeLocale}`);
|
|
184
211
|
}
|
|
185
|
-
const numberAsFloat = prepareNumberForFormatting(number, currencyPrecision ?? 0, isSmallestUnit);
|
|
212
|
+
const numberAsFloat = prepareNumberForFormatting(number, currencyPrecision ?? 0, validCurrency, isSmallestUnit);
|
|
186
213
|
const formatter = getCurrencyFormatter({
|
|
187
214
|
number: numberAsFloat,
|
|
188
215
|
currency: validCurrency,
|
|
@@ -254,7 +281,7 @@ const getCurrencyObject = ({ number, browserSafeLocale, currency, stripZeros, is
|
|
|
254
281
|
const validCurrency = getValidCurrency(currency, geoLocation);
|
|
255
282
|
const currencyOverride = getCurrencyOverride(validCurrency, geoLocation);
|
|
256
283
|
const currencyPrecision = getPrecisionForLocaleAndCurrency(browserSafeLocale, validCurrency, forceLatin);
|
|
257
|
-
const numberAsFloat = prepareNumberForFormatting(number, currencyPrecision ?? 0, isSmallestUnit);
|
|
284
|
+
const numberAsFloat = prepareNumberForFormatting(number, currencyPrecision ?? 0, validCurrency, isSmallestUnit);
|
|
258
285
|
const formatter = getCurrencyFormatter({
|
|
259
286
|
number: numberAsFloat,
|
|
260
287
|
currency: validCurrency,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/number-formatters",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.10",
|
|
4
4
|
"description": "Number formatting utilities",
|
|
5
5
|
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/number-formatters/#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@babel/core": "7.29.0",
|
|
41
41
|
"@babel/preset-react": "7.28.5",
|
|
42
|
-
"@jest/globals": "30.
|
|
42
|
+
"@jest/globals": "30.4.1",
|
|
43
43
|
"@knighted/duel": "4.0.2",
|
|
44
44
|
"@types/jest": "30.0.0",
|
|
45
45
|
"@typescript/native-preview": "7.0.0-dev.20260225.1",
|
|
46
|
-
"jest": "30.
|
|
46
|
+
"jest": "30.4.2",
|
|
47
47
|
"jetpack-js-tools": "workspace:*",
|
|
48
48
|
"typescript": "5.9.3"
|
|
49
49
|
}
|
|
@@ -98,6 +98,37 @@ function getCurrencyFormatter( {
|
|
|
98
98
|
} );
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Smallest-unit exponent overrides for currencies where browser ICU's
|
|
103
|
+
* `maximumFractionDigits` disagrees with the API's smallest-unit encoding.
|
|
104
|
+
*
|
|
105
|
+
* Keep this list minimal — the backend is the source of truth for the API's
|
|
106
|
+
* smallest-unit encoding, so adding speculative entries here risks silent
|
|
107
|
+
* drift. Only add a currency once we've verified that browsers report a
|
|
108
|
+
* value the API does not use.
|
|
109
|
+
*
|
|
110
|
+
* - IDR: modern Chrome / Node 24+ ICU reports 0; the API encodes with exponent 2.
|
|
111
|
+
* - HUF: same browser/API divergence as IDR.
|
|
112
|
+
*/
|
|
113
|
+
const SMALLEST_UNIT_EXPONENT_OVERRIDES: Record< string, number > = {
|
|
114
|
+
IDR: 2,
|
|
115
|
+
HUF: 2,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Returns the smallest unit exponent for a currency.
|
|
120
|
+
*
|
|
121
|
+
* Falls back to the browser-derived display precision for any currency not in
|
|
122
|
+
* the override map — i.e. existing behavior is preserved for everything except
|
|
123
|
+
* the explicitly listed currencies.
|
|
124
|
+
* @param currency - The currency code (ISO 4217)
|
|
125
|
+
* @param fallback - The browser-derived precision to use when no override applies
|
|
126
|
+
* @return number - The smallest unit exponent
|
|
127
|
+
*/
|
|
128
|
+
function getSmallestUnitExponent( currency: string, fallback: number ): number {
|
|
129
|
+
return SMALLEST_UNIT_EXPONENT_OVERRIDES[ currency ] ?? fallback;
|
|
130
|
+
}
|
|
131
|
+
|
|
101
132
|
/**
|
|
102
133
|
* Returns the precision for a given locale and currency.
|
|
103
134
|
* @param browserSafeLocale - The browser safe locale.
|
|
@@ -139,15 +170,15 @@ function scaleNumberForPrecision( number: number, currencyPrecision: number ): n
|
|
|
139
170
|
/**
|
|
140
171
|
* Prepares a number for formatting.
|
|
141
172
|
* @param number - The number to prepare.
|
|
142
|
-
* @param currencyPrecision - The precision to
|
|
173
|
+
* @param currencyPrecision - The display precision (from the browser) to round the result to.
|
|
174
|
+
* @param currency - The currency code, used to look up any smallest-unit exponent override.
|
|
143
175
|
* @param isSmallestUnit - Whether the number is the smallest unit of a currency.
|
|
144
176
|
* @return {number} The prepared number.
|
|
145
177
|
*/
|
|
146
178
|
function prepareNumberForFormatting(
|
|
147
179
|
number: number,
|
|
148
|
-
// currencyPrecision here must be the precision of the currency, regardless
|
|
149
|
-
// of what precision is requested for display!
|
|
150
180
|
currencyPrecision: number,
|
|
181
|
+
currency: string,
|
|
151
182
|
isSmallestUnit?: boolean
|
|
152
183
|
): number {
|
|
153
184
|
if ( isNaN( number ) ) {
|
|
@@ -162,7 +193,7 @@ function prepareNumberForFormatting(
|
|
|
162
193
|
number
|
|
163
194
|
);
|
|
164
195
|
}
|
|
165
|
-
const smallestUnitDivisor = 10 ** currencyPrecision;
|
|
196
|
+
const smallestUnitDivisor = 10 ** getSmallestUnitExponent( currency, currencyPrecision );
|
|
166
197
|
return scaleNumberForPrecision( Math.round( number ) / smallestUnitDivisor, currencyPrecision );
|
|
167
198
|
}
|
|
168
199
|
|
|
@@ -235,6 +266,7 @@ const numberFormatCurrency = ( {
|
|
|
235
266
|
const numberAsFloat = prepareNumberForFormatting(
|
|
236
267
|
number,
|
|
237
268
|
currencyPrecision ?? 0,
|
|
269
|
+
validCurrency,
|
|
238
270
|
isSmallestUnit
|
|
239
271
|
);
|
|
240
272
|
const formatter = getCurrencyFormatter( {
|
|
@@ -326,6 +358,7 @@ const getCurrencyObject = ( {
|
|
|
326
358
|
const numberAsFloat = prepareNumberForFormatting(
|
|
327
359
|
number,
|
|
328
360
|
currencyPrecision ?? 0,
|
|
361
|
+
validCurrency,
|
|
329
362
|
isSmallestUnit
|
|
330
363
|
);
|
|
331
364
|
const formatter = getCurrencyFormatter( {
|