Add getAcceptLanguageAsLocales
The use of Locale.LanguageRange for the Accept-Language header makes sense as it gives the most flexibility for a client to set a weighted list and for a server to do filtering via Locale#filter. This commit adds an additional convenience method that turns the LangugeRange list to a list of Locale's also filtering out a wildcard (i.e. "*"). A List<Locale> is the most basic way to access prefered languages and needed when filtering is not required. Issue: SPR-15024
This commit is contained in:
parent
c85d768b3c
commit
fa56361ad2
|
@ -443,13 +443,16 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
* Set the acceptable language ranges,
|
||||
* as specified by the {@literal Accept-Language} header.
|
||||
* @see Locale.LanguageRange
|
||||
* @since 5.0
|
||||
*/
|
||||
public void setAcceptLanguage(List<Locale.LanguageRange> languages) {
|
||||
Assert.notNull(languages, "'languages' must not be null");
|
||||
DecimalFormat df = new DecimalFormat("0.0", DECIMAL_FORMAT_SYMBOLS);
|
||||
List<String> 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<String> 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<String, String>, Serializable
|
|||
* Return the acceptable language ranges,
|
||||
* as specified by the {@literal Accept-Language} header
|
||||
* @see Locale.LanguageRange
|
||||
* @since 5.0
|
||||
*/
|
||||
public List<Locale.LanguageRange> getAcceptLanguage() {
|
||||
String value = getFirst(ACCEPT_LANGUAGE);
|
||||
|
@ -467,6 +471,22 @@ public class HttpHeaders implements MultiValueMap<String, String>, 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<Locale> getAcceptLanguageAsLocales() {
|
||||
List<Locale.LanguageRange> 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<String, String>, Serializable
|
|||
* as specified by the {@literal Content-Language} header.
|
||||
* <p>Use {@code set(CONTENT_LANGUAGE, ...)} if you need
|
||||
* to set multiple content languages.</p>
|
||||
* @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<String, String>, Serializable
|
|||
* <p>Returns {@code null} when the content language is unknown.
|
||||
* <p>Use {@code getValuesAsList(CONTENT_LANGUAGE)} if you need
|
||||
* to get multiple content languages.</p>
|
||||
* @since 5.0
|
||||
*/
|
||||
public Locale getContentLanguage() {
|
||||
return getValuesAsList(CONTENT_LANGUAGE)
|
||||
|
|
|
@ -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<Locale.LanguageRange> languages = headers.getAcceptLanguage();
|
||||
Locale.LanguageRange[] languageArray = new Locale.LanguageRange[]{
|
||||
|
||||
List<Locale.LanguageRange> 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<Locale> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue