MODX: Multi language website using browser ACCEPT_LANGUAGE


    We need to have a multi language website using babel, but the problem is that current tutorial takes ONLY the first language sent by browser (using rewrite rules) neglecting how ACCEPT-LANGUAGE parameter is designed to work. If we use ContextRouter, then it depends on HTTP_HOST parameter which has to be entered per each context.

Thus I came up with this solution which switches the context by checking the languages order including its locale and accordingly switch the context. A replacement for the official tutorial plugin, hooks to the same event (OnHandleRequest).

The official tutorial still applies, you only need to remove the language redirect rules from .htaccess because this is what we are doing with this plugin.

# The Friendly URLs part
# detect language when requesting the root (/)
RewriteCond %{HTTP:Accept-Language} !^de [NC]
RewriteRule ^$ en/ [R=301,L]
RewriteRule ^$ de/ [R=301,L]

* This results that the home page request remains with the root URL without a 301 redirect while it is returned in the first browser language which matches any of our contexts.

NB. I wrote this assuming the use of subfolder, to make this subdomain compatible slight changes are needed; to be covered later.

The plugin

<?php
if($modx->context->get('key') == "mgr"){ return; }
$tstart = microtime(true);

$url_array = explode("/",$_SERVER["REQUEST_URI"]);
$URIbaseURL = $url_array[1];

//make 2 arrays of (context => culturekey, context => base_url)
$ctxLang;
$ctxBaseURL;
$ctxArray = array();
$babelContexts = explode(',', $modx->getOption('babel.contextKeys'));
foreach ($babelContexts as $context) {
	$ctx = $modx->getContext($context);
	$ctxLang[$ctx->key] = $ctx->config['cultureKey'] ;
	$ctxBaseURL[$ctx->key] = $ctx->config['base_url'];
	array_push($ctxArray, array('Key' => $ctx->key, 'CultureKey'=> $ctx->config['cultureKey'], 'BaseURL' => $ctx->config['base_url'] ) );
}
$modx->log(modX::LOG_LEVEL_DEBUG, 'GateWay: Routing contexts: ' . print_r($ctxArray,true));
unset($ctxArray, $ctx);
    
/* Switch context based on URL */
if(isset($URIbaseURL)){
    $ctxMatch = array_search("/$URIbaseURL/", $ctxBaseURL);
    if($ctxMatch){
        $modx->switchContext($ctxMatch);
        $modx->log(modX::LOG_LEVEL_DEBUG, "GateWay: routing to context ($ctxMatch) by base_url (/$URIbaseURL/) in " . performance($tstart) );
		return;
    }
}
    
/* Switch context based on browser ACCEPT_LANGUAGE */
$acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
if(isset($acceptLang)) {
	$modx->log(modX::LOG_LEVEL_DEBUG, 'GateWay: Accept-Language: ' . $acceptLang);
	//Parse language and change context accordingly
	$acceptLang = explode(',', $acceptLang);
	foreach($acceptLang as $lang){
		$lang =  strstr($lang, ';', true)? strstr($lang, ';', true) : $lang ;
		$ctxMatch = array_search($lang, $ctxLang);
		if ($ctxMatch){
		    $modx->switchContext($ctxMatch);
			$modx->log(modX::LOG_LEVEL_DEBUG, "GateWay: Language($lang) Matched context: $ctxMatch in " . performance($tstart) );
			return;
		}
		else{  //Language not matching so we check sub lang eg. (en) in (en-CA)
			$sublang =  strstr($lang, '-', true)? strstr($lang, '-', true) : '' ;
			$ctxMatch = array_search($sublang, $ctxLang);
			if ($ctxMatch){
			    $modx->switchContext($ctxMatch);
				$modx->log(modX::LOG_LEVEL_DEBUG, "GateWay: subLanguage($sublang) from ($lang) matched context: ($ctxMatch) in " . performance($tstart) );
				return;
			}
			else{
				$modx->log(modX::LOG_LEVEL_DEBUG, "GateWay: no language match for ($lang)-($sublang)");
			}
		}
	}
}
$modx->switchContext($modx->getOption('default_context'));
$modx->log(modX::LOG_LEVEL_DEBUG, "GateWay: no language match or base-url set; Default context returned.");
return;

function performance($tstart = 0){
    $tend = microtime(true);
    $runTime = sprintf('%2.4f s', $tend - $tstart);
    return $runTime;
}

 

.htaccess

Enable Rewrite Engine and set base to root
RewriteEngine On
RewriteBase /
 
Files redirect
* Do the same for other static files and folders
# redirect all requests to /en/assets* and /de/assets* to /assets*
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(en|de|ru)/favicon.ico$ favicon.ico [L,QSA]
RewriteRule ^(en|de|ru)/assets(.*)$ assets$2 [L,QSA]
RewriteRule ^(en|de|ru)/files(.*)$ files$2 [L,QSA]
 
Enable friendly URL (has to be after files redirect)
# The Friendly URLs part
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(en|de|ru)?/?(.*)$ index.php?q=$2 [L,QSA]

 Context settings

  • culturekey     eg. "de"
  • base_url        eg. "/de/"
  • site_start       document id of main page for the context
  • site_url          eg. "//www.domain.com/de/"
  • error_page    document id of page not found (404)

 NB.

  • Each context has to has the culturekey setting set, including the default "web"
  • Notice "//" in site_url setting; that is to preserve HTTP/HTTPS status