The language negotiation "Path prefix with language fallback" is not compatible with Boost.
Is there a recommended way for browser detection with Boost?
Thanks.

Comments

mikeytown2’s picture

Browser detection would be done at the Apache level or JavaScript level since PHP is not run serving a page from the boost cache.

Apache would use HTTP_USER_AGENT to give them the correct page on the first request
http://www.askapache.com/htaccess/mod_rewrite-variables-cheatsheet.html
http://stackoverflow.com/questions/1005153/auto-detect-mobile-browser-vi...

JavaScript would have to redirect after the page loaded to the correct page.
http://www.quirksmode.org/js/detect.html

Just wondering how does Path prefix with language fallback work exactly? Examples would be helpful.

fthieme’s picture

forget what I said...

From the settings page:
Path prefix with language fallback. The presentation language is determined by examining the path for a language code or other custom string that matches the path prefix (if any) specified for each language. If a suitable prefix is not identified, the display language is determined by the user's language preferences from the My Account page, or by the browser's language settings. If a presentation language cannot be determined, the default language is used.

mikeytown2’s picture

Thinking about this issue; I need to replicate Drupal's language_from_browser() in apache. Something that can't be done. lighttpd should be able to & I bet nginx can as well. One thing they both need is the language_list() from Drupal so it knows what languages it can match to.

Looking at this: http://tech-blog.borychowski.com/index.php/2009/03/htaccess/redirect-acc...
Using apache the best one could do is match on the first language & if none was found default to one picked by you. Lets say your site supports German (de) French (fr) and English (en); with English as the default.

These UNTESTED rules would go above the boost rules

  # 'Accept-Language' header starts with 'de' and path doesn't start with /de/
  RewriteCond %{HTTP:Accept-Language} ^(de.*) [NC]
  RewriteCond %{REQUEST_URI} !(^/de/.*) [NC]
  RewriteRule ^(.*)$ /de/$1 [L,R=301]

  # 'Accept-Language' header starts with 'fr' and path doesn't start with /fr/
  RewriteCond %{HTTP:Accept-Language} ^(fr.*) [NC]
  RewriteCond %{REQUEST_URI} !(^/fr/.*) [NC]
  RewriteRule ^(.*)$ /fr/$1 [L,R=301]

  #For every other language (including English) use English
  RewriteCond %{REQUEST_URI} !(^/en/.*) [NC]
  RewriteRule ^(.*)$ /en/$1 [L,R=301]

Option B
If request doesn't have a language prefix use PHP to select one. This would require a database bootstrap with the these 2 variables from the variable table (language_count & language_default).

PHP Code would kinda look like this UNTESTED code called boost_language.php

  include_once './includes/bootstrap.inc';
  drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);

  // Parse current request and return its components.
  global $base_root;
  $url = parse_url($base_root . request_uri());

  // Pull original path out
  $url['path'] = $_REQUEST['q'];
  unset($_REQUEST['q']);

  // Re-glue query string
  $url['query'] = str_replace('&', '&', urldecode(http_build_query($_REQUEST['q'])));

  // Browser accept-language parsing.
  $language = language_from_browser()

  // Fall back on the default if everything else fails.
  if (!$language) {
    $language = language_default();
  }

  // 302 redirect
  drupal_goto($language['language'] . $url['path'], $url['query'], $url['fragment'], 302);

The htaccess rules would go above boost and look this this; once again UNTESTED code

  RewriteCond %{REQUEST_URI} !(^/(en|de|fr)/.*) [NC]
  RewriteRule ^(.*)$ boost_language.php?q=$1 [L,QSA]
seg108’s picture

Thanks for the detailed suggestions. Option A gives me a redirect loop. Not familiar territory for me, so unsure how to proceed. I adapted only slightly for my languages:

 # 'Accept-Language' header starts with 'et' and path doesn't start with /et/
  RewriteCond %{HTTP:Accept-Language} ^(et.*) [NC]
  RewriteCond %{REQUEST_URI} !(^/et/.*) [NC]
  RewriteRule ^(.*)$ /et/$1 [L,R=301]

  # 'Accept-Language' header starts with 'fi' and path doesn't start with /fi/
  RewriteCond %{HTTP:Accept-Language} ^(fi.*) [NC]
  RewriteCond %{REQUEST_URI} !(^/fi/.*) [NC]
  RewriteRule ^(.*)$ /fi/$1 [L,R=301]

  #For every other language (including English) use English
  RewriteCond %{REQUEST_URI} !(^/en/.*) [NC]
  RewriteRule ^(.*)$ /en/$1 [L,R=301]
mikeytown2’s picture

