SKU // 103

JavaScript toLocaleString()

toString() gives computer-style text. toLocaleString() formats a value the way people in a particular country expect to read it: thousands separators, currency symbols, percentages, and dates in the local order. A locale is a language and country code, like "en-US" (English, United States) or "de-DE" (German, Germany).

// Numbers

With no arguments, it uses the reader's own browser settings. For predictable results, name the locale:

const num = 1234567.89;

console.log(num.toLocaleString("en-US"));
console.log(num.toLocaleString("de-DE"));
console.log(num.toLocaleString("no-NO"));
console.log(num.toLocaleString("en-IN"));   // India groups digits differently
1,234,567.89 1.234.567,89 1 234 567,89 12,34,567.89

Germany swaps the comma and the dot. Norway uses spaces. India groups by thousands once, then by hundreds (lakhs and crores).

Trap: those are not ordinary spaces
Locales that group with spaces use a special non-breaking space (so a number never wraps onto two lines). It looks like a space but is a different character, so num.toLocaleString("no-NO") === "1 234 567,89" typed on a keyboard is false. Format for display only; never compare formatted text.

// Currency

Pass an options object with style: "currency" and a three-letter currency code:

const price = 1299.95;

console.log(price.toLocaleString("en-US", { style: "currency", currency: "USD" }));
console.log(price.toLocaleString("de-DE", { style: "currency", currency: "EUR" }));
console.log(price.toLocaleString("no-NO", { style: "currency", currency: "NOK" }));
console.log(price.toLocaleString("en-US", { style: "currency", currency: "EUR" }));
console.log(price.toLocaleString("ja-JP", { style: "currency", currency: "JPY" }));
$1,299.95 1.299,95 € 1 299,95 kr €1,299.95 ¥1,300

The locale decides the layout and the currency decides the symbol: euros in "en-US" still put the symbol first. Yen have no cents, so the last one rounds to a whole number.

Watch out: the currency code is required
style: "currency" on its own is TypeError: Currency code is required with currency style. JavaScript will not guess which money you mean.

// Percentages

style: "percent" multiplies by 100 and adds the % sign, so pass a fraction:

console.log((0.875).toLocaleString("en-US", { style: "percent" }));
console.log((0.875).toLocaleString("en-US", { style: "percent", minimumFractionDigits: 1 }));
console.log((87.5).toLocaleString("en-US", { style: "percent" }));   // already a percent!
88% 87.5% 8,750%

By default a percentage is rounded to a whole number, which is why 0.875 shows as 88%.

// Controlling Decimal Digits

console.log((3.14159).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }));
console.log((5).toLocaleString("en-US", { minimumFractionDigits: 2 }));
console.log((7).toLocaleString("en-US", { minimumIntegerDigits: 2 }));
3.14 5.00 07
minimumFractionDigitsPad with zeros to at least this many decimals
maximumFractionDigitsRound to at most this many decimals
minimumIntegerDigitsPad the whole-number part with leading zeros
useGrouping: falseLeave out thousands separators
Note
The method name is toLocaleString, with an e. Typing num.toLocalString() is TypeError: num.toLocalString is not a function.

// It Returns a String

Like toFixed, the result is text. Do all your math first and format last. Formatted text will not convert back cleanly:

const text = (1234.5).toLocaleString("en-US");

console.log(text, typeof text);
console.log(Number(text));                    // the comma breaks it
console.log(Number(text.replaceAll(",", "")));
1,234.5 string NaN 1234.5

// Dates

Dates are where toLocaleString shines. Each locale puts the day, month, and time in its own order:

const d = new Date(2026, 0, 5, 14, 30);

console.log(d.toLocaleString("en-US"));
console.log(d.toLocaleString("en-GB"));
console.log(d.toLocaleDateString("en-US"));   // date part only
console.log(d.toLocaleTimeString("en-US"));   // time part only
1/5/2026, 2:30:00 PM 05/01/2026, 14:30:00 1/5/2026 2:30:00 PM

In the UK, 05/01/2026 is the 5th of January. In the US it would mean May 1st. Formatting with the reader's locale avoids that confusion.

// Date Formatting Options

const d = new Date(2026, 0, 5);

console.log(d.toLocaleString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric" }));
console.log(d.toLocaleString("en-US", { month: "long" }));
console.log(d.toLocaleString("fr-FR", { month: "long" }));
console.log(d.toLocaleString("es-ES", { weekday: "long" }));
Monday, January 5, 2026 January janvier lunes

This is the easy way to get month and day names in any language, instead of the lookup arrays from JS Date Get.

// Time Zones

A date is one exact moment, but the clock reading depends on where you are. The timeZone option chooses where:

const newYear = new Date("2026-01-01");   // a date-only string means midnight UTC

console.log(newYear.toLocaleString("en-US", { timeZone: "UTC" }));
console.log(newYear.toLocaleString("en-US", { timeZone: "America/New_York" }));
1/1/2026, 12:00:00 AM 12/31/2025, 7:00:00 PM
W3Schools example gotcha
W3Schools' array example formats new Date("2026-01-01"). That is midnight in London, which is still December 31st, 2025 anywhere in the Americas. A student in Baltimore sees a date from the wrong year. Use new Date(2026, 0, 1) (your local midnight) or pass a timeZone.

// Arrays and BigInts

On an array, toLocaleString formats each item, then joins them with commas, which can get confusing in locales that also use commas inside numbers:

console.log([1234.5, 99].toLocaleString("en-US"));
console.log([1234.5, 99].toLocaleString("de-DE"));
console.log((123456789012345678901234567890n).toLocaleString("en-US"));
1,234.5,99 1.234,5,99 123,456,789,012,345,678,901,234,567,890

// Real Example: Readable File Sizes

W3Schools shows a clever use: turning a raw byte count into something a person can read.

function fileSize(bytes) {
  const opts = { maximumFractionDigits: 1 };
  if (bytes < 1024) return bytes + " bytes";
  if (bytes < 1024 * 1024) return (bytes / 1024).toLocaleString("en-US", opts) + " KB";
  return (bytes / 1024 / 1024).toLocaleString("en-US", opts) + " MB";
}

console.log(fileSize(500));
console.log(fileSize(1536));
console.log(fileSize(1536000));
500 bytes 1.5 KB 1.5 MB

// A Few More Options

console.log((1234567).toLocaleString("en-US", { notation: "compact" }));
console.log((42).toLocaleString("en-US", { style: "unit", unit: "kilometer-per-hour" }));
console.log((1234567).toLocaleString("en-US", { useGrouping: false }));
1.2M 42 km/h 1234567

// Quick Check

What does this log?

console.log((0.5).toLocaleString("en-US", { style: "percent" }));
Show answer
50%. The percent style multiplies by 100, so 0.5 means half.

Which gives "$1,299.95"?

  1. (1299.95).toLocaleString("en-US")
  2. (1299.95).toLocaleString("en-US", { currency: "USD" })
  3. (1299.95).toLocaleString("en-US", { style: "currency", currency: "USD" })
  4. (1299.95).toFixed(2)
Show answer
C. A gives "1,299.95" with no symbol. B has a currency but no currency style, so the currency is ignored. D gives "1299.95".

Why is Number((1234.5).toLocaleString("en-US")) equal to NaN?

Show answer
The formatted text is "1,234.5", and Number() does not accept commas. Formatted strings are for people to read, not for math: keep the original number for calculations.

// Try It

  1. Click Open Playground. Format the number for "en-US", "de-DE", and "fr-FR" and compare the separators
  2. Show a price of 19.5 as dollars, euros (in "de-DE"), and yen
  3. Print today's date as "Monday, January 5, 2026" style text in English and in Spanish
  4. Open the fileSize example with its Try it Yourself button and use it on five numbers of your choice, from bytes up to hundreds of MB
// Video Walkthroughs
Last updated: Oct 10, 2026, 1:48 AM ET