Question

I've my site set up so I just need to add "?lang=en" or "?lang=es" to change languages English / Spanish.

When I enter the site with, for ex, "http://domain.com/something/something_else?lang=es", a cookie is set so I continue to navigate the site in that language.

I would like to redirect my users first by the "Accept-Language" value of their browser, but then allow them to continue to navigate the site in other language if they want to.

What would be the best way to do it? Would .htaccess work along with the cookie that's set when the language is chosen?

EDIT: Here's my updated code with Paul answer:

EDIT2: Oh, I just have "en" and "es" languages. I'm not sure on how this code wpuld choose only between this two or set the default... :/

    if (isset($_GET["lang"]))
        $this->setLanguage($_GET["lang"]);
    elseif (isset($_COOKIE["language"]))
        $this->setLanguage($_COOKIE["language"]);
    elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
        {
        // Parse the Accept-Language according to:
        // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
        preg_match_all(
           '/([a-z]{1,8})' . // First part of language e.g en
           '(-[a-z]{1,8})*\s*' . // other parts of language e.g -us
           // Optional quality factor
           '(;\s*q\s*=\s*((1(\.0{0,3}))|(0(\.[0-9]{0,3}))))?/i',
           $_SERVER['HTTP_ACCEPT_LANGUAGE'],
           $langParse);

        $langs = $langParse[1];
        $quals = $langParse[4];

        $numLanguages = count($langs);
        $langArr = array();

        for ($num = 0; $num < $numLanguages; $num++)
        {
           $newLang = strtoupper($langs[$num]);
           $newQual = isset($quals[$num]) ?
              (empty($quals[$num]) ? 1.0 : floatval($quals[$num])) : 0.0;

           // Choose whether to upgrade or set the quality factor for the
           // primary language.
           $langArr[$newLang] = (isset($langArr[$newLang])) ?
              max($langArr[$newLang], $newQual) : $newQual;
        }

        // sort list based on value
        arsort($langArr, SORT_NUMERIC);
        $acceptedLanguages = array_keys($langArr);
        $preferredLanguage = reset($acceptedLanguages);

        $this->setLanguage($preferredLanguage);
     }
     else
        $this->setLanguage("en");
Was it helpful?

Solution

I do this in PHP. Accept-Language is a complex thing. A browser can suggest more than one language that it would like to accept (each with a quality factor that shows which it prefers). For my site I have a default language to display (which is shown when none of the Accept-Languages is in my translation list). Otherwise if there is no language set (setLang) I choose it based on the most acceptable for the browser by parsing the Accept-Language. The function I use is below (it contains my session manager for setting cookies - but you could reimplement that with direct calls to $_SESSION[etc] = $foo;).

Edit: Unfortunately my website only has translations for the primary languages (EN, ES, FR) rather than (en_US, en_GB, es_MX, es_ES) so I choose the highest quality factor specified in these for the primary language.

   public function setLanguage($setLang='')
   {
      if (!empty($setLang))
      {
         $this->setup['Session']->set($this->setup['Lang_Key'], $setLang);
      }
      else
      {
         if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
         {
            // Parse the Accept-Language according to:
            //    http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
            preg_match_all(
               '/([a-z]{1,8})' . // First part of language e.g en
               '(-[a-z]{1,8})*\s*' . // other parts of language e.g -us
               // Optional quality factor
               '(;\s*q\s*=\s*((1(\.0{0,3}))|(0(\.[0-9]{0,3}))))?/i',
               $_SERVER['HTTP_ACCEPT_LANGUAGE'],
               $langParse);

            $langs = $langParse[1];
            $quals = $langParse[4];

            $numLanguages = count($langs);
            $langArr = array();

            for ($num = 0; $num < $numLanguages; $num++)
            {
               $newLang = strtoupper($langs[$num]);
               $newQual = isset($quals[$num]) ?
                  (empty($quals[$num]) ? 1.0 : floatval($quals[$num])) : 0.0;

               // Choose whether to upgrade or set the quality factor for the
               // primary language.
               $langArr[$newLang] = (isset($langArr[$newLang])) ?
                  max($langArr[$newLang], $newQual) : $newQual;
            }

            // sort list based on value
            arsort($langArr, SORT_NUMERIC);
            $acceptedLanguages = array_keys($langArr);
            $preferredLanguage = reset($acceptedLanguages);

            $this->setup['Session']->set(
               $this->setup['Lang_Key'], $preferredLanguage);
         }
         else
         {
            $this->setup['Session']->set(
               $this->setup['Lang_Key'], $this->setup['Default_Language']);
         }
      }

      return $this->setup['Session']->get($this->setup['Lang_Key']);
   }

OTHER TIPS

I do this in PHP. Accept-Language is a complex thing. A browser can suggest more than one language that it would like to accept (each with a quality factor that shows which it prefers).

Unfortunately my website only has translations for the primary languages (EN, ES, FR) rather than (en_US, en_GB, es_MX, es_ES) so I choose the highest quality factor specified in these for the primary language.

Below is an untested edit which should remove most or all of the dependencies from my code. Sorry, things were confusing with my previous answer. I had a few calls to my function with some checking of languages are done elsewhere. The code below should set the session language variable, which you should use elsewhere for determining the correct translation.

It seems a lot less complicated than my previous answer and I will have to implement this in my own code before long. For people who need specific translations (EN_US, EN_GB) then the below code should be modified to take account of Match 2 in the preg_match_all.

$websiteLanguages = array('EN', 'ES');
session_start();

// The user wants a specific language regardless of their accept settings.
if (isset($_GET["lang"]))
{
    $_SESSION["language"] = $_GET["lang"];
    return;
}

// A language has already been decided upon.
if (isset($_SESSION["language"]))
{
   return;
}

// No language has been chosen we should choose from the accept language.  
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
{
    // Parse the Accept-Language according to:
    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
    preg_match_all(
       '/([a-z]{1,8})' . // M1 - First part of language e.g en
       '(-[a-z]{1,8})*\s*' . // M2 -other parts of language e.g -us
       // Optional quality factor M3 ;q=, M4 - Quality Factor
       '(;\s*q\s*=\s*((1(\.0{0,3}))|(0(\.[0-9]{0,3}))))?/i',
       $_SERVER['HTTP_ACCEPT_LANGUAGE'],
       $langParse);

    $langs = $langParse[1]; // M1 - First part of language
    $quals = $langParse[4]; // M4 - Quality Factor

    $numLanguages = count($langs);
    $langArr = array();

    for ($num = 0; $num < $numLanguages; $num++)
    {
       $newLang = strtoupper($langs[$num]);
       $newQual = isset($quals[$num]) ?
          (empty($quals[$num]) ? 1.0 : floatval($quals[$num])) : 0.0;

       // Choose whether to upgrade or set the quality factor for the
       // primary language.
       $langArr[$newLang] = (isset($langArr[$newLang])) ?
          max($langArr[$newLang], $newQual) : $newQual;
    }

    // sort list based on value
    // langArr will now be an array like: array('EN' => 1, 'ES' => 0.5)
    arsort($langArr, SORT_NUMERIC);

    // The languages the client accepts in order of preference.
    $acceptedLanguages = array_keys($langArr);

    // Set the most preferred language that we have a translation for.
    foreach ($acceptedLanguages as $preferredLanguage)
    {
        if (in_array($preferredLanguage, $websiteLanguages))
        {
           $_SESSION['Language'] = $preferredLanguage;
           return;
        }
    }
 }

 // We will have returned by now if a language could be chosen, otherwise use
 // our default language.
 $_SESSION['Language'] = "EN";
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top