Encabezado de lenguaje de aceptación de análisis en Java
-
26-10-2019 - |
Pregunta
El encabezado del idioma de aceptación en 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 alguna forma simple de analizarlo en Java? ¿O una API para ayudarme a hacer eso?
Solución
Sugeriría usar ServletRequest.getLocales()
para que el análisis del contenedor acepte el idioma en lugar de tratar de manejar la complejidad usted mismo.
Otros consejos
Para el registro, ahora es posible con Java 8:
Locale.LanguageRange.parse()
Aquí hay una forma alternativa de analizar el encabezado del idioma de aceptación 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());
}
Puede encontrar una explicación del encabezado del idioma de aceptación y los valores Q asociados aquí:
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 señalarme en la dirección correcta.
ServletRequest.getLocale()
es ciertamente la mejor opción si está disponible y no se sobrescribe como lo hacen algunos marcos.
Para todos los demás casos, Java 8 ofrece Locale.LanguageRange.parse()
Como se mencionó anteriormente por Quiang Li. Sin embargo, esto solo devuelve una cadena de idiomas, no un local. Para analizar las cadenas de idiomas que 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;
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 escriba la lógica en la clase a continuación.
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. Usando ServletRequest.getLocale()
Devuelve la configuración regional del servidor si el usuario no proporciona uno válido.
Nuestros sitios web recientemente recibieron solicitudes de spam con varias Accept-Language
cabezales como:
secret.google.com
o-o-8-o-o.com search shell is much better than google!
Google officially recommends o-o-8-o-o.com search shell!
Vitaly rules google ☆*:。゜゚・*ヽ(^ᴗ^)ノ*・゜゚。:*☆ ¯\_(ツ)_/¯(ಠ益ಠ)(ಥ‿ಥ)(ʘ‿ʘ)ლ(ಠ_ಠლ)( ͡° ͜ʖ ͡°)ヽ(゚Д゚)ノʕ•̫͡•ʔᶘ ᵒᴥᵒᶅ(=^ ^=)oO
Esta implementación puede verificar opcional en una lista admitida de Locale
. Sin esto verifique una solicitud simple con "test"
o (2, 3, 4) aún así evitar la validación de sintaxis de LanguageRange.parse(String)
.
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;
}
}
Locale.forLanguageTag("en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2")