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

Bootstrapping Zend_Translate with a LangSelector Plugin

This entry is part 4 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 3 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'); ?>

Bootstrap Zend_Translate

This entry is part 2 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.