Browser 如何根据客户端浏览器在nginx中重写位置';什么语言?

Browser 如何根据客户端浏览器在nginx中重写位置';什么语言?,browser,nginx,rewrite,Browser,Nginx,Rewrite,如何根据客户端浏览器的语言重写nginx中的位置 例如: 我的浏览器接受语言是“uk,ru,en”。 当我请求位置mysite.org时,nginx必须转发到mysite.org/uk当您无法将AcceptLanguageModule模块添加到系统中时,您可以通过此设置管理$language\u后缀 rewrite (.*) $1/$http_accept_language 更具弹性的方法将使用map: map $http_accept_language $lang { def

如何根据客户端浏览器的语言重写nginx中的位置

例如: 我的浏览器接受语言是“uk,ru,en”。
当我请求位置mysite.org时,nginx必须转发到mysite.org/uk

当您无法将AcceptLanguageModule模块添加到系统中时,您可以通过此设置管理$language\u后缀

rewrite (.*) $1/$http_accept_language
更具弹性的方法将使用map:

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

...

rewrite (.*) $1/$lang;

下面是原始问题的编译示例(基于我的案例,经验证可与nginx-1.1.x一起使用):


好吧,我也遇到了同样的问题,并且“误用”了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

我认为使用nginx
map$http\u accept\u语言不是个好主意,因为
它不尊重质量值(
q
Accept Language
标题中)。 让我们想象一下你有:

map $http_accept_language $lang {
    default en;
    ~en en;
    ~da da;
}
客户端将发送
接受语言:da、en gb;q=0.8,en;q=0.7

使用nginx映射将始终将
$lang
映射到
en
,因为它只在标题字符串中找到。 但是正确的映射将是
$lang=da
(因为在这种情况下,Danisch的质量值
q=1
,大于English
q=0.7

在RFC中介绍更多信息:

使用AcceptLanguageModule的缺点是您不能再依赖自动系统更新。对于每一次nginx更新(甚至是安全更新),您都必须自己编译nginx。 第二个缺点是,模块假定接受语言已经按质量值排序。 我更喜欢Lua,因为它可以很容易地安装在基于debian的发行版中:

apt-get install nginx-extras
我的同事Fillipo用Lua编写了很棒的脚本。它正确地处理质量值,并相应地重定向用户。 我已经修改了剧本。它接受支持的语言作为输入参数,并根据Accept language标头返回最合格的语言。有了返回值,您可以做任何您想做的事情。它可以用于重写、设置语言cookie

我只对根路径(location=/)使用语言确定。用户LangCookie优先于浏览器。 我的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;
   }
}

简单的解决方案,没有MapModule和AcceptLanguageModule:

   if ( $http_accept_language ~ ^(..) ) {
         set $lang $1;
   }
   set $args hl=$lang&$args;
请注意,“set$args hl=$lang&$args”在“hl”查询参数中设置所需的语言代码(例如“en”、“fr”、“es”等)。 当然,如果查询参数不合适,您可以在其他重写规则中使用$lang。 例如:


上面的Lua示例可以,但如果浏览器不发送任何Accept Language标头,则会失败并出现错误500

在此基础上添加以下内容:

if ngx.var.http_accept_language == nil then
ngx.redirect("/en/")
end
你可以用。Nginx必须重新编译,但其工作量比集成Lua要少


我知道这是一个非常古老的线程,但我在尝试解决同一问题时发现了它。我只是想和大家分享我最终得到的解决方案。它与上面发布的不同,因为如果Accept语言中提到了几种语言,它将从我们可以提供服务的语言中选择第一种

    #
    # 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标头中的语言按其首选顺序列出。通常这是正确的,但不是官方要求的。例如,如果标题为“接受语言:da,en-US;q=0.1,pt BR;q=1”,则变量$lang将设置为“en”,因为它位于“pt”之前,即使pt的权重更大


在没有外部脚本的nginx中,选择考虑所有权重的正确语言似乎是不可能的。这个解决方案在所有实际情况下对我来说都足够好,并且它不需要任何外部模块

除了上面的@Marks答案之外,它不尊重语言偏好。下面是一个LUA代码块,它将
接受语言头
值解析为语言和首选项值

-- 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))

实际上,~和表达式之间不应该有空格。这需要AcceptLanguageModule吗?我不使用,它总是将我带到英文页面,即使我将浏览器配置为仅使用法语。这不需要AcceptLanguageModule。此映射不符合首选项。要导入此映射,可以将正则表达式从
~es
~*^es这将只将语言映射到出现在
接受语言
标题上的第一种语言(假设浏览器首先提供首选语言,我认为这是非常常见的),这非常好,但是为什么不对原始脚本进行PR呢?受此解决方案的启发,我提出了自己的支持get参数和cookie的解决方案。您可以在这里试用:>此解决方案在所有实际情况下对我来说都足够好,并且不需要任何外部模块。虽然是真的,但谁想与这种正则表达式搏斗呢?@whaefelinger好吧,我知道谁想,我知道。:-)这里提到的所有好的解决方案都需要在nginx上安装一些额外的组件。我觉得安装nginx之外的任何东西对于这样一个小任务来说都是一种过火的行为。所以我花了半个小时想出了一个regexp来解决这个问题。我想为什么不分享它,也许它可以帮助其他人谁不认为正则表达式地狱?>…[谁不认为正则表达式地狱?那不完全是我写的。我只是想象一个可怜的家伙必须支持大量的语言,然后出了问题(除此之外,你的解决方案是按降序排列的)。顺便说一句,添加LUA很容易,而且得到了很好的支持。LUA的足迹很小,速度也很快。因此,LUA似乎是一个很好的候选人。然而,在标准LUA中,regex支持很差。此外,还需要第三方lib。那么在NGINX中的集成可能会变得很糟糕。然而,我明白
$ 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]
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