Platform & Advanced APIs
Internationalization
i18n separates code from locale-sensitive behavior:
Locale drives number/date/currency formatting, collation, and message selection; ResourceBundle externalizes translatable text; everything Unicode flows in UTF-8.Locale= language + region (+ script/variant):Locale.forLanguageTag("sr-Latn-RS")- Format numbers/currency/dates with
NumberFormat/DateTimeFormatter.withLocale— never string-build them ResourceBundle+messages_de_DE.propertiesselects translations by locale with fallbackMessageFormat/ChoiceFormathandle placeholder order and plural forms per language- Collation (
Collator) sorts text per locale rules —String.compareTois code-point order, not human order - Never default silently: locale-sensitive APIs have overloads taking an explicit
Locale
double amount = 1234567.89;
NumberFormat de = NumberFormat.getCurrencyInstance(Locale.GERMANY);
NumberFormat us = NumberFormat.getCurrencyInstance(Locale.US);
de.format(amount); // "1.234.567,89 €"
us.format(amount); // "$1,234,567.89"
var fmt = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);
LocalDate.now().format(fmt.withLocale(Locale.FRANCE)); // "mardi 7 juillet 2026"// messages_en.properties: cart.items={0,choice,0#no items|1#one item|1<{0} items}
// messages_de.properties: cart.items={0,choice,0#keine Artikel|1#ein Artikel|1<{0} Artikel}
ResourceBundle msgs = ResourceBundle.getBundle("messages", userLocale);
String text = MessageFormat.format(msgs.getString("cart.items"), itemCount);The deeper lessons (Core Java II ch. 7): translatable strings must be whole messages with placeholders — concatenating fragments breaks under different word orders; plurals need ChoiceFormat/ICU rules, not if (n == 1); case conversion is locale-sensitive (the Turkish dotless-ı makes "I".toLowerCase() locale-dependent — use toLowerCase(Locale.ROOT) for protocol strings); and sorting user-visible lists needs Collator, which knows that ä sorts differently in German and Swedish.