Skip to content

Commit 20b77a4

Browse files
fix(localize): allow multiple characters for currency symbols
1 parent 9e39c6c commit 20b77a4

File tree

4 files changed

+67
-47
lines changed

4 files changed

+67
-47
lines changed

packages/localize/src/number/formatNumberToParts.js

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,12 @@ export function formatNumberToParts(number, options) {
2626
}
2727
let formattedParts = [];
2828
const formattedNumber = Intl.NumberFormat(computedLocale, options).format(parsedNumber);
29-
const regexSymbol = /[A-Z.,\s0-9]/;
30-
const regexCode = /[A-Z]/;
31-
32-
// U+002D, Hyphen-Minus, -
33-
const regexMinusSign = /[-]/;
34-
29+
const regexCurrency = /[.,\s0-9]/;
30+
const regexMinusSign = /[-]/; // U+002D, Hyphen-Minus, -
3531
const regexNum = /[0-9]/;
3632
const regexSeparator = /[.,]/;
3733
const regexSpace = /[\s]/;
38-
let currencyCode = '';
34+
let currency = '';
3935
let numberPart = '';
4036
let fraction = false;
4137
for (let i = 0; i < formattedNumber.length; i += 1) {
@@ -47,34 +43,17 @@ export function formatNumberToParts(number, options) {
4743
if (regexNum.test(formattedNumber[i])) {
4844
numberPart += formattedNumber[i];
4945
}
50-
// detect currency symbol
51-
if (!regexSymbol.test(formattedNumber[i]) && !regexMinusSign.test(formattedNumber[i])) {
52-
// Write number grouping
53-
if (numberPart && !fraction) {
54-
formattedParts.push({ type: 'integer', value: numberPart });
55-
numberPart = '';
56-
} else if (numberPart) {
57-
formattedParts.push({ type: 'fraction', value: numberPart });
58-
numberPart = '';
59-
}
60-
formattedParts.push({ type: 'currency', value: formattedNumber[i] });
46+
47+
// detect currency (symbol or code)
48+
if (!regexCurrency.test(formattedNumber[i]) && !regexMinusSign.test(formattedNumber[i])) {
49+
currency += formattedNumber[i];
6150
}
62-
// detect currency code
63-
if (regexCode.test(formattedNumber[i])) {
64-
currencyCode += formattedNumber[i];
65-
// Write number grouping
66-
if (numberPart && !fraction) {
67-
formattedParts.push({ type: 'integer', value: numberPart });
68-
numberPart = '';
69-
} else if (numberPart) {
70-
formattedParts.push({ type: 'fraction', value: numberPart });
71-
numberPart = '';
72-
}
73-
if (currencyCode.length === 3) {
74-
formattedParts.push({ type: 'currency', value: currencyCode });
75-
currencyCode = '';
76-
}
51+
// push when another character then currency or end of loop
52+
if ((regexCurrency.test(formattedNumber[i]) || formattedNumber.length === i + 1) && currency) {
53+
formattedParts.push({ type: 'currency', value: currency });
54+
currency = '';
7755
}
56+
7857
// detect dot and comma separators
7958
if (regexSeparator.test(formattedNumber[i])) {
8059
// Write number grouping

packages/localize/src/number/normalizeIntl.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function normalizeIntl(formattedParts, options, _locale) {
2727
normalize = forceSpaceInsteadOfZeroForGroup(normalize);
2828
}
2929
// Force space between currency code and number
30-
if (_locale === 'en-GB' || _locale === 'en-US' || _locale === 'en-AU') {
30+
if (_locale === 'en-GB' || _locale === 'en-US' || _locale === 'en-AU' || _locale === 'en-PH') {
3131
normalize = forceSpaceBetweenCurrencyCodeAndNumber(normalize, options);
3232
}
3333
// Force missing Japanese Yen symbol

packages/localize/test/number/formatNumber.test.js

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('formatNumber', () => {
2424

2525
it('can display currency as symbol', () => {
2626
expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('€123,456.79');
27-
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('$123,456.79');
27+
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('US$123,456.79');
2828
});
2929

3030
it('uses minus U+2212 (and not dash) to indicate negative numbers ', () => {
@@ -187,8 +187,8 @@ describe('formatNumber', () => {
187187
expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('USD 123,456.79');
188188
expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('JPY 123,457');
189189
expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('€123,456.79');
190-
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('$123,456.79');
191-
expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('¥123,457');
190+
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('US$123,456.79');
191+
expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('JP¥123,457');
192192
});
193193
});
194194

@@ -223,8 +223,8 @@ describe('formatNumber', () => {
223223
expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('USD 123,456.79');
224224
expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('JPY 123,457');
225225
expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('€123,456.79');
226-
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('$123,456.79');
227-
expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('¥123,457');
226+
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('US$123,456.79');
227+
expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('JP¥123,457');
228228
});
229229
});
230230

@@ -235,8 +235,8 @@ describe('formatNumber', () => {
235235
expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('123.456,79 USD');
236236
expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('123.457 JPY');
237237
expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('€ 123.456,79');
238-
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('$ 123.456,79');
239-
expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('¥ 123.457');
238+
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('US$ 123.456,79');
239+
expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('JP¥ 123.457');
240240
});
241241
});
242242

@@ -247,8 +247,8 @@ describe('formatNumber', () => {
247247
expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('123.456,79 USD');
248248
expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('123.457 JPY');
249249
expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('€ 123.456,79');
250-
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('$ 123.456,79');
251-
expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('¥ 123.457');
250+
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('US$ 123.456,79');
251+
expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('JP¥ 123.457');
252252
});
253253
});
254254

@@ -259,19 +259,19 @@ describe('formatNumber', () => {
259259
expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('123 456,79 USD');
260260
expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('123 457 JPY');
261261
expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('123 456,79 €');
262-
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('123 456,79 $');
262+
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('123 456,79 $US');
263263
expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('123 457 ¥');
264264
});
265265
});
266266

