<?php
namespace Croogo\Translate\Middleware;
use Cake\Core\Configure;
use Cake\Core\InstanceConfigTrait;
use Cake\I18n\I18n;
use Cake\Network\Request;
use Cake\Utility\Hash;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
/**
* I18nMiddleware
*
* Detect language based on request header
*
* Based on ADmad/cakephp-i18n package
*/
class I18nMiddleware
{
use InstanceConfigTrait;
/**
* Default config.
*
* ### Valid keys
*
* - `detectLanguage`: If `true` will attempt to get browser locale and
* redirect to similar language available in app when going to site root.
* Default `true`.
* - `defaultLanguage`: Default language for app. Default `en_US`.
* - `languages`: Languages available in app. Default `[]`.
*
* @var array
*/
protected $_defaultConfig = [
'detectLanguage' => true,
'defaultLanguage' => 'en_US',
'languages' => [],
];
/**
* Constructor.
*
* @param array $config Settings for the filter.
*/
public function __construct($config = [])
{
if (isset($config['languages'])) {
$config['languages'] = Hash::normalize($config['languages']);
}
$this->setConfig($config);
}
/**
* Sets appropriate locale and lang to I18n::getLocale() and App.language config
* respectively based on "lang" request param.
*
* @param \Psr\Http\Message\ServerRequestInterface $request The request.
* @param \Psr\Http\Message\ResponseInterface $response The response.
* @param callable $next Callback to invoke the next middleware.
*
* @return \Psr\Http\Message\ResponseInterface A response
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
$config = $this->getConfig();
$url = $request->getUri()->getPath();
$langs = $config['languages'];
$requestParams = $request->getAttribute('params');
$lang = isset($requestParams['lang']) ? $requestParams['lang'] : $config['defaultLanguage'];
if (isset($langs[$lang])) {
I18n::setLocale($langs[$lang]['locale']);
} else {
I18n::setLocale($lang);
}
Configure::write('App.language', $lang);
return $next($request, $response);
}
/**
* Get languages accepted by browser and return the one matching one of
* those in config var `I18n.languages`.
*
* You should set config var `I18n.languages` to an array containing
* languages available in your app.
*
* @param \Psr\Http\Message\ServerRequestInterface $request The request.
* @param string|null $default Default language to return if no match is found.
*
* @return string
*/
public function detectLanguage(ServerRequestInterface $request, $default = null)
{
if (empty($default)) {
$lang = $this->getConfig('defaultLanguage');
} else {
$lang = $default;
}
$cakeRequest = new Request();
$browserLangs = $cakeRequest->acceptLanguage();
foreach ($browserLangs as $k => $langKey) {
if (strpos($langKey, '-') !== false) {
$browserLangs[$k] = substr($langKey, 0, 2);
}
}
$acceptedLangs = array_intersect(
$browserLangs,
array_keys($this->getConfig('languages'))
);
if (!empty($acceptedLangs)) {
$lang = reset($acceptedLangs);
}
return $lang;
}
}