Класс Locale в Java: что такое локаль и как её задать - Вопросы

Всего: 6 вопросов

1. 

Что такое локаль и за что отвечает класс java.util.Locale?

Локаль — это набор региональных настроек: язык, страна (регион) и при необходимости письменность и вариант. В Java её представляет класс java.util.Locale, например ru_RU — русский язык, Россия.

Сам Locale ничего не форматирует. Это «ярлык» языка и региона, который передают в NumberFormat, DateTimeFormatter, ResourceBundle, String.format и другие классы, а уже они выбирают разделители чисел, названия месяцев или файл с переводом.

При старте JVM берёт локаль по умолчанию из настроек операционной системы, но её можно изменить или передать нужную локаль в конкретный метод.

2. 

Какими способами можно создать объект Locale и какой выбрать в современной Java?

  • Константа: Locale.FRANCE, Locale.US — ошибиться нельзя, но констант мало.
  • Locale.of("ru", "RU") (Java 19+) — основной способ, аргументы не проверяет.
  • Locale.forLanguageTag("ru-RU") (Java 7+) — из тега BCP 47 через дефис; некорректный подтег молча отбрасывается.
  • new Locale.Builder().setLanguage("ru").setRegion("RU").build() (Java 7+) — проверяет значения и бросает IllformedLocaleException.
  • new Locale("ru", "RU") — конструктор, deprecated с Java 19.

Три современных способа дают равные объекты: Locale.of("ru", "RU").equals(Locale.forLanguageTag("ru-RU")) возвращает true. Код языка пишется строчными буквами (ISO 639), код страны — заглавными (ISO 3166). На Java 8–18, где Locale.of нет, используйте Locale.forLanguageTag.

3. 

Какие константы есть в классе Locale и почему среди них нет русской локали?

Константы бывают двух видов:

  • язык + страна: Locale.US (en_US), Locale.UK (en_GB), Locale.CANADA, Locale.CANADA_FRENCH, Locale.FRANCE, Locale.GERMANY, Locale.ITALY, Locale.JAPAN, Locale.KOREA, Locale.CHINA, Locale.TAIWAN;
  • только язык: Locale.ENGLISH (en), Locale.FRENCH, Locale.GERMAN и др.;
  • плюс нейтральная Locale.ROOT.

Константы появились в Java 1.1 для небольшого набора стран, и список с тех пор не расширяли. Поэтому русскую локаль создают через Locale.of("ru", "RU") или объявляют свою константу: static final Locale RU = Locale.of("ru", "RU");.

Две частые ловушки: Locale.UK — это Великобритания, а не Украина (украинская локаль — Locale.of("uk", "UA")), а у Locale.ENGLISH нет страны, значит, нет и валюты, в отличие от Locale.US.

4. 

Что такое Locale.ROOT и чем он лучше Locale.US для машинных форматов?

Locale.ROOT — нейтральная локаль, у которой язык и страна равны пустой строке. Её toString() возвращает "", а toLanguageTag() — "und" (undetermined, «неопределённый язык» в BCP 47). Locale.ROOT равна Locale.of("") и является базовой для всех остальных локалей.

Где применяется:

  • операции, которые не должны зависеть от языка: toUpperCase(Locale.ROOT), String.format(Locale.ROOT, ...) для ключей, логов, CSV, протоколов;
  • базовые ресурсы: файл text.properties без суффикса соответствует Locale.ROOT.

Locale.US тоже даёт точку как десятичный разделитель, но это настройки реальных пользователей американского английского. Locale.ROOT специально предназначен для независимых от языка операций и сразу показывает читателю кода, что результат не для человека.

5. 

Как узнать и изменить локаль по умолчанию в Java и почему на сервере не стоит вызывать Locale.setDefault?

Текущую локаль возвращает Locale.getDefault(). JVM определяет её один раз при старте по настройкам ОС. Изменить можно вызовом Locale.setDefault(Locale.CANADA) или параметрами запуска:

java -Duser.language=ru -Duser.country=RU Main

С Java 7 у локали по умолчанию две категории Locale.Category: DISPLAY — язык названий (getDisplayCountry() и т.п.), FORMAT — формат чисел, дат и валют. setDefault(Locale) меняет обе, а setDefault(Category, Locale) — только одну и не трогает общую getDefault(). setDefault(null) бросает NullPointerException.

setDefault действует на всю JVM, а не на поток или пользователя. На сервере, где одновременно работают пользователи с разными языками, локаль запроса передают в форматтеры явно: NumberFormat.getInstance(locale), ResourceBundle.getBundle("text", locale) (в Spring её хранит LocaleContextHolder).

6. 

Как получить Locale из строки вида "ru_RU" и какие ошибки при этом проходят молча?

toString() выдаёт код с подчёркиванием (ru_RU), а forLanguageTag ждёт тег BCP 47 с дефисом (ru-RU). Поэтому подчёркивание нужно заменить:

Locale good = Locale.forLanguageTag("ru_RU".replace('_', '-')); // ru_RU
String tag = good.toLanguageTag();                                // "ru-RU"

Что происходит без замены:

  • Locale.forLanguageTag("ru_RU") без исключения возвращает пустую локаль, равную Locale.ROOT, и приложение молча переключается на базовые ресурсы;
  • Locale.of("ru_RU") не проверяет аргумент: язык становится "ru_ru", страна — пустой;
  • new Locale.Builder().setLanguage("ru_RU") бросает IllformedLocaleException.

Если код локали приходит извне (настройки, БД, HTTP-запрос), используйте Locale.Builder: ошибка всплывёт сразу, а не в виде непереведённого текста.

Страница 1 из 1