diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index 1c26558e6d..4a1b87e967 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -20,6 +20,8 @@ import java.io.Serializable; import java.net.InetSocketAddress; import java.net.URI; import java.nio.charset.Charset; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -37,6 +39,7 @@ import java.util.Set; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.springframework.util.Assert; import org.springframework.util.LinkedCaseInsensitiveMap; @@ -384,6 +387,8 @@ public class HttpHeaders implements MultiValueMap, Serializable */ private static final Pattern ETAG_HEADER_VALUE_PATTERN = Pattern.compile("\\*|\\s*((W\\/)?(\"[^\"]*\"))\\s*,?"); + private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = new DecimalFormatSymbols(Locale.ENGLISH); + private static TimeZone GMT = TimeZone.getTimeZone("GMT"); @@ -434,6 +439,34 @@ public class HttpHeaders implements MultiValueMap, Serializable return MediaType.parseMediaTypes(get(ACCEPT)); } + /** + * Set the acceptable language ranges, + * as specified by the {@literal Accept-Language} header. + * @see Locale.LanguageRange + */ + public void setAcceptLanguage(List languages) { + Assert.notNull(languages, "'languages' must not be null"); + DecimalFormat df = new DecimalFormat("0.0", DECIMAL_FORMAT_SYMBOLS); + List values = languages + .stream() + .map(r -> (r.getWeight() == Locale.LanguageRange.MAX_WEIGHT ? r.getRange() : r.getRange() + ";q=" + df.format(r.getWeight()))) + .collect(Collectors.toList()); + set(ACCEPT_LANGUAGE, toCommaDelimitedString(values)); + } + + /** + * Return the acceptable language ranges, + * as specified by the {@literal Accept-Language} header + * @see Locale.LanguageRange + */ + public List getAcceptLanguage() { + String value = getFirst(ACCEPT_LANGUAGE); + if (value != null) { + return Locale.LanguageRange.parse(value); + } + return Collections.emptyList(); + } + /** * Set the (new) value of the {@code Access-Control-Allow-Credentials} response header. */ diff --git a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java index 87bf12f8e7..84bdfc8c04 100644 --- a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java +++ b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java @@ -422,4 +422,21 @@ public class HttpHeadersTests { assertEquals(HttpMethod.POST, headers.getAccessControlRequestMethod()); } + @Test + public void acceptLanguage() { + assertTrue(headers.getAcceptLanguage().isEmpty()); + String headerValue = "fr-ch, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5"; + headers.setAcceptLanguage(Locale.LanguageRange.parse(headerValue)); + assertEquals(headerValue, headers.getFirst(HttpHeaders.ACCEPT_LANGUAGE)); + List languages = headers.getAcceptLanguage(); + Locale.LanguageRange[] languageArray = new Locale.LanguageRange[]{ + new Locale.LanguageRange("fr-ch"), + new Locale.LanguageRange("fr", 0.9), + new Locale.LanguageRange("en", 0.8), + new Locale.LanguageRange("de", 0.7), + new Locale.LanguageRange("*", 0.5) + }; + assertArrayEquals(languageArray, languages.toArray()); + } + }