Walllter
Walllter

Reputation: 350

Database Translations Zend Framework 2

I have problem with creating custom translator from database in ZF2. I have a DB like this enter image description here

and files:

1)Application/module.config.php

'service_manager' => array(
    'abstract_factories' => array(),
    'factories' => array(
        'translator' => function  (\Zend\ServiceManager\ServiceManager $serviceManager)
            {
                $pluginManager = new \Zend\I18n\Translator\LoaderPluginManager();
                $pluginManager->setServiceLocator($serviceManager);
                $pluginManager->setFactory('DatabaseTranslationLoaderFactory', function($serviceManager)
                {
                    $translator = new \Zend\I18n\Translator\DatabaseTranslationLoaderFactory();
                    return $translator->createService($serviceManager);
                });
                $translator = new \Zend\I18n\Translator\Translator(array());
                $translator->setFallbackLocale('en_US');
                $translator->setPluginManager($pluginManager);
                $translator->addRemoteTranslations('DatabaseTranslationLoaderFactory');
                return $translator;
            },
    ),
),

'translator' => array(
    'locale' => 'en_US',
    'translation_file_patterns' => array(
        array(
            'type'     => 'Zend\I18n\Translator\Loader\Database',
            'base_dir' => __DIR__ . '/../language',
            'pattern'  => '%s.mo',
        ),
    ),
),

2) Zend/I18n/Translator/Loader/Database.php

<?php

namespace Zend\I18n\Translator\Loader;

use Zend\Db\Adapter\Adapter;
use Zend\Db\Sql\Sql;
use Zend\I18n\Translator\Plural\Rule as PluralRule;
use Zend\I18n\Translator\TextDomain;


class Database implements RemoteLoaderInterface {

protected $dbAdapter;


public $dbAdapter;


public function __construct(Adapter $dbAdapter = null)
{
    if ($dbAdapter === null)
    {
        $configArray = array('driver' => 'Pdo_Mysql',
            'database' => 'dbname',
            'username' => 'username',
            'password' => 'pswd',
            'hostname' => 'localhost',
            'charset' => 'utf-8',
        );
        $dbAdapter = new Adapter($configArray);
    }
    $this->dbAdapter = $dbAdapter;
}


public function load($locale, $textDomain)
{
    $sql = new Sql($this->dbAdapter);
    $select = $sql->select('ic_var')->columns(array('value'))
                  ->where(array('language' => $locale, 'name' => $textDomain));

    $messages = $this->dbAdapter->query(
        $sql->getSqlStringForSqlObject($select),
        Adapter::QUERY_MODE_EXECUTE
    );

    $textDomain = new TextDomain();

    foreach ($messages as $message) {
        if (isset($textDomain[$message['name']])) {
            if (!is_array($textDomain[$message['name']])) {
                $textDomain[$message['name']] = array(
                    $message['plural_index'] => $textDomain[$message['name']]
                );
            }
            $textDomain[$message['name']][$message['plural_index']] = $message['value'];
        } else {
            $textDomain[$message['name']] = $message['value'];
        }
    }
    return $textDomain;
}
}

3) Zend/I18n/Translator/DatabaseTranslationLoaderFactory.php

<?php

namespace Zend\I18n\Translator;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\I18n\Translator\Loader\Database;

class DatabaseTranslationLoaderFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        return new Database($serviceLocator->get('Zend\Db\Adapter\Adapter'));
    }
}

4) Application/Module.php

public function onBootstrap(MvcEvent $e)
{
    $translator = $e->getApplication()->getServiceManager()->get('translator');
    $translator->addTranslationFile(
        'DatabaseTranslationLoader',
        'text-domain',
        'text-domain'
    );
}

But translation doesn`t work, because db adapter not find in loader:

Fatal error: Uncaught exception 'Zend\I18n\Exception\RuntimeException' with message 'Specified loader is not a file loader'

Thanks for your answers!

Upvotes: 1

Views: 2439

Answers (2)

Valerii
Valerii

Reputation: 1

I register custom remote loader to the pluginManager in module.config.php like this

'translator' => [
    'loaderpluginmanager' => [
        'factories' => [
            'database' => function($lpm){
                $sm = $lpm->getServiceLocator();
                $loader = new Zf2Translation\Loader\DatabaseTranslationLoader($sm);
                return $loader;
            },
        ],
    ],
    'remote_translation' => [
        [
            'type' => 'database', 
        ],
    ],
]

Next in Database Loader class

use Zend\I18n\Translator\Loader\RemoteLoaderInterface;
class DatabaseTranslationLoader implements RemoteLoaderInterface
{
    protected $dbAdapter;
    protected $sm;

    public function __construct(ServiceManager $sm)
    {
        $this->sm = $sm;
        $this->dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
    }
}

I hope it helps.

Upvotes: 0

Bram Gerritsen
Bram Gerritsen

Reputation: 7238

First of all you shouldn't define your custom classes in the Zend namespace as this is reserved a namespace for the ZF2 library and you don't want to touch (or add) files in the vendor directory. Just put the custom classes in your own namespace outside the vendor folder. i.e. MyI18n

You can register you custom remote loader to the pluginManager in module.config.php.

return array(
    'translator' => array(
        'loaderpluginmanager' => array(
            'factories' => array(
                'database' => 'MyI18n\Translator\DatabaseTranslationLoaderFactory',
            )
        ),
        'remote_translation' => array(
            array(
                'type' => 'database' //This sets the database loader for the default textDomain
            ),
        ),
    )
);

You don't have to overwrite the Translator factory if you want to add a custom loader, so just remove that code in your Module.php. Als remove the configuration under translation_file_patterns as this is only needed for file loaders.

EDIT

For the above to work you need to overwrite the TranslatorServiceFactory because ZF has no support to register custom loaders on the plugin manager.

namespace MyNamespace\Translator;

use Zend\Mvc\Service\TranslatorServiceFactory as BaseTranslatorFactory;

class TranslatorServiceFactory extends BaseTranslatorFactory
{
    /**
     * @param ServiceLocatorInterface $serviceLocator
     * @return MvcTranslator
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $translator = parent::createService($serviceLocator);

        $config = $serviceLocator->get('Config');
        $pluginManagerConfig = isset($config['translator']['loaderpluginmanager']) ? $config['translator']['loaderpluginmanager'] : array();
        $pluginManager = new LoaderPluginManager(new Config($pluginManagerConfig));
        $pluginManager->setServiceLocator($serviceLocator);
        $translator->setPluginManager($pluginManager);

        return $translator;
    }
}

Now register your custom factory in the service configuration:

class Module
{
    public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'MvcTranslator' => 'MyNamespace\Translator\TranslatorServiceFactory',
            )
        )
    }
}

Upvotes: 2

Related Questions