Zend_Translate with Dynamic Parameters

Pascal Opitz posted a nice little snippet; Just a quick snippet to have dynamic parameters in the underscore function, without having to write sprintf every time.

<?php
class Translate extends Zend_Translate {
  public function _() {
    $args = func_get_args();
    $num = func_num_args();

    $adapter = $this->getAdapter();

    $args[0] = $adapter->_($args[0]);

    if($num <= 1) {
      return $args[0];
    }
    return call_user_func_array('sprintf', $args);
  }
}

Usage would be something like the following:

$t = new Translate('array', $array_translation, $lang);
echo $t->_('My name is %s', 'Pascal');
echo $t->_('I have a %s and a %s', 'Cat', 'Horse');

Note: The above little snippet solves one of the major gripes I have with the default _() function.

via Zend_Translate with dynamic parameters.

Zend_Validate messages translated to Swedish

Thomas Wei­d­ner commits the hackix.com Swedish translations to trunk.

Danny spent some time translating the ~200 validation messages in /resources/languages/sv/Zend_Validate.php make sure to give them an eye over after your next trunk (1.10.2+) checkout and report any suggestions & feedback here.

Zend_Translate & TMX adapter – Source language strings as id

Thomas Weidner describes some additional functionality only available from Zend Framework 1.10.2+ and forward, for those of us that do *not* wish to use a separate message id/key but rather the source language string in the source code.

Set the “useId” option to false.
In this case the source language is used as message key and the source must be set as first translation.





Nachricht 1
message 1


Note the ‘useId’ => false below;

$translate = new Zend_Translate('tmx', $file, $locale, array('useId' => false));

Then “Nachricht 1” will be used as message key instead of “0001”.
Note that this works only as with 1.10.2 (or trunk as of today).

Zend_Form Translated Country & Currency Lists

A very common question is how do I get a localized / translated list of countries, currencies etc for a company registration form or similar.

Here is a easy to use sample; For your cut’n’paste pleasure 🙂


'HtmlTag'),
array('tag' => 'div', 'class' => 'element')),
'Label',
array(array('row' => 'HtmlTag'),
array('tag' => 'li')),
);

private $buttonDecorators = array(
'ViewHelper',
array(array('data' => 'HtmlTag'),
array('tag' => 'div', 'class' => 'button')),
array(array('row' => 'HtmlTag'),
array('tag' => 'li')),
);

public function init()
{
$this->setMethod('post');

$companyName = new Zend_Form_Element_Text('name', array(
'decorators' => $this->elementDecorators,
'label' => _('Company name'),
'description' => _('Enter the company name'),
'required' => true,
'filters' => array(
'StringTrim'
),
'validators' => array(
array('StringLength', false, array(6, 50))
),
'class' => 'input-text'
));

$accountNumber = new Zend_Form_Element_Text('accountno', array(
'decorators' => $this->elementDecorators,
'label' => _('Account number'),
'description' => _('Enter the ORG/VAT number.'),
'required' => true,
'filters' => array(
'StringTrim'
),
'validators' => array(
array('StringLength', false, array(12, 25))
),
'class' => 'input-text'
));
/**
* Generate a Country select box with the localized country
* names based upon the current application wide locale.
*/
$locale = Zend_Registry::getInstance()->get("Zend_Locale");

$countries = ($locale->getTranslationList('Territory',
$locale->getLanguage(),
2));

asort($countries, SORT_LOCALE_STRING);

$country = new Zend_Form_Element_Select('country', array(
'decorators' => $this->elementDecorators,
'label' => _('Country'),
'description' => _('Select the Country of Incorporation.'),
'required' => true,
'filters' => array(
'StringTrim'
),
'class' => 'input-select'
));

$country->addMultiOptions($countries)
->setValue($locale->getRegion());

/**
* Generate a Currency select box with the localizes currency
* names based upon the current application wide locale.
*/
$currencies= ($locale->getTranslationList('NameToCurrency',
$locale->getLanguage(),
2));

asort($currencies, SORT_LOCALE_STRING);

$currency = new Zend_Form_Element_Select('currency', array(
'decorators' => $this->elementDecorators,
'label' => _('Currency'),
'description' => _('Select the billing currency.'),
'required' => true,
'filters' => array(
'StringTrim'
),
'class' => 'input-select'
));

$currency->addMultiOptions($currencies)
->setValue('EUR');

$submit = new Zend_Form_Element_Submit('register', array(
'decorators' => $this->buttonDecorators,
'label' => _('Register'),
'class' => 'input-submit'
));
$this->addElements(Array($companyName,
$country,
$accountNumber,
$currency,
$submit));
}
}

