Java Locale: Create, Match and Use Locales with Examples

Java Locale examples on Java 25. Create locales with Locale.of() and forLanguageTag(), set the default, match Accept-Language and format dates and numbers.

Diagram splitting the language tag zh-Hant-TW-u-nu-hanidec into language, script, region and Unicode extension

A Java Locale (java.util.Locale) is an object that names a language and, optionally, a region, a script and a variant, such as en-US or fr-CA, and the locale-sensitive classes use it to format dates, numbers and messages for that audience. The Locale class itself formats nothing; we pass it to classes such as DateTimeFormatter, NumberFormat and ResourceBundle.

We use Locale whenever an app serves users in more than one language or country, for example, to show prices with the right separators, to print month names in the user’s language and to load translated text.

The following example builds a locale for Canadian French and uses it with two locale-sensitive classes.

Locale canadianFrench = Locale.of("fr", "CA");
String tag = canadianFrench.toLanguageTag();                       // "fr-CA"
String name = canadianFrench.getDisplayName(Locale.US);            // "French (Canada)"
String number = NumberFormat.getInstance(canadianFrench).format(1234.5);   // "1 234,5"
String month = LocalDate.of(2026, 3, 14).format(DateTimeFormatter.ofPattern("d MMMM", canadianFrench));   // "14 mars"

Notice that the grouping separator is a no-break space and the decimal separator is a comma, and we wrote neither. We look at the parts of a locale first, followed by the five ways to create one, the default locale and its categories, and the classes that use a locale, including language matching for HTTP requests.

1. Introduction to Locale

A Locale follows the IETF BCP 47 standard for language tags, the same tags that browsers send in the Accept-Language header. Every part except the language is optional.

Diagram splitting the language tag zh-Hant-TW-u-nu-hanidec into language, script, region and Unicode extension
Each part of a BCP 47 tag maps to one getter of Locale; only the language is required
PartGetterExample valueMeaning
LanguagegetLanguage()en, fr, zhISO 639 code, always lowercase
ScriptgetScript()Hant, LatnISO 15924 writing system
RegiongetCountry()US, CA, TWISO 3166 code, always uppercase
VariantgetVariant()POSIXRarely used dialect or platform tag
ExtensiongetUnicodeLocaleType(“nu”)-u-nu-thai, -u-ca-japaneseNumber system, calendar and other preferences

A class that changes its output by Locale is called locale-sensitive. The ones we meet most often are DateTimeFormatter for dates, NumberFormat for numbers and currencies, ResourceBundle and MessageFormat for translated text, and the methods String.format(), toUpperCase() and toLowerCase().

Locale taiwan = Locale.forLanguageTag("zh-Hant-TW");
String language = taiwan.getLanguage();          // "zh"
String script = taiwan.getScript();              // "Hant"
String region = taiwan.getCountry();             // "TW"
String english = taiwan.getDisplayName(Locale.US);   // "Chinese (Traditional, Taiwan)"
String iso3 = Locale.US.getISO3Country();        // "USA"

2. Creating the Locale Instance

Java offers five ways to get a Locale. They differ in how much they validate, so the choice depends on where the value comes from.

2.1. Inbuilt Constants

The Locale class has constants for common languages and countries. Constants named after a country, such as Locale.US, Locale.GERMANY and Locale.JAPAN, contain a language and a region. Constants named after a language, such as Locale.ENGLISH and Locale.FRENCH, have no region, which matters for currency formatting.

String usRegion = Locale.US.getCountry();                 // "US"
String englishRegion = Locale.ENGLISH.getCountry();       // ""
boolean sameAsBuilt = Locale.US.equals(new Locale.Builder().setLanguage("en").setRegion("US").build());   // true

For other languages, we build the locale ourselves. Java has no constant for India, Brazil or Spain, for example.

2.2. Locale Constructors (Deprecated in Java 19)

The constructors new Locale(language), new Locale(language, country) and new Locale(language, country, variant) still work, but Java 19 deprecated them. They do not validate their arguments, and the name suggests that each call creates a new object, whereas locales are cached values. Existing code compiles with a deprecation warning, and we replace each call with Locale.of().

2.3. Locale.of() Methods [Since Java 19]

The static Locale.of() methods take the same arguments as the old constructors and return a cached instance. They normalize the case, so Locale.of(“EN”, “us”) is the same locale as Locale.US.

Locale hindiIndia = Locale.of("hi", "IN");     // hi_IN
Locale spanish = Locale.of("es");              // es
Locale normalized = Locale.of("EN", "us");     // en_US
boolean same = normalized.equals(Locale.US);   // true

