Как настроить i18n в Spring boot 2 + Webflux + Thymeleaf?
Я просто начинаю новый проект на основе Spring boot 2 + Webflux. Об обновлении версии весеннего ботинка и замене spring-boot-starter-web
классы spring-boot-starter-webflux
например
- WebMvcConfigurerAdapter
- LocaleResolver
- LocaleChangeInterceptor
не хватает. Как теперь я могу настроить defaultLocale и перехватчик для изменения языка?
Ответы
Ответ 1
Просто добавьте WebFilter
который устанавливает заголовок Accept-Language
из значения параметра запроса. Следующий пример получает язык из параметра запроса языка в URI, например http://localhost:8080/examples?language=es
:
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.adapter.HttpWebHandlerAdapter;
import reactor.core.publisher.Mono;
import static org.springframework.util.StringUtils.isEmpty;
@Component
public class LanguageQueryParameterWebFilter implements WebFilter {
private final ApplicationContext applicationContext;
private HttpWebHandlerAdapter httpWebHandlerAdapter;
public LanguageQueryParameterWebFilter(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@EventListener(ApplicationReadyEvent.class)
public void loadHttpHandler() {
this.httpWebHandlerAdapter = applicationContext.getBean(HttpWebHandlerAdapter.class);
}
@Override
public Mono<Void> filter(final ServerWebExchange exchange, final WebFilterChain chain) {
final ServerHttpRequest request = exchange.getRequest();
final MultiValueMap<String, String> queryParams = request.getQueryParams();
final String languageValue = queryParams.getFirst("language");
final ServerWebExchange localizedExchange = getServerWebExchange(languageValue, exchange);
return chain.filter(localizedExchange);
}
private ServerWebExchange getServerWebExchange(final String languageValue, final ServerWebExchange exchange) {
return isEmpty(languageValue)
? exchange
: getLocalizedServerWebExchange(languageValue, exchange);
}
private ServerWebExchange getLocalizedServerWebExchange(final String languageValue, final ServerWebExchange exchange) {
final ServerHttpRequest httpRequest = exchange.getRequest()
.mutate()
.headers(httpHeaders -> httpHeaders.set("Accept-Language", languageValue))
.build();
return new DefaultServerWebExchange(httpRequest, exchange.getResponse(),
httpWebHandlerAdapter.getSessionManager(), httpWebHandlerAdapter.getCodecConfigurer(),
httpWebHandlerAdapter.getLocaleContextResolver());
}
}
Он использует @EventListener(ApplicationReadyEvent.class)
, чтобы избежать циклических зависимостей.
Не стесняйтесь протестировать его и предоставить отзывы об этом POC.
Ответ 2
С spring-boot-starter-webflux есть
- DelegatingWebFluxConfiguration
- LocaleContextResolver
Например, чтобы использовать параметр запроса "lang" для явного управления локалью:
-
LocaleContextResolver
, чтобы resolveLocaleContext()
возвращал SimpleLocaleContext
определенный параметром GET "lang". Я QueryParamLocaleContextResolver
эту реализацию QueryParamLocaleContextResolver
. Обратите внимание, что по умолчанию LocaleContextResolver
является org.springframework.web.server.i18n.AcceptHeaderLocaleContextResolver
.
-
Создайте класс @Configuration
который расширяет DelegatingWebFluxConfiguration
. Переопределите DelegatingWebFluxConfiguration.localeContextResolver()
для возврата QueryParamLocaleContextResolver
который мы только что создали на шаге 1. Назовите этот класс конфигурации WebConfig
.
-
В WebConfig
переопределите DelegatingWebFluxConfiguration.configureViewResolvers()
и добавьте компонент ThymeleafReactiveViewResolver
в качестве средства ThymeleafReactiveViewResolver
представления. Мы делаем это потому, что по какой-то причине DelegatingWebFluxConfiguration
пропустит ThymeleafReactiveViewResolver
после шага 2.
Также я должен упомянуть, что для использования i18n с реактивным стеком необходим этот компонент:
@Bean
public MessageSource messageSource() {
final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("classpath:/messages");
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(5);
return messageSource;
}
После создания естественного шаблона, некоторых файлов свойств и контроллера вы увидите, что:
Только не забудьте <meta charset="UTF-8">
в <head>
, иначе вы можете увидеть некоторые неприятные отображения китайских иероглифов.
Ответ 3
Другое решение с помощью пружинного веб-потока для начальной загрузки, которое является намного более чистым, состоит в том, чтобы определить свой собственный HttpHandler
с помощью WebHttpHandlerBuilder
, в котором вы можете установить свой LocaleContextResolver
.
Документация (см. 1.2.2. WebHandler API): https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-config-customize
MyLocaleContextResolver.java
public class MyLocaleContextResolver implements LocaleContextResolver {
@Override
public LocaleContext resolveLocaleContext(ServerWebExchange exchange) {
return new SimpleLocaleContext(Locale.FRENCH);
}
@Override
public void setLocaleContext(ServerWebExchange exchange, LocaleContext localeContext) {
throw new UnsupportedOperationException();
}
}
Затем в файле конфигурации (с пометкой @Configuration
) или в файле приложения для весенней загрузки определите свой собственный компонент HttpHandler
.
Application.java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public HttpHandler httpHandler(ApplicationContext context) {
MyLocaleContextResolver localeContextResolver = new MyLocaleContextResolver();
return WebHttpHandlerBuilder.applicationContext(context)
.localeContextResolver(localeContextResolver) // set your own locale resolver
.build();
}
}
Вот оно!