【问题标题】:Spring REST Handle locale changeSpring REST 处理语言环境更改
【发布时间】:2014-05-19 12:42:13
【问题描述】:

我正在尝试处理 Spring 3 REST 应用程序中的语言环境更改。

但是语言环境没有改为fr。

控制台日志显示: 2014-05-19 14:29:46,214 调试 [AbstractExceptionHandler] 语言环境:en

这是我的配置:

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:messages/messages", "classpath:messages/validation");
        // If true, the key of the message will be displayed if the key is not found, instead of throwing an exception
        messageSource.setUseCodeAsDefaultMessage(true);
        messageSource.setDefaultEncoding("UTF-8");
        // The value 0 means always reload the messages to be developer friendly
        messageSource.setCacheSeconds(0);
        return messageSource;
    }

    // The locale interceptor provides a way to switch the language in any page just by passing the lang=’en’, lang=’fr’, and so on to the url
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("lang");
        registry.addInterceptor(localeChangeInterceptor);
    }

    @Bean
    public LocaleResolver localeResolver() {
        CookieLocaleResolver localeResolver = new CookieLocaleResolver();
        localeResolver.setDefaultLocale(new Locale("en"));
        return localeResolver;
    }

这是我的异常处理程序:

@ControllerAdvice
public class AdminExceptionHandler extends AbstractExceptionHandler {

    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public ResponseEntity<ErrorInfo> nullPointerException(HttpServletRequest request, NullPointerException e) {
        String url = request.getRequestURL().toString();
        String errorMessage = localizeErrorMessage("error.npe", new Object[] { e.getMessage() });
        return new ResponseEntity<ErrorInfo>(new ErrorInfo(url, errorMessage), HttpStatus.INTERNAL_SERVER_ERROR);
    }

}

public class AbstractExceptionHandler {

    private static Logger logger = LoggerFactory.getLogger(AbstractExceptionHandler.class);

    @Autowired
    private MessageSource messageSource;

    protected String localizeErrorMessage(String errorCode, Object args[]) {
        Locale locale = LocaleContextHolder.getLocale();
        logger.debug("locale: " + locale);
        return messageSource.getMessage(errorCode, args, locale);
    }

    protected String localizeErrorMessage(String errorCode) {
        return localizeErrorMessage(errorCode, null);
    }

    protected String extractAdminIdFromUrl(String url) {
        String adminId = null;
        try {
            URI uri = new URI(url);
            String path = uri.getPath();
            adminId = path.substring(path.lastIndexOf('/') + 1);
        } catch (URISyntaxException e1) {
            e1.printStackTrace();
        }
        return adminId;
    }

}

这是我的测试:

@Test
public void testExceptionLocalizedMessage() throws Exception {
    HttpHeaders httpHeaders = Common.createAuthenticationHeaders("stephane" + ":" + PASSWORD);

    MvcResult resultGet = this.mockMvc.perform(
            get("/error/npe").headers(httpHeaders)
            .param("lang", "fr")
            .accept(MediaType.APPLICATION_JSON)
        )
        .andExpect(status().isInternalServerError())
        .andExpect(jsonPath("$.message").value("Une erreur inconnue s'est produite. Veuillez nous excuser."))
        .andReturn();

    httpHeaders.add("Accept-Language", "fr");
    resultGet = this.mockMvc.perform(
            get("/error/npe").headers(httpHeaders)
            .accept(MediaType.APPLICATION_JSON)
        )
        .andExpect(status().isInternalServerError())
        .andExpect(jsonPath("$.message").value("Une erreur inconnue s'est produite. Veuillez nous excuser."))
        .andReturn();
}

我想在 ?lang=en 中处理 url 中的语言环境参数,并将 Accept-Language 标头作为后备。

作为一个 REST 应用程序,我正在考虑使用 AcceptHeaderLocaleResolver 类,但它不支持通过 url 参数设置语言环境。

我认为在 REST 应用程序中使用 SessionLocaleResolver 类没有什么意义。

剩下的就是 CookieLocaleResolver 类,我并不特别相信它应该在 REST 应用程序中使用。

无论如何,检索到的语言环境仍然是 en 而不是 fr,正如我所期望的那样。

编辑:

在测试中,使用语句:

httpHeaders.add("Accept-Language", Locale.FRENCH.getLanguage());

不设置语言环境。 但是使用 locale() 可以。 此测试通过:

this.mockMvc.perform(
        get("/error/npe").headers(httpHeaders).locale(Locale.FRENCH)
        .accept(MediaType.APPLICATION_JSON))
        .andDo(print()
    )
    .andExpect(status().isInternalServerError())
    .andExpect(jsonPath("$.message").value(localizeErrorMessage("error.npe", Locale.FRENCH)))
    .andReturn();