267267
describe('fr-BE', () => {
268268
it('supports basics', () => {
269-
localize.locale = 'fr-FR';
269+
localize.locale = 'fr-BE';
270270
expect(formatNumber(123456.789, currencyCode('EUR'))).to.equal('123 456,79 EUR');
271271
expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('123 456,79 USD');
272272
expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('123 457 JPY');
273273
expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('123 456,79 €');
274-
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('123 456,79 $');
274+
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('123 456,79 $US');
275275
expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('123 457 ¥');
276276
});
277277
});
@@ -293,5 +293,19 @@ describe('formatNumber', () => {
293293
expect(formatNumber(1234.567, currencyCode('EUR'))).to.equal('1 234,57 EUR');
294294
});
295295
});
296+
297+
describe('cs-CZ', () => {
298+
it('supports basics', () => {
299+
localize.locale = 'cs-CZ';
300+
expect(formatNumber(123456.789, currencyCode('EUR'))).to.equal('123 456,79 EUR');
301+
expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('123 456,79 USD');
302+
expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('123 457 JPY');
303+
expect(formatNumber(123456.789, currencyCode('CZK'))).to.equal('123 456,79 CZK');
304+
expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('123 456,79 €');
305+
expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('123 456,79 US$');
306+
expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('123 457 JP¥');
307+
expect(formatNumber(123456.789, currencySymbol('CZK'))).to.equal('123 456,79 Kč');
308+
});
309+
});
296310
});
297311
});

packages/localize/test/number/formatNumberToParts.test.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,34 @@ const stringifyParts = parts => parts.map(part => part.value).join('');
1717
describe('formatNumberToParts', () => {
1818
afterEach(localizeTearDown);
1919

20-
describe("style: 'currency'", () => {
20+
describe("style: 'currency symbol'", () => {
21+
const specs = [
22+
['en-GB', 'EUR', 1234.5, [c('€'), i('1'), g(','), i('234'), d('.'), f('50')]],
23+
['en-GB', 'USD', 1234.5, [c('US$'), i('1'), g(','), i('234'), d('.'), f('50')]],
24+
['nl-NL', 'EUR', 1234.5, [c('€'), l(' '), i('1'), g('.'), i('234'), d(','), f('50')]],
25+
['nl-NL', 'USD', 1234.5, [c('US$'), l(' '), i('1'), g('.'), i('234'), d(','), f('50')]],
26+
['nl-BE', 'EUR', 1234.5, [c('€'), l(' '), i('1'), g('.'), i('234'), d(','), f('50')]],
27+
['nl-BE', 'USD', 1234.5, [c('US$'), l(' '), i('1'), g('.'), i('234'), d(','), f('50')]],
28+
['fr-FR', 'EUR', 1234.5, [i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('€')]],
29+
['fr-FR', 'USD', 1234.5, [i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('$US')]],
30+
['fr-BE', 'EUR', 1234.5, [i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('€')]],
31+
['fr-BE', 'USD', 1234.5, [i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('$US')]],
32+
];
33+
34+
specs.forEach(([locale, currency, amount, expectedResult]) => {
35+
it(`formats ${locale} ${currency} ${amount} as "${stringifyParts(expectedResult)}"`, () => {
36+
localize.locale = locale;
37+
expect(
38+
formatNumberToParts(amount, {
39+
style: 'currency',
40+
currency,
41+
}),
42+
).to.deep.equal(expectedResult);
43+
});
44+
});
45+
});
46+
47+
describe("style: 'currency code'", () => {
2148
const specs = [
2249
['en-GB', 'EUR', 1234.5, [c('EUR'), l(' '), i('1'), g(','), i('234'), d('.'), f('50')]],
2350
['en-GB', 'EUR', -1234.5, [m, c('EUR'), l(' '), i('1'), g(','), i('234'), d('.'), f('50')]],

0 commit comments

Comments
 (0)