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 d0c2c9fdea..5bde57cead 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -443,13 +443,16 @@ public class HttpHeaders implements MultiValueMap, Serializable * Set the acceptable language ranges, * as specified by the {@literal Accept-Language} header. * @see Locale.LanguageRange + * @since 5.0 */ 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()))) + DecimalFormat decimal = new DecimalFormat("0.0", DECIMAL_FORMAT_SYMBOLS); + List values = languages.stream() + .map(range -> + range.getWeight() == Locale.LanguageRange.MAX_WEIGHT ? + range.getRange() : + range.getRange() + ";q=" + decimal.format(range.getWeight())) .collect(Collectors.toList()); set(ACCEPT_LANGUAGE, toCommaDelimitedString(values)); } @@ -458,6 +461,7 @@ public class HttpHeaders implements MultiValueMap, Serializable * Return the acceptable language ranges, * as specified by the {@literal Accept-Language} header * @see Locale.LanguageRange + * @since 5.0 */ public List getAcceptLanguage() { String value = getFirst(ACCEPT_LANGUAGE); @@ -467,6 +471,22 @@ public class HttpHeaders implements MultiValueMap, Serializable return Collections.emptyList(); } + /** + * A variant of {@link #getAcceptLanguage()} that converts each + * {@link java.util.Locale.LanguageRange} to a {@link Locale}. + * @since 5.0 + */ + public List getAcceptLanguageAsLocales() { + List ranges = getAcceptLanguage(); + if (ranges.isEmpty()) { + return Collections.emptyList(); + } + return ranges.stream() + .map(range -> Locale.forLanguageTag(range.getRange())) + .filter(locale -> StringUtils.hasText(locale.getDisplayName())) + .collect(Collectors.toList()); + } + /** * Set the (new) value of the {@code Access-Control-Allow-Credentials} response header. */ @@ -767,6 +787,7 @@ public class HttpHeaders implements MultiValueMap, Serializable * as specified by the {@literal Content-Language} header. *

Use {@code set(CONTENT_LANGUAGE, ...)} if you need * to set multiple content languages.

+ * @since 5.0 */ public void setContentLanguage(Locale locale) { Assert.notNull(locale, "'locale' must not be null"); @@ -779,6 +800,7 @@ public class HttpHeaders implements MultiValueMap, Serializable *

Returns {@code null} when the content language is unknown. *

Use {@code getValuesAsList(CONTENT_LANGUAGE)} if you need * to get multiple content languages.

+ * @since 5.0 */ public Locale getContentLanguage() { return getValuesAsList(CONTENT_LANGUAGE) 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 d164de2ca1..d2d94bc69f 100644 --- a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java +++ b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java @@ -424,30 +424,39 @@ public class HttpHeadersTests { @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"; + 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[]{ + + List expectedRanges = Arrays.asList( new Locale.LanguageRange("fr-ch"), new Locale.LanguageRange("fr", 0.9), - new Locale.LanguageRange("en", 0.8), + new Locale.LanguageRange("en-*", 0.8), new Locale.LanguageRange("de", 0.7), new Locale.LanguageRange("*", 0.5) - }; - assertArrayEquals(languageArray, languages.toArray()); + ); + assertEquals(expectedRanges, headers.getAcceptLanguage()); + + List expectedLocales = Arrays.asList( + Locale.forLanguageTag("fr-ch"), + Locale.forLanguageTag("fr"), + Locale.forLanguageTag("en"), + Locale.forLanguageTag("de") + ); + assertEquals(expectedLocales, headers.getAcceptLanguageAsLocales()); } @Test public void contentLanguage() { - assertNull(headers.getContentLanguage()); headers.setContentLanguage(Locale.FRANCE); assertEquals(Locale.FRANCE, headers.getContentLanguage()); assertEquals("fr-FR", headers.getFirst(HttpHeaders.CONTENT_LANGUAGE)); - headers.clear(); - headers.set(HttpHeaders.CONTENT_LANGUAGE, Locale.GERMAN.toLanguageTag() + ", " + Locale.CANADA); - assertEquals(Locale.GERMAN, headers.getContentLanguage()); + } + + @Test + public void contentLanguageSerialized() { + headers.set(HttpHeaders.CONTENT_LANGUAGE, "de, en_CA"); + assertEquals("Expected one (first) locale", Locale.GERMAN, headers.getContentLanguage()); } }