Класс Locale в Java: что такое локаль и как её задать - Вопросы
Всего: 6 вопросов
1. Что такое локаль и за что отвечает класс java.util.Locale?
Что такое локаль и за что отвечает класс java.util.Locale?
Локаль — это набор региональных настроек: язык, страна (регион) и при необходимости письменность и вариант. В Java её представляет класс java.util.Locale, например ru_RU — русский язык, Россия.
Сам Locale ничего не форматирует. Это «ярлык» языка и региона, который передают в NumberFormat, DateTimeFormatter, ResourceBundle, String.format и другие классы, а уже они выбирают разделители чисел, названия месяцев или файл с переводом.
При старте JVM берёт локаль по умолчанию из настроек операционной системы, но её можно изменить или передать нужную локаль в конкретный метод.
2. Какими способами можно создать объект Locale и какой выбрать в современной Java?
Какими способами можно создать объект 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 и почему среди них нет русской локали?
Константы бывают двух видов:
- язык + страна:
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 и чем он лучше 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?
Как узнать и изменить локаль по умолчанию в 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" и какие ошибки при этом проходят молча?
Как получить 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: ошибка всплывёт сразу, а не в виде непереведённого текста.