Одной из важнейших характеристик любого приложения является возможность поддержки интернационализации. Это отличный способ увеличить число пользователей приложения.
В этой статье мы рассмотрим, как Spring Boot поддерживает интернационализацию и как реализовать ее в вашем приложении.
Пакеты ресурсов
Одной из важных особенностей поддержки интернационализации в Spring Boot являются пакеты ресурсов. Пакет ресурсов - это набор файлов свойств, содержащих пары ключ-значение для различных языков. Файлы свойств могут быть загружены во время выполнения, в зависимости от предпочитаемого пользователем языка, и соответствующие переводы могут быть отображены в приложении.
Чтобы создать пакет ресурсов в Spring Boot, необходимо создать файл свойств для каждого языка, который вы хотите поддерживать. Файл свойств должен иметь одинаковое имя, но другое расширение, которое представляет код языка. Например, если вы хотите поддерживать английский и русский языки, вы создадите два файла свойств: messages_en.properties
и messages_ru.properties
.
В файлах свойств вы можете определить пары ключ-значение для различных сообщений в вашем приложении. Например, вот так:
label.one=test
label.two=test two
Аналогично для русского языка:
label.one=тест
label.two=тест два
Проблемы с кириллицей
По стандарту файлы ресурсов должны быть закодированы в ISO-8859-1
. Поэтому если просто написать label.one=тест
, на выходе получим ????
.
Необходимо преобразовать кириллицу в Unicode.
Я для этого использую плагин в Idea: String Manipulation.
MessageSource
Spring Boot предоставляет интерфейс MessageSource
, который упрощает загрузку и доступ к сообщениям в пакетах ресурсов. Интерфейс MessageSource
включает несколько методов, которые вы можете использовать для получения сообщений на основе их ключей и предпочитаемого языка пользователя.
private final MessageSource messageSource;
public String getLocalizedMessage(String key, Object[] args) {
Locale locale = LocaleContextHolder.getLocale();
return messageSource.getMessage(key, args, locale);
}
В приведенном выше примере мы используем интерфейс MessageSource
для получения локализованного сообщения на основе ключа и некоторых аргументов. Метод извлекает предпочитаемый язык пользователя из LocaleContextHolder
.
Параметры сообщения
Изменяемые параметры полезны, когда вам нужно динамически генерировать сообщения, включающие переменные, такие как имена пользователей, даты и числа.
Чтобы реализовать изменяемые параметры в сообщениях ресурсов, необходимо использовать заполнители для переменных. Например, предположим, у вас есть сообщение, которое приветствует пользователя и включает его имя. Вы можете создать ключ сообщения следующим образом:
welcome=Welcome, {0}!
В приведенном выше примере {0}
- это заполнитель для имени пользователя.
Чтобы заменить заполнитель значением, вы можете использовать метод getMessage()
, который принимает массив аргументов. Например:
String welcomeMessage = messageSource.getMessage("welcome.message", new Object[] {"John"}, Locale.US);
Именованные параметры
Можно также использовать именованные заполнители вместо пронумерованных. Например:
welcome.message=Welcome, {name}!
Чтобы заменить именованный заполнитель значением, вместо массива можно использовать Map
. Например:
Map<String, Object> args = new HashMap<>();
args.put("name", "John");
String welcomeMessage = messageSource.getMessage("welcome.message", args, Locale.US);
Демо проект
Напишем небольшое REST приложение с одним эндпойнтом.
Для начала создадим пакет ресурсов с названием messages
. После чего настроим бин MessageSource
:
@Configuration
public class AppConfig {
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setCacheSeconds(3600);
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
Обратите внимание на метод setBasename
, который указывает название пакета ресурсов.
Теперь создадим эндпойнт:
@GetMapping("/hello")
public String hello(@RequestParam("name") String name, HttpServletRequest request) {
return messageSource.getMessage(
"welcome",
new Object[]{name},
localeResolver.resolveLocale(request)
);
}
В эндпойнте hello мы принимаем имя пользователя в качестве параметра запроса и используем его в шаблоне ответа.
Также нам необходимо как-то определить язык пользователя. LocaleContextHolder
в данном случае не подходит, так как у нас многопользовательское приложение. Поэтому мы создадим свою реализацию LocaleResolver
public class UserLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
final String lang = request.getHeader("Language");
return Locale.forLanguageTag(lang);
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
// Not needed for this example
}
}
И добавим бин в AppConfig
:
@Bean
public LocaleResolver localeResolver() {
return new UserLocaleResolver();
}
@Component
или @Service
не будет работать над UserLocaleResolver
. Нужно именно создать через Java-конфигурацию.В нашем примере локализация определяется по передаваемому http-header. В зависимости от ваших требований, механизм определения будет отличаться. Например, можно получать установленный язык пользователя из БД.
Вернемся к нашему эндпойнту. Используя метод MessageSource.getMessage()
мы передаем ключ шаблона, а также параметры, которые необходимо доставить в заполнители ({0}
). Если в шаблоне есть параметры, а вы их не передадите, то будет выброшено исключение. Также вы получите исключение, если в файле ресурсов не окажется шаблона с переданным названием.
MessageSource
, чтобы упростить его использование.Сообщения ошибок валидации
Если вы используете Spring Boot Validation, о котором я рассказывал в статье: Валидация данных в Spring Boot. То вам необходимо также сообщать об ошибках пользователя на его языке.
Если вы настроили свой LocaleResolver
, то больше вам ничего не нужно делать. Spring также использует LocaleResolver
, чтобы выводить сообщения на языке пользователя.
Например, укажем ограничение на длину имени: @Size(max = 10) @RequestParam("name") String name
. И выполним запрос:
Вы можете добавить собственный текст ошибки. Для этого создайте файл ValidationMessages_ru.properties
, и напишите там текст сообщения.
jakarta.validation.custom.name=\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430\u044f \u0434\u043b\u0438\u043d\u0430 \u0438\u043c\u0435\u043d\u0438. \u0418\u043c\u044f \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043e\u0442 {min} \u0434\u043e {max}
И используйте кастомное значение в поле message
у аннотаций валидации:
@Size(max = 10, message = "{jakarta.validation.custom.name}")
Вы также можете переопределить значения, которые используются по умолчанию, чтобы не приходилось указывать ключ в message
.
Заключение
В этой статье мы рассмотрели, как Spring Boot поддерживает интернационализацию и как реализовать ее в ваших веб-приложениях.
Поддерживая интернационализацию, вы можете расширить охват вашего приложения на глобальную аудиторию, повысить вовлеченность пользователей и улучшить пользовательский опыт.
Spring Boot предоставляет мощную и гибкую основу для создания приложений, которые можно легко адаптировать к различным языкам. С помощью наборов ресурсов Spring Boot, интерфейса MessageSource
вы можете создавать надежные и масштабируемые приложения, поддерживающие интернационализацию.