How to configure i18n in Spring boot 2 + Webflux + Thymeleaf?

南笙酒味 提交于 2019-12-14 04:25:30

问题


I just start a new project based on Spring boot 2 + Webflux. On upgrading version of spring boot and replace spring-boot-starter-web with spring-boot-starter-webflux classes like

  • WebMvcConfigurerAdapter
  • LocaleResolver
  • LocaleChangeInterceptor

are missing. How now can I configure defaultLocale, and interceptor to change the language?


回答1:


Just add a WebFilter that sets the Accept-Language header from the value of a query parameter. The following example gets the language from the language query parameter on URIs like 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());
    }
}

It uses @EventListener(ApplicationReadyEvent.class) in order to avoid cyclic dependencies.

Feel free to test it and provide feedback on this POC.




回答2:


With spring-boot-starter-webflux, there are

  • DelegatingWebFluxConfiguration
  • LocaleContextResolver

For example, to use a query parameter "lang" to explicitly control the locale:

  1. Implement LocaleContextResolver, so that resolveLocaleContext() returns a SimpleLocaleContext determined by a GET parameter of "lang". I name this implementation QueryParamLocaleContextResolver. Note that the default LocaleContextResolver is an org.springframework.web.server.i18n.AcceptHeaderLocaleContextResolver.

  2. Create a @Configuration class that extends DelegatingWebFluxConfiguration. Override DelegatingWebFluxConfiguration.localeContextResolver() to return QueryParamLocaleContextResolver that we just created in step 1. Name this configuration class WebConfig.

  3. In WebConfig, override DelegatingWebFluxConfiguration.configureViewResolvers() and add the ThymeleafReactiveViewResolver bean as a view resolver. We do this because, for some reason, DelegatingWebFluxConfiguration will miss ThymeleafReactiveViewResolver after step 2.

Also, I have to mention that, to use i18n with the reactive stack, this bean is necessary:

    @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;
}

After creating a natural template, some properties files, and a controller, you will see that:

  • localhost:8080/test?lang=zh gives you the Chinese version

  • localhost:8080/test?lang=en gives you the English version

Just don't forget <meta charset="UTF-8"> in <head>, otherwise you may see some nasty display of Chinese characters.




回答3:


Another solution with spring boot starter web flux, which is much more cleaner, is to define your own HttpHandler using WebHttpHandlerBuilder in which you can set your LocaleContextResolver.

Documentation (see 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();
    }
}

Then in a config file (annotated with @Configuration) or in your spring boot application file, defined your own HttpHandler bean.

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();

    }

}

That's it!



来源:https://stackoverflow.com/questions/47527504/how-to-configure-i18n-in-spring-boot-2-webflux-thymeleaf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!