В этой статье напишем сайт из двух страниц, на котором можно переключать язык. В проекте используется шаблонизатор Thymeleaf.
Описание приложения
Главная страница выглядит так:
Главная страница
По умолчанию язык определяется по заголовку Accept-Language запроса. Но пользователь может изменить язык с интерфейса, и тогда он получает страницы на выбранном языке, невзирая на Accept-Language.
Для смены локали предусмотрены ссылки (они есть на картинке выше):
localhost:8080?lang=ru или localhost:8080?lang=fr
Чтобы переключить язык, достаточно щелкнуть подобную ссылку один раз, и язык сохраняется до истечения куки. Причем все дальнейшие запросы делаются без параметра:
?lang=код-локали
Потому что локаль (Locale) сохраняется в куки или в сессии (ниже покажу разницу).
Прежде всего, давайте поместим в файл сами локализованные значения.
messages_.properties — тут хранятся локализованные значения
Для каждой поддерживаемой локали создадим свой файл messages_код-локали.properties в папке ресурсов. Мы создали три файла:
messages.properties messages_ru.properties messages_fr.properties
messages_fr.properties содержит значения на французском:
greeting=Bonjour, bienvenue sur le site! fr=Fran?ais ru=Russe one=Une autre page main=Maison
messages_ru.properties — на русском.
messages.properties используется, если в файле конкретной локали значение по конкретному ключу отсутствует. В этом случае оно берется из messages.properties. То есть в messages.properties можно поместить значения, общие для всех языков. А можно поместить туда значения на английском, который все понимают, чтобы в случае отсутствия значения на конкретном языке, выводилось значение на английском.
Для начала мы не будем создавать никакой конфигурации, посмотрим, как Spring Boot определяет локаль по умолчанию.
Подход по умолчанию — анализ Accept-Language
AcceptHeaderLocaleResolver
А делает он это, как уже было сказано, анализируя заголовок Accept-Language запроса — с помощью класса AcceptHeaderLocaleResolver.
Этот класс — одна из реализаций интерфейса LocaleResolver — распознавателя локали. Используется по умолчанию, если мы не задали в конфигурации другого LocaleResolver.
Заголовок Accept-Language браузер добавляет самостоятельно, в зависимости от вашей реальной локали. Но можно имитировать его в Postman.
Попробуем в Postman имитировать французскую локаль:
Как видите, выдается страница на французском.
Возможность переключить язык из интерфейса
Теперь добавим возможность переключать язык с интерфейса.
Для этого надо настроить конфигурацию с бинами LocaleChangeInterceptor и LocaleResolver: а именно SessionLocaleResolver либо CookieLocaleResolver.
SessionLocaleResolver
Если сессии поддерживаются, то этот бин подойдет. В таком случае вся конфигурация выглядит так:
package ru.sysout; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.i18n.SessionLocaleResolver; @Configuration public class Config implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()); } @Bean public LocaleChangeInterceptor localeChangeInterceptor() { LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); lci.setParamName("lang"); return lci; } //SessionLocaleResolver @Bean public LocaleResolver localeResolver() { SessionLocaleResolver resolver = new SessionLocaleResolver(); return resolver; } }
LocaleChangeInterceptor позволяет перехватить тот самый параметр ссылки:
?lang=fr
А SessionLocaleResolver позволяет хранить локаль в сессии. То есть в нашем приложении при переключении языка выдается куки JSESSEIONID, и при любых дальнейших запросах:
localhost:8080/ localhost:8080/one
заголовок Accept-Language уже не учитывается. Учитывается передаваемый браузером во всех дальнейших запросах JSESSEIONID, и локаль восстанавливается из соответствующей сессии.
CookieLocaleResolver
Этот бин подойдет, если в приложении сессии не поддерживаются. Чтобы его использовать, в конфигурации надо заменить LocaleResolver:
@Bean public LocaleResolver localeResolver() { CookieLocaleResolver resolver = new CookieLocaleResolver(); resolver.setCookieDomain("localhost"); // 60 minutes resolver.setCookieMaxAge(60 * 60); return resolver; }
Тут мы просто выдаем другой куки (не JSESSIONID, а см. на картинке длинное название org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE=fr):
Время жизни этого куки мы тоже задаем в бине выше.
Вывод локализованных значений из ресурсов в шаблон Thymeleaf
Наконец, обратите внимание на синтаксис шаблона Thymeleaf для вывода значений из ресурсов:
<h1 th:text="#{greeting}"></h1>
Итоги
Мы рассмотрели подход и реализацию интернационализации сайта. Есть еще антипаттерн передачи языка в Path Variable:
localhost:8080/fr/one
Но его использовать не стоит.
Код примера доступен на GitHub.
Огромное спасибо за разжёвывание! Гайд 11/10.