.htaccessmod-rewriteapache2multiviewsmod-negotiation

Language selection via MultiViews only works partially when requesting another resource


I have a little site that's available in different languages which are selected based on the following criteria (increasing order of preference):

  1. Accept-Language sent by the browser
  2. A cookie specifying the preferred language
  3. The request path containing a language preference at the end

The site itself consists of only static HTML pages and insofar possible I'd like to keep it that way. I implemented the language selection via mod_negotiation, mod_rewrite and the following .htaccess file (shortened to omit declaration of languages and charsets):

Options FollowSymLinks MultiViews
DirectoryIndex index

Header set Pragma no-cache

RewriteEngine on
RewriteRule ^([a-zA-Z-]{2,5})$ index [CO=language:$1:.example.com:525600,E=LANG:$1]

SetEnvIf Cookie "language=([a-zA-Z-]+)" COOKIE_LANG=$1

SetEnvIf COOKIE_LANG (.+) prefer-language=$1
SetEnvIf REDIRECT_LANG (.+) prefer-language=$1

# plenty of AddLanguage and AddCharset calls

LanguagePriority en-us
DefaultLanguage en-us
ForceLanguagePriority Prefer Fallback

which has been working fine since August or so to allow for the following:

  1. No cookie present, Accept-Language takes over (thanks to MultiViews)
  2. Cookie present, the cookie's language will be used (prefer-language)
  3. The request path is /es or similar and that will be used to (a) set a cookie and (b) set prefer-language to that value.

Now, I have a few more resources on the site which, thanks to MultiViews, can be selected via /resource instead of /resource.html and content negotiation and the cookie option still work fine for selecting the appropriate language here.

Now I wanted to expand URI processing so that things like /resource/en work in directly selecting the resource in a specific language, so that this doesn't only work for /. I tried the following RewriteRule:

RewriteRule ^(([^/]+)/)?([a-zA-Z-]{2,5})$ /$2 [CO=language:$3:.example.com:525600,E=LANG:$3]

and it works insofar that the correct resource is sent and the correct cookie is set, but the preferred language apparently isn't taken from the environment variable anymore. The only thing now forcing the language selection is the cookie, but that is still the same as before on that request, so I won't see the new language until I refreshed the page. Nothing I tried so far with the rules had an effect on that behaviour. Interestingly, with this rule the old behaviour documented above still works fine. It's only if I request something different from / that it does strange things.

This is on shared hosting (within a FreeBSD jail) so I can't enable the rewrite log (as I don't even know the physical path where the files are); besides, it quite clearly seems to ignore the environment variable and only using the cookie for setting the preferred language for some reason. And I haven't figured out why so far. Any help?


Solution

  • I solved the problem now by mixing the language selection between RewriteRule and SetEnvIf:

    SetEnvIf Cookie "language=([a-zA-Z-]+)" prefer-language=$1
    SetEnvIf REDIRECT_prefer-language "(.+)" prefer-language=$1
    SetEnvIf Request_URI "/([a-zA-Z-]{2,5})$" prefer-language=$1
    
    RewriteRule "^(([^/]+)/)?([a-zA-Z-]{2,5})$" /$2 [CO=language:$3:.example.com:525600]
    

    The rule now only sets the cookie and points to the correct resource, while the language selection is handled by matching on the request URI. Still feels weird, especially since it's not easily visible when something needs to be prefixed with REDIRECT_, but at least it works now.