2.4. Locale.Builder

The Locale.Builder class sets each part with its own method and validates it against BCP 47. An invalid value throws IllformedLocaleException, so the builder is the right choice for values that users or configuration files provide.

Locale brazil = new Locale.Builder().setLanguage("pt").setRegion("BR").build();   // pt_BR
Locale thaiDigits = new Locale.Builder().setLanguage("th").setRegion("TH").setUnicodeLocaleKeyword("nu", "thai").build();   // th_TH_#u-nu-thai
Locale bad = new Locale.Builder().setLanguage("en").setRegion("USA1").build();   // IllformedLocaleException: Ill-formed region: USA1 [at index 0]

2.5. Locale.forLanguageTag() Factory Method

If we already have a language tag, such as a value from the Accept-Language header or a user setting, forLanguageTag() turns it into a Locale. It understands scripts and extensions, which the of() methods cannot set.

Locale fromHeader = Locale.forLanguageTag("en-GB");                // en_GB
Locale withScript = Locale.forLanguageTag("sr-Latn-RS");          // sr_RS_#Latn
Locale wrongSeparator = Locale.forLanguageTag("en_US");           // empty locale, tag "und"
String undefined = wrongSeparator.toLanguageTag();                // "und"

The method forLanguageTag() never throws an exception, so a tag written as en_US returns an empty locale without any warning. BCP 47 tags use hyphens, and the en_US form is only the output of Locale.toString(). When the input may be malformed, we use new Locale.Builder().setLanguageTag(tag), which throws IllformedLocaleException instead.

2.6. Language Ranges

Users often accept several languages in order of preference. A browser sends a header such as fr-CH, fr;q=0.9, en;q=0.8, where the q value is a weight between 0 and 1. The class Locale.LanguageRange parses such a list, and two methods pick from the locales our app supports.

  • The method Locale.filter() returns every supported locale that matches a range, ordered by the user’s preference.
  • The method Locale.lookup() returns the single best locale, or null when nothing matches. It shortens a range from the end, so fr-CH becomes fr, but it never extends fr to fr-FR.
List<Locale.LanguageRange> wanted = Locale.LanguageRange.parse("fr-CH,fr;q=0.9,en;q=0.8");
List<Locale> supported = List.of(Locale.US, Locale.FRANCE, Locale.GERMANY);
List<Locale> matches = Locale.filter(wanted, supported);          // [fr_FR, en_US]
Locale best = Locale.lookup(wanted, supported);                   // null
Locale bestByLanguage = Locale.lookup(wanted, List.of(Locale.US, Locale.FRENCH, Locale.GERMANY));   // fr

Say a travel booking site is available in US English, French and German. A visitor from Geneva sends the header from the example, and filter() picks French first, followed by English, so the visitor sees French pages. The site never shows German, because the visitor did not ask for it.

3. Setting the Default Locale

Every locale-sensitive method that we call without a Locale uses the default locale of the JVM. The JVM sets it at startup from the operating system, and we can override it with the system properties user.language and user.country, for example, java -Duser.language=de -Duser.country=DE -jar app.jar. Reading the current value is covered in Getting the Current Locale in Java.

3.1. Using Locale.setDefault()

The method Locale.setDefault() changes the default for the whole JVM, in every thread. It suits a desktop app or a test, but in a server that handles many users at once, one request would change the language of all others. So in web apps we pass the user’s locale as an argument instead.

Locale original = Locale.getDefault();
Locale.setDefault(Locale.FRANCE);
String monthName = LocalDate.of(2026, 3, 14).format(DateTimeFormatter.ofPattern("MMMM"));   // "mars"
Locale.setDefault(original);

3.2. Setting Locale Categories

The default locale has two categories, defined by the enum Locale.Category. We can set them separately, for example, an English user interface with German number and date formats.

  • Locale.Category.DISPLAY is the language of the user interface, used by ResourceBundle and by getDisplayName().
  • Locale.Category.FORMAT is the locale for formatting dates, numbers and currencies.
Locale.setDefault(Locale.Category.DISPLAY, Locale.US);
Locale.setDefault(Locale.Category.FORMAT, Locale.GERMANY);
String amount = String.format("%,.2f", 1234.5);                 // "1.234,50"
String label = Locale.GERMANY.getDisplayName();                 // "German (Germany)"
Locale.setDefault(Locale.US);

Calling Locale.setDefault(Locale) sets both categories at once, which is why the last line resets the example.

4. Using Locale

A Locale becomes useful when we pass it to the classes that format or translate. All of them accept it as a method argument, which keeps each call independent of the JVM default.

