The accept-language header in request is usually a long complex string -
Eg.
Accept-Language : en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2
We are using Spring boot and Java 8. This works
In ApplicationConfig.java write this
@Bean
public LocaleResolver localeResolver() {
return new SmartLocaleResolver();
}
and I have this list in my constants class that has languages that we support
List<Locale> locales = Arrays.asList(new Locale("en"),
new Locale("es"),
new Locale("fr"),
new Locale("es", "MX"),
new Locale("zh"),
new Locale("ja"));
and write the logic in the below class.
public class SmartLocaleResolver extends AcceptHeaderLocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
if (StringUtils.isBlank(request.getHeader("Accept-Language"))) {
return Locale.getDefault();
}
List<Locale.LanguageRange> ranges = Locale.LanguageRange.parse("da,es-MX;q=0.8");
Locale locale = Locale.lookup(ranges, locales);
return locale ;
}
}
Here's an alternative way to parse the Accept-Language header which doesn't require a servlet container:
String header = "en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2";
for (String str : header.split(",")){
String[] arr = str.trim().replace("-", "_").split(";");
//Parse the locale
Locale locale = null;
String[] l = arr[0].split("_");
switch(l.length){
case 2: locale = new Locale(l[0], l[1]); break;
case 3: locale = new Locale(l[0], l[1], l[2]); break;
default: locale = new Locale(l[0]); break;
}
//Parse the q-value
Double q = 1.0D;
for (String s : arr){
s = s.trim();
if (s.startsWith("q=")){
q = Double.parseDouble(s.substring(2).trim());
break;
}
}
//Print the Locale and associated q-value
System.out.println(q + " - " + arr[0] + "\t " + locale.getDisplayLanguage());
}
You can find an explanation of the Accept-Language header and associated q-values here:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
Many thanks to Karl Knechtel and Mike Samuel. Thier comments to the original question helped point me in the right direction.
For the record, now it is possible with Java 8:
Locale.LanguageRange.parse()
ServletRequest.getLocale()
is certainly the best option if it is available and not overwritten as some frameworks do.
For all other cases Java 8 offers Locale.LanguageRange.parse()
as previously mentioned by Quiang Li. This however only gives back a Language String, not a Locale. To parse the language strings you can use Locale.forLanguageTag()
(available since Java 7):
final List<Locale> acceptedLocales = new ArrayList<>();
final String userLocale = request.getHeader("Accept-Language");
if (userLocale != null) {
final List<LanguageRange> ranges = Locale.LanguageRange.parse(userLocale);
if (ranges != null) {
ranges.forEach(languageRange -> {
final String localeString = languageRange.getRange();
final Locale locale = Locale.forLanguageTag(localeString);
acceptedLocales.add(locale);
});
}
}
return acceptedLocales;
Locale.forLanguageTag("en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2")
I would suggest using ServletRequest.getLocales() to let the container parse Accept-Language rather than trying to manage the complexity yourself.