如何根据客户端浏览器的语言重写nginx中的位置?
例如: 我的浏览器接受语言是' uk,ru,en '。 当我请求位置时,mysite.org nginx必须转发到mysite.org/uk
答案 0 :(得分:19)
如果无法将AcceptLanguageModule模块添加到系统中,则可以通过此设置管理$ language_suffix。
rewrite (.*) $1/$http_accept_language
更具弹性的方法是使用地图:
map $http_accept_language $lang {
default en;
~es es;
~fr fr;
}
...
rewrite (.*) $1/$lang;
答案 1 :(得分:9)
我认为使用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 map始终将$lang
映射到en
,因为它只是在标题字符串中找到。
但是正确的映射将是$lang = da
(因为在这种情况下,Danisch的质量值q=1
大于英语q=0.7
RFC中的更多内容:http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
答案 2 :(得分:9)
使用AcceptLanguageModule的缺点是您不能再依赖自动系统更新了。每次更新nginx(甚至是安全版)都需要自己编译Nginx。 第二个缺点是模块假定接受语言已经按质量值排序。 我更喜欢Lua,因为它可以很容易地安装在基于debian的发行版中:
apt-get install nginx-extras
我的同事Fillipo在Lua制作了很棒的nginx-http-accept-lang剧本。它正确处理质量值并相应地重定向用户。 我已经为该脚本制作了small modification。它接受支持的语言作为输入参数,并根据Accept-Language标头返回最合格的语言。使用返回值,您可以随心所欲。它可以用于重写,设置lang cookie ......
我只使用语言确定仅用于根路径(location = /)。而用户lang 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;
}
}
答案 3 :(得分: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
答案 4 :(得分:1)
简单的解决方案,没有MapModule和AcceptLanguageModule:
if ( $http_accept_language ~ ^(..) ) {
set $lang $1;
}
set $args hl=$lang&$args;
请注意,"设置$ 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;
}
答案 5 :(得分:1)
上面的Lua示例很好,但如果浏览器没有发送任何Accept-Language标题,则会因错误500而失败。
在它上面添加:
if ngx.var.http_accept_language == nil then
ngx.redirect("/en/")
end
答案 6 :(得分:0)
您可以使用nginx_accept_language_module。 Nginx必须重新编译,但它比集成Lua更少工作。
答案 7 :(得分:0)
我知道这是一个非常老的线程,但是我在尝试解决相同问题时发现了它。只想分享我最终附带的解决方案。它与上面发布的语言不同,因为“接受语言”中提到了几种语言,它将选择我们可以提供的第一种语言。
#
# 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”之前尽管pt的权重更大。
在没有外部脚本的情况下,nginx似乎不可能考虑所有权重来选择正确的语言。在所有实际情况下,该解决方案对我来说都足够好,并且不需要任何外部模块。
答案 8 :(得分:0)
除了上面的@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
答案 9 :(得分:-2)
所以这里是原始问题的编译示例(根据我的案例验证与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;
}