-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
21: The Intl API for Formatting Numbers and Dates
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'), andorderDate(Date object). - It should accept a second argument:
locale(string, e.g., 'en-US', 'de-DE'). - Use
Intl.NumberFormatto format the amount according to the currency and locale. - Use
Intl.DateTimeFormatto 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."
There are no comments for now.