Translating PHP application using gettext & msgfmt with .po / .mo files

Firstly install the gettext cmd line tools.

brew install gettext
brew link gettext

If your not a big fan of homebrew you can do the same using macports. Personally I really like homebrew and if you’d like to give it a try go to Homebrew. This will install useful cmds such as xgettext, msgfmt, msginit & msgmerge.

Fire up a fresh terminal and cd into your PHP apps working directory /Applications/MAMP/htdocs/my_app/ in my case.

I’d suggest getting yourself a local environment to test this on. I’m using MAMP and have created a new folder in the htdocs called localetest. Call it whatever you like and create a few PHP files with strings of echo gettext("This is translatable string"); or echo _("This is translatable string");

Once you’ve done this open up a terminal and cd into your cd /Applications/MAMP/htdocs/yourfoldername/

Once in it your going to run a command which will recursively search through the PHP files in this entire directory looking for translatable strings.

find . -name "*.php" | xargs xgettext -a --from-code=utf-8

or more simply

find . -iname "*.php" | xargs xgettext

The first part of this command will search recursively starting from the current level of directory structure for .php files. xargs builds and executes from standard input and executes the xgettext command for each file. This command searches the input from the found files for strings of gettext() or _() in a .php file and outputs them into a messages.po file.

Run the command and you’ll end up with a file a little like this one.

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSIONn"
"Report-Msgid-Bugs-To: n"
"POT-Creation-Date: 2013-01-17 10:13+0000n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONEn"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>n"
"Language-Team: LANGUAGE <LL@li.org>n"
"Language: pt_PTn"
"MIME-Version: 1.0n"
"Content-Type: text/plain; charset=UTF-8n"
"Content-Transfer-Encoding: 8bitn"
#: another.php:3
msgid "Another string in file 2"
msgstr "mumbo jumbo"
#: index.php:3
msgid "Lets make this a translatable string"
msgstr "oh mumbo jumbo la la la"
#: index.php:4
msgid "Another translatable string"
msgstr "Otra cadena traducible"
#: index.php:5
msgid "Something here"
msgstr "Algo aquí"

If you look toward the bottom of this file you’ll notice it’s picked up our translatable strings. Also, notice, I have manually filled in the language this is going to correspond to under “Language: pt_PTn” and also set the character under the content type: “Content-Type: text/plain; charset=UTF-8n”. You’ll also notice I made some mumbo jumbo translations (which are not portuguese) to illustrate the translation.

Create a directory structure in your app which is going to help you organise your translations for instance mine is something like this.

Screen Shot 2013 01 17 at 11.29.56 Translating PHP application using gettext & msgfmt with .po / .mo files

Example folder structure for gettext

Once you have created th

Once you create this you will need to fire off a few more cmd line cmds. So fire up your terminal boys…

The next cmd you will run will create a .mo file which is a machine readable binary file used by gettext in PHP to quickly provide the translations.

msgfmt -cv -o locale/pt/LC_MESSAGES/messages.mo locale/messages_pt.po

After you run this cmd you should get a response finishing in 4 translated messages. This is actually the last step in the creation of files. Next we need to configure our app to detect the locale and forcibly translate our gettext snippets on the fly for us.

This can be achieved in a multitude of different ways and my solution below will not suit all. It is mainly here for demonstration purposes only. In the top of any of your .PHP files in your local directory add the following code.

$locale = "pt_PT";
putenv("LC_ALL=$locale");
setlocale(LC_ALL, $locale);
bindtextdomain("messages", dirname(__FILE__).'/locale');
textdomain("messages");

This will force your app to use the locale pt_PT and in doing so use the translations provided by you in the .po file.

Important: Each time you provide more translations you must re-build the .mo files with the following command.

msgfmt -cv -o locale/pt/LC_MESSAGES/messages.mo locale/messages_pt.po

Edit:

I recently found that when receiving translations from Windows users (as is the case quite often) the .po files need to be re-saved with UTF-8 encoding. I also found sometimes Windows users might get .BOM extension added to the files as well. This will not be interpreted by msgfmt correctly and will result in a fatal error on compilation. A save in the correct encoding will fix this.

Also another handy tip when installing gettext for translating:

Since I wrote this post the brew formula for gettext has changed and now is ‘keg-only’ you will find it is not symlinked and symlinking it may cause issues when installing and compiling other applications.

Even though it is not symlinked you can still find the binary like so:?
/usr/local/Cellar/gettext/0.18.2/bin/msgfmt -cv -o locale/fr_FR/LC_MESSAGES/messages.mo translations/messages_fr.po

The new year

As 2013 gets well underway I took a quiet minute to reflect on the year gone by and all that lays ahead in 2013.

2012

2012 was a roller coaster year, full of some tremendous highs and the lowest lows – by all accounts it was an interesting one.

In business I guided my company Bytewire Ltd to it’s most successful year to date, gaining more clients and generating more revenue than any previous year. Alot of big wins we’re made and alot of lessons learnt. With long hours and much effort we managed to turn around alot of projects for a very small team and all the while improve my own coding ten fold. I learnt new languages, played around with new techniques, built out my own servers, and got to grips with more than one E-commerce platform.

Personally 2012 taught me alot about myself and what i’m capable of if I just remain dedicated, hard working and driven. There is alot to be said of those that grab each day by the reigns.

A couple of important lessons i’ve learnt this year:

  • Life’s too short
  • Change is good
  • Be a little selfish when it comes to your development and career, push for what you want!
  • Don’t let people play your ideas down, be heard, but equally listen to the opinions of others just as you wish yours.

2013

2013 holds alot in store for me personally and i’m excited about the challenges it will present. I’m looking forward to moving out from my parents place, getting fully back into football (after lengthy time out with a broken leg) and getting to new places around the world on holiday. All the while i’m as always excited by the prospect of new creative and interesting projects that both challenge and push my professional abilities.

This years all about reading more and learning more – particularly looking forward to getting into mobile at the end of the month.

2013’s bucket list

  • Sky dive
  • Fly somewhere further than 6 hours away
  • Go boarding (not been since broke my leg)
  • Volunteer for an animal charity
  • Be a film extra

I’m all set to make 2013 my bitch. So…until next year folks…have a great year.

Very handy bash_profile terminal command for connecting to localhosts through tunnel with browser stack

Open up your ~/.bash_profile don’t worry if it doesnt exist already, just make it.

browser_stack(){
	java -jar ~/Downloads/BrowserStackTunnel.jar FEnE97YWyQEqa2MnMIHm $@,80,0;
}

Copy and paste the above line in the file and save.

Open terminal force a reload of your profile with the following and run the command like so:

. ~/.profile && . ~/.bash_profile

Then run:

browser_stack local_domain

Best installation guidelines for MAMP and PHPUnit

These are the best guidelines I have found and mashed together recently for installing and configuring PEAR and PHPUnit for use with MAMP.

Firstly tell PEAR to auto discover:

sudo /Applications/MAMP/bin/php/php5.4.4/bin/pear config-set auto_discover 1

Secondly install the PHPUnit PEAR package:

sudo /Applications/MAMP/bin/php/php5.4.4/bin/pear install pear.phpunit.de/PHPUnit

Thirdly link it to your profile:

sudo ln -s /Applications/MAMP/bin/php/php5.4.4/bin/phpunit /usr/local/bin/phpunit

Lastly test it:

phpunit --version

Should output:
PHPUnit 3.7.10 by Sebastian Bergmann.

All done!