Skip to content
This repository has been archived by the owner on Apr 14, 2021. It is now read-only.

[web-compat] fixes #221: normalize spacing around currency codes #254

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions src/11.numberformat.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,36 @@ function PartitionNumberPattern(numberFormat, x) {
ild = data.symbols[nums] || data.symbols.latn,
pattern;

// web-compat issue #221
function preprocessCurrencyCode(currencyCode, pattern, beginIndex, endIndex) {
/**
* http://cldr.unicode.org/translation/number-patterns
* This method solves a particular issues related to this note from the link above:
*
* ¤ This will be replaced by a currency symbol, such as $ or USD. Note: by default a space is automatically
* added between letters in a currency symbol and adjacent numbers. So you don't need to add a space between
* them if your language writes "$12" but "USD 12".
*
* Specifically, this polyfill doesn't support currency names (it is not part of the data we produce from CLDR), in
* which case we fallback to using the currency code. The same applies if the currency symbol is not available. This
* left us with two skeletons that require some preprocessing. The problem is that there are locales that do not have
* a symbol, and they already assume an space is needed between the ¤ and the value. On top of that, there are locales
* in which the symbol goes before or after the value. This function takes all that into consideration and tries to
* produce the most accurate currency code, value and non-breaking space combination by analysing the skeleton.
*
* Empirical rules:
* - If the skeleton already have the non-breaking space, we do nothing because it is a locale without a currency symbol
* - If the pattern ends on the last character, the space already comes from CLDR when needed and we do nothing.
*/
const NON_BREAKING_SPACE = '\xa0'; // Non-breakable space is char 0xa0 (160 dec)
if (pattern.length > endIndex + 1) {
if (pattern.charCodeAt(endIndex + 1) !== NON_BREAKING_SPACE) {
return currencyCode + NON_BREAKING_SPACE;
}
}
return currencyCode;
}

// 1. If x is not NaN and x < 0, then:
if (!isNaN(x) && x < 0) {
// a. Let x be -x.
Expand Down Expand Up @@ -685,17 +715,17 @@ function PartitionNumberPattern(numberFormat, x) {
// iii. If numberFormat.[[currencyDisplay]] is "code", then
if (internal['[[currencyDisplay]]'] === "code") {
// 1. Let cd be currency.
cd = currency;
cd = preprocessCurrencyCode(currency, pattern, beginIndex, endIndex);
}
// iv. Else if numberFormat.[[currencyDisplay]] is "symbol", then
else if (internal['[[currencyDisplay]]'] === "symbol") {
// 1. Let cd be an ILD string representing currency in short form. If the implementation does not have such a representation of currency, use currency itself.
cd = data.currencies[currency] || currency;
cd = data.currencies[currency] || preprocessCurrencyCode(currency, pattern, beginIndex, endIndex);
}
// v. Else if numberFormat.[[currencyDisplay]] is "name", then
else if (internal['[[currencyDisplay]]'] === "name") {
// 1. Let cd be an ILD string representing currency in long form. If the implementation does not have such a representation of currency, then use currency itself.
cd = currency;
cd = preprocessCurrencyCode(currency, pattern, beginIndex, endIndex);
}
// vi. Add new part record { [[type]]: "currency", [[value]]: cd } as a new element of the list result.
arrPush.call(result, { '[[type]]': 'currency', '[[value]]': cd });
Expand Down
30 changes: 30 additions & 0 deletions tests/sanity.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,33 @@ assert(new IntlPolyfill.DateTimeFormat('en-us', {
assert(new IntlPolyfill.DateTimeFormat('en-GB', {
hour: 'numeric'
}).format(new Date(2016, 0, 1, 6)), '6', 'single second should be 2-digit');

// issue #221
assert(new IntlPolyfill.NumberFormat('es-MX', {
style: 'currency',
currency: 'PYG',
}).format(12345), 'PYG\xa012,345', 'space after currency code when in fallback mode');
assert(new IntlPolyfill.NumberFormat('en-US', {
style: 'currency',
currencyDisplay: 'code',
currency: 'USD',
}).format(12345), 'USD\xa012,345.00', 'space after currency code when in fallback mode');
assert(new IntlPolyfill.NumberFormat('en-US', {
style: 'currency',
currencyDisplay: 'name',
currency: 'USD',
}).format(12345), 'USD\xa012,345.00', 'space after currency name when in fallback mode');
assert(new IntlPolyfill.NumberFormat('cs', {
style: 'currency',
currency: 'CSK',
}).format(12345), '12\xa0345,00\xa0Kčs', 'existing space before currency symbol should be preserved for locales with extended symbol');
assert(new IntlPolyfill.NumberFormat('cs', {
style: 'currency',
currency: 'CSK',
currencyDisplay: 'code',
}).format(12345), '12\xa0345,00\xa0CSK', 'existing space before currency code should be preserved for locales with extended symbol');
assert(new IntlPolyfill.NumberFormat('bn', {
style: 'currency',
currency: 'BDT',
currencyDisplay: 'name',
}).format(12345), '১২,৩৪৫.০০BDT', 'no space is needed before currency code when currency code is at the end of the skeleton');