Home » Php » php – Advanced customization of translations in Symfony2

php – Advanced customization of translations in Symfony2

Posted by: admin July 12, 2020 Leave a comment

Questions:

I have a Symfony2 project and I am using Translation component for translating text. I have all translations in yml file like so

translation-identifier: Translated text here

Translating text looks like this from Twig

'translation-identifier'|trans({}, 'domain')

The thing is, in some cases I would like to have two different texts for same translation (not for pluralization). Here’s how I would like it to work:

  1. Define two texts in yml file for translations that need to have different texts. Each would have it’s own unique suffix

    translation-identifier-suffix1
    
    translation-identifier-suffix2
    
  2. Define a global rule that would define which suffix should be choosen. Psuedocode below:

     public function getSuffix() {
       return rand(0, 10) < 5 ? '-suffix1' : '-suffix2';
     }
    
  3. Twig (and PHP) would look the same – I would still specify just the identifier without suffix. Translator would then append suffix to the identifier and try to find a match. If there would be no match it would try to find a match again without suffix.

How to&Answers:

AFAIK, Translator component doesn’t support it.

But if you want same kind of behavior, you could do by overriding the translator service.

1) Override the service

# app/config/config.yml
parameters:
    translator.class:      Acme\HelloBundle\Translation\Translator

First, you can set the parameter holding the service’s class name to your own class by setting it in app/config/config.yml.
FYI: https://github.com/symfony/FrameworkBundle/blob/master/Resources/config/translation.xml

2) Extend the translator class provided symfony framework bundle.
FYI: https://github.com/symfony/FrameworkBundle/blob/master/Translation/Translator.php

3) Overwrite the trans function which is provider by translator component.
https://github.com/symfony/Translation/blob/master/Translator.php

Hope this helps!

Answer:

Here is the extended translator class in case anyone ever needs it

<?php

    namespace Acme\HelloBundle\Translation;

    use Symfony\Bundle\FrameworkBundle\Translation\Translator as BaseTranslator;
    use Symfony\Component\Translation\MessageSelector;
    use Symfony\Component\DependencyInjection\ContainerInterface;

    class Translator extends BaseTranslator {

        const SUFFIX_1 = '_suffix1';
        const SUFFIX_2 = '_suffix2';

        private $suffix;

        public function __construct(ContainerInterface $container, MessageSelector $selector, $loaderIds = array(), array $options = array()) {
            parent::__construct($container, $selector, $loaderIds, $options);
            $this->suffix = $this->getSuffix($container);
        }

        public function trans($id, array $parameters = array(), $domain = 'messages', $locale = null) {     
            if ($locale === null)
                $locale = $this->getLocale();

            if (!isset($this->catalogues[$locale]))
                $this->loadCatalogue($locale);

            if($this->suffix !== null && $this->catalogues[$locale]->has((string) ($id . $this->suffix), $domain))
                $id .= $this->suffix;

            return strtr($this->catalogues[$locale]->get((string) $id, $domain), $parameters);
        }

        private function getSuffix($container) {
            return rand(0, 10) < 5 ? self::SUFFIX_1 : self::SUFFIX_2;
        }

    }

?>

Answer:

As of Symfony 3, Venu’s answer no longer works completely, as the translator.class parameter is no longer used.

To load your custom translator class, you now need to create a compiler pass.

<?php

namespace Acme\HelloBundle\DependencyInjection\Compiler;

use Acme\HelloBundle\Translation\Translator;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class TranslatorOverridePass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $container->getDefinition('translator.default')->setClass(Translator::class);
    }
}

And this compiler pass needs to be added to the container.

<?php

namespace Acme\HelloBundle;

use Acme\HelloBundle\DependencyInjection\Compiler\TranslatorOverridePass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class AcmeHelloBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(new TranslatorOverridePass());
    }
}