diff --git a/packages/localize/src/number/formatNumberToParts.js b/packages/localize/src/number/formatNumberToParts.js index d89cc0df8..7f01c5816 100644 --- a/packages/localize/src/number/formatNumberToParts.js +++ b/packages/localize/src/number/formatNumberToParts.js @@ -26,16 +26,12 @@ export function formatNumberToParts(number, options) { } let formattedParts = []; const formattedNumber = Intl.NumberFormat(computedLocale, options).format(parsedNumber); - const regexSymbol = /[A-Z.,\s0-9]/; - const regexCode = /[A-Z]/; - - // U+002D, Hyphen-Minus, - - const regexMinusSign = /[-]/; - + const regexCurrency = /[.,\s0-9]/; + const regexMinusSign = /[-]/; // U+002D, Hyphen-Minus, - const regexNum = /[0-9]/; const regexSeparator = /[.,]/; const regexSpace = /[\s]/; - let currencyCode = ''; + let currency = ''; let numberPart = ''; let fraction = false; for (let i = 0; i < formattedNumber.length; i += 1) { @@ -47,34 +43,17 @@ export function formatNumberToParts(number, options) { if (regexNum.test(formattedNumber[i])) { numberPart += formattedNumber[i]; } - // detect currency symbol - if (!regexSymbol.test(formattedNumber[i]) && !regexMinusSign.test(formattedNumber[i])) { - // Write number grouping - if (numberPart && !fraction) { - formattedParts.push({ type: 'integer', value: numberPart }); - numberPart = ''; - } else if (numberPart) { - formattedParts.push({ type: 'fraction', value: numberPart }); - numberPart = ''; - } - formattedParts.push({ type: 'currency', value: formattedNumber[i] }); + + // detect currency (symbol or code) + if (!regexCurrency.test(formattedNumber[i]) && !regexMinusSign.test(formattedNumber[i])) { + currency += formattedNumber[i]; } - // detect currency code - if (regexCode.test(formattedNumber[i])) { - currencyCode += formattedNumber[i]; - // Write number grouping - if (numberPart && !fraction) { - formattedParts.push({ type: 'integer', value: numberPart }); - numberPart = ''; - } else if (numberPart) { - formattedParts.push({ type: 'fraction', value: numberPart }); - numberPart = ''; - } - if (currencyCode.length === 3) { - formattedParts.push({ type: 'currency', value: currencyCode }); - currencyCode = ''; - } + // push when another character then currency or end of loop + if ((regexCurrency.test(formattedNumber[i]) || formattedNumber.length === i + 1) && currency) { + formattedParts.push({ type: 'currency', value: currency }); + currency = ''; } + // detect dot and comma separators if (regexSeparator.test(formattedNumber[i])) { // Write number grouping diff --git a/packages/localize/src/number/normalizeIntl.js b/packages/localize/src/number/normalizeIntl.js index 92e8b4643..7a84bf863 100644 --- a/packages/localize/src/number/normalizeIntl.js +++ b/packages/localize/src/number/normalizeIntl.js @@ -27,7 +27,7 @@ export function normalizeIntl(formattedParts, options, _locale) { normalize = forceSpaceInsteadOfZeroForGroup(normalize); } // Force space between currency code and number - if (_locale === 'en-GB' || _locale === 'en-US' || _locale === 'en-AU') { + if (_locale === 'en-GB' || _locale === 'en-US' || _locale === 'en-AU' || _locale === 'en-PH') { normalize = forceSpaceBetweenCurrencyCodeAndNumber(normalize, options); } // Force missing Japanese Yen symbol diff --git a/packages/localize/test/number/formatNumber.test.js b/packages/localize/test/number/formatNumber.test.js index ae77c709e..03c817a64 100644 --- a/packages/localize/test/number/formatNumber.test.js +++ b/packages/localize/test/number/formatNumber.test.js @@ -24,7 +24,7 @@ describe('formatNumber', () => { it('can display currency as symbol', () => { expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('€123,456.79'); - expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('$123,456.79'); + expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('US$123,456.79'); }); it('uses minus U+2212 (and not dash) to indicate negative numbers ', () => { @@ -187,8 +187,8 @@ describe('formatNumber', () => { expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('USD 123,456.79'); expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('JPY 123,457'); expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('€123,456.79'); - expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('$123,456.79'); - expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('¥123,457'); + expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('US$123,456.79'); + expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('JP¥123,457'); }); }); @@ -223,8 +223,8 @@ describe('formatNumber', () => { expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('USD 123,456.79'); expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('JPY 123,457'); expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('€123,456.79'); - expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('$123,456.79'); - expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('¥123,457'); + expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('US$123,456.79'); + expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('JP¥123,457'); }); }); @@ -235,8 +235,8 @@ describe('formatNumber', () => { expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('123.456,79 USD'); expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('123.457 JPY'); expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('€ 123.456,79'); - expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('$ 123.456,79'); - expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('¥ 123.457'); + expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('US$ 123.456,79'); + expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('JP¥ 123.457'); }); }); @@ -247,8 +247,8 @@ describe('formatNumber', () => { expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('123.456,79 USD'); expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('123.457 JPY'); expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('€ 123.456,79'); - expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('$ 123.456,79'); - expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('¥ 123.457'); + expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('US$ 123.456,79'); + expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('JP¥ 123.457'); }); }); @@ -259,19 +259,19 @@ describe('formatNumber', () => { expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('123 456,79 USD'); expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('123 457 JPY'); expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('123 456,79 €'); - expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('123 456,79 $'); + expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('123 456,79 $US'); expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('123 457 ¥'); }); }); describe('fr-BE', () => { it('supports basics', () => { - localize.locale = 'fr-FR'; + localize.locale = 'fr-BE'; expect(formatNumber(123456.789, currencyCode('EUR'))).to.equal('123 456,79 EUR'); expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('123 456,79 USD'); expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('123 457 JPY'); expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('123 456,79 €'); - expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('123 456,79 $'); + expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('123 456,79 $US'); expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('123 457 ¥'); }); }); @@ -293,5 +293,19 @@ describe('formatNumber', () => { expect(formatNumber(1234.567, currencyCode('EUR'))).to.equal('1 234,57 EUR'); }); }); + + describe('cs-CZ', () => { + it('supports basics', () => { + localize.locale = 'cs-CZ'; + expect(formatNumber(123456.789, currencyCode('EUR'))).to.equal('123 456,79 EUR'); + expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('123 456,79 USD'); + expect(formatNumber(123456.789, currencyCode('JPY'))).to.equal('123 457 JPY'); + expect(formatNumber(123456.789, currencyCode('CZK'))).to.equal('123 456,79 CZK'); + expect(formatNumber(123456.789, currencySymbol('EUR'))).to.equal('123 456,79 €'); + expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('123 456,79 US$'); + expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('123 457 JP¥'); + expect(formatNumber(123456.789, currencySymbol('CZK'))).to.equal('123 456,79 Kč'); + }); + }); }); }); diff --git a/packages/localize/test/number/formatNumberToParts.test.js b/packages/localize/test/number/formatNumberToParts.test.js index 96aec103e..d3aa21a42 100644 --- a/packages/localize/test/number/formatNumberToParts.test.js +++ b/packages/localize/test/number/formatNumberToParts.test.js @@ -17,7 +17,34 @@ const stringifyParts = parts => parts.map(part => part.value).join(''); describe('formatNumberToParts', () => { afterEach(localizeTearDown); - describe("style: 'currency'", () => { + describe("style: 'currency symbol'", () => { + const specs = [ + ['en-GB', 'EUR', 1234.5, [c('€'), i('1'), g(','), i('234'), d('.'), f('50')]], + ['en-GB', 'USD', 1234.5, [c('US$'), i('1'), g(','), i('234'), d('.'), f('50')]], + ['nl-NL', 'EUR', 1234.5, [c('€'), l(' '), i('1'), g('.'), i('234'), d(','), f('50')]], + ['nl-NL', 'USD', 1234.5, [c('US$'), l(' '), i('1'), g('.'), i('234'), d(','), f('50')]], + ['nl-BE', 'EUR', 1234.5, [c('€'), l(' '), i('1'), g('.'), i('234'), d(','), f('50')]], + ['nl-BE', 'USD', 1234.5, [c('US$'), l(' '), i('1'), g('.'), i('234'), d(','), f('50')]], + ['fr-FR', 'EUR', 1234.5, [i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('€')]], + ['fr-FR', 'USD', 1234.5, [i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('$US')]], + ['fr-BE', 'EUR', 1234.5, [i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('€')]], + ['fr-BE', 'USD', 1234.5, [i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('$US')]], + ]; + + specs.forEach(([locale, currency, amount, expectedResult]) => { + it(`formats ${locale} ${currency} ${amount} as "${stringifyParts(expectedResult)}"`, () => { + localize.locale = locale; + expect( + formatNumberToParts(amount, { + style: 'currency', + currency, + }), + ).to.deep.equal(expectedResult); + }); + }); + }); + + describe("style: 'currency code'", () => { const specs = [ ['en-GB', 'EUR', 1234.5, [c('EUR'), l(' '), i('1'), g(','), i('234'), d('.'), f('50')]], ['en-GB', 'EUR', -1234.5, [m, c('EUR'), l(' '), i('1'), g(','), i('234'), d('.'), f('50')]],