在Java中解析Accept-Language标头

时间:2011-07-26 00:58:38

标签: java servlets http-accept-language

请求中的accept-language标头通常是一个很长的复杂字符串 -

EG。

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 个答案:

答案 0 :(得分:41)

我建议使用ServletRequest.getLocales()让容器解析Accept-Language,而不是试图自己管理复杂性。

答案 1 :(得分:33)

对于记录,现在可以使用Java 8

Locale.LanguageRange.parse()

答案 2 :(得分:15)

这是解析不需要servlet容器的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

非常感谢Karl Knechtel和Mike Samuel。对原始问题的评论有助于我指出正确的方向。

答案 3 :(得分:3)

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;

答案 4 :(得分:3)

我们正在使用Spring启动和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 ;
        }
}

答案 5 :(得分:1)

上述解决方案缺乏某种验证。如果用户未提供有效的语言环境,则使用ServletRequest.getLocale()返回服务器语言环境。

我们的网站最近收到了各种Accept-Language heades的垃圾邮件请求,例如:

  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
  5. 此实现可以针对受支持的有效Locale列表进行可选检查。如果没有此检查,"test"或(2,3,4)的简单请求仍会绕过LanguageRange.parse(String)的仅语法验证。

    可选允许使用空值和空值来允许搜索引擎抓取工具。

    Servlet过滤器

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

答案 6 :(得分:0)

Locale.forLanguageTag("en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2")