Try this

  # 'Accept-Language' header starts with 'et' and path doesn't start with /et/
  RewriteCond %{HTTP:Accept-Language} ^et(.*) [NC]
  RewriteCond %{REQUEST_URI} !(^/(et|fi|en)/.*) [NC]
  RewriteRule ^(.*)$ /et$1 [L,R=302]

  # 'Accept-Language' header starts with 'fi' and path doesn't start with /fi/
  RewriteCond %{HTTP:Accept-Language} ^fi(.*) [NC]
  RewriteCond %{REQUEST_URI} !(^/(et|fi|en)/.*) [NC]
  RewriteRule ^(.*)$ /fi$1 [L,R=302]

  #For every other language (including English) use English
  RewriteCond %{REQUEST_URI} !(^/(et|fi|en)/(.*)) [NC]
  RewriteRule ^(.*)$ /en$1 [L,R=302]
seg108’s picture

Looping: enenenen

mikeytown2’s picture

Been playing around with this. If you have global redirect on to redirect all urls to the clean version then this might work exactly how it should.

  # 'Accept-Language' header starts with 'et' and path doesn't start with /et/
  RewriteCond %{HTTP:Accept-Language} ^et(.*) [NC]
  RewriteCond %{REQUEST_URI} !(^/(et|fi|en)/(.*)) [NC]
  RewriteRule ^(.*)$ index.php?q=/et/$1 [L,QSA]

  # 'Accept-Language' header starts with 'fi' and path doesn't start with /fi/
  RewriteCond %{HTTP:Accept-Language} ^fi(.*) [NC]
  RewriteCond %{REQUEST_URI} !(^/(et|fi|en)/(.*)) [NC]
  RewriteRule ^(.*)$ index.php?q=/fi/$1 [L,QSA]

  #For every other language (including English) use English
  RewriteCond %{REQUEST_URI} !(^/(et|fi|en)/(.*)) [NC]
  RewriteRule ^(.*)$ index.php?q=/en/$1 [L,QSA]
seg108’s picture

404. With "non-clean to clean" on (global redirect), seeing site, but returns 404 error for all pages. Deslash is off.

mikeytown2’s picture

try this

  # 'Accept-Language' header starts with 'et' and path doesn't start with /et/
  RewriteCond %{HTTP:Accept-Language} ^et(.*) [NC]
  RewriteCond %{REQUEST_URI} !(^/(et|fi|en)(.*)) [NC]
  RewriteRule ^(.*)$ index.php?q=/et/$1 [L,QSA]

  # 'Accept-Language' header starts with 'fi' and path doesn't start with /fi/
  RewriteCond %{HTTP:Accept-Language} ^fi(.*) [NC]
  RewriteCond %{REQUEST_URI} !(^/(et|fi|en)(.*)) [NC]
  RewriteRule ^(.*)$ index.php?q=/fi/$1 [L,QSA]

  #For every other language (including English) use English
  RewriteCond %{REQUEST_URI} !(^/(et|fi|en)(.*)) [NC]
  RewriteRule ^(.*)$ index.php?q=/en/$1 [L,QSA]

Also put this in http://drupal.org/project/search404. It will say what the url was trying to access in the textfield (search box) when you hit a 404

fthieme’s picture

The problem is, that everything is localized. Some queries aren't prefixed with /en/ or something and will be prefixed by these rules, making them fail.

My last attemt was this:

  # 'Accept-Language' header starts with 'de' and path doesn't start with /de/ or /en/
  RewriteCond %{HTTP:Accept-Language} ^de(.*) [NC]
  RewriteCond %{REQUEST_URI} !((^/(de|en)/.*)|(^/index.php.*)) [NC]
  RewriteRule ^(.*)$ /de/$1 [L,R=302]

  #For every other language (including English) use English
  RewriteCond %{REQUEST_URI} !((^/(de|en)/.*)|(^/index.php.*)) [NC]
  RewriteRule ^(.*)$ /en/$1 [L,R=302]
seg108’s picture

Here is solution I am now using for front page only:

  RewriteCond %{HTTP:Accept-Language} ^et.*$ [NC]
  RewriteCond %{REQUEST_URI} ^/$ [NC]
  RewriteCond %{QUERY_STRING} !(^q\=) [NC]
  RewriteRule ^(.*)$ /et [L,R=302]

  RewriteCond %{HTTP:Accept-Language} ^fi.*$ [NC]
  RewriteCond %{REQUEST_URI} ^/$ [NC]
  RewriteCond %{QUERY_STRING} !(^q\=) [NC]
  RewriteRule ^(.*)$ /fi [L,R=302]
giorgosk’s picture

solution #12 works like a charm
maybe it can be documented in the readme.txt as a workaround for language negotiations

funature’s picture

i'm also looking for a solution for this problem. #12 is a good one, but there is a problem, if the user's browser language is "et", it will be redirected to /et, but when the user wanna visit the english vision, it will be redirected to /et again, not the english frontpage. that means, when the browser language is not the drupal-default-language, the vistor can not go to the drupal-default-language frontpage.

mikeytown2’s picture

/en for English would work correct?

funature’s picture

