Разбор заголовка Accept-Language в Java - PullRequest
35 голосов
/ 26 июля 2011

Заголовок языка подтверждения в запросе обычно представляет собой длинную сложную строку -

Например.

Accept-Language : en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2

Есть ли простой способ разобрать его в Java? Или API, чтобы помочь мне сделать это?

Ответы [ 7 ]

42 голосов
/ 27 июля 2011

Я бы предложил использовать ServletRequest.getLocales(), чтобы позволить контейнеру анализировать Accept-Language, а не пытаться самостоятельно управлять сложностью.

33 голосов
/ 12 марта 2015

Для записи, теперь это возможно с Java 8 :

Locale.LanguageRange.parse()
15 голосов
/ 27 августа 2012

Вот альтернативный способ анализа заголовка Accept-Language, который не требует контейнера сервлета:

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());
}

Вы можете найти объяснение заголовка Accept-Language и связанных с ним значений q здесь:

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

Большое спасибо Карлу Кнехтелю и Майку Самуэлю. Их комментарии к первоначальному вопросу помогли мне указать верное направление.

3 голосов
/ 21 апреля 2016

Мы используем Spring boot и Java 8. Это работает

В ApplicationConfig.java напишите это

@Bean

public LocaleResolver localeResolver() {
    return new SmartLocaleResolver();
}

, и у меня есть этот список в моем классе констант, в котором есть языки, которые мы поддерживаем

List<Locale> locales = Arrays.asList(new Locale("en"),
                                         new Locale("es"),
                                         new Locale("fr"),
                                         new Locale("es", "MX"),
                                         new Locale("zh"),
                                         new Locale("ja"));

и напишите логику в следующем классе.

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 ;
        }
}
3 голосов
/ 31 августа 2015

ServletRequest.getLocale(), безусловно, лучший вариант, если он доступен и не перезаписан, как это делают некоторые фреймворки.

Для всех остальных случаев Java 8 предлагает Locale.LanguageRange.parse(), как ранее упоминал Quiang Li.Это, однако, возвращает только языковую строку, а не локаль.Для разбора языковых строк вы можете использовать Locale.forLanguageTag() (доступно начиная с 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;
1 голос
/ 20 декабря 2016

В приведенных выше решениях отсутствует какая-либо проверка. Использование ServletRequest.getLocale() возвращает языковой стандарт сервера, если пользователь не предоставляет действительный.

На наши веб-сайты в последнее время поступали запросы на спам с различными Accept-Language заголовками, такими как:

  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

Эта реализация может дополнительно проверять список поддерживаемых Locale. Без этой проверки простой запрос с "test" или (2, 3, 4) по-прежнему обходится только для проверки синтаксиса LanguageRange.parse(String).

Опционально разрешает пустые и нулевые значения, чтобы разрешить поисковому роботу.

Фильтр сервлетов

final String headerAcceptLanguage = request.getHeader("Accept-Language");

// check valid
if (!HttpHeaderUtils.isHeaderAcceptLanguageValid(headerAcceptLanguage, true, Locale.getAvailableLocales()))
    return;

Полезность

/**
 * 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;
    }
}
0 голосов
/ 29 сентября 2015
Locale.forLanguageTag("en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2")
...