Number.toLocaleString() with custom separators
Asked Answered
T

4

5

I need to format a number in JavaScript with separators that may be defined at runtime.

The combination of thousand and decimal separator may not match a specific locale.

Is there a way to provide the thousand and decimal separator in JavaScript toLocaleString()? or to use NumberFormat explicitly with values I define?

I see examples using locale codes, and some using other values ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString ) but these don't cover my use case.

Trefoil answered 8/11, 2016 at 11:12 Comment(6)
https://mcmap.net/q/2030541/-change-decimal-mark/…Ferdelance
As I mentioned in the question, I do not know the expected decimal and thousand separator until runtime so setting a locale is not feasible for this example.Trefoil
There are practically three different separators, ,, . and space, it's possible to detect the correct combination at runtime, and pick a suitable language tag. Or are the delimiters fully custom, like a, x etc.?Ferdelance
@Ferdelance They are certainly at the moment only standard separators, but it's possible they could be switched about based on the installation. You're right that the correct combination could be detected but I'm not sure of the best way to approach this, do you have any suggestions.Trefoil
Create an object and name the properties according to separator combinations. Something like in this jsFiddle. I just couldn't figure out a locale using space as thousand separator and decimal point, maybe you'll find it.Ferdelance
Interesting solution! I don't think the customer at the moment would like using a locale which doesn't match their country (even though they expect custom (non standard) formats) go figure! but this is worth knowingTrefoil
L
8

One option is to format to a string with known separators, and then do a find/replace with the unknown separators.

function formatNum(num, separator, fraction) {
  var str = num.toLocaleString('en-US');
  str = str.replace(/\./, fraction);
  str = str.replace(/,/g, separator);
  return str;
}

formatNum(12342.2, "a", "x");
//12a342x2
Lighterman answered 8/11, 2016 at 11:43 Comment(4)
This is both a sensible and horrible solution! I both hate and love it!Trefoil
THIS is in fact a very clever and elegant solution larry! As long the en-US format doens't change everthign should be fine!Burnight
Important note: With this code, the fraction symbol anything except "," as all commas will be replaced by the thousands separator.Burnight
num.toLocaleString('fullwide') should work better and to get rid of potential scientific formatJara
Q
1

The original answer on this page won't work if a comma is used as the decimal separator (the parameter fraction). That's problematic, because comma is used as the decimal separator widely e.g. in Europe (Spain, France, Norway, Czechia, Denmark...).

In order to make it work, the replacement can be done in two steps instead using an intermediate placeholder for the fraction symbol:

function formatNum(num, separator, fraction) {
    return num
        .toLocaleString('en-US');
        .replace(/\./, "<fraction>")
        .replace(/,/g, separator)
        .replace(/<fraction>/, fraction);
}

formatNum(12342.2, " ", ",");
//12 342,2
Querulous answered 25/11, 2022 at 12:23 Comment(0)
G
1

There's a Intl.NumberFormat method called formatToParts with enough granularity to do this correctly.

Here's changing all thousand separators to underscore:

const formatter = Intl.NumberFormat(undefined); // Undefined locale == use the user's resolved locale

const parts = formatter.formatToParts(123456789.987654321);
parts.filter(p => p.type === 'group').forEach(p => p.value = '_');
const formatted = parts.map(p => p.value).join('');
formatted // '123_456_789.988'

FWIW, the formatToParts() return value looks like this:

[{type: 'integer',  value: '123'},
 {type: 'group',    value: ','  },
 {type: 'integer',  value: '456'},
 {type: 'group',    value: ','  },
 {type: 'integer',  value: '789'},
 {type: 'decimal',  value: '.'  },
 {type: 'fraction', value: '988'}]

..so if you need to adjust the decimal separator, it's easy as well.

If the MDN doc doesn't have enough detail the ecma 402 spec covers these parts.

Gerund answered 26/7, 2023 at 21:27 Comment(0)
T
0

If you want a fully customized number presentation, the correct answer is Intl.NumberFormater.

But if all you want is to control grouping and the length of fractional part, there's an easier (far easier, actually) solution:

const num = (123.45).toLocaleString([], {
    maximumFractionDigits: 2,
    useGrouping: false,
});

An empty array of locales causes the system to use default locale. The benefit is that you don't have to specify explicit locale, making the code fit for any given client's locale.

Ref: mdn:Number.toLocaleString()

Trishatriskelion answered 2/10, 2024 at 8:22 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.