How to translate zend framework 2 config arrays
In zend framework 2 (ZF2), php arrays are the main method of configuring the application. Sometimes, there are strings in there which must be translated to other languages. However, the translator is not available yet at the time the config is merged, since it has to be configured first.
ZF2 offers the Zend\Config\Processor\Translator to do the job. However, there are some problems with that:
- You probably don't want to translate everything in your config, only specific strings. Running all config values through the translator may have unintended side-effects.
- Caching serveral merged configurations in different languages is another problem for this solution.
- Code scanning (e.g. with Poedit) does not work.
Here's how you can deal with all these problems: simply use an empty translator (no translations specified) in your config file. Here I translate the navigation of my application
$translator = new \Zend\I18n\Translator\Translator;
return array(
'navigation' => array(
'default' => array(
'admin' => array(
'label' => $translator->translate('Control Panel'),
'controller' => 'admin',
'action' => 'index',
'route' => 'survey/default',
),
),
),
);
Poedit will recognize the translate('') function if set up correctly and you'll be able to edit the translation files. When displaying the "translated" string, make sure to pass it through the real translator with loaded translations (gettext files from Poedit). Here I display the translated 'Control Panel'
string in my breadcrumb partial viewscript:
$label = $this->translate($page->label);
echo (sprintf('<li>%s</li>', $label));
Written by Adrian Imfeld
Related protips
5 Responses
Hi !
When I tried your method, I had issue because no translator was specified... poedit regognized it... navigation was translated... but not beadcrumbs... (don't ask me why).
But I found here a great part of the solution !
Instead of instanciating an empty translator, I called the factory() method wich returns a fully operationnal translator :) the only problem was to inject options
=> Before that, I write the translator's options in a separate autoload file (only one language folder for my entire application) :
config/autoload/translator.global.php
and now it works :
<?php
$config = require 'translator.global.php';
$options = (array)$config['translator'];
$translator = \Zend\I18n\Translator\Translator::factory($options);
//$translator = new \Zend\I18n\Translator\Translator;
return array(
'navigation' => array(
'default' => array(
array(
'label' => $translator->translate('Home'),
'route' => 'home',
),
[...]
),
),
);
notice that I also put all the navigation in config/autoload/navigation.global.php file
@nono1971: Interesting solution, but you might run into some trouble when translating directly in your config like this. Translating into exactly one language which is determined before the config merge process should work. But I suspect you may not be able to to translate into other languages (e.g. according to user languange preference) or to display the original English strings. Once the config is merged with the translated strings, you cannot then translate to some different language.
Maybe you use a custom translator class and which is not picked up in the breadcrumbs view helper? I had the same problem and I fixed it like this:
Here's my custom translator class
namespace MyLib\I18n\Translator;
class Translator extends \Zend\Mvc\I18n\Translator
{
// Some costum translate functions like translateFrontend() and translateBackend()
// These functions are parsed by poedit into separate translation files.
}
Here's the translator factory for creating the custom translator:
namespace MyLib\I18n\Translator;
use Zend\ServiceManager\ServiceLocatorInterface;
class TranslatorServiceFactory extends \Zend\Mvc\Service\TranslatorServiceFactory
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
// Configure the translator
$config = $serviceLocator->get('Config');
$trConfig = isset($config['translator']) ? $config['translator'] : array();
$translator = Translator::factory($trConfig);
return $translator;
}
}
And heres the Module::getServiceConfig() to set the translator factory. The custom translator will then be used in viewhelpers (e.g. breadcrumbs).
public function getServiceConfig()
{
// See http://packages.zendframework.com/docs/latest/manual/en/modules/zend.service-manager.quick-start.html
return array(
// As of zf 2.2.2, the translator service has been renamed to mvctranslator (https://github.com/zendframework/zf2/issues/4910)
'allow_override' => true,
'factories' => array(
'mvcTranslator' => 'MyLib\I18n\Translator\TranslatorServiceFactory',
'navigation' => 'Zend\Navigation\Service\DefaultNavigationFactory',
),
'aliases' => array(
'translator' => 'mvcTranslator',
),
);
}
@nono1971: After looking at your code again, you probably don't use a custom translator, since you're using Zend\I18n\Translator\Translator::factory()
. To make the breadcrumbs view helper use the correct translator instance, try:
public function getServiceConfig()
{
return array(
'allow_override' => true,
'aliases' => array(
'translator' => 'mvcTranslator',
),
);
}
@aimfeld: Thanks for your answers :)
Infact, I do translate for multiple language, but not with this code ;)
=> this was my configuration for the default language of my application :) ( but you're almost right :I didn't implement it yet when I posted last night ;) )
but I did inbeetween :)
=> I only implemented multiple languages for registred users.
so I integrated in my ACL :
As I check the user level, I also get his favorite language (set on registration)
in my ACL : module/MyAclLib/module.php :
public function onBootstrap(\Zend\EventManager\EventInterface $e)
{
$application = $e->getApplication();
$em = $application->getEventManager();
$em->attach('route', array($this, 'onRoute'), -100);
}
public function onRoute(\Zend\EventManager\EventInterface $e)
{
$application = $e->getApplication();
$sm = $application->getServiceManager();
$auth = $sm->get('Zend\Authentication\AuthenticationService');
if ($auth->hasIdentity()) {
$usr = $auth->getIdentity();
$translator= $sm->get('translator')->setLocale($usr->language_file);
// and the rest of the acl... but that is not the point
}
}
of course, it is not enough : the patch for navigation breadcrumbs still translate in french (default language of my app).
to fix it, I make the same chek in config/autoload/navigation.global.php like this :
$config = require 'translator.global.php';
$options = (array)$config['translator'];
$translator = \Zend\I18n\Translator\Translator::factory($options);
$auth = new \Zend\Authentication\AuthenticationService();
if ($auth->hasIdentity()){
$user = $auth->getIdentity();
$translator->setLocale($user->language_file);
}
evenything works perfectly for me like this now :)