Skip to Content
Course content

21: The Intl API for Formatting Numbers and Dates

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I’ve spent a lot of time reviewing code from developers who are just starting to build apps for a global audience. One thing I see constantly is the "manual string" approach to formatting. It usually starts out innocent—you just need to put a dollar sign in front of a number and maybe limit the decimals to two. It feels easy, and for a local project, it works. But the moment your app hits a user in Berlin or Tokyo, that "easy" code becomes a bug report.

The danger of hardcoded symbols

Let's look at how most people handle currency first. You've probably written something like this at some point:

const price = 1250.5;
const formattedPrice = '$' + price.toFixed(2); 
// Result: "$1250.50"

This is what I call the "naive way." It assumes three things that aren't always true: that the currency symbol is always a prefix, that the symbol is always a dollar sign, and that a period is the universal decimal separator. If you show that string to a user in Germany, they're going to be confused because they expect the Euro symbol at the end and a comma for the decimal—something like 1.250,50 €.

You could try to write a giant switch statement to handle every country in the world, but that's a nightmare to maintain. I don't want you spending your weekend updating a list of currency symbols. Instead, we use the Intl.NumberFormat object. It's built into the browser and knows exactly how every locale on earth handles their money.

const price = 1250.5;

// For a US user
const usFormatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
});
console.log(usFormatter.format(price)); // "$1,250.50"

// For a German user
const deFormatter = new Intl.NumberFormat('de-DE', {
  style: 'currency',
  currency: 'EUR',
});
console.log(deFormatter.format(price)); // "1.250,50 €"

The trade-off here is a tiny bit more boilerplate—you have to instantiate the formatter—but the payoff is that your app is instantly professional in dozens of languages. I usually recommend creating these formatters once and reusing them across your app rather than recreating them inside a loop, which can be a performance hit.

Where manual date slicing breaks

Dates are even more treacherous. I've seen developers try to build date strings by grabbing the month and day manually, like date.getMonth() + 1 + '/' + date.getDate(). This is a recipe for disaster. Not only is the month index zero-based (which always trips people up), but the order of day and month changes depending on where you are. In the US, it's Month/Day/Year; in most of the rest of the world, it's Day/Month/Year.

If you use toDateString() or toLocaleDateString() without arguments, you're relying on the user's browser settings, which is better, but it gives you zero control over the style. What if you want a long-form date for a header but a short-form date for a table cell?

This is where Intl.DateTimeFormat comes in. It lets you define the "shape" of the date while letting the API handle the locale-specific ordering.

const now = new Date();

// A formal, long-form date
const longDate = new Intl.DateTimeFormat('en-GB', { 
  dateStyle: 'full', 
  timeStyle: 'short' 
}).format(now);
// Result: "Tuesday, 21 May 2024 at 14:30"

// A compact version for a UI badge
const shortDate = new Intl.DateTimeFormat('en-GB', { 
  dateStyle: 'short' 
}).format(now);
// Result: "21/05/2024"

I love the dateStyle and timeStyle options because they remove the guesswork. You don't have to remember if "short" means MM/DD or DD/MM; the API just knows based on the locale string you provided (like 'en-GB' or 'ja-JP'). If you need something even more specific, you can pass an array of options like weekday: 'long' or month: 'short' to cherry-pick exactly what appears.




📋 Practical Task

Build a Multi-Locale Order Summary Formatter

You are building a checkout confirmation page for an international store. You need to create a function called formatOrderSummary that takes an order object and a locale string, and returns a human-readable summary.

Requirements:

  • The function should accept an object with these properties: amount (number), currency (string, e.g., 'USD', 'EUR', 'JPY'), and orderDate (Date object).
  • It should accept a second argument: locale (string, e.g., 'en-US', 'de-DE').
  • Use Intl.NumberFormat to format the amount according to the currency and locale.
  • Use Intl.DateTimeFormat to format the date using the 'medium' dateStyle.
  • Return a template literal string in this format: "Your order from [Formatted Date] was [Formatted Amount]."

Test your function with these cases:

const order = {
  amount: 4500.75,
  currency: 'EUR',
  orderDate: new Date('2023-12-25T10:00:00')
};

console.log(formatOrderSummary(order, 'de-DE')); 
// Expected output: "Your order from 25. Dez. 2023 was 4.500,75 €."

console.log(formatOrderSummary(order, 'en-US')); 
// Expected output: "Your order from Dec 25, 2023 was €4,500.75."
Rating
0 0

There are no comments for now.

to be the first to leave a comment.