Parse Accept-Idioma encabezado en Java
servlets http-accept-language (7)
Aquí hay una forma alternativa de analizar el encabezado Accept-Language que no requiere un contenedor de servlet:
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());
}
Aquí puede encontrar una explicación del encabezado Accept-Language y los valores q asociados:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
Muchas gracias a Karl Knechtel y Mike Samuel. Sus comentarios a la pregunta original me ayudaron a orientarme en la dirección correcta.
El encabezado accept-language en la solicitud suele ser una cadena larga y compleja
P.ej.
Accept-Language : en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2
¿Hay una manera simple de analizarlo en Java? ¿O una API para ayudarme a hacer eso?
Estamos usando Spring boot y Java 8. Esto funciona
En ApplicationConfig.java escribe esto
@Bean
public LocaleResolver localeResolver() {
return new SmartLocaleResolver();
}
y tengo esta lista en mi clase de constantes que tiene idiomas que apoyamos
List<Locale> locales = Arrays.asList(new Locale("en"),
new Locale("es"),
new Locale("fr"),
new Locale("es", "MX"),
new Locale("zh"),
new Locale("ja"));
y escribe la lógica en la siguiente clase.
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 ;
}
}
Las soluciones anteriores carecen de algún tipo de validación. El uso de ServletRequest.getLocale()
devuelve la configuración regional del servidor si el usuario no proporciona una válida.
Recientemente, nuestros sitios web recibieron solicitudes de correo no deseado con varias Accept-Language
como:
-
secret.google.com
-
oo-8-oo.com search shell is much better than google!
-
Google officially recommends oo-8-oo.com search shell!
-
Vitaly rules google ☆*:。゜゚・*ヽ(^ᴗ^)ノ*・゜゚。:*☆ ¯/_(ツ)_/¯(ಠ益ಠ)(ಥ‿ಥ)(ʘ‿ʘ)ლ(ಠ_ಠლ)( ͡° ͜ʖ ͡°)ヽ(゚Д゚)ノʕ•̫͡•ʔᶘ ᵒᴥᵒᶅ(=^ ^=)oO
Esta implementación puede verificar opcionalmente contra una lista compatible de Locale
válida. Sin este control, una solicitud simple con "test"
o (2, 3, 4) aún omite la validación sintaxis de LanguageRange.parse(String)
.
Es opcional, permite valores vacíos y nulos para permitir el rastreador del motor de búsqueda.
Filtro de servlet
final String headerAcceptLanguage = request.getHeader("Accept-Language");
// check valid
if (!HttpHeaderUtils.isHeaderAcceptLanguageValid(headerAcceptLanguage, true, Locale.getAvailableLocales()))
return;
Utilidad
/**
* Checks if the given accept-language request header can be parsed.<br>
* <br>
* Optional the parsed LanguageRange''s can be checked against the provided
* <code>locales</code> so that at least one locale must match.
*
* @see LanguageRange#parse(String)
*
* @param acceptLanguage
* @param isBlankValid Set to <code>true</code> if blank values are also
* valid
* @param locales Optional collection of valid Locale to validate any
* against.
*
* @return <code>true</code> if it can be parsed
*/
public static boolean isHeaderAcceptLanguageValid(final String acceptLanguage, final boolean isBlankValid,
final Locale[] locales)
{
// allow null or empty
if (StringUtils.isBlank(acceptLanguage))
return isBlankValid;
try
{
// check syntax
final List<LanguageRange> languageRanges = Locale.LanguageRange.parse(acceptLanguage);
// wrong syntax
if (languageRanges.isEmpty())
return false;
// no valid locale''s to check against
if (ArrayUtils.isEmpty(locales))
return true;
// check if any valid locale exists
for (final LanguageRange languageRange : languageRanges)
{
final Locale locale = Locale.forLanguageTag(languageRange.getRange());
// validate available locale
if (ArrayUtils.contains(locales, locale))
return true;
}
return false;
}
catch (final Exception e)
{
return false;
}
}
Para el registro, ahora es posible con Java 8 :
Locale.LanguageRange.parse()
Sugeriría usar ServletRequest.getLocales para permitir que el contenedor analice Accept-Language en lugar de tratar de administrar la complejidad usted mismo.
ServletRequest.getLocale()
es sin duda la mejor opción si está disponible y no se sobrescribe como lo hacen algunos frameworks.
Para todos los demás casos, Java 8 ofrece Locale.LanguageRange.parse()
como lo mencionó anteriormente Quiang Li. Sin embargo, esto solo devuelve una Cadena de idioma, no una Configuración regional. Para analizar las cadenas de idioma, puede usar Locale.forLanguageTag()
(disponible desde 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")