【问题讨论】:

  • 我已将语言环境解析器配置为处理 lang=fr,但使用 Accept-Language 标头传递语言环境。难怪没有解决语言环境。
  • 但是如果使用 Accept-Language 标头发送,它仍然看不到语言环境。我尝试了 CookieLocaleResolver 类和 SessionLocaleResolver 类,没有效果。
  • 如果您更关心 SEO,则在路径中传递语言允许爬虫以所有支持的语言为您的网站编制索引。让我们密切关注这个问题 - Core Spring 团队正在考虑这个 PR 来支持这个问题 - github.com/spring-projects/spring-framework/issues/25791

标签: spring rest locale


【解决方案1】:

我找到了解决方案。现在正在使用 Accept-Language 标头和 cookie。

public class WebConfiguration extends WebMvcConfigurerAdapter {

    @Bean
    public LocaleResolver localeResolver() {
        return new SmartLocaleResolver();
    }

}

public class SmartLocaleResolver extends CookieLocaleResolver {

    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        String acceptLanguage = request.getHeader("Accept-Language");
        if (acceptLanguage == null || acceptLanguage.trim().isEmpty()) {
            return super.determineDefaultLocale(request);
        }
        return request.getLocale();
    }

}

更新:根据 Thor 的评论,这里是一个解析器,它首先检查 cookie,如果没有找到,则检查请求标头:

@Override
public Locale resolveLocale(HttpServletRequest request) {
    Locale locale = super.determineDefaultLocale(request);
    if (null == locale) {
        String acceptLanguage = request.getHeader("Accept-Language");
        if (acceptLanguage != null && !acceptLanguage.trim().isEmpty()) {
            locale = request.getLocale();
        }
    }
    return locale;
}

或者使用更简单的实现(未测试):

private AcceptHeaderLocaleResolver acceptHeaderLocaleResolver = new AcceptHeaderLocaleResolver();

@Override
public Locale resolveLocale(HttpServletRequest request) {
    Locale locale = super.determineDefaultLocale(request);
    if (null == locale) {
        locale = acceptHeaderLocaleResolver.resolveLocale(request);
    }
    return locale;
}

更新:上述解决方案不再有效。

我现在正在尝试在标题中传递接受的语言:

httpHeaders.add(HttpHeaders.ACCEPT_LANGUAGE, "fr_FR");

并在此语言环境解析器中检索它:

@Override
public Locale resolveLocale(HttpServletRequest request) {
    for (String httpHeaderName : Collections.list(request.getHeaderNames())) {
        logger.debug("===========>> Header name: " + httpHeaderName);
    }
    String acceptLanguage = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
    logger.debug("===========>> acceptLanguage: " + acceptLanguage);
    Locale locale = super.resolveLocale(request);
    logger.debug("===========>> acceptLanguage locale: " + locale.getDisplayCountry());
    if (null == locale) {
        locale = getDefaultLocale();
        logger.debug("===========>> Default locale: " + locale.getDisplayCountry());
    }
    return locale;
}

但是===========&gt;&gt; Header name logger 的输出中没有Accept-LanguageacceptLanguage logger 是空的。

【讨论】:

  • 太棒了!我会用这个。
  • 也许切换解析器,所以先检查cookie,如果没有cookie则使用accept-language。
  • Hei Thor, takk for din input, kul at se en nordmann idag :-) Selv er jeg franskmann från Provence og jeg kan snakke lit norsk bare fordi jeg har jobbet et par år hos BBS (Nets) i Norge,det var morsomt! Akkurat nå er jeg på oppdrag i Syd Frankrike og vi har et riktigt norsk vær idag, det er gråt, blåsigt ogstormigt som i Bergen :-)
  • Hei Stephane, veldig kjekt å få en hilsen her inne。 Ja nå har vel høsten begynt for alvor for dere også。我 år hadde vi varmt vær til langt ut i oktober。 Jeg ble glad når du oppdaterte ditt svar, jeg skal ta det i bruk nå i en app for vitenskapelig publisering (npi.nsd.no) der vi skal ha to språk。
  • 请注意,CookieLocaleResolver 是一个实现,它使用在自定义设置的情况下发送回用户的 cookie,回退到指定的默认语言环境或请求的接受标头语言环境。 -- 可能会使 SmartLocaleResolver 变得多余。
【解决方案2】:

当我们使用时

@Bean
public SessionLocaleResolver localeResolver(){
     SessionLocaleResolver localeResolver = new SessionLocaleResolver();
     localeResolver.setDefaultLocale(Locale.US);
     return  localeResolver;
 }


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

它能够接受来自查询参数的语言环境

{{url}}/com-manh-cp-ext-order/api/ext/ex23order/greeting?language=es

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-03
    相关资源
    最近更新 更多