java servlets http-accept-language

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:

  1. secret.google.com
  2. oo-8-oo.com search shell is much better than google!
  3. Google officially recommends oo-8-oo.com search shell!
  4. 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")