Allow locale to be overridden by "Accept-Language"

Previously, when `spring.mvc.locale` was specified, that locale was used
regardless of the client's preferences. This commit adds an extra
`spring.mvc.locale-resolver` property that can control how the locale is
resolved. The default is `ACCEPT_HEADER` but can be set to `FIXED` to
restore the previous behaviour.

Closes gh-6083
This commit is contained in:
Stephane Nicoll 2016-06-16 14:48:55 +02:00
parent 7a5880c900
commit d54474b81c
4 changed files with 73 additions and 5 deletions

View File

@ -82,6 +82,7 @@ import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.servlet.i18n.FixedLocaleResolver;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
@ -103,6 +104,7 @@ import org.springframework.web.servlet.view.InternalResourceViewResolver;
* @author Andy Wilkinson
* @author Sébastien Deleuze
* @author Eddú Meléndez
* @author Stephane Nicoll
*/
@Configuration
@ConditionalOnWebApplication
@ -224,7 +226,14 @@ public class WebMvcAutoConfiguration {
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
else {
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
}
@Bean

View File

@ -42,10 +42,15 @@ public class WebMvcProperties {
private DefaultMessageCodesResolver.Format messageCodesResolverFormat;
/**
* Locale to use.
* Locale to use. By default, this locale is overridden by the "Accept-Language" header.
*/
private Locale locale;
/**
* Define how the locale should be resolved.
*/
private LocaleResolver localeResolver = LocaleResolver.ACCEPT_HEADER;
/**
* Date format to use (e.g. dd/MM/yyyy).
*/
@ -105,6 +110,14 @@ public class WebMvcProperties {
this.locale = locale;
}
public LocaleResolver getLocaleResolver() {
return this.localeResolver;
}
public void setLocaleResolver(LocaleResolver localeResolver) {
this.localeResolver = localeResolver;
}
public String getDateFormat() {
return this.dateFormat;
}
@ -240,4 +253,19 @@ public class WebMvcProperties {
}
public enum LocaleResolver {
/**
* Always use the configured locale.
*/
FIXED,
/**
* Use the "Accept-Language" header or the configured locale if the header
* is not set.
*/
ACCEPT_HEADER
}
}

View File

@ -49,6 +49,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.ReflectionUtils;
@ -64,6 +65,7 @@ import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.servlet.i18n.FixedLocaleResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@ -258,14 +260,42 @@ public class WebMvcAutoConfigurationTests {
@Test
public void overrideLocale() throws Exception {
load(AllResources.class, "spring.mvc.locale:en_UK",
"spring.mvc.locale-resolver=fixed");
// mock request and set user preferred locale
MockHttpServletRequest request = new MockHttpServletRequest();
request.addPreferredLocale(StringUtils.parseLocaleString("nl_NL"));
request.addHeader(HttpHeaders.ACCEPT_LANGUAGE, "nl_NL");
LocaleResolver localeResolver = this.context.getBean(LocaleResolver.class);
assertThat(localeResolver).isInstanceOf(FixedLocaleResolver.class);
Locale locale = localeResolver.resolveLocale(request);
// test locale resolver uses fixed locale and not user preferred locale
assertThat(locale.toString()).isEqualTo("en_UK");
}
@Test
public void useAcceptHeaderLocale() {
load(AllResources.class, "spring.mvc.locale:en_UK");
// mock request and set user preferred locale
MockHttpServletRequest request = new MockHttpServletRequest();
request.addPreferredLocale(StringUtils.parseLocaleString("nl_NL"));
request.addHeader(HttpHeaders.ACCEPT_LANGUAGE, "nl_NL");
LocaleResolver localeResolver = this.context.getBean(LocaleResolver.class);
assertThat(localeResolver).isInstanceOf(AcceptHeaderLocaleResolver.class);
Locale locale = localeResolver.resolveLocale(request);
assertThat(localeResolver).isInstanceOf(FixedLocaleResolver.class);
// test locale resolver uses fixed locale and not user preferred locale
// test locale resolver uses user preferred locale
assertThat(locale.toString()).isEqualTo("nl_NL");
}
@Test
public void useDefaultLocaleIfAcceptHeaderNoSet() {
load(AllResources.class, "spring.mvc.locale:en_UK");
// mock request and set user preferred locale
MockHttpServletRequest request = new MockHttpServletRequest();
LocaleResolver localeResolver = this.context.getBean(LocaleResolver.class);
assertThat(localeResolver).isInstanceOf(AcceptHeaderLocaleResolver.class);
Locale locale = localeResolver.resolveLocale(request);
// test locale resolver uses default locale if no header is set
assertThat(locale.toString()).isEqualTo("en_UK");
}

View File

@ -336,7 +336,8 @@ content into your application; rather pick only the properties that you need.
spring.mvc.dispatch-options-request=false # Dispatch OPTIONS requests to the FrameworkServlet doService method.
spring.mvc.favicon.enabled=true # Enable resolution of favicon.ico.
spring.mvc.ignore-default-model-on-redirect=true # If the content of the "default" model should be ignored during redirect scenarios.
spring.mvc.locale= # Locale to use.
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.
spring.mvc.media-types.*= # Maps file extensions to media types for content negotiation.
spring.mvc.message-codes-resolver-format= # Formatting strategy for message codes. For instance `PREFIX_ERROR_CODE`.
spring.mvc.servlet.load-on-startup=-1 # Load on startup priority of the Spring Web Services servlet.