6

Can I tell spring-boot to automatically resolve the requested locale by a queryparameter, eg &lang=en?

I would like to give the query param precedence over Accept-Language parameter.

I found the following two properties, but nothing about a query param.

spring.mvc.locale= # Locale to use. By default, this locale is overridden by the "Accept-Language" header.
spring.mvc.locale-resolver=accept-header # Define how the locale should be resolved.

I tried as follows, which gives an exception:

@Configuration
public class AppConfig extends WebMvcConfigurerAdapter {
   @Bean
   public LocaleChangeInterceptor localeChangeInterceptor() {
       LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
       lci.setParamName("lang");
       return lci;
   }

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
       registry.addInterceptor(localeChangeInterceptor());
   }
}

Results in:

java.lang.UnsupportedOperationException: Cannot change HTTP accept header - use a different locale resolution strategy

8
  • 1
    Spring Boot == Spring Framework. So this still applies. So yes. Commented Aug 15, 2018 at 14:50
  • So and how exactly can I use a query param to set the locale? The docs only states that a query param format can be used to set the response output format. Commented Aug 15, 2018 at 15:01
  • You also need a different LocaleResolver. Commented Aug 15, 2018 at 17:20
  • And which one would I have to use instead?? Because spring.mvc.locale-resolver offers ony accept-header or fixed, which is both not what I want. Commented Aug 15, 2018 at 21:04
  • As stated Spring Boot == Spring Framework. Just define your own LocaleResolver as you would in a regular spring application. Commented Aug 16, 2018 at 5:31

2 Answers 2

4

The solution is probably as follows. Still I find the setup very counterintuitive. Especially I would have expected that:

  • the LocaleChangeInterceptor registers itself, but does not and have to call addInterceptors() explicit
  • the spring.mvc.locale parameter is still set into the custom LocaleResolver, but does not and have to override manually from WebMvcProperties

If all of this is desired, the docs might probably need more explanation on this.

@Configuration
public class AppConfig extends WebMvcConfigurerAdapter {
   @Bean
   public LocaleChangeInterceptor localeChangeInterceptor() {
       LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
       lci.setParamName("lang");
       return lci;
   }

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
       registry.addInterceptor(localeChangeInterceptor());
   }

   @Bean
   public AcceptHeaderLocaleResolver localeResolver(WebMvcProperties mvcProperties) {
        AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver() {
            @Override
            public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
                LocaleContextHolder.setLocale(locale);
            }
        };

        localeResolver.setDefaultLocale(mvcProperties.getLocale());
        return localeResolver;
    }
}

Update improved version after discussion:

@Configuration
public class AppConfig implements WebMvcConfigurer {
   @Bean
   public AcceptHeaderLocaleResolver localeResolver(WebMvcProperties mvcProperties) {
        AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver() {
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            String locale = request.getParameter("lang");
            return locale != null
                    ? org.springframework.util.StringUtils.parseLocaleString(locale)
                    : super.resolveLocale(request);
        }
        };

        localeResolver.setDefaultLocale(mvcProperties.getLocale());
        return localeResolver;
    }
}
Sign up to request clarification or add additional context in comments.

10 Comments

You are making things overly complex. Read the documentation... Register a different LocaleResolver one that supports changing the locale.
Well that's exactly what I did (apart fro the fact that I inherited AcceptHeaderLocaleResolver, so I can rely on the accept-language header if the query-parameter lang is missing. Even a different LocaleResolver would only change the locale by calling LocaleContextHolder.setLocale(locale), so that's what I did here?
No that isn't what you did... You hacked a non modifiable strategy in a half modifiable strategy. Your solution will only work if the lang parameter is available. So changing the locale will only work once and afterwards will fallback to the previous one again. As stated use one of the other strategies, see docs.spring.io/spring/docs/current/spring-framework-reference/… for a description of available options. Hint don't use fixed or accept header but one that supports changes.
Ok we're probably misunderstanding each other. I'm not creating a webpage application, but a rest service that should output some messages based on the selected locale. Thus, the locale is "static" during one servlet request. If no lang is provided, the default fallback should be used throughout the entire user rest request. Otherwise use the query param, favored over accept-language heder..
Then why bother with the LocaleChangeInterceptor? Just create your own LocaleResolver then which does exactly that.
|
0

First, let's see what happen:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.UnsupportedOperationException: Cannot change HTTP accept header - use a different locale resolution strategy
...
Caused by: java.lang.UnsupportedOperationException: Cannot change HTTP accept header - use a different locale resolution strategy
    at org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver.setLocale(AcceptHeaderLocaleResolver.java:142) ~[spring-webmvc-5.3.2.jar:5.3.2]
    at org.springframework.web.servlet.i18n.LocaleChangeInterceptor.preHandle(LocaleChangeInterceptor.java:154) ~[spring-webmvc-5.3.2.jar:5.3.2]
...

After debug, you will know:

  1. accept-header not support preHandle, you cannot use LocaleChangeInterceptor
  2. accept-header have not any supported languages, you must add them manual. You can see this issue.

accept-header only work when you set supported languages and the language match Content-Language from client browser.

So, you needn't use spring.mvc.locale-resolver or spring.web.locale-resolver . Nowspring 5.3.2, no properties you need.

The way for your example to solve this is appending another LocaleResolver: spring.io

    @Bean
    public LocaleResolver localeResolver() {
        return new SessionLocaleResolver();//new CookieLocaleResolver();
    }

And set:

spring:
  main:
    allow-bean-definition-overriding: true # SpringBoot>=2.1.0 
  web:
    locale: en # default

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.