We’re using HAproxy in front of a XWiki 11.10.3 instance.
For SEO reasons, the language should be encoded in the URL, so the same site can be accessed with different links like so:
https://domain.eu/bin/view/<WIKI_SPACE>/<LANGUAGE_CODE>/<PAGE_PATH>
-
https://domain.eu/bin/view/Public/de/Applications/iOS/
for the German version (default language) -
https://domain.eu/bin/view/Public/en/Applications/iOS/
for the English version
As far as we understood, in XWiki the language is set by providing a language parameter, causing XWiki to set the language within a Cookie and issuing a redirect. The URLs for both languages are the same (https://domain.eu/bin/view/Public/Applications/iOS
) with the language controlled by the Cookie value.
Due to the redirect, a “simple URL rewrite” (/en
→ ?language=en
) isn’t possible as it would end in an redirect loop until it eventually stops at XWiki’s default language.
We tried to alter the Cookie values with some success in terms of
-
https://domain.eu/bin/view/Public/de/Applications/iOS
resulting in the German page language and -
https://domain.eu/bin/view/Public/en/Applications/iOS
resulting in the English language version
without further redirects. However, the links within the page don’t know about our URL rewriting, always pointing to a XWiki URL without the respective /de
or /en
part of the path, causing XWiki falling back to its default language.
Ex.:
Page https://domain.eu/bin/view/Public/en/Applications/iOS
does contain a link to another child-page called User-Manual
which will be rendered as https://domain.eu/bin/view/Public/Applications/iOS/User-Manual/
(without language in path).
In this specific example, clicking on the link from the English version of a page will result in getting the default language page (which is German) served as per our reverse proxy rules (see below).
We might be able to further improve the Cookie handling on reverse proxy side, giving the cookie value priority over the language code in the URL. But even when, it probably will result in a redirect for every single link without a language code.
So, is it even possible to achieve what we want? While we’ve read about custom URL schemes, documentation is rather limited. In addition, we would like to stay near XWiki defaults as far as possible to minimize the risk of failure during upgrades.
PS: This is our HAproxy test configuration:
frontend domain.eu
bind *:80
# Prettify URLs
# Language parameter ?language=de|en has to be part of the path
# Language DE should be default, thus redirecting every request without language specification or any language other than EN
# https://domain.eu/bin/view/Main/[?language=de] -> https://domain.eu/bin/view/Main/de
# https://domain.eu/bin/view/Main/?language=en -> https://domain.eu/bin/view/Main/en
# Determine language
acl is_invalid_language_path path_reg ^\/$|\/bin\/view\/(Main|Public|Customer|OnPremise|Partner)($|\/$|\/(?!de\/|en\/).*)
acl has_valid_language_parameter urlp_reg(language) -i ^(en|de)$
http-request set-var(req.lang) str("de") if is_invalid_language_path !has_valid_language_parameter
http-request set-var(req.lang) query,regsub(\".*language=(\S{2}).*\",\1) if is_invalid_language_path has_valid_language_parameter
http-request set-var(req.lang) path,regsub(\"^\/bin\/view\/(Main|Public|Customer|OnPremise|Partner)\/(\S{2}).*\",\2) if !is_invalid_language_path
# Determine wiki name
acl is_root_path path /
http-request set-var(req.wiki) str("Main") if is_root_path
http-request set-var(req.wiki) path,regsub(\"^\/bin\/view\/(Main|Public|Customer|OnPremise|Partner).*\",\1) if !is_root_path
# Determine requested page (either \3 or \4 is populated (exclusively) if there's an actual page requested, otherwise both will return an empty string)
http-request set-var(req.page) path,regsub(\"^\/$|\/bin\/view\/(Main|Public|Customer|OnPremise|Partner)($|\/$|\/(?!\S{2}\/)(.*)|\/\S{2}\/(.*))\",\3\4)
# Remove language from URL parameters
# This will also impact above ACL `has_valid_language_parameter` in terms of it being always `false` when used beyond this line
http-request set-query "%[query,regsub(\"(^language=\\S{2}&?|&language=\\S{2})\",,)]"
acl has_additional_query_parameters query -m len gt 0
# Finally redirect to "pretty" URL
http-request redirect code 302 location http://%[hdr(host)]/bin/view/%[var(req.wiki)]/%[var(req.lang)]/%[var(req.page)]?%[query] if is_invalid_language_path has_additional_query_parameters
http-request redirect code 302 location http://%[hdr(host)]/bin/view/%[var(req.wiki)]/%[var(req.lang)]/%[var(req.page)] if is_invalid_language_path !has_additional_query_parameters
default_backend xwiki
backend xwiki
option forwardfor
# When changing language, XWiki redirects from parameterized URL (/?language=en|de) to parameterless URL, saving language choice as cookie
# We want to avoid that redirect (which would reset the language to default DE) and set the cookie ourselves before forwarding request to XWiki
acl has_cookie_language hdr_reg(Cookie) -i ^.*language=\S{2}.*$
http-request replace-value Cookie "(.*)language=\S{2}(.*)" "\1 language=%[var(req.lang)]\2" if has_cookie_language
http-request replace-value Cookie "(.*)" "\1; language=%[var(req.lang)]" if !has_cookie_language
acl is_rewritable_path path_reg -i ^\/bin\/view\/(Main|Public|Customer|OnPremise|Partner).*
acl has_additional_query_parameters query -m len gt 0
# http-request set-query language=%[var(req.lang)]&%[query] if has_additional_query_parameters is_rewritable_path
# http-request set-query language=%[var(req.lang)] if !has_additional_query_parameters is_rewritable_path
http-request set-path /bin/view/%[var(req.wiki)]/%[var(req.page)] if is_rewritable_path
server xwiki localhost:8080 check port 8080