リクエストの Accept-Language ヘッダーは通常、長くて複雑な文字列です。
例えば。
Accept-Language : en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2
Javaでそれを解析する簡単な方法はありますか? または、それを行うのに役立つ API はありますか?
リクエストの Accept-Language ヘッダーは通常、長くて複雑な文字列です。
例えば。
Accept-Language : en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2
Javaでそれを解析する簡単な方法はありますか? または、それを行うのに役立つ API はありますか?
ServletRequest.getLocales()
自分で複雑さを管理しようとするのではなく、コンテナーに Accept-Language を解析させるために使用することをお勧めします。
記録として、 Java 8で可能になりました。
Locale.LanguageRange.parse()
サーブレット コンテナーを必要としない 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 に感謝します。元の質問へのコメントは、私を正しい方向に導くのに役立ちました。
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 ;
}
}
ServletRequest.getLocale()
利用可能で、一部のフレームワークのように上書きされない場合は、確かに最良のオプションです。
Locale.LanguageRange.parse()
他のすべてのケースについては、Quiang Li が以前に述べたように、Java 8 が提供します。ただし、これはロケールではなく、言語文字列のみを返します。使用できる言語文字列を解析するには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;
上記のソリューションには、ある種の検証が欠けています。ServletRequest.getLocale()
ユーザーが有効なロケールを提供しない場合、Usingはサーバー ロケールを返します。
Accept-Language
私たちのウェブサイトは最近、次のようなさまざまなヘッダーのスパム リクエストを受け取りました。
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
この実装は、有効なのサポートされているリストに対してオプションでチェックできますLocale
。このチェックがなければ、"test"
or (2, 3, 4) を使用した単純なリクエストは、 の構文のみの検証をバイパスしますLanguageRange.parse(String)
。
オプションで、空の値と null 値を使用して検索エンジンのクローラーを許可できます。
サーブレット フィルタ
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;
}
}
Locale.forLanguageTag("en-ca,en;q=0.8,en-us;q=0.6,de-de;q=0.4,de;q=0.2")