【问题标题】:Retrieve locale based on the Accept-Language in Spring Boot根据 Spring Boot 中的 Accept-Language 检索语言环境
【发布时间】:2019-04-17 22:35:39
【问题描述】:

我有一个 Spring Boot (2.1.3.RELEASE) 应用程序,它使用 Jersey 来定义(RESTful)端点。我正在尝试根据用户代理发送的语言环境读取和传播一些消息。

我已经配置了这些 bean:

@Bean
public LocaleResolver localeResolver() {
  final AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
  resolver.setSupportedLocales(Arrays.asList(Locale.GERMANY, Locale.US));
  resolver.setDefaultLocale(Locale.ENGLISH);
  return resolver;
}

@Bean
public MessageSource messageSource() { // Not sure if this is needed
  final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
  messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name());
  messageSource.setBasenames("classpath:/messages");
  messageSource.setUseCodeAsDefaultMessage(true);
  messageSource.setCacheSeconds(5);
  return messageSource;
}

...还有捆绑包(在../src/main/resources/ 内),例如:messages.properties(后备)、messages_en_US.propertiesmessages_de_DE.properties 等。

现在,挑战在于我不确定如何“读取”用户代理发送的语言环境,以便正确读取捆绑包中的消息。我正在注入一个MessageSource ms,并以编程方式读取如下消息:

final Locale locale = ???
ms.getMessage("message.duplicate-token", null, locale);

有什么线索吗?

我试过LocaleContextHolder.getLocale(),但总是en_US。如果我为getMessage 调用硬编码相应的语言环境,我就能够检索到正确的消息。所以我知道设置/配置在大多数情况下都有效。


客户端使用 Accept-Language 标头发送区域设置 - 以及以下值:de-DEen-US 等。

【问题讨论】:

  • 我建议你在AcceptHeaderLocaleResolver 类的resolveLocale 方法中设置一个断点,这样你就可以看到发生了什么,或许还能弄清楚为什么它没有按照你的预期解决。
  • 确实,我在发帖之前尝试过这样的事情;它永远不会停止在那个方法上,而是在启动时。

标签: java spring-boot internationalization


【解决方案1】:

您需要添加一个 LocaleChangeInterceptor 并配置 bean,如下所示: 更多信息请参考Spring Boot internationalization

@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
    LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
    lci.setParamName("lang");
    return lci;
}

如果您只想使用“Accept-Language”标头,那么您可以扩展 AcceptHeaderLocaleResolver 并可以自定义:

package com.deb.demo.config;

import java.util.Arrays;
import java.util.List;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;

import org.springframework.util.StringUtils;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;

public class CustomLocaleResolver extends AcceptHeaderLocaleResolver {


    List<Locale> LOCALES = Arrays.asList(new Locale("en"),new Locale("es"),new Locale("fr"));


  @Override
  public Locale resolveLocale(HttpServletRequest request) {
     if (StringUtils.isEmpty(request.getHeader("Accept-Language"))) {
         return Locale.getDefault();
       }
     List<Locale.LanguageRange> list = Locale.LanguageRange.parse(request.getHeader("Accept-Language"));
     Locale locale = Locale.lookup(list,LOCALES);
      return locale;
     }
}

【讨论】:

  • 我不能在 URL 中使用参数;我只能依赖请求中的 Accept-Language 标头。
  • 我之前也看过那个;这就是为什么我指定我使用 Spring Boot 和 Jersey 来定义(RESTful)端点。不知何故,LocaleContextHolder.getLocale() 总是有来自 JVM 的值。我确定这是因为 Jersey 在中间,但这是应用程序的当前设置,我无法更改它。我暂时使用了一种解决方法:ContainerRequestFilter,从标题中读取语言环境并将其分配给LocaleContextHolder。无论如何,如果您使用 Spring Boot(及其 MVC 部分),该示例将完美运行。
【解决方案2】:

我正在使用一个 bean

  @Bean
  public LocaleResolver localeResolver() {
    AcceptHeaderLocaleResolver slr = new AcceptHeaderLocaleResolver();
    slr.setDefaultLocale(Locale.UK);
    return slr;
  }

然后是另一个

  @Bean
  public LanguageUtil languageUtil() {
    return new LanguageUtil();
  }

  private Locale getLocale() {
    return LocaleContextHolder.getLocale();
  }

  public String getLocalizedMessage(String messageKey) {
    return messageSource.getMessage(messageKey, null, getLocale());
  }

header保存在LocaleContextHolder中,需要的时候可以使用。

【讨论】:

    【解决方案3】:

    创建自定义 AcceptHeaderLocaleResolver

    public class AcceptHeaderResolver extends AcceptHeaderLocaleResolver {
    
        List<Locale> LOCALES = Arrays.asList(new Locale("en"), new Locale("ar"));
    
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            String headerLang = request.getHeader("Accept-Language");
            return headerLang == null || headerLang.isEmpty()
                    ? Locale.getDefault()
                    : Locale.lookup(Locale.LanguageRange.parse(headerLang), LOCALES);
        }
    
    
    }
    

    别忘了在@Configuration文件中使用它

    @Bean
    public LocaleResolver sessionLocaleResolver() {
        AcceptHeaderResolver localeResolver = new AcceptHeaderResolver();
        return localeResolver;
    }
    

    【讨论】:

      【解决方案4】:

      您创建的 LocaleResolver bean 仅在 Spring MVC 中使用,而不在 Jersey 容器中使用。使用 LocaleResolver 的是 Spring 的 DispatcherServlet。

      所以 LocaleContextHolder.getLocale();将根据调用 Jersey 控制器或 Spring MVC 控制器返回不同的本地。

      【讨论】:

        【解决方案5】:

        无需扩展AcceptHeaderLocaleResolver。 创建一个 bean 定义,例如:

        @Bean
        public LocalResolver localeResolver() {
            AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
            localeResolver.setSupportedLocales(Arrays.asList(new Locale("fa"), new Locale("en")));
            localeResolver.setDefaultLocale(new Locale("fa"));
            return localeResolver;
        }
        

        【讨论】:

          【解决方案6】:

          如果您想在 REST 控制器级别获取此内容,您可以直接在 REST 方法中获取语言环境实例。春天有这个魔力

          @GetMapping("/status")
          public ResponseEntitiy<String> getStatus(final Locale locale) {
          
          }
          

          【讨论】:

            猜你喜欢
            • 2020-06-03
            • 1970-01-01
            • 1970-01-01
            • 2015-08-03
            • 2019-08-29
            • 1970-01-01
            • 2018-06-13
            • 2014-09-15
            • 1970-01-01
            相关资源
            最近更新 更多