Skip to content

Commit

Permalink
Route listener now injects language into translator and user. Added m…
Browse files Browse the repository at this point in the history
…ore documentation
  • Loading branch information
xelax90 committed Nov 7, 2016
1 parent 2ae865d commit 1b8707a
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 16 deletions.
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,72 @@
# Language Rotue for Zend Framework 2 and 3

Modue that adds a language to the beginning of every route and sets the selected language in the MvcTranslator.
Modue that adds a language to the beginning of every route and sets the selected
language in the MvcTranslator.

## Installation

Installation of LanguageRoute uses composer. For composer documentation, please
refer to [getcomposer.org](http://getcomposer.org/).

```sh
composer require xelax90/zf2-language-route
```

Then add `ZF2LanguageRoute` to your `config/application.config.php`

To display the language switch you will need the
[usrz/bootstrap-languages](https://github.com/usrz/bootstrap-languages) package.
Add the languages.css to your stylesheets and place the languages.png in the
same folder.

## Configuration

You can configure which prefix belongs to which language. For this, copy the
config/zf2-language-route.global.php into your config/autoload folder and
edit the array. It should have the same order as your MvcTranslator
configuration to correctly guess the default language.

You can also change the default route name that is used to switched the language
on a page with no RouteMatch instance (like 404).

## Usage

After installation the router will automatically add the language before any
assembled URL. It will also inject the 'locale' parameter into any RouteMatch
instance. To read it you can use the following example code inside any
Controller

```php
$locale = $this->getEvent()->getRouteMatch()->getParam('locale');
```

The router will also check if the locale parameter is provided when assembling
a route. Use it to force a specific language prefix:

```php
$this->url()->fromRoute('home', ['locale' => 'de_DE']); // will point to /de
$this->url()->fromRoute('home', ['locale' => 'en_US']); // will point to /en
```

### Language Switch

The languageSwitch ViewHelper is able to render a simple dropdown to change the
current language. It tries to stay on the same page. If a 404 error is generated,
it will link to the `home` route. You can adjust the home route via configuration.
Sample usage:

```php
echo $this->languageSwitch($renderMode, $currentLocale, $options);
```

All arguments are optional.

The helper has four different rendering modes:
* `LanguageSwitch::RENDER_TYPE_LIST_ITEM` (default): Bootstrap navigation dropdown list item
* `LanguageSwitch::RENDER_TYPE_NAVBAR`: Full Bootstrap navbar list with dropdown list item inside
* `LanguageSwitch::RENDER_TYPE_DIV`: DIV container with div elements for each option
* `LanguageSwitch::RENDER_TYPE_SELECT`: Select form element
* `LanguageSwitch::RENDER_TYPE_PARTIAL`: Custom partial ViewScript

You can pass options as array to modify classes output classes and other things.
Please check the implementation for details about options.
34 changes: 34 additions & 0 deletions config/zf2-language-route.global.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
use ZF2LanguageRoute\Options\Factory\LanguageRouteOptionsFactory;

// ===========================
// Do not edit before this line


$languageConfig = [
/**
* Array of languages allowed for language route. The key is the prefix
* which is attached to the url (e.g. en), the value is the associated
* locale (e.g. 'en_US')
*/
'languages' => [
'en' => 'en_US',
'de' => 'de_DE'
],

/**
* This route name will be used if no RouteMatch instance is provided to
* the languageSwitch ViewHelper. This happens for example if a 404 error
* occurs.
* @var string
*/
'homeRoute' => 'home'
];


// Do not edit below this line
// ===========================

return [
LanguageRouteOptionsFactory::CONFIG_KEY => $languageConfig
];
11 changes: 9 additions & 2 deletions src/Listener/Factory/RouteListenerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,15 @@
*/
class RouteListenerFactory implements FactoryInterface{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) {
$options = $container->get(LanguageRouteOptions::class);
$languageOptions = $container->get(LanguageRouteOptions::class);
$router = $container->get('router');
$request = $container->get('request');
$translator = $container->get('MvcTranslator');
$authService = null;
if($container->has('zfcuser_auth_service')){
$authService = $container->get('zfcuser_auth_service');
}

return new $requestedName($options);
return new $requestedName($languageOptions, $router, $request, $translator, $authService);
}
}
50 changes: 46 additions & 4 deletions src/Listener/RouteListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@
use Zend\EventManager\EventManagerInterface;
use ZF2LanguageRoute\Options\LanguageRouteOptions;
use Zend\Mvc\MvcEvent;
use Zend\Router\RouteStackInterface;
use Zend\Stdlib\RequestInterface;
use ZF2LanguageRoute\Mvc\Router\Http\LanguageTreeRouteStack;
use Zend\I18n\Translator\TranslatorInterface;
use Zend\Authentication\AuthenticationServiceInterface;
use ZF2LanguageRoute\Entity\LocaleUserInterface;

/**
* Description of RouteListener
* Injects language into translator and updates user locale
*
* @author schurix
*/
Expand All @@ -35,14 +41,50 @@ class RouteListener extends AbstractListenerAggregate{
/** @var LanguageRouteOptions */
protected $options;

function __construct(LanguageRouteOptions $options) {
/** @var RouteStackInterface */
protected $router;

/** @var RequestInterface */
protected $request;

/** @var AuthenticationServiceInterface */
protected $authService;
/** @var TranslatorInterface */
protected $translator;

function __construct(LanguageRouteOptions $options, RouteStackInterface $router, RequestInterface $request, TranslatorInterface $translator, AuthenticationServiceInterface $authService = null) {
$this->options = $options;
$this->router = $router;
$this->request = $request;
$this->authService = $authService;
$this->translator = $translator;
}


public function attach(EventManagerInterface $events, $priority = 1) {
public function attach(EventManagerInterface $events, $priority = 10) {
$this->listeners[] = $events->attach(MvcEvent::EVENT_ROUTE, [$this, 'onRoute'], $priority);
}

public function onRoute(){
public function onRoute($e){
$router = $this->router;
if(!$router instanceof LanguageTreeRouteStack){
return;
}
$this->router->match($this->request);
$locale = $this->router->getLastMatchedLocale();
if(empty($locale)){
return;
}

if(is_callable([$this->translator, 'setLocale'])){
$this->translator->setLocale($locale);
}

if($this->authService && $this->authService->hasIdentity()){
$user = $this->authService->getIdentity();
if($user instanceof LocaleUserInterface){
$user->setLocale($locale);
}
}
}
}
19 changes: 15 additions & 4 deletions src/Mvc/Router/Http/LanguageTreeRouteStack.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class LanguageTreeRouteStack extends TranslatorAwareTreeRouteStack {
/** @var AuthenticationServiceInterface */
protected $authenticationService;

/** @var string */
protected $lastMatchedLocale;

function getLanguageOptions() {
return $this->languageOptions;
}
Expand All @@ -56,6 +59,15 @@ function getAuthenticationService() {
function setAuthenticationService(AuthenticationServiceInterface $authenticationService) {
$this->authenticationService = $authenticationService;
}

/**
* Returns the locale that was found in the last matched URL. It is also
* stored if no RouteMatch instance is provided (e.g. 404 error)
* @return string
*/
function getLastMatchedLocale() {
return $this->lastMatchedLocale;
}

/**
* assemble(): defined by \Zend\Mvc\Router\RouteInterface interface.
Expand Down Expand Up @@ -159,10 +171,6 @@ public function match(RequestInterface $request, $pathOffset = null, array $opti
// if language was provided, save the locale and adjust the baseUrl
$locale = $languages[$pathParts[0]];
$this->setBaseUrl($oldBase . '/'.$pathParts[0]);
if(is_callable(array($translator, 'setLocale'))){
// change translator locale
$translator->setLocale($locale);
}
} elseif(!empty($this->getAuthenticationService()) && $this->getAuthenticationService()->hasIdentity()) {
// try to get user language if no language was provided by url
$user = $this->getAuthenticationService()->getIdentity();
Expand All @@ -179,6 +187,9 @@ public function match(RequestInterface $request, $pathOffset = null, array $opti
$locale = $translator->getLocale();
}

// set the last matched locale
$this->lastMatchedLocale = $locale;

$res = parent::match($request, $pathOffset, $options);
$this->setBaseUrl($oldBase);
if($res instanceof RouteMatch && !empty($locale)){
Expand Down
17 changes: 17 additions & 0 deletions src/Options/LanguageRouteOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,28 @@ class LanguageRouteOptions extends AbstractOptions{
*/
protected $languages = ['de' => 'de_DE', 'en' => 'en_US'];

/**
* This route name will be used if no RouteMatch instance is provided to
* the languageSwitch ViewHelper. This happens for example if a 404 error
* occurs.
* @var string
*/
protected $homeRoute = 'home';

function getLanguages() {
return $this->languages;
}

function setLanguages(array $languages) {
$this->languages = $languages;
}

function getHomeRoute() {
return $this->homeRoute;
}

function setHomeRoute($homeRoute) {
$this->homeRoute = $homeRoute;
}

}
18 changes: 13 additions & 5 deletions src/View/Helper/LanguageSwitch.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ class LanguageSwitch extends AbstractTranslatorHelper{
protected static $navbarBoxFormat = '<li class="%s">%s</li>';
protected static $navbarOptionsFormat = '<ul class="%s">%s</ul>';
protected static $navbarOptionFormat = '<li class="%s">%s</li>';
protected static $navbarCaptionLinkFormat = '<a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="%s" lang="%s">%s</span><span class="caret"></span></a>';
protected static $navbarOptionLinkFormat = '<a href="%s"><span class="%s" lang="%s">%s</span></a>';
protected static $navbarCaptionLinkFormat = '<a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="%s" lang="%s"></span><span class="sr-only">%s</span><span class="caret"></span></a>';
protected static $navbarOptionLinkFormat = '<a href="%s"><span class="%s" lang="%s"></span><span class="sr-only">%s</span></a>';
protected static $navbarBoxClass = 'dropdown';
protected static $navbarOptionsClass = 'dropdown-menu';
protected static $navbarOptionClass = 'language-option';
Expand Down Expand Up @@ -65,7 +65,7 @@ function getLanguageOptions() {
return $this->languageOptions;
}

public function __invoke($currentLocale = null, $renderType = self::RENDER_TYPE_LIST_ITEM, $config = array()) {
public function __invoke($renderType = self::RENDER_TYPE_LIST_ITEM, $currentLocale = null, $config = array()) {
/* @var $renderer \Zend\View\Renderer\PhpRenderer */
$renderer = $this->getView();
if (!method_exists($renderer, 'plugin')) {
Expand Down Expand Up @@ -165,7 +165,11 @@ protected function renderDiv($translator, $locales, $currentLocale = null, $conf
$parameters = $this->getRouteMatch()->getParams();
}
$parameters['locale'] = $locale;
$url = $urlPlugin(null, $parameters);
$routeName = null;
if(!$this->getRouteMatch()){
$routeName = $this->getLanguageOptions()->getHomeRoute();
}
$url = $urlPlugin($routeName, $parameters);
$link = sprintf(static::$divOptionLinkFormat, $optionLinkClass, $url, $localeKey);
$options .= sprintf(static::$divOptionFormat, $optClass, $link);
}
Expand Down Expand Up @@ -230,7 +234,11 @@ protected function renderListitem($translator, $locales, $currentLocale = null,
$parameters = $this->getRouteMatch()->getParams();
}
$parameters['locale'] = $locale;
$url = $urlPlugin(null, $parameters);
$routeName = null;
if(!$this->getRouteMatch()){
$routeName = $this->getLanguageOptions()->getHomeRoute();
}
$url = $urlPlugin($routeName, $parameters);
$link = sprintf(static::$navbarOptionLinkFormat, $url, $optionLinkClass, $localeKey, $locale);
$options .= sprintf(static::$navbarOptionFormat, $optClass, $link);
}
Expand Down

0 comments on commit 1b8707a

Please sign in to comment.