Selecting best language based on Browser language and available languages.

Your application is international and you have to select the best language for the user. Your user may have selected 'fr_CA' and you have 'fr_FR' or 'fr' available. What do you do? Here is a tip.

The code below will walk through the languages requested by the browser and try to find:

  • An exact match (fr_fr <-> fr_fr);
  • An approximate match (fr_fr <-> fr );
  • A more approximate match (fr_fr <-> fr_ca).
  • The default language (that you can define in the Yii configuration (/config/main.php).

In the code below, 'getSupportedLanguages' looks for languages that are in the application's '/messages' directory. These are the supposedly available languages.

Then this list of messages is compared with the list provided by the browser (with help from Yii::app()->request->getPreferredLanguages() ) according to the sequence mentionned above.

At the time of writing, the code is not very much tested in production, but some small tests indicate it is ok.

You can call this code in an 'onBeginRequest' as is suggested in this forum entry or in another location that may be more appropriate for you.

Generally you'ld call 'Utils::setLanguageFromBrowser()'. Change the class name to your likings, or include in your Utility class.

class Utils {
    /**
     * Set the best language based on browser request.
     */
    public static function setLanguageFromBrowser() {
        Yii::app()->setLanguage(self::getBestLanguageFromBrowser());
    }

    /**
     * Determine the best language base on browser preferences.
     *
     * @return string
     */
    public static function getBestLanguageFromBrowser() {
        $langs=Yii::app()->request->getPreferredLanguages();
        $langs=array_map(function($l){return Yii::app()->locale->getCanonicalID($l);},$langs);
        $supported=Utils::getSupportedLanguages();

        $lang=null;
        /* Find exact language */
        foreach($langs as $lang) {
            if(in_array($lang, $supported)) {
                return $lang;
            }
        }
        /**
         * Find base language for locale.
         */
        foreach($langs as $lang) {
            if(in_array(substr($lang,0,2), $supported)) {
                return $lang;
            }
        }
        /**
         * Find language that is the closest.
         */
        foreach($langs as $lang) {
            foreach($supported as $l) {
                if(substr($l,0,2)===substr($lang,0,2)) {
                    return $l;
                }
            }
        }

        return Yii::app()->language; // Default, standard language.
   }

   /**
    * Determine the languages supported by this platform.
    *
    * @return multitype:string
    */
   public static function getSupportedLanguages() {
        $translations=array();
        $dirs=new DirectoryIterator(Yii::app()->messages->basePath);
        foreach($dirs as $dir) {
            if($dir->isDir() && !$dir->isDot()) {
                $translations[$dir->getFilename()]=$dir->getFilename();
            }
        }
        return $translations;
        /* Alternative code if you want to include source language.
         * My source language is a fake because I use special codes for the source.
        return in_array(Yii::app()->sourceLanguage, $translations)
                ? $translations
                : array_merge(
                    $translations,
                    array(
                            Yii::app()->sourceLanguage => Yii::app()->sourceLanguage
                    )
            );
         */
    }
}