Как переписать местоположение в nginx в зависимости от языка клиентского браузера?
Например: Мой браузер принимает язык - "uk, ru, en". Когда я запрашиваю местоположение mysite.org, nginx должен переслать mysite.org/uk
Как переписать местоположение в nginx в зависимости от языка клиентского браузера?
Например: Мой браузер принимает язык - "uk, ru, en". Когда я запрашиваю местоположение mysite.org, nginx должен переслать mysite.org/uk
Вы можете управлять $language_suffix этим параметром, если вы не можете добавить модуль AcceptLanguageModule в свою систему.
rewrite (.*) $1/$http_accept_language
Более устойчивый подход будет использовать карту:
map $http_accept_language $lang {
default en;
~es es;
~fr fr;
}
...
rewrite (.*) $1/$lang;
Я думаю, что не стоит использовать 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
Недостатком использования 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;
}
}
Хорошо, у меня была такая же проблема и "неправильное использование" 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
Простое решение без 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;
}
Приведенный выше пример Lua прекрасен, но с ошибкой 500 будет ошибкой, если браузер не отправит заголовки Accept-Language.
Добавьте это поверх него:
if ngx.var.http_accept_language == nil then
ngx.redirect("/en/")
end
Вы можете использовать nginx_accept_language_module. Nginx нужно перекомпилировать, но его меньше работать, чем интегрировать Lua.
Я знаю, что это очень старая тема, но я нашел ее, пытаясь решить ту же проблему. Просто хотел поделиться решением, с которым я наконец пришел. Он отличается от тех, которые были опубликованы выше, так как если в 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 без внешних скриптов. Это решение было достаточно для меня во всех практических случаях и не требовало никаких внешних модулей.
В дополнение к @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
Итак, здесь скомпилированный пример исходного вопроса (на основе моего дела, проверенного для работы с 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;
}