web-dev-qa-db-ja.com

クライアントブラウザの言語に応じてnginxで場所を書き換える方法は?

クライアントブラウザの言語に応じてnginxで場所を書き換える方法は?

例:私のブラウザのaccept-languageは 'uk、ru、en'です。場所をリクエストすると、mysite.orgnginxはmysite.org/ukに転送する必要があります

25
RKI

AcceptLanguageModuleモジュールをシステムに追加できない場合は、この設定で$ language_suffixを管理できます。

rewrite (.*) $1/$http_accept_language

より回復力のあるアプローチでは、マップを使用します。

map $http_accept_language $lang {
        default en;
        ~es es;
        ~fr fr;
}

...

rewrite (.*) $1/$lang;
21
Brian Coca

Nginx map $http_accept_languageは品質値(Accept-Languageヘッダーのq)を尊重しないため、使用するのは良い考えではないと思います。あなたが持っていると想像してみましょう:

map $http_accept_language $lang {
    default en;
    ~en en;
    ~da da;
}

そして、クライアントはAccept-Language: da, en-gb;q=0.8, en;q=0.7を送信します

Nginxマップを使用すると、ヘッダー文字列で見つかるだけなので、常に$langenにマップされます。ただし、正しいマッピングは$lang = daになります(この場合、Danischの品質値は英語のq=1よりも大きいq=0.7であるため)RFCでこれについて詳しく説明します: http:// www .w3.org/Protocols/rfc2616/rfc2616-sec14.html

12
mauron85

AcceptLanguageModuleを使用することの欠点は、自動システム更新に依存できなくなることです。そして、すべてのnginxアップデート(セキュリティアップデートも含む)では、Nginxを自分でコンパイルする必要があります。 2番目の欠点は、モジュールが、accept-languageがすでに品質値でソートされていることを前提としていることです。私はLuaを好みます。なぜなら、それはDebianベースのディストリビューションに簡単にインストールできるからです。

apt-get install nginx-extras

私の同僚のFillipoは、Luaで素晴らしい nginx-http-accept-lang スクリプトを作成しました。品質値を正しく処理し、それに応じてユーザーをリダイレクトします。そのスクリプトに 小さな変更 を作成しました。サポートされている言語を入力パラメーターとして受け入れ、Accept-Languageヘッダーに従って最も修飾された言語を返します。戻り値を使用すると、やりたいことが何でもできます。書き換え、langcookieの設定に使用できます...

ルートパスのみに言語決定を使用しています(場所= /)。また、ユーザーのlangcookieはブラウザーよりも優先されます。私のnginxconfは次のようになります:

map $cookie_lang $pref_lang {
    default "";
    ~en en;
    ~sk sk;
}

server {
    listen 80 default_server;

    root /usr/share/nginx/html;
    index index.html index.htm;

    # Make site accessible from http://localhost/
    server_name localhost;

    location = / {
        # $lang_sup holds comma separated languages supported by site
        set $lang_sup "en,sk";
        set_by_lua_file $lang /etc/nginx/lang.lua $lang_sup;
        if ($pref_lang) {
            set $lang $pref_lang;
        }
        add_header Set-Cookie lang=$lang;
        rewrite (.*) $scheme://$server_name/$lang$1;
    }

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
   }
}
11
mauron85

さて、私は同じ問題を抱えており、ブラウザの言語に基づいてリダイレクトを可能にするためにLuaを「誤用」しました。

# Use Lua for HTTP redirect so the site works
# without the proxy backend.
location = / {
    rewrite_by_lua '
        for lang in (ngx.var.http_accept_language .. ","):gmatch("([^,]*),") do
            if string.sub(lang, 0, 2) == "en" then
                ngx.redirect("/en/index.html")
            end
            if string.sub(lang, 0, 2) == "nl" then
                ngx.redirect("/nl/index.html")
            end
            if string.sub(lang, 0, 2) == "de" then
                ngx.redirect("/de/index.html")
            end
        end
        ngx.redirect("/en/index.html")
    ';
}

注:NGINxにはlibluaをコンパイルする必要があります。 Debian/Ubuntuの場合:

apt-get install nginx-extras
5
Mark

上記のLuaの例は問題ありませんが、ブラウザーがAccept-Languageヘッダーを送信しない場合、エラー500で失敗します。

その上にこれを追加します:

if ngx.var.http_accept_language == nil then
ngx.redirect("/en/")
end
1
Eugene V

MapModuleとAcceptLanguageModuleを使用しない単純なソリューション:

   if ( $http_accept_language ~ ^(..) ) {
         set $lang $1;
   }
   set $args hl=$lang&$args;

「set $ args hl = $ lang&$ args」は、「hl」クエリパラメータに目的の言語コード(「en」、「fr」、「es」など)を設定することに注意してください。もちろん、クエリパラメータが適合しない場合は、他の書き換えルールで$ langを使用できます。例:

location ~/my/dir/path/ {
          rewrite ^/my/dir/path/ /my/dir/path/$1/ break;
          proxy_pass http://upstream_server;
   }
1
developer33

