This may seem like yet-another-PoEdit post, but, I write it because I hadn't found any that brought Zend Framework 2 and Twig together in a stepwise fashion that made sense to someone who's just looking to integrate translations into their ZF2 apps. If you're doing it under the gun, and fire up POEdit quickly because you "usually do" figure things out, you're likely to !%!@#^@#$@… [and I did for awhile too, bug in Poedit that's enough to make you double up on your Lisinopril].


Getting Ready

  • Download PoEdit if you haven't already.
  • Require Twig-Gettext-Extractor by adding "umpirsky/twig-gettext-extractor": "dev-master"  to your composer.json file (and run your composer update command thereafter)
  • Install gettext on your system.  If command 'xgettext' doesn't work from your Terminal, you need to install it (edit: added these instructions at the end if you need 'em).


EDIT: I uncovered a few minor gremlins in the umpirsky extractor, I forked it and tweaked ever so slightly to make your life happy again.


Configuring PoEdit

Download PoEdit.  I'm doing this from a Mac, so your mileage may vary a tiny bit.  If you are a Mac user like me, using version 1.6, there's a pesky bug that makes TMP files vanish.  You can get around this by launching Poedit from the terminal with:

WXTRACE=poedit,poedit.tmp,poedit.execute /Applications/ --verbose --keep-temp-files

  • Go to PoEdit > Preferences > Personalize.  Fill in your name and email.
  • Still in this Preferences panel, go to Parsers
  • PHP should already be in there, select it and click Edit. 
  • In the panel that appears, change your list of extensions to *.php, *.phtml
  • Click OK to close
  • Now to add a new parser for Twig templates, click New
  • Configure as such:
Language Twig
List of extensions *.twig
Parser command /path/to/your/project/vendor/bin/twig-gettext-extractor --sort-output --force-po -o %o %C %K -L PHP --files %F
An item in keywords list -k%k
An item in input files list %f
Source code charset --from-code=%c
  • Click OK
  • Click OK again, that should close the preferences dialog. 


In debugging, I symlinked twig-gettext-extractor into /usr/local/bin.  Simplifies the whole affair for the configuration of separate catalogs.  If you do this, you can simplify the parser command considerably.


Creating a Catalog

  • Go to File > New Catalog -- this opens a new settings panel
  • Fill out the Translations Properties panel completely* , setting Charset and Source Code Charset to UTF-8 (this is likely right, tweak if need be). * you can ignore plural forms for now.
  • Head over to the Paths  tab
  • Put a dot in Base Path
  • In the Paths box on the bottom of this tab, use the GUI (throwback to Windows 95 here)  to add your full folder paths (it's the only way I could get it to work, Base Path seems to break things for me)
  • Click on the Keywords  tab, and set it to this list:
Sources keywords list

Sources keywords list

  • Click OK


Update the Catalog

When you click OK, it should parse through and update the catalog.  Note: I ran into a strange bug with version 1.5.7 on OSX where it wouldn't parse unless I started the application from the command line - it's the line I noted above.  Tinker with the UI and put your strings in.  I had my en_US.po file saved in my module's language folder, you should too.  When you save your "catalog", it'll crunch down a .po and a .mo.  Now you just need to tell your ZF2 app to use them.


Module Configuration


I've read a few different ways of doing this online, I settled on this one and it works.  In your module.config.php, make sure you have this config array in place:


    'translator' => array(
'locale' => 'en_US',
'translation_file_patterns' => array(
'type' => 'gettext',
'base_dir' => __DIR__ . '/../language',
'pattern' => '',

This tells your module how to behave: what type of translator to use, and where to find the gettext .mo file.

Then, you need to set the translator up.  This is done in your Module.php inside of the onBootstrap block.  In my case, I have a series of stacked libraries that each have their own translator setups, and have only changed the Module.php for my core "Application" module.


$translator = $e->getApplication()->getServiceManager()->get('translator');
$translator->setLocale( ( isset( $_COOKIE['locale'] ) ? $_COOKIE['locale'] : 'en_US' ) )
->setFallbackLocale( 'en_US' );

I use cookies to switch languages (via URL that sets cookies), so I've set it up to use the locale cookie plainly, with en_US as the basic locale (this is the name of your catalog btw).


Puttin' It All Together

You can use these translations from anywhere!  Controllers, with:

$translator = $this->getServiceLocator()->get('translator'); 
 $translator->translate( "Some String In Your Catalog" );

In discrete objects, purists in the crowd will argue that you should be using DI to pass the translator into the objects that need it, don't necessarily rely on the availability of the service locator.  I think the availability of the SL is a hot topic with ZF3.

Most important, is its use in templates.  Great examples here

Compiling Gettext

If you're on Windows, LMGTFY.

Homebrew has a convenient brew install gettext 

Lastly, it's pretty painless to handbomb-in on most awesome OSes:

  1. Download Gettext from
  2. Extract
  3. ./configure
  4. sudo make && make install

Done like dinner. 

Related Reading