1
votes

I've recently started working with ZF3 and I'm working on internationalization of the project.

I've managed to get it work but I'd like to make the translator get the translates from the database instead of reading them from .mo files. This way the user would be able to edit them in admin part of the application.

I have installed the Zend\Mvc\I18n module, registered it as a view_helper

// module's module.config.php:    
'view_helpers' => [
    'invokables' => [
        'translate' => \Zend\I18n\View\Helper\Translate::class
    ]
]

and instead of

// module's module.config.php:
'translator' => [
    'locale' => 'en_US',
    'translation_file_patterns' => [
        [
            'type'     => 'gettext',
            'base_dir' => __DIR__ . '/../language',
            'pattern'  => '%s.mo',
        ],
    ],
]

I have

// module's module.config.php:
'translator' => [
    'locale' => 'en_US',
    'remote_translation' => [
        [
            'type' => Service\Database::class
        ]
    ]
]

and the Database service implements the RemoteLoaderInterface as required in the documentation, right now it has a hard-coded array but will later on replaced by the actual query.

//Database.php
namespace Core\Service;

use Zend\I18n\Translator\Loader\RemoteLoaderInterface;

class Database implements RemoteLoaderInterface {

    private $entityManager;

    public function __construct($entityManager)
    {
        $this->entityManager = $entityManager;
    }

    public function load($locale, $textDomain)
    {
        $array = [];
        $array['and'] = 'i';
        return $array;
    }
}

In my layout printing

// layout.phtml
$this->translate("and") // prints "i"

results in a "i", which is just what I wanted.

Now, my problem is how do I pass the doctrine entity manager to the Database service? I know I have to use a factory in these situations and I have one ready

//DatabaseFactory.php
namespace Core\Service\Factory;

use Core\Service\Database;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;

class DatabaseFactory implements FactoryInterface 
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $entityManager = $container->get('doctrine.entitymanager.orm_default');

        // Instantiate the service and inject dependencies
        return new Database($entityManager);
    }

}

but I simply can't pass it in the module configuration because it requires the class to implement the RemoteLoaderInterface.

// module's module.config.php:   
'remote_translation' => [
    [
        'type' => Service\Factory\DatabaseFactory::class // does not work
    ]
]

Is there a workaround or anything I'm missing? Any help would be appreciated. Thanks in advice!

2

2 Answers

0
votes

This is a little bit tricky. You have to setup a translation loader factory before rendering occurs.

module\Application\src\Module.php:

public function init (ModuleManager $manager)
{
    $eventManager = $manager->getEventManager();
    $sharedEventManager = $eventManager->getSharedManager();

    $sharedEventManager->attach(__NAMESPACE__, 'dispatch',
        [$this, 'onDispatch'], 100);
}

public function onDispatch (MvcEvent $event)
{
    $translator = $event->getApplication ()->getServiceManager ()->get (\Zend\I18n\Translator\TranslatorInterface::class);
    $translator->getPluginManager()->setFactory(\Application\Translator\Database::class, \Application\Translator\DatabaseFactory::class);

I don't know of any other way to configure service locator for the translator.

0
votes

It looks like you forgot to register the class and its factory with the service manager:

// module's module.config.php
'translator' => [
    'loaderpluginmanager' => [
        'factories' => [
            Core\Service\Database::class => Service\Factory\DatabaseFactory::class,
        ],
    ],
    'locale' => 'en_US',
    'remote_translation' => [
        [
            'type' => Core\Service\Database::class
        ]
    ]
]