How to rewrite location in nginx depending on the client-browser's language?

前端 未结 10 1892
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-02 17:25

How to rewrite location in nginx depending on the client-browser\'s language?

For example: My browser accept-language is \'uk,ru,en\'. When I request locati

相关标签:
10条回答
  • 2020-12-02 17:41

    Simple solution, without MapModule and AcceptLanguageModule :

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

    Note that the "set $args hl=$lang&$args" sets the desired language code (eg. "en", "fr", "es", etc) in the "hl" query parameter. Of course you can use $lang in other rewriting rules if the query parameter does not fit. Example:

    location ~/my/dir/path/ {
              rewrite ^/my/dir/path/ /my/dir/path/$1/ break;
              proxy_pass http://upstream_server;
       }
    
    0 讨论(0)
  • 2020-12-02 17:51

    Okay, I've had the same problem and "misuse" Lua to make a redirect possible based on the browser language.

    # 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")
        ';
    }
    

    Note: NGINx needs to have liblua compiled to it. For Debian/Ubuntu:

    apt-get install nginx-extras
    
    0 讨论(0)
  • 2020-12-02 17:55

    You can manage $language_suffix by this setting when you cannot add AcceptLanguageModule module into your system.

    rewrite (.*) $1/$http_accept_language
    

    A more resilient approach would use a map:

    map $http_accept_language $lang {
            default en;
            ~es es;
            ~fr fr;
    }
    
    ...
    
    rewrite (.*) $1/$lang;
    
    0 讨论(0)
  • 2020-12-02 17:55

    I know this is a very old thread, but I found it when trying to solve the same problem. Just wanted to share the solution I finally came with. It is different to the ones published above as if there are several languages mentioned in the Accept-Language, it will pick the first mentioned among the ones we can serve.

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

    The limitation of this solution is that it assumes the languages in the Accept-Language header are listed in the order of their preference. Usually this is true, but it is not officially required. For example, if the header is "Accept-Language: da, en-US;q=0.1, pt-BR;q=1", the variable $lang will be set to "en" because it comes before "pt" even though pt has larger weight.

    Choosing the right language taking into account all the weights does not seem to be possible in nginx without external scripts. This solution was good enough for me in all practical cases and it did not require any external modules.

    0 讨论(0)
  • 2020-12-02 17:57

    So here's the compiled example for the original question (based on my case verified to work with 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;
    }
    
    0 讨论(0)
  • 2020-12-02 17:58

    In addition to @Marks answer above which does not honor language preferences. Here's a LUA chunk of code parsing Accept-Language Header value into language and preference value

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

    When applying, I'm getting:

    $ 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]
    

    Eventually I'm using than a LUA script like below to redirect:

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

    This gives:

    $ lua test.lua 'en-US;q=.7 , de;q=0.9 , fr ; q = 0.1 , dk ; q  =  1 '
    winner: dk
    
    0 讨论(0)
提交回复
热议问题