For some more samples and references for these types of functions, check out;
http://framework.zend.com/manual/en/zend.locale.functions.html

Translate Toolkit & Pootle

This entry is part [part not set] of 1 in the series Translation Tools

I’ve got a whole lot of questions about what tools other than poedit that exists especially for teams, so I’ll start going through and try to review tools available and add them to the new Translation Tools article series.

I hope you’ll find this useful.

The Translate project provides tools to make it easier for you to localize. Tools that work with standards based file formats like PO and XLIFF because you deserve the best. Our goal is to make your life as a localizer easier, help you work faster and keep your work at a high quality.

Virtaal – Standalone CAT Tool

Demonstration of new features in the latest Virtaal release. Virtaal is an open source computer aided translation and localisation tool. You can read more about it at Virtaal. Virtaal aims to be simple to use for first time localisers yet powerful for experienced localisers. Features include translation memory from many sources, sophisticated searching, a simple and powerful navigation interface and more. The developers are aiming to include terminology support in the next release and make it easier to configure the translation memory and terminology plugins.

Pootle web-based translation tool

If you require team based the Pootle web-based translation tool and translation management solution might be what you want.

Check out:  Translate Toolkit & Pootle website.

Bootstrapping Zend_Translate with a LangSelector Plugin

This entry is part [part not set] of 4 in the series Working with Zend_Translate and Poedit

As an update to the method of having everything related to Zend_Translate and Zend_Locale in the Bootstrap, here is an alternative using an Controller Plugin that does the grunt work of validating, selecting and updating the Zend_Locale, Zend_Registry & Zend_Session using Zend_Session_Namespace. And we are using poedit .po & .mo files as the source as usual.

Please comment as usual if you have a neater way of doing it 🙂

Bootstrap.php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {

protected function _initTranslate()
{
// Get current registry
$registry = Zend_Registry::getInstance();
/**
* Set application wide source Locale
* This is usually your source string language;
* i.e. $this->translate('Hi I am an English String');
*/
$locale = new Zend_Locale('en_US');

/**
* Set up and load the translations (all of them!)
* resources.translate.options.disableNotices = true
* resources.translate.options.logUntranslated = true
*/
$translate = new Zend_Translate('gettext',
APPLICATION_PATH . DIRECTORY_SEPARATOR .'languages', 'auto',
array(
'disableNotices' => true, // This is a very good idea!
'logUntranslated' => false, // Change this if you debug
)
);
/**
* Both of these registry keys are magical and makes
* ZF 1.7+ do automagical things.
*/
$registry->set('Zend_Locale', $locale);
$registry->set('Zend_Translate', $translate);
return $registry;
}
}

This little plugin will check every request for a lang paramenter and act on it.
It does not matter if you set the lang parameter using a custom route :lang/:controller/:action
or via a get/post ?lang= etc. one or all of them will work.

library/App/Controller/Plugin/LangSelector.php


* @name App_Controller_Plugin_LangSelector
* @filesource library/App/Controller/Plugin/LangSelector.php
* @tutorial Instantiate in application.ini with;
* resources.frontController.plugins.LangSelector =
* "App_Controller_Plugin_LangSelector"
* @desc Takes the lang parameneter when set either via a
* route or get/post and switches Locale, This depends
* on the main initTranslate function in Bootstrap.php
* to set the initial Zend_Translate object.
* Inspiration from ZendCasts LangSelector.
*/
class App_Controller_Plugin_LangSelector extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$registry = Zend_Registry::getInstance();
// Get our translate object from registry.
$translate = $registry->get('Zend_Translate');
$currLocale = $translate->getLocale();
// Create Session block and save the locale
$session = new Zend_Session_Namespace('session');

$lang = $request->getParam('lang','');
// Register all your "approved" locales below.
switch($lang) {
case "sv":
$langLocale = 'sv_SE'; break;
case "fr":
$langLocale = 'fr_FR'; break;
case "en":
$langLocale = 'en_US'; break;
default:
/**
* Get a previously set locale from session or set
* the current application wide locale (set in
* Bootstrap)if not.
*/
$langLocale = isset($session->lang) ? $session->lang : $currLocale;
}

$newLocale = new Zend_Locale();
$newLocale->setLocale($langLocale);
$registry->set('Zend_Locale', $newLocale);

$translate->setLocale($langLocale);
$session->lang = $langLocale;

// Save the modified translate back to registry
$registry->set('Zend_Translate', $translate);
}
}

Big thanks to Zend Cast for the inspiration!

How to make POEdit detect source strings in Zend Framework

This entry is part [part not set] of 4 in the series Working with Zend_Translate and Poedit

You will notice that once you have started translating an application using poedit it’s quite a smooth process, what hampers the experience a little bit is the mutitude of ways you can write code in Zend Framework, this is great in every way for developers, but requires a bit of thinking when you need to also translate all the UI strings.
So how do we make poedit detect the strings while making our code pretty?

(And guys PLEASE comment on this article with your own hints, tips & quirks!)

For example;
This is a simple login form using Zend_Form;
Zend_Form is completely Zend_Translate i18n compatable, and will read in all strings from your translation sources without the use of specific $this->translate() calls, which makes the code ALOT prettier, see below;


class Form_Login extends Zend_Form {
public function init() {
$this->setAttrib('id', 'LoginForm');
$this->addElement('text', 'username', array(
'label' => $this->getView()->translate('Username'),
'description' => $this->getView()->translate('Please enter valid username'),
));
$this->addElement('password', 'password', array(
'label' => $this->getView()->translate('Password'),
'description' => $this->getView()->translate('Please enter valid password'),
));
$this->addElement('submit', 'submit', array(
'label' => $this->getView()->translate('Login'),
));
}
}

Is the same thing as; but a LOT shorter and cleaner!

class Form_Login extends Zend_Form {
public function init() {
$this->setAttrib('id', 'LoginForm');
$this->addElement('text', 'username', array(
'label' => _('Username'),
'description' => _('Please enter valid username'),
));
$this->addElement('password', 'password', array(
'label' => _('Password'),
'description' => _('Please enter valid password'),
));
$this->addElement('submit', 'submit', array(
'label' => _('Login'),
));
}
}

Both are caught by the poedit keywords scanner that looks for translate() as well as the translate helper shotcode _().

Some notes (And I’ll keep this section updated while I find more tips & hints and quirks!)

But! It always seem to be a But in there;
These two are NOT the same!
translate("Welcome %s, your last login was %s",$this->user['name'],$this->user['active']); ?>
user['name'],$this->user['active']); ?>
The second one explodes with; “Warning: _() expects exactly 1 parameter, 3 given”

That tells us that;

