Number & Currency Formatting — Locale-Aware Number and Currency Display
In this tutorial, you will learn about Number & Currency Formatting. We cover key concepts, practical examples, and best practices to help you master this topic.
Locale-aware number formatting displays numbers, currencies, and percentages according to cultural conventions using the Intl.NumberFormat API.
What You'll Learn
By the end of this tutorial, you'll understand how to use Intl.NumberFormat for locale-aware number, currency, and percentage formatting, how to handle different digit separators, and how to format compact numbers for international audiences.
Why It Matters
Number formats are culturally specific. "1,234.56" is one thousand two hundred thirty-four in the US but could be misinterpreted in Europe where commas are decimal separators. Currency symbols vary ($, €, ¥, £, ر.س), and their position (before or after the number) differs by locale. Incorrect number formatting causes confusion, especially in financial applications.
Real-World Use
A global e-commerce checkout shows "1,234.56" in the US, "1.234,56" in Germany, "1 234,56" in France, and "¥1,235" in Japan — all from the same number using Intl.NumberFormat with the user's locale and appropriate currency code.
Number Format Variations
graph LR
A[Number 1234567.89
by Locale] --> B[US: 1,234,567.89]
A --> C[Germany: 1.234.567,89]
A --> D[France: 1 234 567,89]
A --> E[Switzerland: 1'234'567.89]
A --> F[India: 12,34,567.89]
A --> G[Saudi Arabia: ١٬٢٣٤٬٥٦٧٫٨٩]
style A fill:#4a90d9,color:#fff
Intl.NumberFormat Basics
// i18n/number-formatting.js — Intl.NumberFormat usage
class NumberFormatter {
constructor(locale = 'en-US') {
this.locale = locale;
}
// Basic decimal formatting
decimal(number, options = {}) {
return new Intl.NumberFormat(this.locale, {
style: 'decimal',
minimumFractionDigits: options.minFraction ?? 0,
maximumFractionDigits: options.maxFraction ?? 2,
...options
}).format(number);
}
// Currency formatting
currency(amount, currencyCode = 'USD') {
return new Intl.NumberFormat(this.locale, {
style: 'currency',
currency: currencyCode,
currencyDisplay: 'symbol'
}).format(amount);
}
// Percentage formatting
percent(number) {
return new Intl.NumberFormat(this.locale, {
style: 'percent',
minimumFractionDigits: 1,
maximumFractionDigits: 2
}).format(number);
}
// Compact notation (1K, 1M, 1B)
compact(number, notation = 'compact') {
return new Intl.NumberFormat(this.locale, {
notation: notation,
compactDisplay: 'short'
}).format(number);
}
// Engineering notation
engineering(number) {
return new Intl.NumberFormat(this.locale, {
notation: 'engineering'
}).format(number);
}
// Scientific notation
scientific(number) {
return new Intl.NumberFormat(this.locale, {
notation: 'scientific'
}).format(number);
}
// With unit
unit(number, unit) {
return new Intl.NumberFormat(this.locale, {
style: 'unit',
unit: unit,
unitDisplay: 'long'
}).format(number);
}
// Sign display
withSign(number) {
return new Intl.NumberFormat(this.locale, {
signDisplay: 'always'
}).format(number);
}
// Demo
demo() {
const num = 1234567.89;
console.log(`Locale: ${this.locale}`);
console.log(` Decimal: ${this.decimal(num)}`);
console.log(` Currency (USD): ${this.currency(1234.56, 'USD')}`);
console.log(` Currency (EUR): ${this.currency(1234.56, 'EUR')}`);
console.log(` Percent: ${this.percent(0.856)}`);
console.log(` Compact: ${this.compact(num)}`);
}
}
// Demo across locales
console.log('=== US English ===');
new NumberFormatter('en-US').demo();
console.log('\\n=== German ===');
new NumberFormatter('de-DE').demo();
console.log('\\n=== French ===');
new NumberFormatter('fr-FR').demo();
console.log('\\n=== Indian English ===');
new NumberFormatter('en-IN').demo();
console.log('\\n=== Arabic (Saudi Arabia) ===');
new NumberFormatter('ar-SA').demo();
console.log('\\n=== Japanese ===');
new NumberFormatter('ja-JP').demo();
Currency Formatting Details
// i18n/currency-formatting.js — Advanced currency formatting
class CurrencyFormatter {
constructor(locale = 'en-US') {
this.locale = locale;
}
// Format with currency symbol
symbol(amount, currency) {
return new Intl.NumberFormat(this.locale, {
style: 'currency',
currency: currency,
currencyDisplay: 'symbol'
}).format(amount);
}
// Format with currency code (USD, EUR, JPY)
code(amount, currency) {
return new Intl.NumberFormat(this.locale, {
style: 'currency',
currency: currency,
currencyDisplay: 'code'
}).format(amount);
}
// Format with currency name
name(amount, currency) {
return new Intl.NumberFormat(this.locale, {
style: 'currency',
currency: currency,
currencyDisplay: 'name'
}).format(amount);
}
// Format without trailing zeros
smart(amount, currency) {
return new Intl.NumberFormat(this.locale, {
style: 'currency',
currency: currency,
minimumFractionDigits: 0,
maximumFractionDigits: 2
}).format(amount);
}
// Accounting format (negative in parentheses)
accounting(amount, currency) {
return new Intl.NumberFormat(this.locale, {
style: 'currency',
currency: currency,
currencySign: 'accounting'
}).format(amount);
}
// Demo all display types
demo(amount = 1234.56) {
const currencies = ['USD', 'EUR', 'GBP', 'JPY', 'SAR', 'INR'];
console.log(`Locale: ${this.locale}`);
currencies.forEach(curr => {
console.log(` ${curr}: ${this.symbol(amount, curr)}`);
});
}
}
console.log('=== Currency Display Types (en-US) ===');
const usCurr = new CurrencyFormatter('en-US');
console.log('Symbol:', usCurr.symbol(1234.56, 'USD')); // $1,234.56
console.log('Code:', usCurr.code(1234.56, 'USD')); // USD 1,234.56
console.log('Name:', usCurr.name(1234.56, 'USD')); // 1,234.56 US dollars
console.log('Accounting (negative):', usCurr.accounting(-500, 'USD')); // ($500.00)
console.log('\\n=== Currency by Locale ===');
new CurrencyFormatter('en-US').demo();
new CurrencyFormatter('de-DE').demo();
new CurrencyFormatter('ja-JP').demo();
new CurrencyFormatter('ar-SA').demo();
new CurrencyFormatter('en-IN').demo();
Unit Formatting
// i18n/unit-formatting.js — Formatting numbers with units
const { NumberFormat } = require('intl');
const locales = ['en-US', 'de-DE', 'ja-JP'];
const units = [
{ value: 5, unit: 'kilometer' },
{ value: 100, unit: 'kilogram' },
{ value: 25, unit: 'liter' },
{ value: 72, unit: 'hour' },
{ value: 500, unit: 'megabyte' },
{ value: 98.6, unit: 'fahrenheit' },
{ value: 37, unit: 'celsius' },
{ value: 120, unit: 'mile-per-hour' },
{ value: 2.5, unit: 'cup' },
];
locales.forEach(locale => {
console.log(`\\n=== ${locale} Unit Formatting ===`);
units.forEach(({ value, unit }) => {
const formatted = new Intl.NumberFormat(locale, {
style: 'unit',
unit: unit,
unitDisplay: 'long'
}).format(value);
console.log(` ${value} ${unit} → ${formatted}`);
});
});
Integration with i18next
// i18n/i18next-numbers.js — Number formatting with i18next formatters
import i18next from 'i18next';
i18next.init({
lng: 'en',
resources: {
en: {
translation: {
'price': 'Price: {price, number, currency}',
'discount': 'Save {percent, number, percent}',
'distance': '{km, number, unit} from city center',
'compact_views': '{views, number, compact} views',
'revenue': 'Revenue: {amount, number, accounting}'
}
}
},
interpolation: {
format: (value, format, lng) => {
if (format === 'currency') {
return new Intl.NumberFormat(lng, {
style: 'currency',
currency: 'USD'
}).format(value);
}
if (format === 'percent') {
return new Intl.NumberFormat(lng, {
style: 'percent',
maximumFractionDigits: 0
}).format(value);
}
if (format === 'compact') {
return new Intl.NumberFormat(lng, {
notation: 'compact'
}).format(value);
}
if (format === 'accounting') {
return new Intl.NumberFormat(lng, {
style: 'currency',
currency: 'USD',
currencySign: 'accounting'
}).format(value);
}
return value;
}
}
});
// Usage in app:
// t('price', { price: 29.99 }) // Price: $29.99
// t('discount', { percent: 0.20 }) // Save 20%
// t('compact_views', { views: 1500 }) // 1.5K views
Common Mistakes
- Assuming the decimal separator is always ".". Many locales use "," for decimals and "." for thousands (1.234,56 in Germany). Always use Intl.NumberFormat instead of toFixed() + string concatenation.
- Hardcoding currency symbol positions. "$1,234.56" places the symbol before the number. In some locales, the symbol goes after (1.234,56 €) or uses a different character (ر.س for Saudi Riyal). Let Intl.NumberFormat handle positioning.
- Using the wrong currency code. "€123" is ambiguous — which of the 25+ countries using the Euro? Use locale-appropriate currency codes: EUR for Euros, USD for US Dollars, JPY for Japanese Yen.
- Forcing decimal places for currencies without sub-units. JPY and KRW have no decimal sub-units. "¥1,234.56" is incorrect — it should be "¥1,235" (or "¥1,234" if the price is exact). Use maximumFractionDigits based on the currency.
- Not handling compact notation for large numbers. Displaying "1234567890" instead of "1.2B" wastes space and reduces readability. Use notation: 'compact' for social media counts, file sizes, and analytics.
Practice Questions
- How does Intl.NumberFormat handle digit grouping separators across locales?
- What currency display options are available in Intl.NumberFormat?
- How do you format negative currency values in accounting style?
- Why should you never hardcode decimal or thousand separators?
- How does compact notation differ from engineering notation?
Challenge: Build a multi-currency pricing table that displays the same product price ($29.99) in 8+ locale-currency combinations (en-US/USD, de-DE/EUR, ja-JP/JPY, ar-SA/SAR, en-IN/INR, fr-FR/EUR, zh-CN/CNY, pt-BR/BRL). Include currency symbol display, accounting format for discounts, compact notation for view counts, and percentage formatting for tax rates.
FAQ
Mini Project
Build a global pricing dashboard: a table showing product prices in 10+ countries with locale-appropriate formatting (currency symbol, decimal/thousand separators, currency code display option), a toggle between symbol/code/name display, percentage discount formatting, compact view counts, accounting format for refunds, and unit formatting for weights and dimensions.
What's Next
You've mastered number and currency formatting. Next, learn about RTL Layout for building right-to-left layouts for Arabic and Hebrew.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro