How to format currency with locale-specific symbols
Display prices with the correct currency symbol, position, and formatting for any locale
Introduction
Currency symbols identify which currency a price represents. The dollar sign indicates US dollars, the euro symbol indicates euros, and the pound sign indicates British pounds. These symbols are essential for international applications because users need to know which currency they are viewing or spending.
Different countries format currency amounts differently. Americans write $1,234.56 with the symbol before the amount. Germans write 1.234,56 € with the symbol after the amount and different separators. French formatting uses 1 234,56 € with spaces between digit groups. When you hardcode currency formatting like "$" + amount, you assume all users follow the same conventions.
JavaScript provides the Intl.NumberFormat API to format currency amounts with locale-appropriate symbols and conventions. This lesson explains how currency formatting varies by locale and how to format prices correctly for any language or region.
Currency symbols vary by locale
Different currencies use different symbols. The US dollar uses $, the euro uses €, the British pound uses £, the Japanese yen uses ¥, and the Swiss franc uses Fr. or CHF depending on context. Each symbol helps users quickly identify which currency they are viewing.
Some symbols represent multiple currencies. The dollar sign $ is used for US dollars, Canadian dollars, Australian dollars, Mexican pesos, and several other currencies. Without additional context, users cannot tell which dollar currency a price represents.
Currency symbol position varies by locale. English-speaking countries typically place the symbol before the amount like $100. Many European countries place the symbol after the amount like 100 €. Some countries include a space between the amount and symbol while others do not.
These differences mean you cannot simply concatenate a symbol and a number. You need formatting logic that understands both the currency being displayed and the locale of the user viewing it.
Format currency with Intl.NumberFormat
The Intl.NumberFormat constructor creates a currency formatter when you pass style: 'currency' in the options. You must also specify which currency to format using the currency option with an ISO 4217 currency code.
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
console.log(formatter.format(1234.56));
// Output: "$1,234.56"
This creates a formatter for US English that displays amounts in US dollars. The format() method converts the number to a string with the dollar symbol, thousands separators, and two decimal places.
The currency option requires a three-letter ISO 4217 code. Common codes include USD for US dollars, EUR for euros, GBP for British pounds, JPY for Japanese yen, and CAD for Canadian dollars.
const usdFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
const eurFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
});
const gbpFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'GBP'
});
console.log(usdFormatter.format(100));
// Output: "$100.00"
console.log(eurFormatter.format(100));
// Output: "€100.00"
console.log(gbpFormatter.format(100));
// Output: "£100.00"
Each formatter automatically inserts the appropriate currency symbol. You do not need to know which symbol corresponds to each currency code.
Locale determines symbol position and formatting
The locale parameter controls how currency amounts are formatted, including symbol position, digit grouping, and decimal separators. The same currency code produces different output depending on the locale.
const usFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
});
const deFormatter = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
});
const frFormatter = new Intl.NumberFormat('fr-FR', {
style: 'currency',
currency: 'EUR'
});
console.log(usFormatter.format(1234.56));
// Output: "€1,234.56"
console.log(deFormatter.format(1234.56));
// Output: "1.234,56 €"
console.log(frFormatter.format(1234.56));
// Output: "1 234,56 €"
All three formatters display euros, but each uses different conventions. The US English formatter places the symbol before the amount with a period as the decimal separator. The German formatter places the symbol after the amount with a space, uses periods for thousands separators, and uses a comma as the decimal separator. The French formatter uses spaces for thousands separators.
The Intl API handles these differences automatically based on the locale you specify. You do not need to know the formatting rules for each locale.
Currency formatting includes decimal places
Currency formatters automatically determine the appropriate number of decimal places based on the currency. Most currencies use two decimal places for cents or equivalent fractions.
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
console.log(formatter.format(100));
// Output: "$100.00"
console.log(formatter.format(100.5));
// Output: "$100.50"
console.log(formatter.format(100.567));
// Output: "$100.57"
The formatter always displays two decimal places for US dollars, padding with zeros when necessary and rounding when the input has more precision.
Some currencies have zero decimal places. Japanese yen does not have a fractional unit, so amounts are displayed without decimal places.
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'JPY'
});
console.log(formatter.format(1234.56));
// Output: "¥1,235"
The formatter rounds to the nearest whole number because yen amounts do not include fractional units. The Intl API knows the decimal precision for each currency and applies it automatically.
Format currency for user's locale
Instead of hardcoding a specific locale, you can use the user's browser language preferences. The navigator.language property returns the user's preferred locale.
const userLocale = navigator.language;
const formatter = new Intl.NumberFormat(userLocale, {
style: 'currency',
currency: 'USD'
});
console.log(formatter.format(1234.56));
// Output varies by user's locale
// For en-US: "$1,234.56"
// For de-DE: "1.234,56 $"
// For fr-FR: "1 234,56 $US"
This approach displays currency amounts according to each user's formatting expectations. A German user sees the symbol after the amount with German separators, while an American user sees the symbol before the amount with American separators.
You can also pass the entire navigator.languages array to enable fallback behavior when the user's top preference is unavailable.
const formatter = new Intl.NumberFormat(navigator.languages, {
style: 'currency',
currency: 'USD'
});
console.log(formatter.format(1234.56));
The API uses the first locale it supports from the array, providing automatic fallback handling.
Reusing currency formatters
Creating a new Intl.NumberFormat instance involves loading locale data and processing options. When you format multiple prices with the same locale and currency, create the formatter once and reuse it.
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
const prices = [19.99, 29.99, 49.99, 99.99];
prices.forEach(price => {
console.log(formatter.format(price));
});
// Output:
// "$19.99"
// "$29.99"
// "$49.99"
// "$99.99"
This pattern is more efficient than creating a new formatter for each price. The performance difference becomes significant when formatting arrays or lists with many values.
Displaying prices in applications
You can use currency formatters anywhere you display prices to users. This includes product listings, shopping carts, checkout pages, invoices, and financial dashboards.
const formatter = new Intl.NumberFormat(navigator.language, {
style: 'currency',
currency: 'USD'
});
const productPrice = 29.99;
const taxAmount = 2.40;
const totalPrice = productPrice + taxAmount;
document.getElementById('product-price').textContent = formatter.format(productPrice);
document.getElementById('tax-amount').textContent = formatter.format(taxAmount);
document.getElementById('total-price').textContent = formatter.format(totalPrice);
The formatted strings work like any other string value. You can insert them into text content, attributes, or any context where you display information to users.
Handling multiple currencies
Applications that support multiple currencies need to create separate formatters for each currency. The currency code determines which symbol appears, while the locale determines how the amount is formatted.
const locale = navigator.language;
const usdFormatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency: 'USD'
});
const eurFormatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency: 'EUR'
});
const gbpFormatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency: 'GBP'
});
console.log(usdFormatter.format(100));
console.log(eurFormatter.format(100));
console.log(gbpFormatter.format(100));
Each formatter displays the appropriate symbol and follows the user's locale conventions for that currency. This ensures prices are both accurate and readable regardless of which currency or locale combination is used.