Skip to content

Commit 1b8d4e4

Browse files
authored
Merge pull request #119 from codemix/language-changed-event
Implement languageChanged event
2 parents 8b65782 + 4c7bb37 commit 1b8d4e4

File tree

5 files changed

+210
-0
lines changed

5 files changed

+210
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*.swp
2+
.vimrc
23
composer.lock
34
/vendor

LanguageChangedEvent.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
namespace codemix\localeurls;
3+
4+
use yii\base\Event;
5+
6+
/**
7+
* This event represents a change of persisted user language via URL.
8+
*/
9+
class LanguageChangedEvent extends Event
10+
{
11+
/**
12+
* @var string the new language
13+
*/
14+
public $language;
15+
16+
/**
17+
* @var string|null the old language
18+
*/
19+
public $oldLanguage;
20+
}

README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,63 @@ This will give you:
252252
/demo/action
253253

254254

255+
#### Language Change Event
256+
257+
When persistence is enabled, the component will fire a `languageChanged` event
258+
whenever the language stored in session or cookie changes. Here's an example
259+
how this can be used:
260+
261+
```php
262+
<?php
263+
264+
'urlManager' => [
265+
'class' => 'codemix\localeurls\UrlManager',
266+
'languages' => ['en', 'fr', 'de'],
267+
'on languageChanged' => `\app\helpers\MyHelper::languageChanged',
268+
]
269+
```
270+
271+
The static class method in `MyHelper` could look like this:
272+
273+
```php
274+
<?php
275+
public static function languageChanged($event)
276+
{
277+
// $event->language: new language
278+
// $event->oldLanguage: old language
279+
280+
// Save the current language to user record
281+
$user = Yii::$app->user;
282+
if (!$user->isGuest) {
283+
$user->identity->language = $event->language;
284+
$user->identity->save();
285+
}
286+
}
287+
```
288+
289+
You could then for example restore the user language in the `afterLogin` event
290+
of a custom `user` component:
291+
292+
```php
293+
<?php
294+
public function afterLogin($identity, $cookieBased, $duration)
295+
{
296+
parent::afterLogin($identity, $cookieBased, $duration);
297+
$language = $identity->language;
298+
if ($language !==null && Yii::$app->language !== $language) {
299+
Yii::$app
300+
->getResponse()
301+
->redirect(['site/index', 'language' => $language]);
302+
Yii::$app->end();
303+
}
304+
}
305+
```
306+
307+
> **Note:** A language may already have been selected before a new user signs
308+
> up. So remember to also save the app language in the user model when
309+
> inserting a new user.
310+
311+
255312
### Language Detection
256313

257314
If a user visits your site for the first time and there's no language stored in session

UrlManager.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
*/
1818
class UrlManager extends BaseUrlManager
1919
{
20+
const EVENT_LANGUAGE_CHANGED = 'languageChanged';
21+
2022
/**
2123
* @var array list of available language codes. More specific patterns
2224
* should come first, e.g. 'en_us' before 'en'. This can also contain
@@ -406,6 +408,15 @@ protected function processLocaleUrl($normalized)
406408
*/
407409
protected function persistLanguage($language)
408410
{
411+
if ($this->hasEventHandlers(self::EVENT_LANGUAGE_CHANGED)) {
412+
$oldLanguage = $this->loadPersistedLanguage();
413+
if ($oldLanguage !== $language) {
414+
$this->trigger(self::EVENT_LANGUAGE_CHANGED, new LanguageChangedEvent([
415+
'oldLanguage' => $oldLanguage,
416+
'language' => $language,
417+
]));
418+
}
419+
}
409420
Yii::$app->session[$this->languageSessionKey] = $language;
410421
Yii::trace("Persisting language '$language' in session.", __METHOD__);
411422
if ($this->languageCookieDuration) {

tests/EventTest.php

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
use yii\helpers\Url;
4+
use \codemix\localeurls\LanguageChangedEvent;
5+
6+
class EventTest extends TestCase
7+
{
8+
protected $eventExpected = true;
9+
protected $eventFired = false;
10+
protected $expectedLanguage;
11+
protected $expectedOldLanguage;
12+
13+
/**
14+
* @inheritdoc
15+
*/
16+
protected function tearDown()
17+
{
18+
parent::tearDown();
19+
$this->eventExpected = true;
20+
$this->eventFired = false;
21+
$this->expectedLanguage = null;
22+
$this->expectedOldLanguage = null;
23+
}
24+
25+
public function testFiresIfNoLanguagePersisted()
26+
{
27+
$this->mockUrlManager([
28+
'languages' => ['fr', 'en', 'de'],
29+
'on languageChanged' => [$this, 'languageChangedHandler'],
30+
]);
31+
$this->expectedLanguage = 'fr';
32+
33+
$this->assertFalse($this->eventFired);
34+
$this->mockRequest('/fr/site/page');
35+
$this->assertTrue($this->eventFired);
36+
}
37+
38+
public function testFiresOnCookieLanguageChange()
39+
{
40+
$_COOKIE['_language'] = 'de';
41+
$this->mockUrlManager([
42+
'languages' => ['fr', 'en', 'de'],
43+
'on languageChanged' => [$this, 'languageChangedHandler'],
44+
]);
45+
$this->expectedLanguage = 'fr';
46+
$this->expectedOldLanguage = 'de';
47+
48+
$this->assertFalse($this->eventFired);
49+
$this->mockRequest('/fr/site/page');
50+
$this->assertTrue($this->eventFired);
51+
}
52+
53+
public function testFiresOnSessionLanguageChange()
54+
{
55+
@session_start();
56+
$_SESSION['_language'] = 'de';
57+
$this->mockUrlManager([
58+
'languages' => ['fr', 'en', 'de'],
59+
'on languageChanged' => [$this, 'languageChangedHandler'],
60+
]);
61+
$this->expectedLanguage = 'fr';
62+
$this->expectedOldLanguage = 'de';
63+
64+
$this->assertFalse($this->eventFired);
65+
$this->mockRequest('/fr/site/page');
66+
$this->assertTrue($this->eventFired);
67+
}
68+
69+
public function testFiresNotIfNoCookieLanguageChange()
70+
{
71+
$_COOKIE['_language'] = 'fr';
72+
$this->mockUrlManager([
73+
'languages' => ['fr', 'en', 'de'],
74+
'on languageChanged' => [$this, 'languageChangedHandler'],
75+
]);
76+
77+
$this->assertFalse($this->eventFired);
78+
$this->mockRequest('/fr/site/page');
79+
$this->assertFalse($this->eventFired);
80+
}
81+
82+
public function testFiresNotIfNoSessionLanguageChange()
83+
{
84+
@session_start();
85+
$_SESSION['_language'] = 'fr';
86+
$this->mockUrlManager([
87+
'languages' => ['fr', 'en', 'de'],
88+
'on languageChanged' => [$this, 'languageChangedHandler'],
89+
]);
90+
91+
$this->assertFalse($this->eventFired);
92+
$this->mockRequest('/fr/site/page');
93+
$this->assertFalse($this->eventFired);
94+
}
95+
96+
public function testFiresNotIfPersistenceDisabled()
97+
{
98+
$this->mockUrlManager([
99+
'languages' => ['fr', 'en', 'de'],
100+
'on languageChanged' => [$this, 'languageChangedHandler'],
101+
'enableLanguagePersistence' => false,
102+
]);
103+
$this->expectedLanguage = 'fr';
104+
105+
$this->assertFalse($this->eventFired);
106+
$this->mockRequest('/fr/site/page');
107+
$this->assertFalse($this->eventFired);
108+
}
109+
110+
/**
111+
* Event handler
112+
*/
113+
public function languageChangedHandler($event)
114+
{
115+
$this->assertInstanceOf('\codemix\localeurls\LanguageChangedEvent', $event);
116+
$this->assertTrue($this->eventExpected);
117+
$this->assertEquals($this->expectedLanguage, $event->language);
118+
$this->assertEquals($this->expectedOldLanguage, $event->oldLanguage);
119+
$this->eventFired = true;
120+
}
121+
}

0 commit comments

Comments
 (0)