これは非常に古いスレッドであることは知っていますが、同じ問題を解決しようとしたときに見つかりました。私がついに思いついたソリューションを共有したかっただけです。 Accept-Languageで言及されている言語がいくつかあるかのように、上記で公開されている言語とは異なり、提供できる言語の中から最初に言及されている言語が選択されます。

    #
    # Determine what language to redirect to
    # this sets the value of $lang variable to the language depending on the contents of Accept-Language request header
    # the regexp pattern automatically matches a known language that is not preceded by another known language
    # If no known language is found, it uses some heuristics (like RU for (uk|be|ky|kk|ba|tt|uz|sr|mk|bg) languages)
    #
    map $http_accept_language $lang {
        default en;
        "~*^((|,)\s*(?!(ru|es|fr|pt|en))\w+(-\w+)?(;q=[\d\.]+)?)*(|,)\s*en\b" en;
        "~*^((|,)\s*(?!(ru|es|fr|pt|en))\w+(-\w+)?(;q=[\d\.]+)?)*(|,)\s*es\b" es;
        "~*^((|,)\s*(?!(ru|es|fr|pt|en))\w+(-\w+)?(;q=[\d\.]+)?)*(|,)\s*ru\b" ru;
        "~*^((|,)\s*(?!(ru|es|fr|pt|en))\w+(-\w+)?(;q=[\d\.]+)?)*(|,)\s*fr\b" fr;
        "~*^((|,)\s*(?!(ru|es|fr|pt|en))\w+(-\w+)?(;q=[\d\.]+)?)*(|,)\s*pt\b" pt;
        "~*(^|,)\s*(uk|be|ky|kk|ba|tt|uz|sr|mk|bg)\b" ru;
        "~*(^|,)\s*(ca|gl)\b" es;
    }

    ...

    rewrite (.*) $1/$lang;

このソリューションの制限は、Accept-Languageヘッダーの言語が優先順にリストされていることを前提としていることです。通常、これは当てはまりますが、公式には必須ではありません。たとえば、ヘッダーが「Accept-Language:da、en-US; q = 0.1、pt-BR; q = 1」の場合、変数$ langは「pt」の前にあるため「en」に設定されます。 ptの方が重みが大きいですが。

すべての重みを考慮して適切な言語を選択することは、外部スクリプトなしのnginxでは不可能のようです。このソリューションは、すべての実用的なケースで私にとって十分であり、外部モジュールを必要としませんでした。

1

nginx_accept_language_module を使用できます。 Nginxは再コンパイルする必要がありますが、Luaを統合するよりも作業が少なくて済みます。

githubへのリンク

0
Dmytro

上記の@Marksの回答に加えて、言語の好みを尊重しません。これがコード解析のLUAチャンクですAccept-Language Header言語と好みの値への値

-- need two LUA regex cause LUA's default regex is pretty broken
-- In my opinion a killer argument against using / supporting LUA

rx = "%s*([a-zA-Z-]+)%s*;%s*q%s*=%s*(%d*.?%d+)"
rx2 = "%s*([a-zA-Z-]+)%s*"

-- (arg .. ",") => concatenation operation
for chunk in (arg .. ","):gmatch("([^,]*),") do
    lang, q = string.match(chunk, rx)
    if (not lang) then
        lang = string.match(chunk, rx2)
        q = 1.0
    end
    print(string.format("lang=[%s] q=[%s]",lang, tonumber(q * 1.0)))
end

申請すると、次のようになります。

$ lua demo.lua 'en-US , de , fr ; q = 0.1 , dk;q=1 '
lang=[en-US] q=[1.0]
lang=[de] q=[1.0]
lang=[fr] q=[0.1]
lang=[dk] q=[1.0]

$ lua demo.lua ' de'
lang=[de] q=[1.0]

$ lua demo.lua ' de;'
lang=[de] q=[1.0]

$ lua demo.lua ' de;q'
lang=[de] q=[1.0]

$ lua demo.lua ' de;q='
lang=[de] q=[1.0]

$ lua demo.lua ' de;q=0'
lang=[de] q=[0.0]

$ lua demo.lua ' de;q=0.1'
lang=[de] q=[0.1]

最終的には、以下のようなLUAスクリプトよりもリダイレクトに使用しています。

rx = "%s*([a-zA-Z-]+)%s*;%s*q%s*=%s*(%d*.?%d+)"
rx2 = "%s*([a-zA-Z-]+)%s*"


sup = {de = 0, en = 0, dk = 0}       -- supported languages
win = {lang = "en", q = 0}           -- default values / winner

for chunk in (arg[1] .. ","):gmatch("([^,]*),") do
    lang, q = string.match(chunk, rx)
    if (not lang) then
        lang = string.match(chunk, rx2)
        q = 1.0
    end
    lang = string.lower(lang)
    -- handle only supported languages
    if (sup[lang]) then
        q = tonumber(q)
        -- update winner table if a better match is found
        if (win.q < q) then
            win.q = q
            win.lang = lang
        end
    end
end

-- which language pref?
print(string.format("winner: %s",win.lang))

これは与える:

$ lua test.lua 'en-US;q=.7 , de;q=0.9 , fr ; q = 0.1 , dk ; q  =  1 '
winner: dk
0
whaefelinger