Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP


[Enhancement] Add an easier way to use i18n view helpers. #2865

coolmic opened this Issue · 9 comments

5 participants


hi, sorry for my bad English.

I have 2 issues.

If I use the translate helper :

echo $this->translate('hello');

and the translator is not set, it will throw an exception.

I am trying to make a reusable modules.
When I make French websites, I need the translator
But if I want to make English websites, I don't need it.
I guess the exception here is unnecessary.

The second issue, is the use of TextDomain.

I use one textDomain per modules, each module embed translation files.

If I want to generate a Form for example, I need to write


<dl class="zend_form">
    <dt><?php echo $this->formLabel($form->get('name')) ?></dt>
    <dd><?php echo $this->formInput($form->get('name')) ?></dd>
    <?php echo $this->formElementErrors($form->get('name')) ?>

    <dt><?php echo $this->formLabel($form->get('password')) ?></dt>
    <dd><?php echo $this->formInput($form->get('password')) ?></dd>
    <?php echo $this->formElementErrors($form->get('password')) ?>

    <dd><?php echo $this->formButton($form->get('submit')) ?></dd>

And if I use partials, the textdomain may be overwritten by another script.

I come with an idea : Why don't use another helper as Proxy.

use Zend\I18n\View\Helper\AbstractTranslatorHelper;
use Zend\I18n\Translator\TranslatorAwareInterface;

class TranslateProxy extends AbstractTranslatorHelper
     * @var array
    protected $translatorTextDomainStack;

    public function __invoke($message = null, $textDomain = null, $locale = null)
        if (is_null($message)) {
            return $this;

        $translator = $this->getTranslator();
        if (null === $translator) {
            return $message;

        if (null === $textDomain) {
            $textDomain = $this->getTranslatorTextDomain();

        return $translator->translate($message, $textDomain, $locale);

    public function pushTranslatorTextDomain($textDomain)
        $this->translatorTextDomainStack[] = $this->getTranslatorTextDomain();

    public function popTranslatorTextDomain()
        $top = array_pop($this->translatorTextDomainStack);
        if (is_null($top)) {
            return null;

        $prev = $this->getTranslatorTextDomain();
        return $prev;

    public function __call($name, $arguments) {
        $plugin = $this->getView()->plugin($name);

        if(!($plugin instanceof TranslatorAwareInterface)) {
            return call_user_func_array($plugin, $arguments);

        $prevTextTranslator = $plugin->getTranslator();
        $prevTextDomain = $plugin->getTranslatorTextDomain();


        $retVal = call_user_func_array($plugin, $arguments);


        return $retVal;
// Start of the script
$translateProxy = $this->translateproxy();


echo $translateProxy->formLabel($form->get('name'));
// Instead of  $this->formLabel


$translateProxy->popTranslatorTextDomain(); // End of the script

On each scripts, we use pushTranslatorTextDomain and popTranslatorTextDomain to ensure there will not have any conflict if we use partial.

What do you think about that? I don't know if it can be PR.


@coolmic for both your problems there are good alternatives.

First, usually you have translations for all your locales. It is a good practice to write all message "to-be-translated" as English sentences, but you translate these at all times, nevertheless of the default locale. There is no need to check if the language is English and then omit the translation.

Translators also give usually the key back as translation when it does not exist. So if you want to translate 'hello', you have a French translation 'hello' => 'bonjour'. If the locale is English, you see "hello". If the locale is French, you see "bonjour".

The second problem: it is in most cases easier to translate the labels directly in the form class itself. Your form looks something like this:


namespace MyModule\Form;

use Zend\Form\Form;
use Zend\I18n\Translate\Translator;

class MyForm extends Form
    public function __construct(Translator $translator)
            'name' => 'name',
            'options' => array(
                'label' => $translator->translate('Name:')

Every label is translated inside the form, so you can make your form view very simple:

<dl class="zend_form">
    <dt><?php echo $this->formLabel($form->get('name')) ?></dt>
    <dd><?php echo $this->formInput($form->get('name')) ?></dd>
    <?php echo $this->formElementErrors($form->get('name')) ?>

If you match two forms you get form two different modules (and thus, two different text domains), you have no trouble in doing so.


Yeah, the first one is not really an issue, I just want to avoid to instanciate the translator object, but it's probably an unnecessary optimization.

For the second one, the translation will be done 2 times,
and for formElementsErrors, it's an hassle with , because I have to translate each template message for each validator.

Well, the main purpose of this ticket is to have feedbacks about the TranslatorProxy.

@coolmic coolmic closed this
@coolmic coolmic reopened this
@DASPRiD DASPRiD was assigned

Is this still an issue?


I think the issue is reopened accidentally


Ok, I will close, it can be reopened if it is deemed still an issue.


I had spoken with dastrid, he reject the proxy, but liked the text domain stack.

But it's still not merged


@DASPRiD is that a WIP PR you're working on?


@ralphschindler Yes it is. I'll try to get it ready for 2.2


ping @DASPRiD any update for DASPRiD@c48c1cf ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.