Lighttpd is a web server with a fast growing user base. This howto will demonstrate how redirects can be done based on the language of the user's browser.
While migrating from our old blogging software to Movable Type we decided it would be a good idea to show the blog's welcome message in English or German depending on the language setting of the user's browser. Since one of the reasons for the switch to the new blog engine was that Movable Type creates static html pages, we avoided cgi scripts or similar workarounds.
We are using Lighttpd to serve all pages, which means that our best option was the mighty mod_magnet module. This allows you to control request handling within Lighttpd by running Lua scripts, which are able to modify most aspects of the way a request is handled. http://blog.credativ.com/ is now rewritten to the file in the correct language with the help of the following Lua snippet:
-- - make sure to configure the script here -----------------------------
language_targets = {}
language_targets["en"] = "/en/index.html"
language_targets["de"] = "/de/index.html"
default_language = "en"
-- - nothing to customize below this line -------------------------------
--
--[[ string:split function taken from http://lua-users.org/wiki/SplitJoin
Thanks to Joan Ordinas
]]
function string:split(sSeparator, nMax, bRegexp)
assert(sSeparator ~= '')
assert(nMax == nil or nMax >= 1)
local aRecord = {}
if self:len() > 0 then
local bPlain = not bRegexp
nMax = nMax or -1
local nField=1 nStart=1
local nFirst,nLast = self:find(sSeparator, nStart, bPlain)
while nFirst and nMax ~= 0 do
aRecord[nField] = self:sub(nStart, nFirst-1)
nField = nField+1
nStart = nLast+1
nFirst,nLast = self:find(sSeparator, nStart, bPlain)
nMax = nMax-1
end
aRecord[nField] = self:sub(nStart)
end
return aRecord
end
--[[ Based on trim14 from http://lua-users.org/wiki/StringTrim ]]
do
require 're'
require 'lpeg'
local ptrim = re.compile"%s* {(%s* %S+)*}"
local match = lpeg.match
function string:trim()
return match(ptrim, self)
end
end
lang_header = lighty.request['Accept-Language']
lighty.env["uri.path"] = language_targets[default_language]
if (lang_header) then
lang_header = string.lower(lang_header)
local lang_order = {}
for i, language in ipairs(string.split(lang_header, ",")) do
language_configs = string.split(language, ";")
language = string.trim(language_configs[1])
table.remove(language_configs, 1)
if ((#language == 2) and string.find(language, "[a-z][a-z]"))
or ((#language == 5) and string.find(language, "[a-z][a-z][-][a-z][a-z]"))
then
local q = 1
for i, config in ipairs(language_configs) do
local config_data = string.split(config, "=")
if (#config_data == 2) then
local lvalue = string.trim(config_data[1])
local rvalue = string.trim(config_data[2])
if lvalue == "q" then
q = tonumber(rvalue)
end
end
end
table.insert(lang_order, {language, q})
end
end
table.sort(lang_order, function(a,b) return (a[2] > b[2]) end)
for i,v in ipairs(lang_order) do
local lang = string.split(v[1], '-')[1]
if language_targets[lang] then
lighty.env["uri.path"] = language_targets[lang]
break
end
end
end
lighty.env["physical.rel-path"] = lighty.env["uri.path"]
lighty.env["physical.path"] = lighty.env["physical.doc-root"] .. lighty.env["physical.rel-path"]
Of course, the Lighttpd configuration has to include mod_magnet. To actually rewrite any request to "/" the configuration must also include the following snippet:
$HTTP["url"] =~ "^/$" {
magnet.attract-physical-path-to = ( "/path/to/your/script.lua" )
}
mod_magnet caches the compiled script and executes it within the core of Lighttpd so it shouldn't introduce any noticeable delay in the delivery of your webpages.
Update: Some people asked for a script to parse the full Accepted-Language header. Although this was not really an issue for us as the two blogs are independent and people have to check which blog to read in any case, I've updated the script to parse the header properly. Also it should be easier to re-use for your own needs now.



Leave a comment