http://www.saaremaaestonia.com/ as example, if you set the language "et" as your browser default language, and you visit http://www.saaremaaestonia.com/, you will be redirected to http://www.saaremaaestonia.com/et, then if you want to visit the english version of the website, it is impossible unless you change the default browser language to english.

mikeytown2’s picture

/en get's redirected to the root... you could set a cookie via hook_boot and in the htaccess rules check for the cookie. Cookie would be their preferred language.

funature’s picture

sorry. i don't know how to do that. is there an example?

janton’s picture

Is there already some smart solution for this?
or also in general how to get browser language detection with path prefix working with boost..
I will try to find a work around, but i'm not really that great of a programmer.

janton’s picture

ok stuppid i did not read the thing about .htacces cookie.
but perhaps i don't need it because straingly i remember it worked already in boost, perhaps this is already implemented?

hamsterbacke42’s picture

#12 worked for me and I can still switch between languages without any problem

fabianx’s picture

Just check referer ...

There is even somewhere a generator to generate such .htaccess language code automatically.

I only have the varnish rule here at the moment, but it should be easy to transfer this to .htaccess code:

  if (req.http.host ~ "(www)?\.(example)\.com" && req.url ~ "^/$" && req.http.accept-language ~ "de" && !(req.http.referer ~ "(www)?\.(example)\.com")) {
    error 301;
  }

So just checking for:

RewriteCond %{HTTP:Referer} www\.example\.com [NC]

should do the trick ...

Best Wishes,

Fabian

mrfelton’s picture

I wonder if anyone has these rules for nginx?

jimyhuang’s picture

Here is our example at drupal 6, especially for sub directory. This should even works without boost.

# If your site is running in a VirtualDocumentRoot at http://example.com/
# uncomment the following line:
# RewriteBase /
RewriteBase /abc

RewriteCond %{HTTP:Accept-Language} ^zh.*$ [NC]
RewriteCond %{HTTP_COOKIE} !DRUPAL_UID
RewriteCond %{REQUEST_URI} ^\/abc\/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule !\.(jpg|css|js|gif|png)$ zh-hans [R=302,L]

RewriteCond %{HTTP:Accept-Language} ^en.*$ [NC]
RewriteCond %{HTTP_COOKIE} !DRUPAL_UID
RewriteCond %{REQUEST_URI} ^\/abc\/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule !\.(jpg|css|js|gif|png)$ en [R=302,L]

# Rewrite URLs of the form 'x' to the form 'index.php?q=x'.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
gagarine’s picture

Version: 6.x-1.18 » 7.x-1.x-dev

I did a configuration for nginx and D7... It works but I'm not sure is the best way to achieve this.

I created a map

map $http_accept_language $lang {
        default de;
        ~*fr fr;
        ~*it it;
}

And I set a conditional redirection than check if arguments are empty

 location ~ \.php$ {
                if ($args = '') {
                        rewrite ^/index.php$ /$lang redirect;
                }
                if ($args = 'q='){
                        rewrite ^/index.php$ /$lang? redirect;
                }
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
...

Complete config https://gist.github.com/3247690

jalves’s picture

Hey!

#12 worked pretty well!

Why the rule is using temporary redirect (302) shouldn't we use permanent redirect (301)?
I don't really know what is the best but based in same research i made about that code i'm a little bit confused..

kspal’s picture

Quadruple thanks for this post and especially #12. My sites' homepage are now fast as expected (in D7.20 + boost)

maxilein’s picture

Title: Language fallback, browser detection » Drupal 6: Language fallback, browser detection
Version: 7.x-1.x-dev » 6.x-1.21
StatusFileSize
new1.42 KB

Here is the solution that worked for me (Drupal 6).

a) browser language detection for front / page while Boost is caching. From #12.

 # Supplement for php language detection on <front>:
  RewriteCond %{HTTP:Accept-Language} ^de.*$ [NC]
  RewriteCond %{REQUEST_URI} ^/$ [NC]
  RewriteCond %{QUERY_STRING} !(^q\=) [NC]
  RewriteRule ^(.*)$ /de [L,R=302]
  #
  RewriteCond %{HTTP:Accept-Language} ^en.*$ [NC]
  RewriteCond %{REQUEST_URI} ^/$ [NC]
  RewriteCond %{QUERY_STRING} !(^q\=) [NC]
  RewriteRule ^(.*)$ /en [L,R=302]

b) I also did want to always have the language prefix for the default language:

In order to make that work I had to take a hack for core (ugly! but in my case harmless. Test! If you use that) I found here: http://eureka.ykyuen.info/2011/06/19/drupal-force-path-prefix-for-defaul...

Then I added this to htaccess:

  # add en/ for default language (combine with patch to language.inc!)
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_URI} !=/favicon.ico
  RewriteCond %{REQUEST_URI} !(^/(de|en)(.*)) [NC]
  RewriteRule ^(.*)$ en/$1 [R,L] 

Patch to language.inc attached. See for comments where to add into htaccess.