diff --git a/README.md b/README.md
index 6ff062b18..2a4085ee7 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,18 @@ Checkout the list of possible variables in the [storybook colors story](https://
A few examples of a theme can be found in the [src/styles/themes/](src/styles/themes/) folder.
+## I18n
+
+Some components, e.g. `ec-amount-input` or `ec-donut` require `Intl` API to format values properly or to detect
+what is the decimal/grouping separator for a current locale. They both do that via [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat)
+which might have issues in some browsers for not having all locales set up properly. See the issues we discovered in this [PR](https://github.com/Ebury/chameleon/pull/156#issuecomment-623705733).
+If you need to support every single locale on the planet, we recommend to polyfill the Intl API using [intl](https://www.npmjs.com/package/intl) package
+so it's consistent across all browsers.
+
+```html
+
+```
+
### CSS variables polyfill
If you support **IE11** browser, you have to include the [CSS vars ponyfill](https://jhildenbiddle.github.io/css-vars-ponyfill/#/) when using our components.
diff --git a/package-lock.json b/package-lock.json
index b545024ad..d65c878b6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@ebury/chameleon-components",
- "version": "0.1.86",
+ "version": "0.1.87",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 120c6aef4..b123337bf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@ebury/chameleon-components",
- "version": "0.1.86",
+ "version": "0.1.87",
"main": "src/main.js",
"sideEffects": false,
"author": "Ebury Team (http://labs.ebury.rocks/)",
diff --git a/src/components/ec-amount-input/ec-amount-input.spec.js b/src/components/ec-amount-input/ec-amount-input.spec.js
index 4d2f402a2..30fa68a44 100644
--- a/src/components/ec-amount-input/ec-amount-input.spec.js
+++ b/src/components/ec-amount-input/ec-amount-input.spec.js
@@ -212,4 +212,3 @@ describe('EcAmountInput', () => {
expect(wrapper.vm.valueAmount).toBe(null);
});
});
-
diff --git a/src/components/ec-amount-input/ec-amount-input.story.js b/src/components/ec-amount-input/ec-amount-input.story.js
index 93386e060..47acd42ea 100644
--- a/src/components/ec-amount-input/ec-amount-input.story.js
+++ b/src/components/ec-amount-input/ec-amount-input.story.js
@@ -18,7 +18,7 @@ stories
default: boolean('Is Masked', false),
},
locale: {
- default: select('Locale', ['en', 'es', 'de-ch', 'jp'], 'en'),
+ default: select('Locale', ['en', 'es', 'de-ch', 'jp', 'sv'], 'en'),
},
currency: {
default: select('Currency', ['GBP', 'EUR', 'JPY', 'INR', 'USD', 'CAD'], 'GBP'),
diff --git a/src/components/ec-amount-input/ec-amount-input.vue b/src/components/ec-amount-input/ec-amount-input.vue
index ff92b940a..d35745a22 100644
--- a/src/components/ec-amount-input/ec-amount-input.vue
+++ b/src/components/ec-amount-input/ec-amount-input.vue
@@ -18,6 +18,7 @@
import EcInputField from '../ec-input-field';
import EcAmount from '../../directives/ec-amount/ec-amount';
import { format, unFormat } from '../../directives/ec-amount/utils';
+import { getDecimalSeparator, getGroupingSeparator } from '../../utils/number-format';
export default {
components: { EcInputField },
@@ -61,12 +62,6 @@ export default {
const options = new Intl.NumberFormat(this.locale, { style: 'currency', currency: this.currency || 'XYZ' }).resolvedOptions();
return options.maximumFractionDigits;
},
- groupingSeparator() {
- return this.getSeparator('group');
- },
- decimalSeparator() {
- return this.getSeparator('decimal');
- },
},
watch: {
value: {
@@ -78,7 +73,7 @@ export default {
}
this.formattedValue = newValue;
- this.unformattedValue = +(unFormat(newValue, this.groupingSeparator, this.decimalSeparator));
+ this.unformattedValue = +(unFormat(newValue, this.getGroupingSeparator(), this.getDecimalSeparator()));
} else {
if (newValue === this.unformattedValue) {
return;
@@ -102,7 +97,7 @@ export default {
},
formattedValue(newValue) {
if (newValue) {
- this.unformattedValue = +(unFormat(newValue, this.groupingSeparator, this.decimalSeparator));
+ this.unformattedValue = +(unFormat(newValue, this.getGroupingSeparator(), this.getDecimalSeparator()));
} else {
this.unformattedValue = null;
}
@@ -113,20 +108,19 @@ export default {
},
},
methods: {
- getSeparator(type) {
- const numberWithDecimalSeparator = 11111.1;
- return new Intl.NumberFormat(this.locale)
- .formatToParts(numberWithDecimalSeparator)
- .find(part => part.type === type)
- .value;
- },
getFormattingOptions() {
return {
precision: this.precision,
- decimalSeparator: this.decimalSeparator,
- groupingSeparator: this.groupingSeparator,
+ decimalSeparator: this.getDecimalSeparator(),
+ groupingSeparator: this.getGroupingSeparator(),
};
},
+ getGroupingSeparator() {
+ return getGroupingSeparator(this.locale);
+ },
+ getDecimalSeparator() {
+ return getDecimalSeparator(this.locale);
+ },
},
};
diff --git a/src/components/ec-currency-input/ec-currency-input.story.js b/src/components/ec-currency-input/ec-currency-input.story.js
index ac4325ed2..1fbcf55d0 100644
--- a/src/components/ec-currency-input/ec-currency-input.story.js
+++ b/src/components/ec-currency-input/ec-currency-input.story.js
@@ -24,7 +24,7 @@ stories
default: text('note', 'Select currency and set amount'),
},
locale: {
- default: select('locale', ['en', 'es', 'de-ch', 'jp'], 'en'),
+ default: select('locale', ['en', 'es', 'de-ch', 'jp', 'sv'], 'en'),
},
currenciesAreLoading: {
default: boolean('currencies are loading', false),
diff --git a/src/utils/number-format.js b/src/utils/number-format.js
new file mode 100644
index 000000000..85545a963
--- /dev/null
+++ b/src/utils/number-format.js
@@ -0,0 +1,46 @@
+export function getDecimalSeparator(locale) {
+ // we could just use formatToParts function but it's not supported on IE11
+ // and there's no polyfill for NumberFormat, only for DateTimeFormat
+ //
+ // return new Intl.NumberFormat(locale)
+ // .formatToParts(1111.1)
+ // .find(part => part.type === 'decimal')
+ // .value;
+ //
+ // so we have to improvised and force the formatter to format number 0.1 in the format
+ // 01 and then get the 2nd character from the string
+
+ const formatted = new Intl.NumberFormat(locale, {
+ useGrouping: false,
+ minimumIntegerDigits: 1,
+ minimumFractionDigits: 1,
+ maximumFractionDigits: 1,
+ minimumSignificantDigits: 1,
+ maximumSignificantDigits: 1,
+ }).format(0.1);
+ return formatted[1];
+}
+
+export function getGroupingSeparator(locale) {
+ // we could just use formatToParts function but it's not supported on IE11
+ // and there's no polyfill for NumberFormat, only for DateTimeFormat
+ //
+ // return new Intl.NumberFormat(locale)
+ // .formatToParts(1111.1)
+ // .find(part => part.type === 'group')
+ // .value;
+ //
+ // so we have to improvised and force the formatter to format number 1000 in the format
+ // 1000 and then get the 2nd character from the string
+
+ const formatted = new Intl.NumberFormat(locale, {
+ useGrouping: true,
+ minimumIntegerDigits: 5,
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 0,
+ minimumSignificantDigits: 5,
+ maximumSignificantDigits: 5,
+ }).format(10000);
+ return formatted[2];
+}
+
diff --git a/src/utils/number-format.spec.js b/src/utils/number-format.spec.js
new file mode 100644
index 000000000..1bddcc443
--- /dev/null
+++ b/src/utils/number-format.spec.js
@@ -0,0 +1,38 @@
+import fs from 'fs';
+import { getDecimalSeparator, getGroupingSeparator } from './number-format';
+
+describe('Utils', () => {
+ // get list of all locales in the world from intl polyfill.
+ const locales = fs.readdirSync('./node_modules/intl/locale-data/jsonp')
+ .filter(fileName => fileName.match(/\.js$/))
+ .map(fileName => fileName.replace(/\.js$/, ''));
+
+ function getExpectedSeparator(locale, type) {
+ return new Intl.NumberFormat(locale)
+ .formatToParts(1111.1)
+ .find(part => part.type === type)
+ .value;
+ }
+
+ describe('getDecimalSeparator ', () => {
+ for (const locale of locales) {
+ it(`should get decimal separator for locale ${locale}`, () => {
+ const expected = getExpectedSeparator(locale, 'decimal'); // get the decimal separator using method that works in every modern browser
+ const separator = getDecimalSeparator(locale); // get the decimal separator using our method
+ expect(separator.length).toBe(1);
+ expect(separator).toBe(expected);
+ });
+ }
+ });
+
+ describe('getGroupingSeparator', () => {
+ for (const locale of locales) {
+ it(`should get group separator for locale ${locale}`, () => {
+ const expected = getExpectedSeparator(locale, 'group'); // get the group separator using method that works in every modern browser
+ const separator = getGroupingSeparator(locale); // get the group separator using our method
+ expect(separator.length).toBe(1);
+ expect(separator).toBe(expected);
+ });
+ }
+ });
+});