Перейти к основному содержимому

Локализация сообщений об исключениях в Java

· 4 мин. чтения

1. Обзор

Исключения в Java используются, чтобы сигнализировать о том, что в программе что-то пошло не так. Помимо создания исключения, мы можем даже добавить сообщение для предоставления дополнительной информации.

В этой статье мы воспользуемся методом getLocalizedMessage для предоставления сообщений об исключениях на английском и французском языках.

2. Пакет ресурсов

Нам нужен способ поиска сообщений с использованием messageKey для идентификации сообщения и Locale для определения того, какой перевод предоставит значение для messageKey . Мы создадим простой класс для абстрагирования доступа к нашему ResourceBundle для получения переводов сообщений на английский и французский языки:

public class Messages {

public static String getMessageForLocale(String messageKey, Locale locale) {
return ResourceBundle.getBundle("messages", locale)
.getString(messageKey);
}

}

Наш класс Messages использует ResourceBundle для загрузки файлов свойств в наш пакет, который находится в корне нашего пути к классам. У нас есть два файла — один для наших английских сообщений и один для наших французских сообщений:

# messages.properties
message.exception = I am an exception.
# messages_fr.properties
message.exception = Je suis une exception.

3. Локализованный класс исключений

Наш подкласс Exception будет использовать Locale по умолчанию , чтобы определить, какой перевод использовать для наших сообщений. Мы получим Locale по умолчанию, используя Locale #getDefault .

Если бы наше приложение работало на сервере, мы бы использовали заголовки HTTP-запроса для определения используемой локали вместо установки значения по умолчанию. Для этой цели мы создадим конструктор, который будет принимать Locale.

Давайте создадим наш подкласс Exception . Для этого мы могли бы расширить либо RuntimeException, либо Exception . Давайте расширим Exception и переопределим getLocalizedMessage :

public class LocalizedException extends Exception {

private final String messageKey;
private final Locale locale;

public LocalizedException(String messageKey) {
this(messageKey, Locale.getDefault());
}

public LocalizedException(String messageKey, Locale locale) {
this.messageKey = messageKey;
this.locale = locale;
}

public String getLocalizedMessage() {
return Messages.getMessageForLocale(messageKey, locale);
}
}

4. Собираем все вместе

Давайте создадим несколько модульных тестов, чтобы убедиться, что все работает. Мы создадим тесты для переводов на английский и французский языки, чтобы проверить передачу пользовательского Locale в исключение во время создания:

@Test
public void givenUsEnglishProvidedLocale_whenLocalizingMessage_thenMessageComesFromDefaultMessage() {
LocalizedException localizedException = new LocalizedException("message.exception", Locale.US);
String usEnglishLocalizedExceptionMessage = localizedException.getLocalizedMessage();

assertThat(usEnglishLocalizedExceptionMessage).isEqualTo("I am an exception.");
}

@Test
public void givenFranceFrenchProvidedLocale_whenLocalizingMessage_thenMessageComesFromFrenchTranslationMessages() {
LocalizedException localizedException = new LocalizedException("message.exception", Locale.FRANCE);
String franceFrenchLocalizedExceptionMessage = localizedException.getLocalizedMessage();

assertThat(franceFrenchLocalizedExceptionMessage).isEqualTo("Je suis une exception.");
}

Наше исключение также может использовать Locale по умолчанию . Давайте создадим еще два теста, чтобы убедиться, что функциональность Locale по умолчанию работает:

@Test
public void givenUsEnglishDefaultLocale_whenLocalizingMessage_thenMessageComesFromDefaultMessages() {
Locale.setDefault(Locale.US);

LocalizedException localizedException = new LocalizedException("message.exception");
String usEnglishLocalizedExceptionMessage = localizedException.getLocalizedMessage();

assertThat(usEnglishLocalizedExceptionMessage).isEqualTo("I am an exception.");
}

@Test
public void givenFranceFrenchDefaultLocale_whenLocalizingMessage_thenMessageComesFromFrenchTranslationMessages() {
Locale.setDefault(Locale.FRANCE);

LocalizedException localizedException = new LocalizedException("message.exception");
String franceFrenchLocalizedExceptionMessage = localizedException.getLocalizedMessage();

assertThat(franceFrenchLocalizedExceptionMessage).isEqualTo("Je suis une exception.");
}

5. Предостережения

5.1. Регистрация Throwables

Нам нужно помнить о структуре ведения журнала, которую мы используем для отправки экземпляров Exception в журнал.

Log4J, Log4J2 и Logback используют getMessage для получения сообщения для записи в приложение журнала. Если мы используем java.util.logging , содержимое поступает из getLocalizedMessage .

Мы могли бы рассмотреть возможность переопределения getMessage для вызова getLocalizedMessage , чтобы нам не пришлось беспокоиться о том, какая реализация ведения журнала используется.

5.2. Серверные приложения

Когда мы локализуем наши сообщения об исключениях для клиентских приложений, нам нужно беспокоиться только о текущем Locale одной системы . Однако, если мы хотим локализовать сообщения об исключениях в приложении на стороне сервера, мы должны помнить, что переключение локали по умолчанию повлияет на все запросы на нашем сервере приложений.

Если мы решим локализовать сообщения об исключениях, мы создадим конструктор для нашего исключения, чтобы принять Locale . Это даст нам возможность локализовать наши сообщения без обновления Locale по умолчанию .

6. Резюме

Локализация сообщений об исключениях довольно проста. Все, что нам нужно сделать, это создать ResourceBundle для наших сообщений, а затем реализовать getLocalizedMessage в наших подклассах Exception .

Как обычно, примеры доступны на GitHub .