or even (if you have short tag mode on;

should be ok to use. except that it’s not producing the translated output in an view.phtml or layout.phtml. so we are forced to use;
translate('Logout'); ?>

Configuring Poedit for Zend Framework Projects

This entry is part [part not set] of 4 in the series Working with Zend_Translate and Poedit

There are a few steps you need to take to configure poedit to work with a Zend Framework project properly. I will take you through the configuration process step by step, and in the end you should have a working installation.

In this tutorial we are on Windows, but the process is the same on Mac & Linux based systems, and poedit even looks much the same on all platforms.

Install poedit and start it, if it’s the first time you run it you should now see a Preferences dialog.

Personalize:
Your name & email – Fill these in
Editor:
You can leave all the options as their defaults, including the Line endings format [Unix]
Translation Memory:
Leave this as is for now.
Parsers:
Select PHP and click Edit.

PHP Language settings

Make sure your dialog matches the one above exactly!

Now click OK twice and you are done with the preferences.

The main poedit window will now come up,  click File -> New Catalog, you should now see a settings window.

Project Info:

Fill in your Project name and version and the rest of the fields making sure you select Charset and Source code charset to UTF8 and selecting the language and country of the translation you are going to create, in my case Language: Swedish and Country: SWEDEN.

Project Info

Now select the Paths tab, and add your projects base path. In my case C:\Zend\Apache2\htdocs\testbench then click the New item tool and add; application

Project Base and application path

Now select the Keywords tab and click the New item tool and add;

  • translate
  • _
  • setLabel
  • setValue
  • setMessage
  • setLegend
  • _refresh
  • append
  • prepend

(Note: If you have any other keywords that come to mind, feel free to comment and I’ll add them to this tutorial)

Now you click OK and the Save as dialog comes up move to your project application directory and select or create the languages directory the path should look something like C:\Zend\Apache2\htdocs\testbench\application\languages and save the file as sv_SE.po (replace this with the language/locale code that you have choosen.)

Now your source code will be scanned after the keywords you specified earlier and the Update Summary dialog will be showing all the strings it detected;

Update Summary

In this example the strings where caught from;

$this->headTitle()->prepend($this->translate('TestBench Application -'));
<?php echo $this->translate("Welcome %s, your last login was %s",$this->user['name'],$this->user['active']); ?>

in my layouts/scripts/layout.phtml file.

When you click OK on the Update Summary Dialog you will be taken to the main poedit window where you can translate the strings.

Main window

As you can see it’s very easy to work with simply enter your translations in the bottom text box.

Now after you are done you simply click File -> Save and two files will be written to your languages directory, in my case sv_SE.po and sv_SE.mo where the .mo file is the compiled version that Zend_Translate uses.

Now if you add new strings to your source code you simply load poedit and open your sv_SE.po file and select Catalog -> Update from sources and it will again show you the Update Summary dialog with all new string as well as changed strings and removed (Obsolete) strings.

There are a ton of good Zend_Translate references out there, google is your friend!

Hope this helps, enjoy!

Bootstrap Zend_Translate

This entry is part [part not set] of 4 in the series Working with Zend_Translate and Poedit

A recurring problem for site developers is implementing a solid way to create and maintain multilingual sites, this article series is my feeble attempt to guide you through how to quickly implement the Zend_Translate in an Zend Framework 1.9.x site.

The procedures and best practices for this is unfortunately like training a dog, everyone has a different way of doing it and an opinion, so the methods and code I show here are taken out of applications that are running in production so if you have a better way of doing it please feel free to comment!.

I usually use poedit a gettext editor which is available for most platforms to create my translation files, and after some initial configuration of the catalog paths so it can see your source files please see Part 1 of this article series.

The bootstrap below looks for the language specific gettext .mo files in /application/languages/ for example /application/languages/sv_SE.mo

Bootstrap.php

protected function _initTranslate() {
// We use the Swedish locale as an example
$locale = new Zend_Locale('sv_SE');
Zend_Registry::set('Zend_Locale', $locale);

// Create Session block and save the locale
$session = new Zend_Session_Namespace('session');
$langLocale = isset($session->lang) ? $session->lang : $locale;

// Set up and load the translations (all of them!)
$translate = new Zend_Translate('gettext', APPLICATION_PATH . DIRECTORY_SEPARATOR .'languages', $langLocale,
array('disableNotices' => true));

//$translate->setLocale($langLocale); // Use this if you only want to load the translation matching current locale, experiment.

// Save it for later
$registry = Zend_Registry::getInstance();
$registry->set('Zend_Translate', $translate);
}

Now when you use statements like

translate('Contact Admin'); ?>
in your layout.phtml or view.phtml files it will be picked up by poedit and you will be presented with a string “Contact Admin” to translate, in my case i’ll just enter “Kontakta Administratören”.

There is some debate on what to put in the translate strings as identifiers, I personaly prefer the actual term to translate in a base language, in this case English instead of some convoluted “IDS0001” type strings.

Poedit will keep track of changes, i.e if I would change the “Contact Admin” to “Contact us” it will tell you on synchronization that “Contact Admin” disappeared and a new translation is required for “Contact us”. It’s quite easy to send those strings to your translators.

Thats it for today.