Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring LocalResolver based on query parameter 'lang=en'?

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

like image 976
membersound Avatar asked Aug 15 '18 14:08

membersound


2 Answers

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;
    }
}
like image 170
membersound Avatar answered Nov 04 '22 05:11

membersound


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
like image 28
kyakya Avatar answered Nov 04 '22 06:11

kyakya