Локализация сайта на Spring Boot

В этой статье напишем сайт из двух страниц, на котором можно переключать язык. В проекте используется шаблонизатор 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
Через знак равенства перечислены код (который будет в Thymeleaf-шаблоне) и значение (которое будет на итоговой html-странице).

messages_ru.properties – на русском.

messages.properties используется, если в файле конкретной локали значение по конкретному ключу отсутствует. В этом случае оно берется из messages.properties. То есть в messages.properties можно поместить значения, общие для всех языков. А можно поместить туда значения на английском, который все понимают, чтобы в случае отсутствия значения на конкретном языке, выводилось значение на английском.

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

Подход по умолчанию – анализ Accept-Language

AcceptHeaderLocaleResolver

А делает он это, как уже было сказано, анализируя заголовок Accept-Language запроса – с помощью класса AcceptHeaderLocaleResolver.

Этот класс – одна из реализаций интерфейса LocaleResolver – распознавателя локали. Используется по умолчанию, если мы не задали в конфигурации другого LocaleResolver.

Заголовок Accept-Language браузер добавляет самостоятельно, в зависимости от вашей реальной локали. Но можно имитировать его в Postman.

Попробуем в Postman имитировать французскую локаль:

Запрос в PostMan с имитацией Accept-Language
Запрос в PostMan с имитацией Accept-Language

Как видите, выдается страница на французском.

Возможность переключить язык из интерфейса

Теперь добавим возможность переключать язык с интерфейса.

Для этого надо настроить конфигурацию с бинами 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.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *