overriding global gettext function _()

5 messages Options
Embed this post
Permalink
whisperstream

overriding global gettext function _()

Reply Threaded More More options
Print post
Permalink
I'm trying to create a mutlilingual site, where some objects have a translator built into them

I would like to avoid the following:

// some code here to get the current translation object
$translator = getUserTranslator();

$obj = new SomeObject( $translator->_('My Shiny Object') );
$obj = new SomeObject( $translator->_('My Other Shiny Object') );
$obj = new SomeObject( $translator->_('My Not so Shiny Object') );

and instead do:

$obj = new SomeObject( 'My Shiny Object' );
$obj = new SomeObject( 'My Other Shiny Object' );
$obj = new SomeObject( 'My Not so Shiny Object' );

Then when the code calls $obj->getName(), the obj gets the default translation object from the registry and translates the text just before it returns it.

The problem with this approach is that it's much harder to parse your source code files to extract all the language strings, because you have to write a parser to look for SomeObject(\'(.*)?\' and 100 other objects that work the same way.

To combat this my latest idea is to override the global gettext function _() with something like the following:

Solution #1:

function &_(&$param)
{
     return $param;
}

Then I can write:
$obj = new SomeObject( _('My Shiny Object') );

Clearly my _ function does nothing useful, but it does make it substantially easier to parse the entire source code for translation strings when I generate my translation file.

Solution #2

function _(&$param)
{
   $translator = Zend_Registry::get('Zend_Translate');
   if($translator !== null)
      $translator->_($param);
   else
      return $param;
}

Here the next effect is the same as above, only my _ function is a little more useful, but I do lose the ability to set my translator for each object.  I kinda prefer Solution #1 at the moment, but maybe I'll be put squarely in my place by a reader and told to the right way to do translation.

I just find that using $translator->_('some thing'), all over the place is repetitive, overcoding.

Thanks for reading any help is appreciated.
Thomas Weidner

Re: overriding global gettext function _()

Reply Threaded More More options
Print post
Permalink
Both solutions are useless in my eyes.
I would prefer a much simpler solution.

SomeObject::_('mytext');

Think of it and you will get a clue of why this is simpler that yours.

Btw: Why would you have to write a parser? poEdit supports all you need for
gettext.
And Zend_Translate has build in logging with 1.8/trunk.

Greetings
Thomas Weidner, I18N Team Leader, Zend Framework
http://www.thomasweidner.com

----- Original Message -----
From: "whisperstream" <[hidden email]>
To: <[hidden email]>
Sent: Tuesday, March 10, 2009 1:53 AM
Subject: [fw-i18n] overriding global gettext function _()


>
> I'm trying to create a mutlilingual site, where some objects have a
> translator built into them
>
> I would like to avoid the following:
>
> // some code here to get the current translation object
> $translator = getUserTranslator();
>
> $obj = new SomeObject( $translator->_('My Shiny Object') );
> $obj = new SomeObject( $translator->_('My Other Shiny Object') );
> $obj = new SomeObject( $translator->_('My Not so Shiny Object') );
>
> and instead do:
>
> $obj = new SomeObject( 'My Shiny Object' );
> $obj = new SomeObject( 'My Other Shiny Object' );
> $obj = new SomeObject( 'My Not so Shiny Object' );
>
> Then when the code calls $obj->getName(), the obj gets the default
> translation object from the registry and translates the text just before
> it
> returns it.
>
> The problem with this approach is that it's much harder to parse your
> source
> code files to extract all the language strings, because you have to write
> a
> parser to look for SomeObject(\'(.*)?\' and 100 other objects that work
> the
> same way.
>
> To combat this my latest idea is to override the global gettext function
> _()
> with something like the following:
>
> Solution #1:
>
> function &_(&$param)
> {
>     return $param;
> }
>
> Then I can write:
> $obj = new SomeObject( _('My Shiny Object') );
>
> Clearly my _ function does nothing useful, but it does make it
> substantially
> easier to parse the entire source code for translation strings when I
> generate my translation file.
>
> Solution #2
>
> function _(&$param)
> {
>   $translator = Zend_Registry::get('Zend_Translate');
>   if($translator !== null)
>      $translator->_($param);
>   else
>      return $param;
> }
>
> Here the next effect is the same as above, only my _ function is a little
> more useful, but I do lose the ability to set my translator for each
> object.
> I kinda prefer Solution #1 at the moment, but maybe I'll be put squarely
> in
> my place by a reader and told to the right way to do translation.
>
> I just find that using $translator->_('some thing'), all over the place is
> repetitive, overcoding.
>
> Thanks for reading any help is appreciated.
> --
> View this message in context:
> http://www.nabble.com/overriding-global-gettext-function-_%28%29-tp22425980p22425980.html
> Sent from the Zend I18N/Locale mailing list archive at Nabble.com.

whisperstream

Re: overriding global gettext function _()

Reply Threaded More More options
Print post
Permalink

thomasW wrote:
Both solutions are useless in my eyes.
I would prefer a much simpler solution.

SomeObject::_('mytext');

Think of it and you will get a clue of why this is simpler that yours.

Btw: Why would you have to write a parser? poEdit supports all you need for
gettext.
And Zend_Translate has build in logging with 1.8/trunk.

Greetings
Thomas Weidner, I18N Team Leader, Zend Framework
http://www.thomasweidner.com
Hmmm, I've been looking at this and thinking about it and still don't understand how creating a static _ function works any better.  I'm trying to avoid doing any translation before it is absolutely required (but maybe this isn't a good idea?).  My someObject object for the most part does calculations, but every now and again I want to display the object title's in a table.  So my thinking was that when $someObject->getTitle() was called the translation code would be executed and never otherwise.  I am basically trying to avoid translation in the constructor.

So long story short I'm confused as to how SomeObject::_('mytext'); works?

I like the logging method you implemented in the 1.8 code, my only counter argument is that sometimes you have code that isn't called often and so might still miss it until after you've completed a release.  However, you could argue back that all code must be tested at least once, but sometimes I'll throw exceptions in the code but not test that that exception is actually thrown when I'm debugging (this may shock ppl, bracing  myself! :) ).

And so for that reason, I would prefer a source code scanner to extract all text strings without having to rely on runtime logging.

Thanks for your input though, it's very useful.

-Axel
Thomas Weidner

Re: overriding global gettext function _()

Reply Threaded More More options
Print post
Permalink
Mixing calculations and translations for the whole application within one
class is really a bad coding standard.
This opens many other problems.

To get you the point why static:

$my = new SomeObject();
$other = $my;
$other->_('translation');

Now you're broken again.
Using a static function you are not error-free but you limit possible
problems.

Also ::_ can be searched which would be impossible when you extend your
SomeObject.

Still:
In my eyes this behaviour is completly useless and unnecessary.
It's better to stick with what's available because there is always a good
reason why a implementation works this way and not different.

Greetings
Thomas Weidner, I18N Team Leader, Zend Framework
http://www.thomasweidner.com


----- Original Message -----
From: "whisperstream" <[hidden email]>
To: <[hidden email]>
Sent: Tuesday, March 10, 2009 3:32 PM
Subject: Re: [fw-i18n] overriding global gettext function _()


>
>
>
> thomasW wrote:
>>
>> Both solutions are useless in my eyes.
>> I would prefer a much simpler solution.
>>
>> SomeObject::_('mytext');
>>
>> Think of it and you will get a clue of why this is simpler that yours.
>>
>> Btw: Why would you have to write a parser? poEdit supports all you need
>> for
>> gettext.
>> And Zend_Translate has build in logging with 1.8/trunk.
>>
>> Greetings
>> Thomas Weidner, I18N Team Leader, Zend Framework
>> http://www.thomasweidner.com
>>
>>
>
> Hmmm, I've been looking at this and thinking about it and still don't
> understand how creating a static _ function works any better.  I'm trying
> to
> avoid doing any translation before it is absolutely required (but maybe
> this
> isn't a good idea?).  My someObject object for the most part does
> calculations, but every now and again I want to display the object title's
> in a table.  So my thinking was that when $someObject->getTitle() was
> called
> the translation code would be executed and never otherwise.  I am
> basically
> trying to avoid translation in the constructor.
>
> So long story short I'm confused as to how SomeObject::_('mytext'); works?
>
> I like the logging method you implemented in the 1.8 code, my only counter
> argument is that sometimes you have code that isn't called often and so
> might still miss it until after you've completed a release.  However, you
> could argue back that all code must be tested at least once, but sometimes
> I'll throw exceptions in the code but not test that that exception is
> actually thrown when I'm debugging (this may shock ppl, bracing  myself!
> :)
> ).
>
> And so for that reason, I would prefer a source code scanner to extract
> all
> text strings without having to rely on runtime logging.
>
> Thanks for your input though, it's very useful.
>
> -Axel
> --
> View this message in context:
> http://www.nabble.com/overriding-global-gettext-function-_%28%29-tp22425980p22435477.html
> Sent from the Zend I18N/Locale mailing list archive at Nabble.com.

whisperstream

Re: overriding global gettext function _()

Reply Threaded More More options
Print post
Permalink

thomasW wrote:
Mixing calculations and translations for the whole application within one
class is really a bad coding standard.
This opens many other problems.

To get you the point why static:

$my = new SomeObject();
$other = $my;
$other->_('translation');

Now you're broken again.
Using a static function you are not error-free but you limit possible
problems.

Also ::_ can be searched which would be impossible when you extend your
SomeObject.

Still:
In my eyes this behaviour is completly useless and unnecessary.
It's better to stick with what's available because there is always a good
reason why a implementation works this way and not different.
I think my original example might have been confusing and be leading you astray, so I'll try and clarify my object.

One example that I'm working on is Exceptions.  E.g. I want to be able to write,

throw new MyProject_Exception_NotSupported('Adapter does not implement this method');

I would prefer NOT to write:
// get the default translator
$translate = Zend_Registry('Zend_Translate');

throw new MyProject_Exception_NotSupported(
       $translate->_('Adapter does not implement this method')
 );

In Zend_Validate_Abstract (ZF 1.7, lines 185-210), the _createMessage function basically does the same thing I'm looking to do. i.e. embed a translate instance inside the object.  So that it doesn't Translate immediately but only when called.

I think it's neater than having to translate the exception string explicitly as in the second example.

Reproducing code form zend framework below:

    /**
     * Constructs and returns a validation failure message with the given message key and value.
     *
     * Returns null if and only if $messageKey does not correspond to an existing template.
     *
     * If a translator is available and a translation exists for $messageKey,
     * the translation will be used.
     *
     * @param  string $messageKey
     * @param  string $value
     * @return string
     */
    protected function _createMessage($messageKey, $value)
    {
        if (!isset($this->_messageTemplates[$messageKey])) {
            return null;
        }

        $message = $this->_messageTemplates[$messageKey];

        if (null !== ($translator = $this->getTranslator())) {
            if ($translator->isTranslated($message)) {
                $message = $translator->translate($message);
            } elseif ($translator->isTranslated($messageKey)) {
                $message = $translator->translate($messageKey);
            }
        }

        if ($this->getObscureValue()) {
            $value = str_repeat('*', strlen($value));
        }

        $message = str_replace('%value%', (string) $value, $message);
        foreach ($this->_messageVariables as $ident => $property) {
            $message = str_replace("%$ident%", (string) $this->$property, $message);
        }
        return $message;
    }

Then if you do use the above method of embedding the translator instance in the object, you need to extract the strings from the code to build up your string translation table.  From here you can opt for scanning the source code or else using your runtime logging of untranslated strings.

Hopefully that was a clearer example of what I am trying to achieve.

-Axel