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?

¿Fue útil?

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:

  1. secret.google.com
  2. o-o-8-o-o.com search shell is much better than google!
  3. Google officially recommends o-o-8-o-o.com search shell!
  4. 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")
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top