Подтвердить что ты не робот

Как переписать местоположение в nginx в зависимости от языка клиентского браузера?

Как переписать местоположение в nginx в зависимости от языка клиентского браузера?

Например: Мой браузер принимает язык - "uk, ru, en". Когда я запрашиваю местоположение mysite.org, nginx должен переслать mysite.org/uk

4b9b3361

Ответ 1

Вы можете управлять $language_suffix этим параметром, если вы не можете добавить модуль AcceptLanguageModule в свою систему.

rewrite (.*) $1/$http_accept_language

Более устойчивый подход будет использовать карту:

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

...

rewrite (.*) $1/$lang;

Ответ 2

Я думаю, что не стоит использовать nginx map $http_accept_language, потому что он не выполняет оценку качества (q в заголовке Accept-Language). Представьте, что у вас есть:

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

И клиент отправит Accept-Language: da, en-gb;q=0.8, en;q=0.7

Использование карты nginx всегда будет отображать $lang в en, потому что оно просто находится в строке заголовка. Но правильное отображение будет $lang = da (потому что Danisch имеет значение качества q=1, которое в этом случае больше английского q=0.7) Подробнее об этом в RFC: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

Ответ 3

Недостатком использования AcceptLanguageModule является то, что вы больше не можете полагаться на автоматические обновления системы. И с каждым обновлением nginx (даже с защитой) вам нужно самому скомпилировать Nginx. Второй недостаток заключается в том, что модуль предполагает, что язык accept уже отсортирован по значениям качества. Я предпочитаю Lua, потому что он может быть легко установлен в дистрибутивах на основе debian:

apt-get install nginx-extras

Мой коллега Филлипо сделал отличный nginx-http-accept-lang script в Lua. Он правильно обрабатывает значения качества и перенаправляет пользователя соответственно. Я сделал небольшую модификацию для этого script. Он принимает поддерживаемые языки в качестве входного параметра и возвращает наиболее квалифицированный язык в соответствии с заголовком Accept-Language. С возвращенным значением вы можете делать все, что хотите. Его можно использовать для перезаписи, установки lang cookie...

Я использую только определение языка только для корневого пути (location =/). И пользовательский файл cookie имеет преимущество над браузером. Мой nginx conf выглядит так:

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

Ответ 4

Хорошо, у меня была такая же проблема и "неправильное использование" 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

Простое решение без MapModule и AcceptLanguageModule:

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

Обратите внимание, что "set $args hl = $lang & $args" устанавливает желаемый код языка (например, "en", "fr", "es" и т.д.) в параметре запроса "hl". Конечно, вы можете использовать $lang в других правилах перезаписи, если параметр запроса не подходит. Пример:

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

Ответ 6

Приведенный выше пример Lua прекрасен, но с ошибкой 500 будет ошибкой, если браузер не отправит заголовки Accept-Language.

Добавьте это поверх него:

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

Ответ 8

Я знаю, что это очень старая тема, но я нашел ее, пытаясь решить ту же проблему. Просто хотел поделиться решением, с которым я наконец пришел. Он отличается от тех, которые были опубликованы выше, так как если в 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 будет установлена в "en", потому что она предшествует даже "pt" хотя пт имеет больший вес.

Выбор правильного языка с учетом всех весов не представляется возможным в nginx без внешних скриптов. Это решение было достаточно для меня во всех практических случаях и не требовало никаких внешних модулей.

Ответ 9

В дополнение к @Marks ответ выше, который не соблюдает языковые предпочтения. Здесь фрагмент кода LUA, анализирующий значение Accept-Language Header в язык и значение предпочтения

-- need two LUA regex cause LUA 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

Ответ 10

Итак, здесь скомпилированный пример исходного вопроса (на основе моего дела, проверенного для работы с nginx-1.1.x):

map $http_accept_language $lang {
    default en;
    ~ru ru;
    ~uk uk;
}

server {
    server_name mysite.org;
    # ...
    rewrite (.*) http://mysite.org/$lang$1;
}