Skip to content

Commit fa56361

Browse files
committed
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
1 parent c85d768 commit fa56361

File tree

2 files changed

+46
-15
lines changed

2 files changed

+46
-15
lines changed

spring-web/src/main/java/org/springframework/http/HttpHeaders.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -443,13 +443,16 @@ public List<MediaType> getAccept() {
443443
* Set the acceptable language ranges,
444444
* as specified by the {@literal Accept-Language} header.
445445
* @see Locale.LanguageRange
446+
* @since 5.0
446447
*/
447448
public void setAcceptLanguage(List<Locale.LanguageRange> languages) {
448449
Assert.notNull(languages, "'languages' must not be null");
449-
DecimalFormat df = new DecimalFormat("0.0", DECIMAL_FORMAT_SYMBOLS);
450-
List<String> values = languages
451-
.stream()
452-
.map(r -> (r.getWeight() == Locale.LanguageRange.MAX_WEIGHT ? r.getRange() : r.getRange() + ";q=" + df.format(r.getWeight())))
450+
DecimalFormat decimal = new DecimalFormat("0.0", DECIMAL_FORMAT_SYMBOLS);
451+
List<String> values = languages.stream()
452+
.map(range ->
453+
range.getWeight() == Locale.LanguageRange.MAX_WEIGHT ?
454+
range.getRange() :
455+
range.getRange() + ";q=" + decimal.format(range.getWeight()))
453456
.collect(Collectors.toList());
454457
set(ACCEPT_LANGUAGE, toCommaDelimitedString(values));
455458
}
@@ -458,6 +461,7 @@ public void setAcceptLanguage(List<Locale.LanguageRange> languages) {
458461
* Return the acceptable language ranges,
459462
* as specified by the {@literal Accept-Language} header
460463
* @see Locale.LanguageRange
464+
* @since 5.0
461465
*/
462466
public List<Locale.LanguageRange> getAcceptLanguage() {
463467
String value = getFirst(ACCEPT_LANGUAGE);
@@ -467,6 +471,22 @@ public List<Locale.LanguageRange> getAcceptLanguage() {
467471
return Collections.emptyList();
468472
}
469473

474+
/**
475+
* A variant of {@link #getAcceptLanguage()} that converts each
476+
* {@link java.util.Locale.LanguageRange} to a {@link Locale}.
477+
* @since 5.0
478+
*/
479+
public List<Locale> getAcceptLanguageAsLocales() {
480+
List<Locale.LanguageRange> ranges = getAcceptLanguage();
481+
if (ranges.isEmpty()) {
482+
return Collections.emptyList();
483+
}
484+
return ranges.stream()
485+
.map(range -> Locale.forLanguageTag(range.getRange()))
486+
.filter(locale -> StringUtils.hasText(locale.getDisplayName()))
487+
.collect(Collectors.toList());
488+
}
489+
470490
/**
471491
* Set the (new) value of the {@code Access-Control-Allow-Credentials} response header.
472492
*/
@@ -767,6 +787,7 @@ public ContentDisposition getContentDisposition() {
767787
* as specified by the {@literal Content-Language} header.
768788
* <p>Use {@code set(CONTENT_LANGUAGE, ...)} if you need
769789
* to set multiple content languages.</p>
790+
* @since 5.0
770791
*/
771792
public void setContentLanguage(Locale locale) {
772793
Assert.notNull(locale, "'locale' must not be null");
@@ -779,6 +800,7 @@ public void setContentLanguage(Locale locale) {
779800
* <p>Returns {@code null} when the content language is unknown.
780801
* <p>Use {@code getValuesAsList(CONTENT_LANGUAGE)} if you need
781802
* to get multiple content languages.</p>
803+
* @since 5.0
782804
*/
783805
public Locale getContentLanguage() {
784806
return getValuesAsList(CONTENT_LANGUAGE)

spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -424,30 +424,39 @@ public void accessControlRequestMethod() {
424424

425425
@Test
426426
public void acceptLanguage() {
427-
assertTrue(headers.getAcceptLanguage().isEmpty());
428-
String headerValue = "fr-ch, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5";
427+
String headerValue = "fr-ch, fr;q=0.9, en-*;q=0.8, de;q=0.7, *;q=0.5";
429428
headers.setAcceptLanguage(Locale.LanguageRange.parse(headerValue));
430429
assertEquals(headerValue, headers.getFirst(HttpHeaders.ACCEPT_LANGUAGE));
431-
List<Locale.LanguageRange> languages = headers.getAcceptLanguage();
432-
Locale.LanguageRange[] languageArray = new Locale.LanguageRange[]{
430+
431+
List<Locale.LanguageRange> expectedRanges = Arrays.asList(
433432
new Locale.LanguageRange("fr-ch"),
434433
new Locale.LanguageRange("fr", 0.9),
435-
new Locale.LanguageRange("en", 0.8),
434+
new Locale.LanguageRange("en-*", 0.8),
436435
new Locale.LanguageRange("de", 0.7),
437436
new Locale.LanguageRange("*", 0.5)
438-
};
439-
assertArrayEquals(languageArray, languages.toArray());
437+
);
438+
assertEquals(expectedRanges, headers.getAcceptLanguage());
439+
440+
List<Locale> expectedLocales = Arrays.asList(
441+
Locale.forLanguageTag("fr-ch"),
442+
Locale.forLanguageTag("fr"),
443+
Locale.forLanguageTag("en"),
444+
Locale.forLanguageTag("de")
445+
);
446+
assertEquals(expectedLocales, headers.getAcceptLanguageAsLocales());
440447
}
441448

442449
@Test
443450
public void contentLanguage() {
444-
assertNull(headers.getContentLanguage());
445451
headers.setContentLanguage(Locale.FRANCE);
446452
assertEquals(Locale.FRANCE, headers.getContentLanguage());
447453
assertEquals("fr-FR", headers.getFirst(HttpHeaders.CONTENT_LANGUAGE));
448-
headers.clear();
449-
headers.set(HttpHeaders.CONTENT_LANGUAGE, Locale.GERMAN.toLanguageTag() + ", " + Locale.CANADA);
450-
assertEquals(Locale.GERMAN, headers.getContentLanguage());
454+
}
455+
456+
@Test
457+
public void contentLanguageSerialized() {
458+
headers.set(HttpHeaders.CONTENT_LANGUAGE, "de, en_CA");
459+
assertEquals("Expected one (first) locale", Locale.GERMAN, headers.getContentLanguage());
451460
}
452461

453462
}

0 commit comments

Comments
 (0)