Spring Boot 如何在Sping Boot 2 + Webflux + Thymeleaf中配置i18n?

vxf3dgd4  于 2023-01-30  发布在  Spring
关注(0)|答案(5)|浏览(168)

我刚开始一个基于Sping Boot 2 + Webflux的新项目。在升级Spring Boot版本时,用spring-boot-starter-webflux类替换spring-boot-starter-web,如

  • WebMvc配置器适配器
  • 本地解析程序
  • 本地更改拦截器

现在我如何配置defaultLocale和拦截器来更改语言?

q8l4jmvw

q8l4jmvw1#

只需添加一个WebFilter,它根据查询参数的值设置Accept-Language标头。以下示例从http://localhost:8080/examples?language=es等URI上的language查询参数获取语言:

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的反馈。

myss37ts

myss37ts2#

使用Spring启动器webflux,有

  • 正在委派WebFluxConfiguration
  • 本地上下文解析器

例如,要使用查询参数“lang”显式控制语言环境:
1.实现LocaleContextResolver,使resolveLocaleContext()返回由“lang”的GET参数确定的SimpleLocaleContext,我将此实现命名为QueryParamLocaleContextResolver,注意默认的LocaleContextResolverorg.springframework.web.server.i18n.AcceptHeaderLocaleContextResolver
1.创建一个扩展DelegatingWebFluxConfiguration@Configuration类。重写DelegatingWebFluxConfiguration.localeContextResolver()以返回我们刚刚在步骤1中创建的QueryParamLocaleContextResolver。将此配置类命名为WebConfig
1.在WebConfig中,覆盖DelegatingWebFluxConfiguration.configureViewResolvers()并添加ThymeleafReactiveViewResolver bean作为视图解析器,我们这样做是因为,由于某些原因,DelegatingWebFluxConfiguration在步骤2之后将错过ThymeleafReactiveViewResolver
另外,我必须提到,要将i18n与React堆栈一起使用,这个bean是必需的:

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

在创建了一个自然模板、一些属性文件和一个控制器之后,您将看到:

  • localhost:8080/test?lang=zh为您提供中文版本
  • localhost:8080/test?lang=en为您提供英语版本

只是不要忘记<head>中的<meta charset="UTF-8">,否则您可能会看到一些讨厌的中文字符显示。

qyyhg6bp

qyyhg6bp3#

从Sping Boot 2.4.0开始,WebFluxAutoConfiguration包含了LocaleContextResolver的bean定义,它允许我们注入自定义的LocaleContextResolver。下面是Spring Boot 2.5.4中的默认bean定义(在早期版本中实现可能有所不同),以供参考:

@Bean
@Override
@ConditionalOnMissingBean(name = WebHttpHandlerBuilder.LOCALE_CONTEXT_RESOLVER_BEAN_NAME)
public LocaleContextResolver localeContextResolver() {
    if (this.webProperties.getLocaleResolver() == WebProperties.LocaleResolver.FIXED) {
        return new FixedLocaleContextResolver(this.webProperties.getLocale());
    }
    AcceptHeaderLocaleContextResolver localeContextResolver = new AcceptHeaderLocaleContextResolver();
    localeContextResolver.setDefaultLocale(this.webProperties.getLocale());
    return localeContextResolver;
}

您可以提供自己的LocaleContextResolver实现,以便通过提供自定义bean定义从查询参数获取语言环境:

//@Component("localeContextResolver")
@Component(WebHttpHandlerBuilder.LOCALE_CONTEXT_RESOLVER_BEAN_NAME)
public class RequestParamLocaleContextResolver implements LocaleContextResolver {
    @Override
    public LocaleContext resolveLocaleContext(ServerWebExchange exchange) {
        List<String> lang = exchange.getRequest().getQueryParams().get("lang");
        Locale targetLocale = null;
        if (lang != null && !lang.isEmpty()) {
            targetLocale = Locale.forLanguageTag(lang.get(0));
        }
        if (targetLocale == null) {
            targetLocale = Locale.getDefault();
        }
        return new SimpleLocaleContext(targetLocale);
    }

    @Override
    public void setLocaleContext(ServerWebExchange exchange, LocaleContext localeContext) {
        throw new UnsupportedOperationException(
            "Cannot change lang query parameter - use a different locale context resolution strategy");
    }
}

注意,框架使用具有特定名称localeContextResolverWebHttpHandlerBuilder.LOCALE_CONTEXT_RESOLVER_BEAN_NAME)的LocaleContextResolver。您需要提供具有给定名称的bean。请参见#24209

slhcrj9b

slhcrj9b4#

spring boot starter web flux的另一个解决方案要干净得多,那就是使用WebHttpHandlerBuilder定义自己的HttpHandler,在WebHttpHandlerBuilder中可以设置你的LocaleContextResolver
文档(参见1.2.2. WebHandler API):www.example.comhttps://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-config-customize

    • 我的本地上下文解析器. 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注解)或您的springboot应用程序文件中,定义您自己的HttpHandlerbean。

    • 应用程序. 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();

    }

}

就是这样!

apeeds0o

apeeds0o5#

根据Jonatan门多萨的回答,但简化了,用的是Kotlin的话:

/**
 * Override Accept-Language header by "lang" query parameter.
 */
@Component
class LanguageQueryParameterWebFilter : WebFilter {
  override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
    val languageValue = exchange.request.queryParams.getFirst("lang") ?: ""
    if (languageValue.isEmpty()) {
      return chain.filter(exchange)
    }
    return chain.filter(
      exchange.mutate().request(
        exchange.request
          .mutate()
          .headers {
            it[HttpHeaders.ACCEPT_LANGUAGE] = languageValue
          }
          .build(),
      ).build(),
    )
  }
}

相关问题