Locale, NumberFormat & resource bundles
Adapting output for different regions and externalising strings for i18n.
Internationalisation basics
Locale represents a language/region combination:
Locale us = Locale.US; // en_US
Locale germany = Locale.GERMANY; // de_DE
Locale custom = new Locale("fr", "CA"); // fr_CA — French Canada
Locale tag = Locale.forLanguageTag("pt-BR"); // BCP 47 tag — preferred in modern code
NumberFormat formats numbers, currencies, and percentages according to a locale:
NumberFormat nf = NumberFormat.getNumberInstance(Locale.GERMANY);
String s = nf.format(1_234_567.89); // "1.234.567,89" (Germany uses . for grouping, , for decimal)
NumberFormat cf = NumberFormat.getCurrencyInstance(Locale.US);
String money = cf.format(9.99); // "$9.99"
NumberFormat pf = NumberFormat.getPercentInstance(Locale.US);
String pct = pf.format(0.75); // "75%"
DecimalFormat is a NumberFormat subclass for custom patterns (useful on the exam):
DecimalFormat df = new DecimalFormat("#,###.00");
df.format(1234.5); // "1,234.50"
Resource bundles externalise user-facing strings so the same code runs in any language:
// messages_en.properties
greeting = Hello
// messages_de.properties
greeting = Hallo
ResourceBundle rb = ResourceBundle.getBundle("messages", Locale.GERMANY);
System.out.println(rb.getString("greeting")); // Hallo
The JVM walks a search order: messages_de_DE.properties → messages_de.properties → messages.properties (the default bundle). If the specific bundle is not found, it falls back to the default.