diff --git a/packages/util/src/format/formatBalance.spec.ts b/packages/util/src/format/formatBalance.spec.ts index 951b7fec3..708734efc 100644 --- a/packages/util/src/format/formatBalance.spec.ts +++ b/packages/util/src/format/formatBalance.spec.ts @@ -189,6 +189,24 @@ describe('formatBalance', (): void => { formatBalance(TESTVAL, { decimals: 15, forceUnit: 'µ' }) ).toEqual('123.4567 µUnit'); }); + + it('formats 123,456 (decimals=0, locale=sl)', (): void => { + expect( + formatBalance(123456, { decimals: 0, locale: 'sl', withSi: true }) + ).toEqual('123,4560 kUnit'); + }); + + it('formats BigInt numbers (locale=sl)', (): void => { + expect( + formatBalance(123456789000n, { decimals: 15, locale: 'sl', withSi: true }) + ).toEqual('123,4567 µUnit'); + }); + + it('formats 123,456,789,000 (decimals=7, forceUnit=base locale=sl)', (): void => { + expect( + formatBalance(TESTVAL, { decimals: 7, forceUnit: '-', locale: 'sl' }) + ).toEqual('12.345,6789 Unit'); + }); }); describe('calcSi', (): void => { diff --git a/packages/util/src/format/formatBalance.ts b/packages/util/src/format/formatBalance.ts index c90668c58..83d6d1a0b 100644 --- a/packages/util/src/format/formatBalance.ts +++ b/packages/util/src/format/formatBalance.ts @@ -7,6 +7,7 @@ import type { SiDef, ToBn } from '../types'; import { bnToBn } from '../bn/toBn'; import { isBoolean } from '../is/boolean'; import { formatDecimal } from './formatDecimal'; +import { getSeparator } from './getSeparator'; import { calcSi, findSi, SI, SI_MID } from './si'; interface Defaults { @@ -48,6 +49,10 @@ interface Options { * @description Returns all trailing zeros, otherwise removes (default = true) */ withZero?: boolean; + /** + * @description The locale to use + */ + locale?: string; } interface BalanceFormatter { @@ -66,7 +71,7 @@ let defaultDecimals = DEFAULT_DECIMALS; let defaultUnit = DEFAULT_UNIT; // Formats a string/number with . notation -function _formatBalance (input?: number | string | BN | bigint | ExtToBn, { decimals = defaultDecimals, forceUnit, withAll = false, withSi = true, withSiFull = false, withUnit = true, withZero = true }: Options = {}): string { +function _formatBalance (input?: number | string | BN | bigint | ExtToBn, { decimals = defaultDecimals, forceUnit, withAll = false, withSi = true, withSiFull = false, withUnit = true, withZero = true, locale = 'en' }: Options = {}): string { // we only work with string inputs here - convert anything // into the string-only value let text = bnToBn(input).toString(); @@ -127,7 +132,9 @@ function _formatBalance (input?: number | string | BN | b : ` ${withSiFull ? `${si.text}${withUnit ? ' ' : ''}` : si.value}${withUnit ? unit : ''}` : ''; - return `${sign}${formatDecimal(pre)}${post && `.${post}`}${units}`; + const { decimal, thousand } = getSeparator(locale); + + return `${sign}${formatDecimal(pre, thousand)}${post && `${decimal}${post}`}${units}`; } export const formatBalance = _formatBalance as BalanceFormatter; diff --git a/packages/util/src/format/formatDecimal.ts b/packages/util/src/format/formatDecimal.ts index 37776cee4..7e2cbb903 100644 --- a/packages/util/src/format/formatDecimal.ts +++ b/packages/util/src/format/formatDecimal.ts @@ -6,9 +6,9 @@ const NUMBER_REGEX = new RegExp('(\\d+?)(?=(\\d{3})+(?!\\d)|$)', 'g'); /** * @name formatDecimal - * @description Formats a number into string format with thousand seperators + * @description Formats a number into string format with thousand separators */ -export function formatDecimal (value: string): string { +export function formatDecimal (value: string, separator = ','): string { // We can do this by adjusting the regx, however for the sake of clarity // we rather strip and re-add the negative sign in the output const isNegative = value[0].startsWith('-'); @@ -17,6 +17,6 @@ export function formatDecimal (value: string): string { : value.match(NUMBER_REGEX); return matched - ? `${isNegative ? '-' : ''}${matched.join(',')}` + ? `${isNegative ? '-' : ''}${matched.join(separator)}` : value; } diff --git a/packages/util/src/format/formatNumber.spec.ts b/packages/util/src/format/formatNumber.spec.ts index d91a93ed7..96f910832 100644 --- a/packages/util/src/format/formatNumber.spec.ts +++ b/packages/util/src/format/formatNumber.spec.ts @@ -37,4 +37,22 @@ describe('formatNumber', (): void => { }) ).toEqual('12,345'); }); + + it('formats negative numbers (locale=sl)', (): void => { + expect( + formatNumber(-123456, { locale: 'sl' }) + ).toEqual('-123.456'); + }); + + it('formats BN numbers (locale=sl)', (): void => { + expect( + formatNumber(new BN(12345), { locale: 'sl' }) + ).toEqual('12.345'); + }); + + it('formats BigInt numbers (locale=sl)', (): void => { + expect( + formatNumber(123456789n, { locale: 'sl' }) + ).toEqual('123.456.789'); + }); }); diff --git a/packages/util/src/format/formatNumber.ts b/packages/util/src/format/formatNumber.ts index f30766e07..e241b1bf5 100644 --- a/packages/util/src/format/formatNumber.ts +++ b/packages/util/src/format/formatNumber.ts @@ -6,11 +6,24 @@ import type { ToBn } from '../types'; import { bnToBn } from '../bn/toBn'; import { formatDecimal } from './formatDecimal'; +import { getSeparator } from './getSeparator'; + +interface Options { + /** + * @description The locale to use + */ + locale?: string; +} /** * @name formatNumber - * @description Formats a number into string format with thousand seperators + * @description Formats a number into string format with thousand separators */ -export function formatNumber (value?: ExtToBn | BN | bigint | number | null): string { - return formatDecimal(bnToBn(value).toString()); +export function formatNumber ( + value?: ExtToBn | BN | bigint | number | null, + { locale = 'en' }: Options = {} +): string { + const { thousand } = getSeparator(locale); + + return formatDecimal(bnToBn(value).toString(), thousand); } diff --git a/packages/util/src/format/getSeparator.spec.ts b/packages/util/src/format/getSeparator.spec.ts new file mode 100644 index 000000000..dc761c6f1 --- /dev/null +++ b/packages/util/src/format/getSeparator.spec.ts @@ -0,0 +1,72 @@ +// Copyright 2017-2023 @polkadot/util authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { getSeparator } from './getSeparator'; + +describe('getSeparator', (): void => { + it('uses system locale', (): void => { + const n = 1000.1; + const locale = { + decimal: n.toLocaleString().substring(5, 6), + thousand: n.toLocaleString().substring(1, 2) + }; + + expect( + getSeparator() + ).toEqual(locale); + }); + + it('uses en locale', (): void => { + expect( + getSeparator('en') + ).toEqual({ + decimal: '.', + thousand: ',' + }); + }); + + it('uses en-gb locale', (): void => { + expect( + getSeparator('en-gb') + ).toEqual({ + decimal: '.', + thousand: ',' + }); + }); + + it('uses sl locale', (): void => { + expect( + getSeparator('sl') + ).toEqual({ + decimal: ',', + thousand: '.' + }); + }); + + it('uses sl-si locale', (): void => { + expect( + getSeparator('sl-si') + ).toEqual({ + decimal: ',', + thousand: '.' + }); + }); + + it('uses it-it locale', (): void => { + expect( + getSeparator('it-it') + ).toEqual({ + decimal: ',', + thousand: '.' + }); + }); + + it('uses ja-jp locale', (): void => { + expect( + getSeparator('ja-jp') + ).toEqual({ + decimal: '.', + thousand: ',' + }); + }); +}); diff --git a/packages/util/src/format/getSeparator.ts b/packages/util/src/format/getSeparator.ts new file mode 100644 index 000000000..18a3580d1 --- /dev/null +++ b/packages/util/src/format/getSeparator.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2023 @polkadot/util authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/** + * Get the decimal and thousand separator of a locale + * @param locale + * @returns {decimal: string, thousand: string} + */ +export function getSeparator (locale?: string): +{ thousand: string, decimal: string } { + return { + decimal: (0.1).toLocaleString(locale).substring(1, 2), + thousand: (1000).toLocaleString(locale).substring(1, 2) + }; +} diff --git a/packages/util/src/string/camelCase.ts b/packages/util/src/string/camelCase.ts index 7d8f4822a..36ed7b088 100644 --- a/packages/util/src/string/camelCase.ts +++ b/packages/util/src/string/camelCase.ts @@ -33,7 +33,7 @@ function formatAllCaps (w: string): string { function converter (format: (w: string, i: number) => string): (value: AnyString) => string { return (value: AnyString): string => { const parts = value - // replace all seperators (including consequtive) with spaces + // replace all separators (including consequtive) with spaces .replace(/[-_., ]+/g, ' ') // we don't want leading or trailing spaces .trim()