Merge pull request #119 from bverhoef/fix/sign-format-currency
Return sign to the front of the currency formatted value in Dutch
This commit is contained in:
commit
938e445781
23 changed files with 856 additions and 908 deletions
|
|
@ -3,13 +3,11 @@ export { getDateFormatBasedOnLocale } from './src/date/getDateFormatBasedOnLocal
|
||||||
export { getMonthNames } from './src/date/getMonthNames.js';
|
export { getMonthNames } from './src/date/getMonthNames.js';
|
||||||
export { getWeekdayNames } from './src/date/getWeekdayNames.js';
|
export { getWeekdayNames } from './src/date/getWeekdayNames.js';
|
||||||
export { parseDate } from './src/date/parseDate.js';
|
export { parseDate } from './src/date/parseDate.js';
|
||||||
export {
|
export { formatNumber } from './src/number/formatNumber.js';
|
||||||
formatNumber,
|
export { formatNumberToParts } from './src/number/formatNumberToParts.js';
|
||||||
formatNumberToParts,
|
export { getDecimalSeparator } from './src/number/getDecimalSeparator.js';
|
||||||
getFractionDigits,
|
export { getFractionDigits } from './src/number/getFractionDigits.js';
|
||||||
getDecimalSeparator,
|
export { getGroupSeparator } from './src/number/getGroupSeparator.js';
|
||||||
getGroupSeparator,
|
|
||||||
} from './src/formatNumber.js';
|
|
||||||
export { localize, setLocalize } from './src/localize.js';
|
export { localize, setLocalize } from './src/localize.js';
|
||||||
export { LocalizeManager } from './src/LocalizeManager.js';
|
export { LocalizeManager } from './src/LocalizeManager.js';
|
||||||
export { LocalizeMixin } from './src/LocalizeMixin.js';
|
export { LocalizeMixin } from './src/LocalizeMixin.js';
|
||||||
|
|
|
||||||
|
|
@ -1,403 +0,0 @@
|
||||||
import { localize } from './localize.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the locale to use
|
|
||||||
*
|
|
||||||
* @param {string} locale Locale to override browser locale
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function getLocale(locale) {
|
|
||||||
if (locale) {
|
|
||||||
return locale;
|
|
||||||
}
|
|
||||||
if (localize && localize.locale) {
|
|
||||||
return localize.locale;
|
|
||||||
}
|
|
||||||
return 'en-GB';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Round the number based on the options
|
|
||||||
*
|
|
||||||
* @param {number} number
|
|
||||||
* @param {string} roundMode
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
function roundNumber(number, roundMode) {
|
|
||||||
switch (roundMode) {
|
|
||||||
case 'floor':
|
|
||||||
return Math.floor(number);
|
|
||||||
case 'ceiling':
|
|
||||||
return Math.ceil(number);
|
|
||||||
case 'round':
|
|
||||||
return Math.round(number);
|
|
||||||
default:
|
|
||||||
throw new Error('roundMode can only be round|floor|ceiling');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {Array} value
|
|
||||||
* @return {Array} value with forced "normal" space
|
|
||||||
*/
|
|
||||||
export function normalSpaces(value) {
|
|
||||||
// If non-breaking space (160) or narrow non-breaking space (8239) then return ' '
|
|
||||||
return value.charCodeAt(0) === 160 || value.charCodeAt(0) === 8239 ? ' ' : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To get the group separator
|
|
||||||
*
|
|
||||||
* @param {string} locale To override the browser locale
|
|
||||||
* @returns {Object} the separator
|
|
||||||
*/
|
|
||||||
export function getGroupSeparator(locale) {
|
|
||||||
const computedLocale = getLocale(locale);
|
|
||||||
const formattedNumber = Intl.NumberFormat(computedLocale, {
|
|
||||||
style: 'decimal',
|
|
||||||
minimumFractionDigits: 0,
|
|
||||||
}).format('1000');
|
|
||||||
return normalSpaces(formattedNumber[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To get the decimal separator
|
|
||||||
*
|
|
||||||
* @param {string} locale To override the browser locale
|
|
||||||
* @returns {Object} the separator
|
|
||||||
*/
|
|
||||||
export function getDecimalSeparator(locale) {
|
|
||||||
const computedLocale = getLocale(locale);
|
|
||||||
const formattedNumber = Intl.NumberFormat(computedLocale, {
|
|
||||||
style: 'decimal',
|
|
||||||
minimumFractionDigits: 1,
|
|
||||||
}).format('1');
|
|
||||||
return formattedNumber[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When number is NaN we should return an empty string or returnIfNaN param
|
|
||||||
*
|
|
||||||
* @param {string} returnIfNaN
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
function emptyStringWhenNumberNan(returnIfNaN) {
|
|
||||||
const stringToReturn = returnIfNaN || localize.formatNumberOptions.returnIfNaN;
|
|
||||||
return stringToReturn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For Dutch and Belgian amounts the currency should be at the end of the string
|
|
||||||
*
|
|
||||||
* @param {Array} formattedParts
|
|
||||||
* @returns {Array}
|
|
||||||
*/
|
|
||||||
function forceCurrencyToEnd(formattedParts) {
|
|
||||||
if (formattedParts[0].type === 'currency') {
|
|
||||||
const moveCur = formattedParts.splice(0, 1);
|
|
||||||
const moveLit = formattedParts.splice(0, 1);
|
|
||||||
formattedParts.push(moveLit[0]);
|
|
||||||
formattedParts.push(moveCur[0]);
|
|
||||||
} else if (formattedParts[0].type === 'minusSign' && formattedParts[1].type === 'currency') {
|
|
||||||
const moveCur = formattedParts.splice(1, 1);
|
|
||||||
const moveLit = formattedParts.splice(1, 1);
|
|
||||||
formattedParts.push(moveLit[0]);
|
|
||||||
formattedParts.push(moveCur[0]);
|
|
||||||
}
|
|
||||||
return formattedParts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When in some locales there is no space between currency and amount it is added
|
|
||||||
*
|
|
||||||
* @param {Array} formattedParts
|
|
||||||
* @param {Object} options
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
function forceSpaceBetweenCurrencyCodeAndNumber(formattedParts, options) {
|
|
||||||
const numberOfParts = formattedParts.length;
|
|
||||||
const literalObject = { type: 'literal', value: ' ' };
|
|
||||||
if (numberOfParts > 1 && options && options.currency && options.currencyDisplay === 'code') {
|
|
||||||
if (formattedParts[0].type === 'currency' && formattedParts[1].type !== 'literal') {
|
|
||||||
// currency in front of a number: EUR 1.00
|
|
||||||
formattedParts.splice(1, 0, literalObject);
|
|
||||||
} else if (
|
|
||||||
formattedParts[0].type === 'minusSign' &&
|
|
||||||
formattedParts[1].type === 'currency' &&
|
|
||||||
formattedParts[2].type !== 'literal'
|
|
||||||
) {
|
|
||||||
// currency in front of a negative number: -EUR 1.00
|
|
||||||
formattedParts.splice(2, 0, literalObject);
|
|
||||||
} else if (
|
|
||||||
formattedParts[numberOfParts - 1].type === 'currency' &&
|
|
||||||
formattedParts[numberOfParts - 2].type !== 'literal'
|
|
||||||
) {
|
|
||||||
// currency in behind a number: 1.00 EUR || -1.00 EUR
|
|
||||||
formattedParts.splice(numberOfParts - 1, 0, literalObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return formattedParts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add separators when they are not present
|
|
||||||
*
|
|
||||||
* @param {Array} formattedParts
|
|
||||||
* @param {string} groupSeparator
|
|
||||||
* @returns {Array}
|
|
||||||
*/
|
|
||||||
function forceAddGroupSeparators(formattedParts, groupSeparator) {
|
|
||||||
let concatArray = [];
|
|
||||||
if (formattedParts[0].type === 'integer') {
|
|
||||||
const getInteger = formattedParts.splice(0, 1);
|
|
||||||
const numberOfDigits = getInteger[0].value.length;
|
|
||||||
const mod3 = numberOfDigits % 3;
|
|
||||||
const groups = Math.floor(numberOfDigits / 3);
|
|
||||||
const numberArray = [];
|
|
||||||
let numberOfGroups = 0;
|
|
||||||
let numberPart = '';
|
|
||||||
let firstGroup = false;
|
|
||||||
// Loop through the integer
|
|
||||||
for (let i = 0; i < numberOfDigits; i += 1) {
|
|
||||||
numberPart += getInteger[0].value[i];
|
|
||||||
// Create first grouping which is < 3
|
|
||||||
if (numberPart.length === mod3 && firstGroup === false) {
|
|
||||||
numberArray.push({ type: 'integer', value: numberPart });
|
|
||||||
if (numberOfDigits > 3) {
|
|
||||||
numberArray.push({ type: 'group', value: groupSeparator });
|
|
||||||
}
|
|
||||||
numberPart = '';
|
|
||||||
firstGroup = true;
|
|
||||||
// Create groupings of 3
|
|
||||||
} else if (numberPart.length === 3 && i < numberOfDigits - 1) {
|
|
||||||
numberOfGroups += 1;
|
|
||||||
numberArray.push({ type: 'integer', value: numberPart });
|
|
||||||
if (numberOfGroups !== groups) {
|
|
||||||
numberArray.push({ type: 'group', value: groupSeparator });
|
|
||||||
}
|
|
||||||
numberPart = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
numberArray.push({ type: 'integer', value: numberPart });
|
|
||||||
concatArray = numberArray.concat(formattedParts);
|
|
||||||
}
|
|
||||||
return concatArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Array} formattedParts
|
|
||||||
* @return {Array} parts with forced "normal" spaces
|
|
||||||
*/
|
|
||||||
function forceNormalSpaces(formattedParts) {
|
|
||||||
const result = [];
|
|
||||||
formattedParts.forEach(part => {
|
|
||||||
result.push({
|
|
||||||
type: part.type,
|
|
||||||
value: normalSpaces(part.value),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function forceYenSymbol(formattedParts, options) {
|
|
||||||
const result = formattedParts;
|
|
||||||
const numberOfParts = result.length;
|
|
||||||
// Change the symbol from JPY to ¥, due to bug in Chrome
|
|
||||||
if (
|
|
||||||
numberOfParts > 1 &&
|
|
||||||
options &&
|
|
||||||
options.currency === 'JPY' &&
|
|
||||||
options.currencyDisplay === 'symbol'
|
|
||||||
) {
|
|
||||||
result[numberOfParts - 1].value = '¥';
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function with all fixes on localize
|
|
||||||
*
|
|
||||||
* @param {Array} formattedParts
|
|
||||||
* @param {Object} options
|
|
||||||
* @param {string} _locale
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
function normalizeIntl(formattedParts, options, _locale) {
|
|
||||||
let normalize = forceNormalSpaces(formattedParts, options);
|
|
||||||
// Dutch and Belgian currency must be moved to end of number
|
|
||||||
if (options && options.style === 'currency') {
|
|
||||||
if (_locale === 'nl-NL' || _locale.slice(-2) === 'BE') {
|
|
||||||
normalize = forceCurrencyToEnd(normalize);
|
|
||||||
}
|
|
||||||
// Add group separator for Bulgarian locale
|
|
||||||
if (_locale === 'bg-BG') {
|
|
||||||
normalize = forceAddGroupSeparators(normalize, getGroupSeparator());
|
|
||||||
}
|
|
||||||
// Force space between currency code and number
|
|
||||||
if (_locale === 'en-GB' || _locale === 'en-US' || _locale === 'en-AU') {
|
|
||||||
normalize = forceSpaceBetweenCurrencyCodeAndNumber(normalize, options);
|
|
||||||
}
|
|
||||||
// Force missing Japanese Yen symbol
|
|
||||||
if (_locale === 'fr-FR' || _locale === 'fr-BE') {
|
|
||||||
normalize = forceYenSymbol(normalize, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return normalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Splits a number up in parts for integer, fraction, group, literal, decimal and currency.
|
|
||||||
*
|
|
||||||
* @param {number} number Number to split up
|
|
||||||
* @param {Object} options Intl options are available extended by roundMode
|
|
||||||
* @returns {Array} Array with parts
|
|
||||||
*/
|
|
||||||
export function formatNumberToParts(number, options) {
|
|
||||||
let parsedNumber = typeof number === 'string' ? parseFloat(number) : number;
|
|
||||||
const computedLocale = getLocale(options && options.locale);
|
|
||||||
// when parsedNumber is not a number we should return an empty string or returnIfNaN
|
|
||||||
if (Number.isNaN(parsedNumber)) {
|
|
||||||
return emptyStringWhenNumberNan(options && options.returnIfNaN);
|
|
||||||
}
|
|
||||||
// If roundMode is given the number is rounded based upon the mode
|
|
||||||
if (options && options.roundMode) {
|
|
||||||
parsedNumber = roundNumber(number, options.roundMode);
|
|
||||||
}
|
|
||||||
let formattedParts = [];
|
|
||||||
const formattedNumber = Intl.NumberFormat(computedLocale, options).format(parsedNumber);
|
|
||||||
const regexSymbol = /[A-Z.,\s0-9]/;
|
|
||||||
const regexCode = /[A-Z]/;
|
|
||||||
const regexMinusSign = /[-]/;
|
|
||||||
const regexNum = /[0-9]/;
|
|
||||||
const regexSeparator = /[.,]/;
|
|
||||||
const regexSpace = /[\s]/;
|
|
||||||
let currencyCode = '';
|
|
||||||
let numberPart = '';
|
|
||||||
let fraction = false;
|
|
||||||
for (let i = 0; i < formattedNumber.length; i += 1) {
|
|
||||||
// detect minusSign
|
|
||||||
if (regexMinusSign.test(formattedNumber[i])) {
|
|
||||||
formattedParts.push({ type: 'minusSign', value: formattedNumber[i] });
|
|
||||||
}
|
|
||||||
// detect numbers
|
|
||||||
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 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 = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// detect dot and comma separators
|
|
||||||
if (regexSeparator.test(formattedNumber[i])) {
|
|
||||||
// Write number grouping
|
|
||||||
if (numberPart) {
|
|
||||||
formattedParts.push({ type: 'integer', value: numberPart });
|
|
||||||
numberPart = '';
|
|
||||||
}
|
|
||||||
const decimal = getDecimalSeparator();
|
|
||||||
if (formattedNumber[i] === decimal) {
|
|
||||||
formattedParts.push({ type: 'decimal', value: formattedNumber[i] });
|
|
||||||
fraction = true;
|
|
||||||
} else {
|
|
||||||
formattedParts.push({ type: 'group', value: formattedNumber[i] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// detect literals (empty spaces) or space group separator
|
|
||||||
if (regexSpace.test(formattedNumber[i])) {
|
|
||||||
const group = getGroupSeparator();
|
|
||||||
const hasNumberPart = !!numberPart;
|
|
||||||
// Write number grouping
|
|
||||||
if (numberPart && !fraction) {
|
|
||||||
formattedParts.push({ type: 'integer', value: numberPart });
|
|
||||||
numberPart = '';
|
|
||||||
} else if (numberPart) {
|
|
||||||
formattedParts.push({ type: 'fraction', value: numberPart });
|
|
||||||
numberPart = '';
|
|
||||||
}
|
|
||||||
// If space equals the group separator it gets type group
|
|
||||||
if (normalSpaces(formattedNumber[i]) === group && hasNumberPart && !fraction) {
|
|
||||||
formattedParts.push({ type: 'group', value: formattedNumber[i] });
|
|
||||||
} else {
|
|
||||||
formattedParts.push({ type: 'literal', value: formattedNumber[i] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Numbers after the decimal sign are fractions, write the last
|
|
||||||
// fractions at the end of the number
|
|
||||||
if (fraction === true && i === formattedNumber.length - 1) {
|
|
||||||
// write last number part
|
|
||||||
if (numberPart) {
|
|
||||||
formattedParts.push({ type: 'fraction', value: numberPart });
|
|
||||||
}
|
|
||||||
// If there are no fractions but we reached the end write the numberpart as integer
|
|
||||||
} else if (i === formattedNumber.length - 1 && numberPart) {
|
|
||||||
formattedParts.push({ type: 'integer', value: numberPart });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
formattedParts = normalizeIntl(formattedParts, options, computedLocale);
|
|
||||||
return formattedParts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @example
|
|
||||||
* getFractionDigits('JOD'); // return 3
|
|
||||||
*
|
|
||||||
* @param {string} currency Currency code e.g. EUR
|
|
||||||
* @return {number} fraction for the given currency
|
|
||||||
*/
|
|
||||||
export function getFractionDigits(currency = 'EUR') {
|
|
||||||
const parts = formatNumberToParts(123, {
|
|
||||||
style: 'currency',
|
|
||||||
currency,
|
|
||||||
});
|
|
||||||
const [fractionPart] = parts.filter(part => part.type === 'fraction');
|
|
||||||
return fractionPart ? fractionPart.value.length : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats a number based on locale and options. It uses Intl for the formatting.
|
|
||||||
*
|
|
||||||
* @param {number} number Number to be formatted
|
|
||||||
* @param {Object} options Intl options are available extended by roundMode
|
|
||||||
* @returns {*} Formatted number
|
|
||||||
*/
|
|
||||||
export function formatNumber(number, options) {
|
|
||||||
if (number === undefined || number === null) return '';
|
|
||||||
const formattedToParts = formatNumberToParts(number, options);
|
|
||||||
// If number is not a number
|
|
||||||
if (
|
|
||||||
formattedToParts === (options && options.returnIfNaN) ||
|
|
||||||
formattedToParts === localize.formatNumberOptions.returnIfNaN
|
|
||||||
) {
|
|
||||||
return formattedToParts;
|
|
||||||
}
|
|
||||||
let printNumberOfParts = '';
|
|
||||||
// update numberOfParts because there may be some parts added
|
|
||||||
const numberOfParts = formattedToParts && formattedToParts.length;
|
|
||||||
for (let i = 0; i < numberOfParts; i += 1) {
|
|
||||||
printNumberOfParts += formattedToParts[i].value;
|
|
||||||
}
|
|
||||||
return printNumberOfParts;
|
|
||||||
}
|
|
||||||
12
packages/localize/src/number/emptyStringWhenNumberNan.js
Normal file
12
packages/localize/src/number/emptyStringWhenNumberNan.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { localize } from '../localize.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When number is NaN we should return an empty string or returnIfNaN param
|
||||||
|
*
|
||||||
|
* @param {string} returnIfNaN
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function emptyStringWhenNumberNan(returnIfNaN) {
|
||||||
|
const stringToReturn = returnIfNaN || localize.formatNumberOptions.returnIfNaN;
|
||||||
|
return stringToReturn;
|
||||||
|
}
|
||||||
44
packages/localize/src/number/forceAddGroupSeparators.js
Normal file
44
packages/localize/src/number/forceAddGroupSeparators.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* Add separators when they are not present
|
||||||
|
*
|
||||||
|
* @param {Array} formattedParts
|
||||||
|
* @param {string} groupSeparator
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
export function forceAddGroupSeparators(formattedParts, groupSeparator) {
|
||||||
|
let concatArray = [];
|
||||||
|
if (formattedParts[0].type === 'integer') {
|
||||||
|
const getInteger = formattedParts.splice(0, 1);
|
||||||
|
const numberOfDigits = getInteger[0].value.length;
|
||||||
|
const mod3 = numberOfDigits % 3;
|
||||||
|
const groups = Math.floor(numberOfDigits / 3);
|
||||||
|
const numberArray = [];
|
||||||
|
let numberOfGroups = 0;
|
||||||
|
let numberPart = '';
|
||||||
|
let firstGroup = false;
|
||||||
|
// Loop through the integer
|
||||||
|
for (let i = 0; i < numberOfDigits; i += 1) {
|
||||||
|
numberPart += getInteger[0].value[i];
|
||||||
|
// Create first grouping which is < 3
|
||||||
|
if (numberPart.length === mod3 && firstGroup === false) {
|
||||||
|
numberArray.push({ type: 'integer', value: numberPart });
|
||||||
|
if (numberOfDigits > 3) {
|
||||||
|
numberArray.push({ type: 'group', value: groupSeparator });
|
||||||
|
}
|
||||||
|
numberPart = '';
|
||||||
|
firstGroup = true;
|
||||||
|
// Create groupings of 3
|
||||||
|
} else if (numberPart.length === 3 && i < numberOfDigits - 1) {
|
||||||
|
numberOfGroups += 1;
|
||||||
|
numberArray.push({ type: 'integer', value: numberPart });
|
||||||
|
if (numberOfGroups !== groups) {
|
||||||
|
numberArray.push({ type: 'group', value: groupSeparator });
|
||||||
|
}
|
||||||
|
numberPart = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numberArray.push({ type: 'integer', value: numberPart });
|
||||||
|
concatArray = numberArray.concat(formattedParts);
|
||||||
|
}
|
||||||
|
return concatArray;
|
||||||
|
}
|
||||||
20
packages/localize/src/number/forceCurrencyToEnd.js
Normal file
20
packages/localize/src/number/forceCurrencyToEnd.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* For Dutch and Belgian amounts the currency should be at the end of the string
|
||||||
|
*
|
||||||
|
* @param {Array} formattedParts
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
export function forceCurrencyToEnd(formattedParts) {
|
||||||
|
if (formattedParts[0].type === 'currency') {
|
||||||
|
const moveCur = formattedParts.splice(0, 1);
|
||||||
|
const moveLit = formattedParts.splice(0, 1);
|
||||||
|
formattedParts.push(moveLit[0]);
|
||||||
|
formattedParts.push(moveCur[0]);
|
||||||
|
} else if (formattedParts[0].type === 'minusSign' && formattedParts[1].type === 'currency') {
|
||||||
|
const moveCur = formattedParts.splice(1, 1);
|
||||||
|
const moveLit = formattedParts.splice(1, 1);
|
||||||
|
formattedParts.push(moveLit[0]);
|
||||||
|
formattedParts.push(moveCur[0]);
|
||||||
|
}
|
||||||
|
return formattedParts;
|
||||||
|
}
|
||||||
16
packages/localize/src/number/forceNormalSpaces.js
Normal file
16
packages/localize/src/number/forceNormalSpaces.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { normalSpaces } from './normalSpaces.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array} formattedParts
|
||||||
|
* @return {Array} parts with forced "normal" spaces
|
||||||
|
*/
|
||||||
|
export function forceNormalSpaces(formattedParts) {
|
||||||
|
const result = [];
|
||||||
|
formattedParts.forEach(part => {
|
||||||
|
result.push({
|
||||||
|
type: part.type,
|
||||||
|
value: normalSpaces(part.value),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* When in some locales there is no space between currency and amount it is added
|
||||||
|
*
|
||||||
|
* @param {Array} formattedParts
|
||||||
|
* @param {Object} options
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function forceSpaceBetweenCurrencyCodeAndNumber(formattedParts, options) {
|
||||||
|
const numberOfParts = formattedParts.length;
|
||||||
|
const literalObject = { type: 'literal', value: ' ' };
|
||||||
|
if (numberOfParts > 1 && options && options.currency && options.currencyDisplay === 'code') {
|
||||||
|
if (formattedParts[0].type === 'currency' && formattedParts[1].type !== 'literal') {
|
||||||
|
// currency in front of a number: EUR 1.00
|
||||||
|
formattedParts.splice(1, 0, literalObject);
|
||||||
|
} else if (
|
||||||
|
formattedParts[0].type === 'minusSign' &&
|
||||||
|
formattedParts[1].type === 'currency' &&
|
||||||
|
formattedParts[2].type !== 'literal'
|
||||||
|
) {
|
||||||
|
// currency in front of a negative number: -EUR 1.00
|
||||||
|
formattedParts.splice(2, 0, literalObject);
|
||||||
|
} else if (
|
||||||
|
formattedParts[numberOfParts - 1].type === 'currency' &&
|
||||||
|
formattedParts[numberOfParts - 2].type !== 'literal'
|
||||||
|
) {
|
||||||
|
// currency in behind a number: 1.00 EUR || -1.00 EUR
|
||||||
|
formattedParts.splice(numberOfParts - 1, 0, literalObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formattedParts;
|
||||||
|
}
|
||||||
14
packages/localize/src/number/forceYenSymbol.js
Normal file
14
packages/localize/src/number/forceYenSymbol.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
export function forceYenSymbol(formattedParts, options) {
|
||||||
|
const result = formattedParts;
|
||||||
|
const numberOfParts = result.length;
|
||||||
|
// Change the symbol from JPY to ¥, due to bug in Chrome
|
||||||
|
if (
|
||||||
|
numberOfParts > 1 &&
|
||||||
|
options &&
|
||||||
|
options.currency === 'JPY' &&
|
||||||
|
options.currencyDisplay === 'symbol'
|
||||||
|
) {
|
||||||
|
result[numberOfParts - 1].value = '¥';
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
28
packages/localize/src/number/formatNumber.js
Normal file
28
packages/localize/src/number/formatNumber.js
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { localize } from '../localize.js';
|
||||||
|
import { formatNumberToParts } from './formatNumberToParts.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a number based on locale and options. It uses Intl for the formatting.
|
||||||
|
*
|
||||||
|
* @param {number} number Number to be formatted
|
||||||
|
* @param {Object} options Intl options are available extended by roundMode
|
||||||
|
* @returns {*} Formatted number
|
||||||
|
*/
|
||||||
|
export function formatNumber(number, options) {
|
||||||
|
if (number === undefined || number === null) return '';
|
||||||
|
const formattedToParts = formatNumberToParts(number, options);
|
||||||
|
// If number is not a number
|
||||||
|
if (
|
||||||
|
formattedToParts === (options && options.returnIfNaN) ||
|
||||||
|
formattedToParts === localize.formatNumberOptions.returnIfNaN
|
||||||
|
) {
|
||||||
|
return formattedToParts;
|
||||||
|
}
|
||||||
|
let printNumberOfParts = '';
|
||||||
|
// update numberOfParts because there may be some parts added
|
||||||
|
const numberOfParts = formattedToParts && formattedToParts.length;
|
||||||
|
for (let i = 0; i < numberOfParts; i += 1) {
|
||||||
|
printNumberOfParts += formattedToParts[i].value;
|
||||||
|
}
|
||||||
|
return printNumberOfParts;
|
||||||
|
}
|
||||||
123
packages/localize/src/number/formatNumberToParts.js
Normal file
123
packages/localize/src/number/formatNumberToParts.js
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
import { emptyStringWhenNumberNan } from './emptyStringWhenNumberNan.js';
|
||||||
|
import { getDecimalSeparator } from './getDecimalSeparator.js';
|
||||||
|
import { getGroupSeparator } from './getGroupSeparator.js';
|
||||||
|
import { getLocale } from './getLocale.js';
|
||||||
|
import { normalizeIntl } from './normalizeIntl.js';
|
||||||
|
import { normalSpaces } from './normalSpaces.js';
|
||||||
|
import { roundNumber } from './roundNumber.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a number up in parts for integer, fraction, group, literal, decimal and currency.
|
||||||
|
*
|
||||||
|
* @param {number} number Number to split up
|
||||||
|
* @param {Object} options Intl options are available extended by roundMode
|
||||||
|
* @returns {Array} Array with parts
|
||||||
|
*/
|
||||||
|
export function formatNumberToParts(number, options) {
|
||||||
|
let parsedNumber = typeof number === 'string' ? parseFloat(number) : number;
|
||||||
|
const computedLocale = getLocale(options && options.locale);
|
||||||
|
// when parsedNumber is not a number we should return an empty string or returnIfNaN
|
||||||
|
if (Number.isNaN(parsedNumber)) {
|
||||||
|
return emptyStringWhenNumberNan(options && options.returnIfNaN);
|
||||||
|
}
|
||||||
|
// If roundMode is given the number is rounded based upon the mode
|
||||||
|
if (options && options.roundMode) {
|
||||||
|
parsedNumber = roundNumber(number, options.roundMode);
|
||||||
|
}
|
||||||
|
let formattedParts = [];
|
||||||
|
const formattedNumber = Intl.NumberFormat(computedLocale, options).format(parsedNumber);
|
||||||
|
const regexSymbol = /[A-Z.,\s0-9]/;
|
||||||
|
const regexCode = /[A-Z]/;
|
||||||
|
const regexMinusSign = /[-]/;
|
||||||
|
const regexNum = /[0-9]/;
|
||||||
|
const regexSeparator = /[.,]/;
|
||||||
|
const regexSpace = /[\s]/;
|
||||||
|
let currencyCode = '';
|
||||||
|
let numberPart = '';
|
||||||
|
let fraction = false;
|
||||||
|
for (let i = 0; i < formattedNumber.length; i += 1) {
|
||||||
|
// detect minusSign
|
||||||
|
if (regexMinusSign.test(formattedNumber[i])) {
|
||||||
|
formattedParts.push({ type: 'minusSign', value: formattedNumber[i] });
|
||||||
|
}
|
||||||
|
// detect numbers
|
||||||
|
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 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 = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// detect dot and comma separators
|
||||||
|
if (regexSeparator.test(formattedNumber[i])) {
|
||||||
|
// Write number grouping
|
||||||
|
if (numberPart) {
|
||||||
|
formattedParts.push({ type: 'integer', value: numberPart });
|
||||||
|
numberPart = '';
|
||||||
|
}
|
||||||
|
const decimal = getDecimalSeparator();
|
||||||
|
if (formattedNumber[i] === decimal) {
|
||||||
|
formattedParts.push({ type: 'decimal', value: formattedNumber[i] });
|
||||||
|
fraction = true;
|
||||||
|
} else {
|
||||||
|
formattedParts.push({ type: 'group', value: formattedNumber[i] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// detect literals (empty spaces) or space group separator
|
||||||
|
if (regexSpace.test(formattedNumber[i])) {
|
||||||
|
const group = getGroupSeparator();
|
||||||
|
const hasNumberPart = !!numberPart;
|
||||||
|
// Write number grouping
|
||||||
|
if (numberPart && !fraction) {
|
||||||
|
formattedParts.push({ type: 'integer', value: numberPart });
|
||||||
|
numberPart = '';
|
||||||
|
} else if (numberPart) {
|
||||||
|
formattedParts.push({ type: 'fraction', value: numberPart });
|
||||||
|
numberPart = '';
|
||||||
|
}
|
||||||
|
// If space equals the group separator it gets type group
|
||||||
|
if (normalSpaces(formattedNumber[i]) === group && hasNumberPart && !fraction) {
|
||||||
|
formattedParts.push({ type: 'group', value: formattedNumber[i] });
|
||||||
|
} else {
|
||||||
|
formattedParts.push({ type: 'literal', value: formattedNumber[i] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Numbers after the decimal sign are fractions, write the last
|
||||||
|
// fractions at the end of the number
|
||||||
|
if (fraction === true && i === formattedNumber.length - 1) {
|
||||||
|
// write last number part
|
||||||
|
if (numberPart) {
|
||||||
|
formattedParts.push({ type: 'fraction', value: numberPart });
|
||||||
|
}
|
||||||
|
// If there are no fractions but we reached the end write the numberpart as integer
|
||||||
|
} else if (i === formattedNumber.length - 1 && numberPart) {
|
||||||
|
formattedParts.push({ type: 'integer', value: numberPart });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formattedParts = normalizeIntl(formattedParts, options, computedLocale);
|
||||||
|
return formattedParts;
|
||||||
|
}
|
||||||
16
packages/localize/src/number/getDecimalSeparator.js
Normal file
16
packages/localize/src/number/getDecimalSeparator.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { getLocale } from './getLocale.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To get the decimal separator
|
||||||
|
*
|
||||||
|
* @param {string} locale To override the browser locale
|
||||||
|
* @returns {Object} the separator
|
||||||
|
*/
|
||||||
|
export function getDecimalSeparator(locale) {
|
||||||
|
const computedLocale = getLocale(locale);
|
||||||
|
const formattedNumber = Intl.NumberFormat(computedLocale, {
|
||||||
|
style: 'decimal',
|
||||||
|
minimumFractionDigits: 1,
|
||||||
|
}).format('1');
|
||||||
|
return formattedNumber[1];
|
||||||
|
}
|
||||||
17
packages/localize/src/number/getFractionDigits.js
Normal file
17
packages/localize/src/number/getFractionDigits.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { formatNumberToParts } from './formatNumberToParts.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @example
|
||||||
|
* getFractionDigits('JOD'); // return 3
|
||||||
|
*
|
||||||
|
* @param {string} currency Currency code e.g. EUR
|
||||||
|
* @return {number} fraction for the given currency
|
||||||
|
*/
|
||||||
|
export function getFractionDigits(currency = 'EUR') {
|
||||||
|
const parts = formatNumberToParts(123, {
|
||||||
|
style: 'currency',
|
||||||
|
currency,
|
||||||
|
});
|
||||||
|
const [fractionPart] = parts.filter(part => part.type === 'fraction');
|
||||||
|
return fractionPart ? fractionPart.value.length : 0;
|
||||||
|
}
|
||||||
17
packages/localize/src/number/getGroupSeparator.js
Normal file
17
packages/localize/src/number/getGroupSeparator.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { getLocale } from './getLocale.js';
|
||||||
|
import { normalSpaces } from './normalSpaces.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To get the group separator
|
||||||
|
*
|
||||||
|
* @param {string} locale To override the browser locale
|
||||||
|
* @returns {Object} the separator
|
||||||
|
*/
|
||||||
|
export function getGroupSeparator(locale) {
|
||||||
|
const computedLocale = getLocale(locale);
|
||||||
|
const formattedNumber = Intl.NumberFormat(computedLocale, {
|
||||||
|
style: 'decimal',
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
}).format('1000');
|
||||||
|
return normalSpaces(formattedNumber[1]);
|
||||||
|
}
|
||||||
17
packages/localize/src/number/getLocale.js
Normal file
17
packages/localize/src/number/getLocale.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { localize } from '../localize.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the locale to use
|
||||||
|
*
|
||||||
|
* @param {string} locale Locale to override browser locale
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function getLocale(locale) {
|
||||||
|
if (locale) {
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
if (localize && localize.locale) {
|
||||||
|
return localize.locale;
|
||||||
|
}
|
||||||
|
return 'en-GB';
|
||||||
|
}
|
||||||
8
packages/localize/src/number/normalSpaces.js
Normal file
8
packages/localize/src/number/normalSpaces.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* @param {Array} value
|
||||||
|
* @return {Array} value with forced "normal" space
|
||||||
|
*/
|
||||||
|
export function normalSpaces(value) {
|
||||||
|
// If non-breaking space (160) or narrow non-breaking space (8239) then return ' '
|
||||||
|
return value.charCodeAt(0) === 160 || value.charCodeAt(0) === 8239 ? ' ' : value;
|
||||||
|
}
|
||||||
37
packages/localize/src/number/normalizeIntl.js
Normal file
37
packages/localize/src/number/normalizeIntl.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { getGroupSeparator } from './getGroupSeparator.js';
|
||||||
|
import { forceAddGroupSeparators } from './forceAddGroupSeparators.js';
|
||||||
|
import { forceCurrencyToEnd } from './forceCurrencyToEnd.js';
|
||||||
|
import { forceNormalSpaces } from './forceNormalSpaces.js';
|
||||||
|
import { forceSpaceBetweenCurrencyCodeAndNumber } from './forceSpaceBetweenCurrencyCodeAndNumber.js';
|
||||||
|
import { forceYenSymbol } from './forceYenSymbol.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function with all fixes on localize
|
||||||
|
*
|
||||||
|
* @param {Array} formattedParts
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {string} _locale
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function normalizeIntl(formattedParts, options, _locale) {
|
||||||
|
let normalize = forceNormalSpaces(formattedParts, options);
|
||||||
|
// Dutch and Belgian currency must be moved to end of number
|
||||||
|
if (options && options.style === 'currency') {
|
||||||
|
if (options.currencyDisplay === 'code' && _locale.slice(0, 2) === 'nl') {
|
||||||
|
normalize = forceCurrencyToEnd(normalize);
|
||||||
|
}
|
||||||
|
// Add group separator for Bulgarian locale
|
||||||
|
if (_locale === 'bg-BG') {
|
||||||
|
normalize = forceAddGroupSeparators(normalize, getGroupSeparator());
|
||||||
|
}
|
||||||
|
// Force space between currency code and number
|
||||||
|
if (_locale === 'en-GB' || _locale === 'en-US' || _locale === 'en-AU') {
|
||||||
|
normalize = forceSpaceBetweenCurrencyCodeAndNumber(normalize, options);
|
||||||
|
}
|
||||||
|
// Force missing Japanese Yen symbol
|
||||||
|
if (_locale === 'fr-FR' || _locale === 'fr-BE') {
|
||||||
|
normalize = forceYenSymbol(normalize, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalize;
|
||||||
|
}
|
||||||
19
packages/localize/src/number/roundNumber.js
Normal file
19
packages/localize/src/number/roundNumber.js
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Round the number based on the options
|
||||||
|
*
|
||||||
|
* @param {number} number
|
||||||
|
* @param {string} roundMode
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function roundNumber(number, roundMode) {
|
||||||
|
switch (roundMode) {
|
||||||
|
case 'floor':
|
||||||
|
return Math.floor(number);
|
||||||
|
case 'ceiling':
|
||||||
|
return Math.ceil(number);
|
||||||
|
case 'round':
|
||||||
|
return Math.round(number);
|
||||||
|
default:
|
||||||
|
throw new Error('roundMode can only be round|floor|ceiling');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,498 +0,0 @@
|
||||||
import { expect } from '@open-wc/testing';
|
|
||||||
|
|
||||||
import { localize } from '../src/localize.js';
|
|
||||||
|
|
||||||
import {
|
|
||||||
formatNumber,
|
|
||||||
formatNumberToParts,
|
|
||||||
getGroupSeparator,
|
|
||||||
getDecimalSeparator,
|
|
||||||
getFractionDigits,
|
|
||||||
} from '../src/formatNumber.js';
|
|
||||||
|
|
||||||
describe('formatNumber', () => {
|
|
||||||
afterEach(() => {
|
|
||||||
// makes sure that between tests the localization is reset to default state
|
|
||||||
document.documentElement.lang = 'en-GB';
|
|
||||||
});
|
|
||||||
|
|
||||||
it('displays the appropriate amount of decimal places based on currencies spec http://www.currency-iso.org/en/home/tables/table-a1.html', async () => {
|
|
||||||
const currencyCode = { style: 'currency', currencyDisplay: 'code' };
|
|
||||||
const currencySymbol = { style: 'currency', currencyDisplay: 'symbol' };
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencyCode })).to.equal(
|
|
||||||
'EUR 123,456.79',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencySymbol })).to.equal(
|
|
||||||
'€123,456.79',
|
|
||||||
);
|
|
||||||
localize.locale = 'nl-NL';
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencyCode })).to.equal(
|
|
||||||
'123.456,79 EUR',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'JPY', ...currencySymbol })).to.equal('123.457 ¥');
|
|
||||||
localize.locale = 'fr-FR';
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencyCode })).to.equal(
|
|
||||||
'123 456,79 EUR',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'JPY', ...currencySymbol })).to.equal('123 457 ¥');
|
|
||||||
localize.locale = 'de-DE';
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencyCode })).to.equal(
|
|
||||||
'123.456,79 EUR',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'JPY', ...currencySymbol })).to.equal('123.457 ¥');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can display currency as code', async () => {
|
|
||||||
const currencyCode = { style: 'currency', currencyDisplay: 'code' };
|
|
||||||
localize.locale = 'nl-NL';
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencyCode })).to.equal(
|
|
||||||
'123.456,79 EUR',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'USD', ...currencyCode })).to.equal(
|
|
||||||
'123.456,79 USD',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can display currency as symbol', async () => {
|
|
||||||
const currencySymbol = { style: 'currency', currencyDisplay: 'symbol' };
|
|
||||||
localize.locale = 'nl-NL';
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencySymbol })).to.equal(
|
|
||||||
'123.456,79 €',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'USD', ...currencySymbol })).to.equal(
|
|
||||||
'123.456,79 $',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses minus (and not dash) to indicate negative numbers ', async () => {
|
|
||||||
expect(formatNumber(-12, { style: 'decimal', maximumFractionDigits: 0 })).to.equal('-12');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('rounds (negative) numbers e.g. `roundMode: round`', async () => {
|
|
||||||
expect(formatNumber(12.4, { style: 'decimal', maximumFractionDigits: 0 })).to.equal('12');
|
|
||||||
expect(formatNumber(12.6, { style: 'decimal', maximumFractionDigits: 0 })).to.equal('13');
|
|
||||||
|
|
||||||
expect(formatNumber(-12.4, { style: 'decimal', maximumFractionDigits: 0 })).to.equal('-12');
|
|
||||||
expect(formatNumber(-12.6, { style: 'decimal', maximumFractionDigits: 0 })).to.equal('-13');
|
|
||||||
});
|
|
||||||
|
|
||||||
it("rounds (negative) numbers up when `roundMode: 'ceiling'`", async () => {
|
|
||||||
expect(formatNumber(12.4, { roundMode: 'ceiling' })).to.equal('13');
|
|
||||||
expect(formatNumber(12.6, { roundMode: 'ceiling' })).to.equal('13');
|
|
||||||
|
|
||||||
expect(formatNumber(-12.4, { roundMode: 'ceiling' })).to.equal('-12');
|
|
||||||
expect(formatNumber(-12.6, { roundMode: 'ceiling' })).to.equal('-12');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('rounds (negative) numbers down when `roundMode: floor`', async () => {
|
|
||||||
expect(formatNumber(12.4, { roundMode: 'floor' })).to.equal('12');
|
|
||||||
expect(formatNumber(12.6, { roundMode: 'floor' })).to.equal('12');
|
|
||||||
|
|
||||||
expect(formatNumber(-12.4, { roundMode: 'floor' })).to.equal('-13');
|
|
||||||
expect(formatNumber(-12.6, { roundMode: 'floor' })).to.equal('-13');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty string when NaN', async () => {
|
|
||||||
expect(formatNumber('foo')).to.equal('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty string when number is undefined', async () => {
|
|
||||||
expect(formatNumber(undefined)).to.equal('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses `localize.formatNumberOptions.returnIfNaN`', async () => {
|
|
||||||
localize.formatNumberOptions.returnIfNaN = '-';
|
|
||||||
expect(formatNumber('foo')).to.equal('-');
|
|
||||||
});
|
|
||||||
|
|
||||||
it("can set what to returns when NaN via `returnIfNaN: 'foo'`", async () => {
|
|
||||||
expect(formatNumber('foo', { returnIfNaN: '-' })).to.equal('-');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses `localize.locale`', async () => {
|
|
||||||
expect(formatNumber(123456.789, { style: 'decimal', maximumFractionDigits: 2 })).to.equal(
|
|
||||||
'123,456.79',
|
|
||||||
);
|
|
||||||
localize.locale = 'de-DE';
|
|
||||||
expect(formatNumber(123456.789, { style: 'decimal', maximumFractionDigits: 2 })).to.equal(
|
|
||||||
'123.456,79',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can set locale to use', async () => {
|
|
||||||
expect(
|
|
||||||
formatNumber(123456.789, { locale: 'en-GB', style: 'decimal', maximumFractionDigits: 2 }),
|
|
||||||
).to.equal('123,456.79');
|
|
||||||
expect(
|
|
||||||
formatNumber(123456.789, { locale: 'de-DE', style: 'decimal', maximumFractionDigits: 2 }),
|
|
||||||
).to.equal('123.456,79');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can specify max decimal places by `maximumFractionDigits: 3`', async () => {
|
|
||||||
expect(formatNumber(123456.789)).to.equal('123,456.789');
|
|
||||||
expect(formatNumber(123456.789, { style: 'decimal', maximumFractionDigits: 3 })).to.equal(
|
|
||||||
'123,456.789',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { style: 'decimal', maximumFractionDigits: 1 })).to.equal(
|
|
||||||
'123,456.8',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can specify min decimal places by `minimumFractionDigits: 3`', async () => {
|
|
||||||
expect(formatNumber(12.3)).to.equal('12.3');
|
|
||||||
expect(formatNumber(12.3456, { style: 'decimal', minimumFractionDigits: 3 })).to.equal(
|
|
||||||
'12.346',
|
|
||||||
);
|
|
||||||
expect(formatNumber(12.3, { style: 'decimal', minimumFractionDigits: 3 })).to.equal('12.300');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can specify to show at least x digits by `minimumIntegerDigits: 5`', async () => {
|
|
||||||
expect(formatNumber(123)).to.equal('123');
|
|
||||||
expect(formatNumber(123, { minimumIntegerDigits: 5 })).to.equal('00,123');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can display 0 decimal places', async () => {
|
|
||||||
expect(formatNumber(12.4, { style: 'decimal', maximumFractionDigits: 0 })).to.equal('12');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can give separators via getGroupSeparator() or getDecimalSeparator()', async () => {
|
|
||||||
expect(getGroupSeparator('en-GB')).to.equal(',');
|
|
||||||
expect(getGroupSeparator('nl-NL')).to.equal('.');
|
|
||||||
expect(getGroupSeparator('fr-FR')).to.equal(' ');
|
|
||||||
|
|
||||||
expect(getDecimalSeparator('en-GB')).to.equal('.');
|
|
||||||
expect(getDecimalSeparator('nl-NL')).to.equal(',');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can give number of fraction digits for a certain currency via getFractionDigits()', async () => {
|
|
||||||
expect(getFractionDigits('JOD')).to.equal(3);
|
|
||||||
expect(getFractionDigits('EUR')).to.equal(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('formats numbers correctly', async () => {
|
|
||||||
localize.locale = 'nl-NL';
|
|
||||||
expect(formatNumber(0, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('0,00');
|
|
||||||
expect(formatNumber(0.1, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('0,10');
|
|
||||||
expect(formatNumber(0.12, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('0,12');
|
|
||||||
expect(
|
|
||||||
formatNumber(0.123, { style: 'decimal', minimumFractionDigits: 2, maximumFractionDigits: 2 }),
|
|
||||||
).to.equal('0,12');
|
|
||||||
expect(
|
|
||||||
formatNumber(0.1234, {
|
|
||||||
style: 'decimal',
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2,
|
|
||||||
}),
|
|
||||||
).to.equal('0,12');
|
|
||||||
expect(
|
|
||||||
formatNumber(0.123456789, {
|
|
||||||
style: 'decimal',
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2,
|
|
||||||
}),
|
|
||||||
).to.equal('0,12');
|
|
||||||
expect(formatNumber(1, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1,00');
|
|
||||||
expect(formatNumber(1.0, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1,00');
|
|
||||||
expect(formatNumber(1.1, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1,10');
|
|
||||||
expect(formatNumber(1.12, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1,12');
|
|
||||||
expect(formatNumber(1.123, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1,123');
|
|
||||||
expect(formatNumber(1.1234, { style: 'decimal', maximumFractionDigits: 2 })).to.equal('1,12');
|
|
||||||
expect(
|
|
||||||
formatNumber(1.12345678, {
|
|
||||||
style: 'decimal',
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2,
|
|
||||||
}),
|
|
||||||
).to.equal('1,12');
|
|
||||||
expect(formatNumber(1000, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1.000,00');
|
|
||||||
expect(
|
|
||||||
formatNumber(112345678, {
|
|
||||||
style: 'decimal',
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2,
|
|
||||||
}),
|
|
||||||
).to.equal('112.345.678,00');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('formats 2-digit decimals correctly', async () => {
|
|
||||||
localize.locale = 'nl-NL';
|
|
||||||
Array.from(new Array(100), (val, index) => index).forEach(i => {
|
|
||||||
const iString = `${i}`;
|
|
||||||
let number = 0.0;
|
|
||||||
number += i * 0.01;
|
|
||||||
expect(formatNumber(number, { style: 'decimal', minimumFractionDigits: 2 })).to.equal(
|
|
||||||
`0,${iString.padStart(2, '0')}`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('normalizeIntl()', () => {
|
|
||||||
afterEach(() => {
|
|
||||||
// makes sure that between tests the localization is reset to default state
|
|
||||||
document.documentElement.lang = 'en-GB';
|
|
||||||
});
|
|
||||||
const currencyCode = { style: 'currency', currencyDisplay: 'code' };
|
|
||||||
const currencySymbol = { style: 'currency', currencyDisplay: 'symbol' };
|
|
||||||
|
|
||||||
it('supports British locale', async () => {
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencyCode })).to.equal(
|
|
||||||
'EUR 123,456.79',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'USD', ...currencyCode })).to.equal(
|
|
||||||
'USD 123,456.79',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencySymbol })).to.equal(
|
|
||||||
'€123,456.79',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'USD', ...currencySymbol })).to.equal(
|
|
||||||
'$123,456.79',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports US locale', async () => {
|
|
||||||
localize.locale = 'en-US';
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencyCode })).to.equal(
|
|
||||||
'EUR 123,456.79',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'USD', ...currencyCode })).to.equal(
|
|
||||||
'USD 123,456.79',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencySymbol })).to.equal(
|
|
||||||
'€123,456.79',
|
|
||||||
);
|
|
||||||
expect(formatNumber(123456.789, { currency: 'USD', ...currencySymbol })).to.equal(
|
|
||||||
'$123,456.79',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports Bulgarian locale', async () => {
|
|
||||||
localize.locale = 'bg-BG';
|
|
||||||
expect(formatNumber(123456.789, { currency: 'EUR', ...currencyCode })).to.equal(
|
|
||||||
'123 456,79 EUR',
|
|
||||||
);
|
|
||||||
expect(formatNumber(1234567890.789, { currency: 'USD', ...currencyCode })).to.equal(
|
|
||||||
'1 234 567 890,79 USD',
|
|
||||||
);
|
|
||||||
expect(formatNumber(12.789, { currency: 'EUR', ...currencyCode })).to.equal('12,79 EUR');
|
|
||||||
expect(formatNumber(12, { currency: 'USD', ...currencyCode })).to.equal('12,00 USD');
|
|
||||||
expect(formatNumber(12.789, { style: 'decimal' })).to.equal('12,789');
|
|
||||||
expect(formatNumber(12, { style: 'decimal', minimumFractionDigits: 3 })).to.equal('12,000');
|
|
||||||
expect(formatNumber(20000, { style: 'decimal', minimumFractionDigits: 3 })).to.equal(
|
|
||||||
'20 000,000',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('formatNumberToParts', () => {
|
|
||||||
afterEach(() => {
|
|
||||||
// makes sure that between tests the localization is reset to default state
|
|
||||||
document.documentElement.lang = 'en-GB';
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('formats based on ISO standards', () => {
|
|
||||||
const specs = [
|
|
||||||
['nl-NL', 'EUR', 1234.5, '1.234,50 EUR'],
|
|
||||||
['nl-NL', 'USD', 1234.5, '1.234,50 USD'],
|
|
||||||
['nl-NL', 'EUR', -1234.5, '-1.234,50 EUR'],
|
|
||||||
['nl-BE', 'EUR', 1234.5, '1.234,50 EUR'],
|
|
||||||
['nl-BE', 'USD', 1234.5, '1.234,50 USD'],
|
|
||||||
['nl-BE', 'EUR', -1234.5, '-1.234,50 EUR'],
|
|
||||||
['en-GB', 'EUR', 1234.5, 'EUR 1,234.50'],
|
|
||||||
['en-GB', 'USD', 1234.5, 'USD 1,234.50'],
|
|
||||||
['en-GB', 'EUR', -1234.5, '-EUR 1,234.50'],
|
|
||||||
['de-DE', 'EUR', 1234.5, '1.234,50 EUR'],
|
|
||||||
['de-DE', 'USD', 1234.5, '1.234,50 USD'],
|
|
||||||
['de-DE', 'EUR', -1234.5, '-1.234,50 EUR'],
|
|
||||||
['fr-BE', 'EUR', 1234.5, '1 234,50 EUR'],
|
|
||||||
['fr-BE', 'USD', 1234.5, '1 234,50 USD'],
|
|
||||||
['fr-BE', 'EUR', -1234.5, '-1 234,50 EUR'],
|
|
||||||
];
|
|
||||||
|
|
||||||
specs.forEach(spec => {
|
|
||||||
const [locale, currency, amount, expectedResult] = spec;
|
|
||||||
|
|
||||||
it(`formats ${locale} ${currency} ${amount} as ${expectedResult}`, () => {
|
|
||||||
localize.locale = locale;
|
|
||||||
const parts = formatNumberToParts(amount, {
|
|
||||||
style: 'currency',
|
|
||||||
currency,
|
|
||||||
currencyDisplay: 'code',
|
|
||||||
});
|
|
||||||
const joinedParts = parts.map(p => p.value).join('');
|
|
||||||
expect(joinedParts).to.equal(expectedResult);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency symbol with dutch locale', async () => {
|
|
||||||
localize.locale = 'nl-NL';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, {
|
|
||||||
style: 'currency',
|
|
||||||
currency: 'EUR',
|
|
||||||
currencyDisplay: 'symbol',
|
|
||||||
});
|
|
||||||
expect(formattedToParts).to.eql([
|
|
||||||
{ type: 'integer', value: '3' },
|
|
||||||
{ type: 'group', value: '.' },
|
|
||||||
{ type: 'integer', value: '500' },
|
|
||||||
{ type: 'decimal', value: ',' },
|
|
||||||
{ type: 'fraction', value: '00' },
|
|
||||||
{ type: 'literal', value: ' ' },
|
|
||||||
{ type: 'currency', value: '€' },
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency symbol with french locale', async () => {
|
|
||||||
localize.locale = 'fr-FR';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, {
|
|
||||||
style: 'currency',
|
|
||||||
currency: 'EUR',
|
|
||||||
currencyDisplay: 'symbol',
|
|
||||||
});
|
|
||||||
expect(Object.keys(formattedToParts).length).to.equal(7);
|
|
||||||
expect(formattedToParts[0].type).to.equal('integer');
|
|
||||||
expect(formattedToParts[0].value).to.equal('3');
|
|
||||||
expect(formattedToParts[1].type).to.equal('group');
|
|
||||||
expect(formattedToParts[1].value).to.equal(' ');
|
|
||||||
expect(formattedToParts[5].type).to.equal('literal');
|
|
||||||
expect(formattedToParts[5].value).to.equal(' ');
|
|
||||||
expect(formattedToParts[6].type).to.equal('currency');
|
|
||||||
expect(formattedToParts[6].value).to.equal('€');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency symbol with British locale', async () => {
|
|
||||||
localize.locale = 'en-GB';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, {
|
|
||||||
style: 'currency',
|
|
||||||
currency: 'EUR',
|
|
||||||
currencyDisplay: 'symbol',
|
|
||||||
});
|
|
||||||
expect(Object.keys(formattedToParts).length).to.equal(6);
|
|
||||||
expect(formattedToParts[2].type).to.equal('group');
|
|
||||||
expect(formattedToParts[2].value).to.equal(',');
|
|
||||||
expect(formattedToParts[4].type).to.equal('decimal');
|
|
||||||
expect(formattedToParts[4].value).to.equal('.');
|
|
||||||
expect(formattedToParts[5].type).to.equal('fraction');
|
|
||||||
expect(formattedToParts[5].value).to.equal('00');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency code with dutch locale', async () => {
|
|
||||||
localize.locale = 'nl-NL';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, {
|
|
||||||
style: 'currency',
|
|
||||||
currency: 'EUR',
|
|
||||||
currencyDisplay: 'code',
|
|
||||||
});
|
|
||||||
expect(Object.keys(formattedToParts).length).to.equal(7);
|
|
||||||
expect(formattedToParts[1].type).to.equal('group');
|
|
||||||
expect(formattedToParts[1].value).to.equal('.');
|
|
||||||
expect(formattedToParts[3].type).to.equal('decimal');
|
|
||||||
expect(formattedToParts[3].value).to.equal(',');
|
|
||||||
expect(formattedToParts[5].type).to.equal('literal');
|
|
||||||
expect(formattedToParts[5].value).to.equal(' ');
|
|
||||||
expect(formattedToParts[6].type).to.equal('currency');
|
|
||||||
expect(formattedToParts[6].value).to.equal('EUR');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency code with french locale', async () => {
|
|
||||||
localize.locale = 'fr-FR';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, {
|
|
||||||
style: 'currency',
|
|
||||||
currency: 'EUR',
|
|
||||||
currencyDisplay: 'code',
|
|
||||||
});
|
|
||||||
expect(Object.keys(formattedToParts).length).to.equal(7);
|
|
||||||
expect(formattedToParts[1].type).to.equal('group');
|
|
||||||
expect(formattedToParts[1].value).to.equal(' ');
|
|
||||||
expect(formattedToParts[3].type).to.equal('decimal');
|
|
||||||
expect(formattedToParts[3].value).to.equal(',');
|
|
||||||
expect(formattedToParts[4].type).to.equal('fraction');
|
|
||||||
expect(formattedToParts[4].value).to.equal('00');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency code with British locale', async () => {
|
|
||||||
localize.locale = 'en-GB';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, {
|
|
||||||
style: 'currency',
|
|
||||||
currency: 'EUR',
|
|
||||||
currencyDisplay: 'code',
|
|
||||||
});
|
|
||||||
expect(Object.keys(formattedToParts).length).to.equal(7);
|
|
||||||
expect(formattedToParts[3].type).to.equal('group');
|
|
||||||
expect(formattedToParts[3].value).to.equal(',');
|
|
||||||
expect(formattedToParts[5].type).to.equal('decimal');
|
|
||||||
expect(formattedToParts[5].value).to.equal('.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency with dutch locale and 2 decimals', async () => {
|
|
||||||
localize.locale = 'nl-NL';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, {
|
|
||||||
style: 'decimal',
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
});
|
|
||||||
expect(Object.keys(formattedToParts).length).to.equal(5);
|
|
||||||
expect(formattedToParts[0].type).to.equal('integer');
|
|
||||||
expect(formattedToParts[0].value).to.equal('3');
|
|
||||||
expect(formattedToParts[1].type).to.equal('group');
|
|
||||||
expect(formattedToParts[1].value).to.equal('.');
|
|
||||||
expect(formattedToParts[2].type).to.equal('integer');
|
|
||||||
expect(formattedToParts[2].value).to.equal('500');
|
|
||||||
expect(formattedToParts[3].type).to.equal('decimal');
|
|
||||||
expect(formattedToParts[3].value).to.equal(',');
|
|
||||||
expect(formattedToParts[4].type).to.equal('fraction');
|
|
||||||
expect(formattedToParts[4].value).to.equal('00');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency with french locale and 2 decimals', async () => {
|
|
||||||
localize.locale = 'fr-FR';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, {
|
|
||||||
style: 'decimal',
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
});
|
|
||||||
expect(Object.keys(formattedToParts).length).to.equal(5);
|
|
||||||
expect(formattedToParts[1].type).to.equal('group');
|
|
||||||
expect(formattedToParts[1].value).to.equal(' ');
|
|
||||||
expect(formattedToParts[3].type).to.equal('decimal');
|
|
||||||
expect(formattedToParts[3].value).to.equal(',');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency with british locale and 2 decimals', async () => {
|
|
||||||
localize.locale = 'en-GB';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, {
|
|
||||||
style: 'decimal',
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
});
|
|
||||||
expect(Object.keys(formattedToParts).length).to.equal(5);
|
|
||||||
expect(formattedToParts[1].type).to.equal('group');
|
|
||||||
expect(formattedToParts[1].value).to.equal(',');
|
|
||||||
expect(formattedToParts[3].type).to.equal('decimal');
|
|
||||||
expect(formattedToParts[3].value).to.equal('.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency with dutch locale without decimals', async () => {
|
|
||||||
localize.locale = 'nl-NL';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, { style: 'decimal' });
|
|
||||||
expect(Object.keys(formattedToParts).length).to.equal(3);
|
|
||||||
expect(formattedToParts[1].type).to.equal('group');
|
|
||||||
expect(formattedToParts[1].value).to.equal('.');
|
|
||||||
expect(formattedToParts[2].type).to.equal('integer');
|
|
||||||
expect(formattedToParts[2].value).to.equal('500');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency with french locale without decimals', async () => {
|
|
||||||
localize.locale = 'fr-FR';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, { style: 'decimal' });
|
|
||||||
expect(Object.keys(formattedToParts).length).to.equal(3);
|
|
||||||
expect(formattedToParts[1].type).to.equal('group');
|
|
||||||
expect(formattedToParts[1].value).to.equal(' ');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports currency with british locale without decimals', async () => {
|
|
||||||
localize.locale = 'en-GB';
|
|
||||||
const formattedToParts = formatNumberToParts(3500, { style: 'decimal' });
|
|
||||||
expect(Object.keys(formattedToParts).length).to.equal(3);
|
|
||||||
expect(formattedToParts[1].type).to.equal('group');
|
|
||||||
expect(formattedToParts[1].value).to.equal(',');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
297
packages/localize/test/number/formatNumber.test.js
Normal file
297
packages/localize/test/number/formatNumber.test.js
Normal file
|
|
@ -0,0 +1,297 @@
|
||||||
|
import { expect } from '@open-wc/testing';
|
||||||
|
import { localize } from '../../src/localize.js';
|
||||||
|
import { localizeTearDown } from '../../test-helpers.js';
|
||||||
|
|
||||||
|
import { formatNumber } from '../../src/number/formatNumber.js';
|
||||||
|
|
||||||
|
const currencyCode = currency => ({ style: 'currency', currencyDisplay: 'code', currency });
|
||||||
|
const currencySymbol = currency => ({ style: 'currency', currencyDisplay: 'symbol', currency });
|
||||||
|
|
||||||
|
describe('formatNumber', () => {
|
||||||
|
afterEach(localizeTearDown);
|
||||||
|
|
||||||
|
it('displays the appropriate amount of decimal places based on currencies spec http://www.currency-iso.org/en/home/tables/table-a1.html', () => {
|
||||||
|
const clean = str => str.replace(/[a-zA-Z]+/g, '').trim();
|
||||||
|
expect(clean(formatNumber(123456.789, currencyCode('JPY')))).to.equal('123,457');
|
||||||
|
expect(clean(formatNumber(123456.789, currencyCode('EUR')))).to.equal('123,456.79');
|
||||||
|
expect(clean(formatNumber(123456.789, currencyCode('BHD')))).to.equal('123,456.789');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can display currency as code', () => {
|
||||||
|
expect(formatNumber(123456.789, currencyCode('EUR'))).to.equal('EUR 123,456.79');
|
||||||
|
expect(formatNumber(123456.789, currencyCode('USD'))).to.equal('USD 123,456.79');
|
||||||
|
});
|
||||||
|
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses minus (and not dash) to indicate negative numbers ', () => {
|
||||||
|
expect(formatNumber(-12, { style: 'decimal', maximumFractionDigits: 0 })).to.equal('-12');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rounds (negative) numbers e.g. `roundMode: round`', () => {
|
||||||
|
expect(formatNumber(12.4, { roundMode: 'round' })).to.equal('12');
|
||||||
|
expect(formatNumber(12.6, { roundMode: 'round' })).to.equal('13');
|
||||||
|
|
||||||
|
expect(formatNumber(-12.4, { roundMode: 'round' })).to.equal('-12');
|
||||||
|
expect(formatNumber(-12.6, { roundMode: 'round' })).to.equal('-13');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rounds (negative) numbers up when `roundMode: 'ceiling'`", () => {
|
||||||
|
expect(formatNumber(12.4, { roundMode: 'ceiling' })).to.equal('13');
|
||||||
|
expect(formatNumber(12.6, { roundMode: 'ceiling' })).to.equal('13');
|
||||||
|
|
||||||
|
expect(formatNumber(-12.4, { roundMode: 'ceiling' })).to.equal('-12');
|
||||||
|
expect(formatNumber(-12.6, { roundMode: 'ceiling' })).to.equal('-12');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rounds (negative) numbers down when `roundMode: floor`', () => {
|
||||||
|
expect(formatNumber(12.4, { roundMode: 'floor' })).to.equal('12');
|
||||||
|
expect(formatNumber(12.6, { roundMode: 'floor' })).to.equal('12');
|
||||||
|
|
||||||
|
expect(formatNumber(-12.4, { roundMode: 'floor' })).to.equal('-13');
|
||||||
|
expect(formatNumber(-12.6, { roundMode: 'floor' })).to.equal('-13');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty string when NaN', () => {
|
||||||
|
expect(formatNumber('foo')).to.equal('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty string when number is undefined', () => {
|
||||||
|
expect(formatNumber(undefined)).to.equal('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses `localize.formatNumberOptions.returnIfNaN`', () => {
|
||||||
|
const savedReturnIfNaN = localize.formatNumberOptions.returnIfNaN;
|
||||||
|
|
||||||
|
localize.formatNumberOptions.returnIfNaN = '-';
|
||||||
|
expect(formatNumber('foo')).to.equal('-');
|
||||||
|
|
||||||
|
localize.formatNumberOptions.returnIfNaN = savedReturnIfNaN;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can set what to returns when NaN via `returnIfNaN: 'foo'`", () => {
|
||||||
|
expect(formatNumber('foo', { returnIfNaN: '-' })).to.equal('-');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses `localize.locale`', () => {
|
||||||
|
expect(formatNumber(123456.789, { style: 'decimal', maximumFractionDigits: 2 })).to.equal(
|
||||||
|
'123,456.79',
|
||||||
|
);
|
||||||
|
localize.locale = 'de-DE';
|
||||||
|
expect(formatNumber(123456.789, { style: 'decimal', maximumFractionDigits: 2 })).to.equal(
|
||||||
|
'123.456,79',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can set locale to use', () => {
|
||||||
|
expect(
|
||||||
|
formatNumber(123456.789, { locale: 'en-GB', style: 'decimal', maximumFractionDigits: 2 }),
|
||||||
|
).to.equal('123,456.79');
|
||||||
|
expect(
|
||||||
|
formatNumber(123456.789, { locale: 'de-DE', style: 'decimal', maximumFractionDigits: 2 }),
|
||||||
|
).to.equal('123.456,79');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can specify max decimal places by `maximumFractionDigits: 3`', () => {
|
||||||
|
expect(formatNumber(123456.789)).to.equal('123,456.789');
|
||||||
|
expect(formatNumber(123456.789, { style: 'decimal', maximumFractionDigits: 3 })).to.equal(
|
||||||
|
'123,456.789',
|
||||||
|
);
|
||||||
|
expect(formatNumber(123456.789, { style: 'decimal', maximumFractionDigits: 1 })).to.equal(
|
||||||
|
'123,456.8',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can specify min decimal places by `minimumFractionDigits: 3`', () => {
|
||||||
|
expect(formatNumber(12.3)).to.equal('12.3');
|
||||||
|
expect(formatNumber(12.3456, { style: 'decimal', minimumFractionDigits: 3 })).to.equal(
|
||||||
|
'12.346',
|
||||||
|
);
|
||||||
|
expect(formatNumber(12.3, { style: 'decimal', minimumFractionDigits: 3 })).to.equal('12.300');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can specify to show at least x digits by `minimumIntegerDigits: 5`', () => {
|
||||||
|
expect(formatNumber(123)).to.equal('123');
|
||||||
|
expect(formatNumber(123, { minimumIntegerDigits: 5 })).to.equal('00,123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can display 0 decimal places', () => {
|
||||||
|
expect(formatNumber(12.4, { style: 'decimal', maximumFractionDigits: 0 })).to.equal('12');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('formats numbers correctly', () => {
|
||||||
|
localize.locale = 'nl-NL';
|
||||||
|
expect(formatNumber(0, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('0,00');
|
||||||
|
expect(formatNumber(0.1, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('0,10');
|
||||||
|
expect(formatNumber(0.12, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('0,12');
|
||||||
|
expect(
|
||||||
|
formatNumber(0.123, { style: 'decimal', minimumFractionDigits: 2, maximumFractionDigits: 2 }),
|
||||||
|
).to.equal('0,12');
|
||||||
|
expect(
|
||||||
|
formatNumber(0.1234, {
|
||||||
|
style: 'decimal',
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
}),
|
||||||
|
).to.equal('0,12');
|
||||||
|
expect(
|
||||||
|
formatNumber(0.123456789, {
|
||||||
|
style: 'decimal',
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
}),
|
||||||
|
).to.equal('0,12');
|
||||||
|
expect(formatNumber(1, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1,00');
|
||||||
|
expect(formatNumber(1.0, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1,00');
|
||||||
|
expect(formatNumber(1.1, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1,10');
|
||||||
|
expect(formatNumber(1.12, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1,12');
|
||||||
|
expect(formatNumber(1.123, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1,123');
|
||||||
|
expect(formatNumber(1.1234, { style: 'decimal', maximumFractionDigits: 2 })).to.equal('1,12');
|
||||||
|
expect(
|
||||||
|
formatNumber(1.12345678, {
|
||||||
|
style: 'decimal',
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
}),
|
||||||
|
).to.equal('1,12');
|
||||||
|
expect(formatNumber(1000, { style: 'decimal', minimumFractionDigits: 2 })).to.equal('1.000,00');
|
||||||
|
expect(
|
||||||
|
formatNumber(112345678, {
|
||||||
|
style: 'decimal',
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
}),
|
||||||
|
).to.equal('112.345.678,00');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('formats 2-digit decimals correctly', () => {
|
||||||
|
localize.locale = 'nl-NL';
|
||||||
|
Array.from(new Array(100), (val, index) => index).forEach(i => {
|
||||||
|
const iString = `${i}`;
|
||||||
|
let number = 0.0;
|
||||||
|
number += i * 0.01;
|
||||||
|
expect(formatNumber(number, { style: 'decimal', minimumFractionDigits: 2 })).to.equal(
|
||||||
|
`0,${iString.padStart(2, '0')}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('normalization', () => {
|
||||||
|
describe('en-GB', () => {
|
||||||
|
it('supports basics', () => {
|
||||||
|
localize.locale = 'en-GB';
|
||||||
|
expect(formatNumber(123456.789, currencyCode('EUR'))).to.equal('EUR 123,456.79');
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('en-US', () => {
|
||||||
|
it('supports basics', () => {
|
||||||
|
localize.locale = 'en-US';
|
||||||
|
expect(formatNumber(123456.789, currencyCode('EUR'))).to.equal('EUR 123,456.79');
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('en-AU', () => {
|
||||||
|
it('supports basics', () => {
|
||||||
|
localize.locale = 'en-AU';
|
||||||
|
expect(formatNumber(123456.789, currencyCode('EUR'))).to.equal('EUR 123,456.79');
|
||||||
|
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'); // TODO: fix
|
||||||
|
// expect(formatNumber(123456.789, currencySymbol('USD'))).to.equal('$123,456.79'); // TODO: fix
|
||||||
|
// expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('¥123,457'); // TODO: fix
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('en-PH', () => {
|
||||||
|
it('supports basics', () => {
|
||||||
|
localize.locale = 'en-PH';
|
||||||
|
expect(formatNumber(123456.789, currencyCode('EUR'))).to.equal('EUR 123,456.79');
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('nl-NL', () => {
|
||||||
|
it('supports basics', () => {
|
||||||
|
localize.locale = 'nl-NL';
|
||||||
|
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('JPY'))).to.equal('¥ 123.457');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('nl-BE', () => {
|
||||||
|
it('supports basics', () => {
|
||||||
|
localize.locale = 'nl-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('JPY'))).to.equal('¥ 123.457');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fr-FR', () => {
|
||||||
|
it('supports basics', () => {
|
||||||
|
localize.locale = 'fr-FR';
|
||||||
|
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('JPY'))).to.equal('123 457 ¥');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fr-BE', () => {
|
||||||
|
it('supports basics', () => {
|
||||||
|
localize.locale = 'fr-FR';
|
||||||
|
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('JPY'))).to.equal('123 457 ¥');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bg-BG', () => {
|
||||||
|
it('supports basics', () => {
|
||||||
|
localize.locale = 'bg-BG';
|
||||||
|
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 $'); // TODO: fix
|
||||||
|
// expect(formatNumber(123456.789, currencySymbol('JPY'))).to.equal('123 457 ¥'); // TODO: fix
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes group separator', () => {
|
||||||
|
localize.locale = 'bg-BG';
|
||||||
|
expect(formatNumber(1.234, currencyCode('EUR'))).to.equal('1,23 EUR');
|
||||||
|
expect(formatNumber(1234.567, currencyCode('EUR'))).to.equal('1 234,57 EUR');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
102
packages/localize/test/number/formatNumberToParts.test.js
Normal file
102
packages/localize/test/number/formatNumberToParts.test.js
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
import { expect } from '@open-wc/testing';
|
||||||
|
import { localize } from '../../src/localize.js';
|
||||||
|
import { localizeTearDown } from '../../test-helpers.js';
|
||||||
|
|
||||||
|
import { formatNumberToParts } from '../../src/number/formatNumberToParts.js';
|
||||||
|
|
||||||
|
const c = v => ({ type: 'currency', value: v });
|
||||||
|
const d = v => ({ type: 'decimal', value: v });
|
||||||
|
const i = v => ({ type: 'integer', value: v });
|
||||||
|
const f = v => ({ type: 'fraction', value: v });
|
||||||
|
const g = v => ({ type: 'group', value: v });
|
||||||
|
const l = v => ({ type: 'literal', value: v });
|
||||||
|
const m = { type: 'minusSign', value: '-' };
|
||||||
|
|
||||||
|
const stringifyParts = parts => parts.map(part => part.value).join('');
|
||||||
|
|
||||||
|
describe('formatNumberToParts', () => {
|
||||||
|
afterEach(localizeTearDown);
|
||||||
|
|
||||||
|
describe("style: 'currency'", () => {
|
||||||
|
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')]],
|
||||||
|
['nl-NL', 'EUR', 1234.5, [i('1'), g('.'), i('234'), d(','), f('50'), l(' '), c('EUR')]],
|
||||||
|
['nl-NL', 'EUR', -1234.5, [m, i('1'), g('.'), i('234'), d(','), f('50'), l(' '), c('EUR')]],
|
||||||
|
['nl-BE', 'EUR', 1234.5, [i('1'), g('.'), i('234'), d(','), f('50'), l(' '), c('EUR')]],
|
||||||
|
['nl-BE', 'EUR', -1234.5, [m, i('1'), g('.'), i('234'), d(','), f('50'), l(' '), c('EUR')]],
|
||||||
|
['fr-FR', 'EUR', 1234.5, [i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('EUR')]],
|
||||||
|
['fr-FR', 'EUR', -1234.5, [m, i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('EUR')]],
|
||||||
|
['fr-BE', 'EUR', 1234.5, [i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('EUR')]],
|
||||||
|
['fr-BE', 'EUR', -1234.5, [m, i('1'), g(' '), i('234'), d(','), f('50'), l(' '), c('EUR')]],
|
||||||
|
];
|
||||||
|
|
||||||
|
specs.forEach(([locale, currency, amount, expectedResult]) => {
|
||||||
|
it(`formats ${locale} ${currency} ${amount} as "${stringifyParts(expectedResult)}"`, () => {
|
||||||
|
localize.locale = locale;
|
||||||
|
expect(
|
||||||
|
formatNumberToParts(amount, {
|
||||||
|
style: 'currency',
|
||||||
|
currencyDisplay: 'code',
|
||||||
|
currency,
|
||||||
|
}),
|
||||||
|
).to.deep.equal(expectedResult);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("style: 'decimal'", () => {
|
||||||
|
describe('no minimumFractionDigits', () => {
|
||||||
|
const specs = [
|
||||||
|
['en-GB', 3500, [i('3'), g(','), i('500')]],
|
||||||
|
['en-GB', -3500, [m, i('3'), g(','), i('500')]],
|
||||||
|
['nl-NL', 3500, [i('3'), g('.'), i('500')]],
|
||||||
|
['nl-NL', -3500, [m, i('3'), g('.'), i('500')]],
|
||||||
|
['nl-BE', 3500, [i('3'), g('.'), i('500')]],
|
||||||
|
['nl-BE', -3500, [m, i('3'), g('.'), i('500')]],
|
||||||
|
['fr-FR', 3500, [i('3'), g(' '), i('500')]],
|
||||||
|
['fr-FR', -3500, [m, i('3'), g(' '), i('500')]],
|
||||||
|
['fr-BE', 3500, [i('3'), g(' '), i('500')]],
|
||||||
|
['fr-BE', -3500, [m, i('3'), g(' '), i('500')]],
|
||||||
|
];
|
||||||
|
|
||||||
|
specs.forEach(([locale, amount, expectedResult]) => {
|
||||||
|
it(`formats ${locale} ${amount} as "${stringifyParts(expectedResult)}"`, () => {
|
||||||
|
localize.locale = locale;
|
||||||
|
expect(
|
||||||
|
formatNumberToParts(amount, {
|
||||||
|
style: 'decimal',
|
||||||
|
}),
|
||||||
|
).to.deep.equal(expectedResult);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('minimumFractionDigits: 2', () => {
|
||||||
|
const specs = [
|
||||||
|
['en-GB', 3500, [i('3'), g(','), i('500'), d('.'), f('00')]],
|
||||||
|
['en-GB', -3500, [m, i('3'), g(','), i('500'), d('.'), f('00')]],
|
||||||
|
['nl-NL', 3500, [i('3'), g('.'), i('500'), d(','), f('00')]],
|
||||||
|
['nl-NL', -3500, [m, i('3'), g('.'), i('500'), d(','), f('00')]],
|
||||||
|
['nl-BE', 3500, [i('3'), g('.'), i('500'), d(','), f('00')]],
|
||||||
|
['nl-BE', -3500, [m, i('3'), g('.'), i('500'), d(','), f('00')]],
|
||||||
|
['fr-FR', 3500, [i('3'), g(' '), i('500'), d(','), f('00')]],
|
||||||
|
['fr-FR', -3500, [m, i('3'), g(' '), i('500'), d(','), f('00')]],
|
||||||
|
['fr-BE', 3500, [i('3'), g(' '), i('500'), d(','), f('00')]],
|
||||||
|
['fr-BE', -3500, [m, i('3'), g(' '), i('500'), d(','), f('00')]],
|
||||||
|
];
|
||||||
|
|
||||||
|
specs.forEach(([locale, amount, expectedResult]) => {
|
||||||
|
it(`formats ${locale} ${amount} as "${stringifyParts(expectedResult)}"`, () => {
|
||||||
|
localize.locale = locale;
|
||||||
|
expect(
|
||||||
|
formatNumberToParts(amount, {
|
||||||
|
style: 'decimal',
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
}),
|
||||||
|
).to.deep.equal(expectedResult);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
11
packages/localize/test/number/getDecimalSeparator.test.js
Normal file
11
packages/localize/test/number/getDecimalSeparator.test.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { expect } from '@open-wc/testing';
|
||||||
|
|
||||||
|
import { getDecimalSeparator } from '../../src/number/getDecimalSeparator.js';
|
||||||
|
|
||||||
|
describe('getDecimalSeparator', () => {
|
||||||
|
it('returns decimal separator for locale', () => {
|
||||||
|
expect(getDecimalSeparator('en-GB')).to.equal('.');
|
||||||
|
expect(getDecimalSeparator('nl-NL')).to.equal(',');
|
||||||
|
expect(getDecimalSeparator('fr-FR')).to.equal(',');
|
||||||
|
});
|
||||||
|
});
|
||||||
11
packages/localize/test/number/getFractionDigits.test.js
Normal file
11
packages/localize/test/number/getFractionDigits.test.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { expect } from '@open-wc/testing';
|
||||||
|
|
||||||
|
import { getFractionDigits } from '../../src/number/getFractionDigits.js';
|
||||||
|
|
||||||
|
describe('getFractionDigits', () => {
|
||||||
|
it('returns number of fraction digits for currency', () => {
|
||||||
|
expect(getFractionDigits('JPY')).to.equal(0);
|
||||||
|
expect(getFractionDigits('EUR')).to.equal(2);
|
||||||
|
expect(getFractionDigits('BHD')).to.equal(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
11
packages/localize/test/number/getGroupSeparator.test.js
Normal file
11
packages/localize/test/number/getGroupSeparator.test.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { expect } from '@open-wc/testing';
|
||||||
|
|
||||||
|
import { getGroupSeparator } from '../../src/number/getGroupSeparator.js';
|
||||||
|
|
||||||
|
describe('getGroupSeparator', () => {
|
||||||
|
it('returns group separator for locale', () => {
|
||||||
|
expect(getGroupSeparator('en-GB')).to.equal(',');
|
||||||
|
expect(getGroupSeparator('nl-NL')).to.equal('.');
|
||||||
|
expect(getGroupSeparator('fr-FR')).to.equal(' ');
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue