Skip to content

Commit

Permalink
Add the LocaleSelectorMiddleware.
Browse files Browse the repository at this point in the history
This ports the behavior of the LocaleSelectorFilter to a PS7 middleware.
I've replaced the implict 'accept all' with the an explicit '*'.
Explicit is generally better than implicit in, and makes it easier to
understand what is going on when reading the code later.
  • Loading branch information
markstory committed May 6, 2016
1 parent 9f31c70 commit 18c3009
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 0 deletions.
61 changes: 61 additions & 0 deletions src/I18n/Middleware/LocaleSelectorMiddleware.php
@@ -0,0 +1,61 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.3.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\I18n\Middleware;

use Cake\I18n\I18n;
use Locale;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

/**
* Sets the runtime default locale for the request based on the
* Accept-Language header. The default will only be set if it
* matches the list of passed valid locales.
*/
class LocaleSelectorMiddleware
{
/**
* List of valid locales for the request
*
* @var array
*/
protected $locales = [];

/**
* Constructor.
*
* @param array $locales A list of accepted locales, or ['*'] to accept any
* locale header value.
*/
public function __construct(array $locales = [])
{
$this->locales = $locales;
}

/**
* @param ServerRequestInterface $request The request.
* @param ResponseInterface $response The response.
* @param callable $next The next middleware to call
* @return \Psr\Http\Message\ResponseInterface A response
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
$locale = Locale::acceptFromHttp($request->getHeaderLine('Accept-Language'));

if ($locale && (in_array($locale, $this->locales) || $this->locales === ['*'])) {
I18n::locale($locale);
}
return $next($request, $response);
}
}
116 changes: 116 additions & 0 deletions tests/TestCase/I18n/Middleware/LocaleSelectorMiddlewareTest.php
@@ -0,0 +1,116 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.3.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Test\TestCase\I18n\Middleware;

use Cake\I18n\I18n;
use Cake\I18n\Middleware\LocaleSelectorMiddleware;
use Cake\TestSuite\TestCase;
use Locale;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequestFactory;

/**
* Test for LocaleSelectorMiddleware
*/
class LocaleSelectorMiddlewareTest extends TestCase
{

/**
* setup
*
* @return void
*/
public function setUp()
{
parent::setUp();
$this->locale = Locale::getDefault();
$this->next = function ($req, $res) {
return $res;
};
}

/**
* Resets the default locale
*
* @return void
*/
public function tearDown()
{
parent::tearDown();
Locale::setDefault($this->locale);
}

/**
* The default locale should not change when there are no accepted
* locales.
*
* @return void
*/
public function testInvokeNoAcceptedLocales()
{
$request = ServerRequestFactory::fromGlobals();
$response = new Response();
$middleware = new LocaleSelectorMiddleware([]);
$middleware($request, $response, $this->next);
$this->assertSame($this->locale, I18n::locale());
}

/**
* The default locale should not change when the request locale is not accepted
*
* @return void
*/
public function testInvokeLocaleNotAccepted()
{
$request = ServerRequestFactory::fromGlobals(['HTTP_ACCEPT_LANGUAGE' => 'en-GB,en;q=0.8,es;q=0.6,da;q=0.4']);
$response = new Response();
$middleware = new LocaleSelectorMiddleware(['en_CA', 'en_US', 'es']);
$middleware($request, $response, $this->next);
$this->assertSame($this->locale, I18n::locale(), 'en-GB is not accepted');
}

/**
* The default locale should change when the request locale is accepted
*
* @return void
*/
public function testInvokeLocaleAccepted()
{
$request = ServerRequestFactory::fromGlobals(['HTTP_ACCEPT_LANGUAGE' => 'es,es-ES;q=0.8,da;q=0.4']);
$response = new Response();
$middleware = new LocaleSelectorMiddleware(['en_CA', 'es']);
$middleware($request, $response, $this->next);
$this->assertSame('es', I18n::locale(), 'es is accepted');
}

/**
* The default locale should change when the '*' is accepted
*
* @return void
*/
public function testInvokeLocaleAcceptAll()
{
$response = new Response();
$middleware = new LocaleSelectorMiddleware(['*']);

$request = ServerRequestFactory::fromGlobals(['HTTP_ACCEPT_LANGUAGE' => 'es,es-ES;q=0.8,da;q=0.4']);
$middleware($request, $response, $this->next);
$this->assertSame('es', I18n::locale(), 'es is accepted');

$request = ServerRequestFactory::fromGlobals(['HTTP_ACCEPT_LANGUAGE' => 'en;q=0.4,es;q=0.6,da;q=0.8']);
$middleware($request, $response, $this->next);
$this->assertSame('da', I18n::locale(), 'da is accepted');
}
}

0 comments on commit 18c3009

Please sign in to comment.