Difference between #7 and #6 of Manage (Target) Language in Multilingual Applications + A Language Selector Widget (i18n)

unchanged
Title
Manage (Target) Language in Multilingual Applications + A Language Selector Widget (i18n)
unchanged
Category
Tutorials
unchanged
Tags
i18n, language, multilanguage, session, cookie, Widgets, behavior, event handler
changed
Content
If you haveIn case of a multilingual application, you
haveone might consider it a reasonable approach to
providestore the preferred language of the user
within a meanssession variable, and after
that, every time a page is requested, to select/change the
language,check this session variable and you
haverender the page in the indicated language.
This tutorial shows a Yii-way of doing this. 
<br>We implement an event handler for the _onBeginRequest_ event; as the
name of the event suggests, this event handler will be called at the beginning
of each request, so its a good place to managecheck
whether a language is provided (via post, session or cookie) and set the
application language inaccordingly. 
<br>We also implement a simple Language-Selector Widget, which can
render the background.language options as ajax-links or as
a drop-down list. 

Here's my way of doing this (seeSee also this
[post](http://www.yiiframework.com/wiki/26/setting-and-maintaining-the-language-in-application-i18n/
"Setting Language") for an
alternative):alternative.

- Provide a language selector in the header of each page using a custom
widget.
- When a language is selected, save it in the Session and in a Cookie.
- Every time a page/url is requested, check if the language is provided (via
POST, session or cookie) and set the application language accordingly.
<br>For this, implement an event handler for the 'onBeginRequest' event.
As the name of the event suggests, this event handler then will be called on the
beginning of every request. 

_Why#### _Why is it necessary to set the application language
for every request?_
<br>--->The requested page will be shown in the _target language_ of
the application, which can be set/get via `Yii::app()->language`. 
<br>--->If this property is not set explicitly, Yii assumes it to be
equal to the _source language_ of the application, which can be set/get via
`Yii::app()->sourceLanguage` and defaults to `'en_us'`.
<br>--->These properties can also be set in the main config file, like 
<br>`'sourceLanguage'=>'en',`
<br>`'language'=>'de',`
<br>--->But hardcoding the target language in the main config file is
not an option if you have multiple target languages. Therefore we store the
current language in the session, and on the beginning of each request set the
target language explicitly, like
<br>`Yii::app()->language = Yii::app()->user->getState('_lang')`.

Now to the implementation...

<br>**'components/widgets/LanguageSelector.php'**:
~~~
[php]
class LanguageSelector extends CWidget
{
    public function run()
    {
        $currentLang = Yii::app()->language;
        $languages = Yii::app()->params->languages;
        $this->render('languageSelector', array('currentLang' =>
$currentLang, 'languages'=>$languages));
    }
}
~~~
I set the available languages in the main config file (see below) and retrieve
it here via `Yii::app()->params->languages`.

<br>**'components/widgets/views/languageSelector.php'**:
~~~
[php]
<?php echo CHtml::form(); ?>
    <div id="language-select">
        <?php 
		if(sizeof($languages) < 4) {
			$lastElement = end($languages);
			foreach($languages as $key=>$lang) {
				if($key != $currentLang) {
					echo CHtml::ajaxLink($lang,'',
						array(
							'type'=>'post',
							'data'=>'_lang='.$key.'&YII_CSRF_TOKEN='.Yii::app()->request->csrfToken,
							'success' => 'function(data) {window.location.reload();}'
						),
						array()
					);
				} else echo '<b>'.$lang.'</b>';
				if($lang != $lastElement) echo ' | ';
			}
		}
		else {
			echo CHtml::dropDownList('_lang', $currentLang, $languages,
				array(
					'submit' => '',
					'csrf'=>true,
				)
			); 
		}
		?>
    </div>
<?php echo CHtml::endForm(); ?>
~~~
If the number of available languages is smaller than four, the languages are
displayed as ajax-links, separated with a '|'. When clicked, an ajax-request of
type 'post' is sent to the current URL and the page is reloaded in case of
success (so it is displayed in the newly selected language). Note that I have to
send the 'YII_CSRF_TOKEN' in the request, because I have CSRF Validation for
cookies enabled in my config file (see below).
<br>If the number of languages is four or more, a dropDownList is
generated. 
<br>You can of course always use a dropDownList if you prefer.

<br>**'views/layouts/main.php'**
<br>Put the widget inside the `<div
id="header">...</div>`:

~~~
[php]
<div  id="language-selector" style="float:right;
margin:5px;">
	<?php 
		$this->widget('application.components.widgets.LanguageSelector');
	?>
</div>
~~~

<br>**'config/main.php'** 
<br>(add the following lines to the file, don't replace its contents):
~~~
[php]
return array(
	'sourceLanguage'=>'en',

	// Associates a behavior-class with the onBeginRequest event.
	// By placing this within the primary array, it applies to the application as a
whole
	'behaviors'=>array(
		'onBeginRequest' => array(
			'class' => 'application.components.behaviors.BeginRequest'
		),
	),

	// application components
	'components'=>array(
		'request'=>array(
			'enableCookieValidation'=>true,
			'enableCsrfValidation'=>true,
		),
                // ...some other components here...
	),
	// application-level parameters
	'params'=>array(
		'languages'=>array('tr'=>'Türkçe', 'en'=>'English',
'de'=>'Deutsch'),
	),
);
~~~

<br>**'components/behaviors/BeginRequest.php'**
~~~
[php]
<?php
class BeginRequest extends CBehavior {
	// The attachEventHandler() mathod attaches an event handler to an event. 
	// So: onBeginRequest, the handleBeginRequest() method will be called.
	public function attach($owner) {
		$owner->attachEventHandler('onBeginRequest', array($this,
'handleBeginRequest'));
	}
	
	public function handleBeginRequest($event) {		
		$app = Yii::app();
		$user = $app->user;
		
		if (isset($_POST['_lang']))
        {
            $app->language = $_POST['_lang'];
			$app->user->setState('_lang', $_POST['_lang']);
			$cookie = new CHttpCookie('_lang', $_POST['_lang']);
			$cookie->expire = time() + (60*60*24*365); // (1 year)
			Yii::app()->request->cookies['_lang'] = $cookie;
        }
        else if ($app->user->hasState('_lang'))
            $app->language = $app->user->getState('_lang');
		else if(isset(Yii::app()->request->cookies['_lang']))
			$app->language = Yii::app()->request->cookies['_lang']->value;
	}
}
~~~

<br>That's it. 
<br>If something is unclear, wrong or incomplete, please let me know.