Internationalisation: Locale, formatting and resource bundles
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 foren-IN.DateTimeFormatterwithofLocalizedDate: dates in the local style and language.ResourceBundle: translated text frommessages_hi.properties,messages_en.propertiesand so on, withMessageFormatfor 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
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// 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.