Stage 3: Core APIs, lesson 12 of 12

Internationalisation: Locale, formatting and resource bundles

Intermediate3 min read@since 19Code runs on your Java 25
Explain it forThe essentials plus production detail and pitfalls.

A Locale identifies a language and region, such as en-IN, hi-IN or en-US. Java uses it to format and sort things the local way:

  • NumberFormat: numbers, percentages and currencies, including Indian digit grouping for en-IN.
  • DateTimeFormatter with ofLocalizedDate: dates in the local style and language.
  • ResourceBundle: translated text from messages_hi.properties, messages_en.properties and so on, with MessageFormat for placeholders.
  • Collator: sorting that follows a language's alphabet rules.

Keep every user-facing text in resource bundles, store times as Instant and format them for the user, and store money as numbers with a currency code, formatting only when you display it. Java 18 made UTF-8 the default charset, which removes many encoding bugs.

Example

Java
Locale india = Locale.of("en", "IN");          // Java 19+ (new Locale(...) before)
NumberFormat rupees = NumberFormat.getCurrencyInstance(india);
System.out.println(rupees.format(125000.5));    // ₹1,25,000.50

NumberFormat us = NumberFormat.getNumberInstance(Locale.US);
System.out.println(us.format(125000.5));        // 125,000.5

DateTimeFormatter hindi = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(Locale.of("hi", "IN"));
System.out.println(LocalDate.of(2026, 11, 8).format(hindi));   // the date in Hindi
Translated messages
// src/main/resources/messages_en.properties:  welcome=Welcome, {0}! You have {1} lessons left.
// src/main/resources/messages_hi.properties:  welcome=स्वागत है, {0}! आपके {1} पाठ बाकी हैं।
ResourceBundle bundle = ResourceBundle.getBundle("messages", Locale.of("hi", "IN"));
String text = MessageFormat.format(bundle.getString("welcome"), "Asha", 12);

Common mistake

Formatting with the server's default locale. A server in the US prints $ and 125,000 for an Indian user; pass the user's Locale explicitly.

Under the hood

Java takes its locale data from the Unicode CLDR project, so formats follow current conventions (which can change between Java versions; don't assert exact formatted strings in tests without fixing the locale). In Spring Boot, MessageSource and LocaleResolver load bundles and pick the user's language from the Accept-Language header. Never build sentences by joining translated fragments: word order differs between languages, so translate whole sentences with placeholders.

Check yourself

Which class formats 125000.5 as ₹1,25,000.50 for India?

How this connects

Where this leads

You've reached the end of this thread. Try a learning path for what's next.

Was this lesson helpful?

Finished reading? Mark it complete to track your progress.