Класс ResourceBundle - Вопросы

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

1. 

Что такое ResourceBundle в Java и как устроен properties-файл для него?

java.util.ResourceBundle — это набор локализованных ресурсов (строк и объектов), который Java загружает для нужной локали по базовому имени. Тексты интерфейса хранятся отдельно от кода, и новый язык добавляется новым файлом без перекомпиляции.

Properties-файл — текстовый файл, где каждая строка задаёт пару «ключ = значение»:

  • строки, начинающиеся с # или !, — комментарии;
  • пробелы вокруг = игнорируются, вместо = можно писать :;
  • длинное значение переносится на следующую строку символом в конце строки;
  • ключи принято группировать через точку: label.button, message.welcome.

Все файлы набора содержат одни и те же ключи, переведённые для своей локали.

2. 

Как называются файлы одного набора ресурсов и как их загрузить методом ResourceBundle.getBundle()?

У всех файлов набора общее базовое имя, к которому через подчёркивание добавляются код языка и код страны: text.properties (базовый файл), text_ru_RU.properties, text_fr_CA.properties.

Набор загружает статический метод ResourceBundle.getBundle(baseName, locale), где baseName — базовое имя без суффиксов и без расширения:

ResourceBundle rb = ResourceBundle.getBundle("text", Locale.of("fr", "CA"));

Файлы ищутся в classpath, а не в рабочей папке: в Maven/Gradle — в src/main/resources. Если файл лежит в пакете, базовое имя указывается полностью через точки: getBundle("com.example.i18n.text", locale). Вызов без локали getBundle("text") использует Locale.getDefault().

3. 

В каком порядке ResourceBundle.getBundle() ищет файлы и почему для Locale.US может вернуться русский текст?

Поиск идёт от самого точного имени к общему. Для запроса fr_CA при локали JVM по умолчанию ru_RU кандидаты такие:

  1. text_fr_CA, text_fr — запрошенная локаль;
  2. text_ru_RU, text_ru — локаль по умолчанию;
  3. text — базовый файл.

Берётся первый найденный файл, а более общие становятся его родителями для недостающих ключей. Поэтому если нет text_en_US и text_en, но есть text_ru, запрос Locale.US на машине с русской локалью вернёт русский бандл, а не text.properties. Исключение — запрос Locale.ROOT: для него сразу берётся базовый файл.

Отключить шаг с локалью по умолчанию можно через ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_DEFAULT) — это полезно на сервере, где локаль JVM не связана с языком пользователя. В именованных модулях перегрузки с Control бросают UnsupportedOperationException.

4. 

В какой кодировке ResourceBundle читает properties-файлы в Java 8 и в Java 9+?

В Java 8 и раньше — в ISO-8859-1, поэтому кириллицу записывали escape-последовательностями uXXXX (утилитой native2ascii или настройкой IDE), иначе выводились «кракозябры».

С Java 9 (JEP 226) PropertyResourceBundle читает файлы в UTF-8, и кириллицу можно писать как есть. Если файл не является корректным UTF-8, он перечитывается как ISO-8859-1. Последовательности uXXXX работают во всех версиях, а прежнее поведение возвращает свойство -Djava.util.PropertyResourceBundle.encoding=ISO-8859-1. Утилита native2ascii из JDK 9 удалена.

Переход на UTF-8 касается только ResourceBundle: Properties.load(InputStream) по-прежнему читает ISO-8859-1. Для UTF-8 в Properties передают Reader: props.load(new InputStreamReader(in, StandardCharsets.UTF_8)).

5. 

Когда возникает MissingResourceException и как его избежать?

MissingResourceException — непроверяемое исключение (наследник RuntimeException). ResourceBundle бросает его в двух случаях:

  • getBundle() не нашёл ни одного файла набора, включая базовый: Can't find bundle for base name labels, locale fr_CA;
  • getString() или getObject() не нашёл ключ ни в файле, ни в его родителях: Can't find resource for bundle java.util.PropertyResourceBundle, key str9. null при этом не возвращается.

Чтобы избежать исключения, держите базовый файл без суффиксов (text.properties) со всеми ключами и проверяйте ключ методом containsKey(). Если файл существует, а исключение всё равно возникает, обычно виновато расположение: файл не попал в classpath или базовое имя указано без пакета.

6. 

Чем ListResourceBundle отличается от PropertyResourceBundle?

Это две стандартные реализации абстрактного класса ResourceBundle:

  • PropertyResourceBundle читает ресурсы из properties-файла (text_fr.properties), значения — только String. Подходит для текстов интерфейса: переводчики правят файлы без программиста.
  • ListResourceBundle берёт ресурсы из Java-класса (class text_fr extends ListResourceBundle, метод getContents() возвращает Object[][]) и хранит объекты любого типа: числа, массивы, иконки. Нестроковые значения читают через getObject().

Для каждого имени-кандидата getBundle() сначала ищет класс, затем properties-файл, поэтому при наличии и класса Messages_fr, и файла Messages_fr.properties загружается класс. Метод getStringArray() с properties-файлом не работает: значение там всегда строка, и будет ClassCastException.

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