Translanslation
in ZF2 is handled pretty easily and one has several ways to do it. In
this blog i will follow the ZendSkeletonApplications approach by using
translation files in *.mo-format. How do we create those files? How do
we tell the application to use the files? What options do we have to set
the locale to use? I will answer all these questions for you to
internationalize your modules.
Use translations in your view-scripts
Before
we create our language (translation) files, let’s first take a look at
what we have at our hands to actually use the translation. In ZF2 we
have a couple of
i18n view helpers available to use. Let’s take a look at an example that I’ve created in a project recently.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<article class="detail">
<h1><?php echo $entity->getTitle();?></h1>
<h2><?php echo $this->translate('Article details');?></h2>
<dl>
<dt><?php echo $this->translate('Price');?></dt>
<dd><?php echo $this->currencyFormat($entity->getPrice(), 'EUR') .' '. $entity->getPriceType();?></dd>
<dt><?php echo $this->translate('Category');?></dt>
<dd><?php echo $category->getTitle()?></dd>
<dt><?php echo $this->translate('Entry Date');?></dt>
<dd><?php echo $this->dateFormat(new DateTime($entity->getEntryDate()), IntlDateFormatter::GREGORIAN);?></dd>
<dt><?php echo $this->translate('Article Description');?></dt>
<dd>
<?php echo $entity->getDescription() . PHP_EOL; ?>
</dd>
<?php if ('' != ($extLink = $entity->getExternalLink())) : ?>
<dt><?php echo $this->translate('External Link');?></dt>
<dd><?php echo $extLink; ?></dd>
<?php endif;?>
</dl>
</article>
|
In this view script you see three different translation view helpers used. The default
translate(), the
currencyFormat() and the
dateFormat().
This is the most basic usage of view helpers and with this, you will be
able to do a couple of things, but you will run into trouble when you
create your own .mo/.po files.
Set up your zf2 module for translation
Let’s
now take a look at the steps we have to do to set up translation for
our modules. First thing would be to ensure that we have a path
available for our translation files to be stored into. I suggest going
with the suggested layout from the ZendSkeletonApplication to make your
Modules as accessible to other developers as possible.
|
./modules
./NAMESPACE
./config
./language
./src
./view
Module.php
|
Now that we’ve taken care of that, let’s configure our module to load translation files from said directory.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<?php
namespace FileManager;
return array(
'router' => array(),
'service_manager' => array(),
'translator' => array(
'translation_file_patterns' => array(
array(
'type' => 'gettext',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.mo',
'text_domain' => __NAMESPACE__,
),
),
),
'controllers' => array(),
'view_manager' => array(),
);
|
Please
take a quick look at the highlighted lines. First one would be to set
your modules namespace into your configuration file. Personally i do
this a lot, as it helps in quite a few configuration issues. Makes
Copy&Paste’ing a little easier for future modules
The important part however are lines 12-14. Here we define three things.
- The directory to load translation files from
- How the files are named
- The Text-Domain of the translation
Pretty much like what is done within the ZendSkeletonApplication isn’t it? Not really.
text_domain ??? This is the part where i had my problems, too. Luckily
Ludwig_
from #zftalk@freenode.net was able to help me out. Each translation
file that is loaded needs to have a text_domain added. If no text_domain
is added, ‘default’ will be assumed. One might think that this is no
problem, but actually there can only be one translation file for each
text_domain. Since there can be only one text_domain i choose my modules
__NAMESPACE__ to be the text_domain, too. As that should pretty much be
unique. With this configuration in place, the language files will be
loaded.
Now we actually need to change our view-script, too. The
translate()-view-helper needs to know about the text_domain, too. Once
again i’ll go with the __NAMESPACE__ to make it a little easier for me
to type.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<?php
namespace FileManager;
?>
<article class="detail">
<h1><?php echo $entity->getTitle();?></h1>
<h2><?php echo $this->translate('Article details', __NAMESPACE__ );?></h2>
<dl>
<dt><?php echo $this->translate('Price', __NAMESPACE__ );?></dt>
<dd><?php echo $this->currencyFormat($entity->getPrice(), 'EUR') .' '. $entity->getPriceType();?></dd>
<dt><?php echo $this->translate('Category', __NAMESPACE__ );?></dt>
<dd><?php echo $category->getTitle()?></dd>
<dt><?php echo $this->translate('Entry Date', __NAMESPACE__ );?></dt>
<dd><?php echo $this->dateFormat(new \DateTime($entity->getEntryDate()), \IntlDateFormatter::GREGORIAN);?></dd>
<dt><?php echo $this->translate('Article Description', __NAMESPACE__ );?></dt>
<dd>
<?php echo $entity->getDescription() . PHP_EOL; ?>
</dd>
<?php if ('' != ($extLink = $entity->getExternalLink())) : ?>
<dt><?php echo $this->translate('External Link', __NAMESPACE__ );?></dt>
<dd><?php echo $extLink; ?></dd>
<?php endif;?>
</dl>
</article>
|
Set up locale in ZF2
When
working with I18n-Features, we have several ways to set up the locale
to use. One way would be to hard code this in our views like the
following.
|
echo $this->translate("Translate me", $textDomain, 'de_DE');
|
It’s
quite obvious that this is more than unlikely to be a handy solution
and will probably only be used in very rare cases where you have to
ensure a very general name not to get translated. So we have to look for
another option. We can set up a locale at the configuration files, too.
For this we need to add the locale key to our translator configuration.
|
return array(
'translator' => array(
'locale' => 'de_DE'
)
)
|
Once
again this feature kind of sucks. We need to make the locale variable.
For this we have several features, too. Personally i like to go with the
clients
HTTP_ACCEPT_LANGUAGE. Of course you could
extend this by first going with the HTTP_ACCEPT_LANGUAGE and later check
if there’s a session variable set (or some user preferences at the
database), for now let’s just go with it though. Time to edit our
Module.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?php
namespace FileManager;
use Zend\Mvc\ModuleRouteListener;
class Module
{
public function onBootstrap($e)
{
$translator = $e->getApplication()->getServiceManager()->get('translator');
$translator
->setLocale(\Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']))
->setFallbackLocale('en_US');
}
//...
}
|
Within
our modules Bootstrap we now set our default Locale to that of the
HTTP_ACCEPT_LANGUAGE. I’d argue that in a lot of cases this is a good
thing and doesn’t need to be changed often. As mentioned previously, you
could potentionally look up $_SESSION-Variable or DB-Entries for
user-specific Locale-settings and overwrite the current locale to make
it better.
In case no valid locale can be built from the
HTTP_ACCEPT_LANGUAGE (you never know what those hackers do!) we still
have a fallback defined, in this example ‘en_US’.
All set and done, let’s resume
Let’s take a quick look at the steps we’ve taken to internationalize our Module.
- We’ve set up a folger for language files
- We’ve set up configuration to load language files from said folder
- We’ve set up the translator to load a Locale depending on users HTTP_ACCEPT_LANGUAGE
- Furthermore we know how to force specific Locale-Settings
- We’ve learned about the text_domain
- We’ve also learned about how to use translation features in our view-scripts
Now
there is only one thing left to do and that is to actually create the
language files. As this is a little off-topic i have created a separate
post for this. Please take a look at
Using PoEdit to create Translation files. It’s a very easy and straight-forward process that doesn’t consume much time at all.
All
in all i hope that i was able to let you in on how to internationalize
your modules. If you encoutner any errors, let me know about it and i
will try my best to help you out. Just drop me a comment and we’ll see
where it goes from there.
ERROR: Class Intl | IntlDateFormatter not found…
A
thing i forgot to mention earlier was, that you really need to have the
intl-extension running on your webserver. Linux users should find the
official documentation helpful enough. Windows users should locate their respective
php.ini and search for ‘intl’, remove the semikolon ‘;’ from the line with the extension
|
//... lots of other extensions
extension=php_intl.dll
//... lots of extensions
|
INSTALL LCOATION PACKAGE FOR PHP :
Ubuntu :
You need to have installed the intl PHP extension.
For APT based systems, try: sudo apt-get install php5-intl
For YUM based systems, try: sudo yum install php-intl
|