4.1. Using ResourceBundle

A ResourceBundle loads translated text from properties files whose names end with the locale, such as messages_fr.properties. Since Java 9, these files are read as UTF-8, so accented characters need no escapes.

# messages.properties (base bundle, the fallback)
booking.confirmed=Your booking is confirmed

# messages_fr.properties
booking.confirmed=Votre réservation est confirmée
ResourceBundle french = ResourceBundle.getBundle("messages", Locale.FRANCE);
String frText = french.getString("booking.confirmed");    // "Votre réservation est confirmée"
ResourceBundle german = ResourceBundle.getBundle("messages", Locale.GERMANY);
String deText = german.getString("booking.confirmed");    // "Your booking is confirmed"

For Locale.FRANCE, Java first looks for messages_fr_FR and next for messages_fr, which exists. For Locale.GERMANY, no German file exists, so Java tries the default locale and finally uses the base file. Spring wraps the same lookup in ResourceBundleMessageSource.

4.2. Using DateTimeFormatter

For dates, we pass the locale to DateTimeFormatter, either with a pattern or with a localized style. The localized styles also change the order of the fields, as Localized Date Time Formats shows.

LocalDate checkIn = LocalDate.of(2026, 3, 14);
String usDate = checkIn.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(Locale.US));   // "March 14, 2026"
String spDate = checkIn.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(Locale.of("es", "ES")));   // "14 de marzo de 2026"
String weekday = checkIn.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ITALY);   // "sabato"

4.3. Using NumberFormat

The NumberFormat class formats numbers, percentages and currencies. A locale with a region also selects the currency, which is why Locale.ENGLISH alone would print the generic currency sign. Currency formatting gets its own article, Localized Currency Formatting.

String usNumber = NumberFormat.getInstance(Locale.US).format(123456789.123);         // "123,456,789.123"
String deNumber = NumberFormat.getInstance(Locale.GERMANY).format(123456789.123);    // "123.456.789,123"
String usPrice = NumberFormat.getCurrencyInstance(Locale.US).format(123.456);        // "$123.46"
String percent = NumberFormat.getPercentInstance(Locale.US).format(0.256);           // "26%"

4.4. Upper and Lower Case

The methods toUpperCase() and toLowerCase() without an argument use the default locale. In Turkish, the uppercase of i is a dotted capital I, so code that compares keys after toUpperCase() fails on a server with a Turkish default. For keys, file names and protocol values, we pass Locale.ROOT.

String turkish = "title".toUpperCase(Locale.of("tr"));     // "TİTLE"
String safeKey = "title".toUpperCase(Locale.ROOT);          // "TITLE"

5. Java Locale FAQs

The tag format, the full list of locales, country names and the old Hebrew code come up in most Locale questions.

5.1. What is the difference between en_US and en-US?

The form en_US is the output of Locale.toString(), and en-US is the BCP 47 language tag from toLanguageTag(). We store and exchange the tag form, because forLanguageTag() reads only hyphens.

5.2. How do we list all available locales?

We call Locale.getAvailableLocales(), or the stream method Locale.availableLocales() added in Java 21. On JDK 25, both return 1,158 locales.

long count = Locale.availableLocales().count();   // 1158

5.3. How do we get a country name from a country code?

We create a locale with the region and call getDisplayCountry() with the language of the output.

String country = Locale.of("", "JP").getDisplayCountry(Locale.US);       // "Japan"
String inGerman = Locale.of("", "JP").getDisplayCountry(Locale.GERMANY);  // "Japan"
String inFrench = Locale.of("", "DE").getDisplayCountry(Locale.FRANCE);   // "Allemagne"

5.4. Why does Locale.of(“iw”) return he?

Since Java 17, Locale maps the old ISO codes iw, ji and in to the current codes he, yi and id. Before Java 17, the mapping went the other way.

6. Conclusion

A Locale names a language with an optional script, region, variant and extensions, and we pass it to the classes that format and translate. Since Java 19, we create locales with Locale.of(), Locale.Builder or forLanguageTag() instead of the deprecated constructors.

The default locale is JVM-wide, with separate DISPLAY and FORMAT categories, so server code passes the user’s locale explicitly. For HTTP requests, Locale.filter() and Locale.lookup() match the user’s preferences against the languages our app supports. Our date and time tutorial lists the formatting guides that build on Locale.

7. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. ¿How can I change the format to a locale? I need to set ‘.’ as decimal separator and ‘\” as grouping separator in the default local so all JComponents use that settings since begining.

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.