Localization
Localization tests Locale, resource bundles, formatting, and locale-sensitive parsing.
Core rules
- A
Localehas language, country, and optional variant;Locale.forLanguageTag("en-US")uses BCP 47 tags.Locale.getDefault()returns the JVM default. ResourceBundle.getBundle("messages", locale)loadsmessages.propertiesplus locale-specific variants. Resolution triesbase_language_country_variant, thenbase_language_country, thenbase_language, then the base bundle, then the default locale.- Properties bundles in Java 8 are read as ISO-8859-1; non-Latin text is usually escaped with Unicode escapes.
MessageFormatinserts arguments with{0},{1}, and supports choice, date, and number subformats. Patterns use single quotes for literal braces.NumberFormatandDateFormatare locale-sensitive and are not thread-safe; create per use or synchronize.DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(locale)formatsjava.timevalues.
Worked example
Locale l = Locale.forLanguageTag("fr-FR");
ResourceBundle b = ResourceBundle.getBundle("app", l);
String s = b.getString("greeting");
If app_fr_FR.properties is missing but app_fr.properties exists, the latter is used. If no French bundle exists, the base app.properties is used; a missing key throws MissingResourceException.
Traps
new Locale("fr", "FR")andLocale.forLanguageTag("fr-FR")both work, butLocaleconstructors do not validate.- Bundle lookup falls back through the parent chain; a key missing everywhere throws
MissingResourceException. MessageFormattreats a single quote as an escape;'{0}'is literal, not a placeholder.NumberFormat.getInstance()uses grouping and locale decimal separators; it is not a parser for arbitrary formats.- Formatting with the default locale gives different output on different machines.