How to Internationalize Your PHP Site

admobjp.pngWe’ve just soft launched admob.com in Japanese so I figured I’d jot down some thoughts that might useful for people wanting to undertake a similar effort. I’ll cover how to internationalize (i18n) just the language strings on your php site using Smarty and gettext. True i18n efforts include number/date formatting and handling foreign currency, but that’s not in the scope of this article.Preparation

  • Get gettext up and runninggettext is a GNU project that has all the tools needed to make i18n easier. PHP has built in gettext support. It’s fairly easy to set up on an linux box once you figure out what set of commands you need to run to start installing locales.
  • Separate of code and display – we use Smarty templating system so there is a clean break between our code and display. Many benefits to doing that this that I won’t cover here.
  • Make a cut off for features – Unless you’re using in-house translators, decide which features/pages won’t make it into your initial release. You’ll have constant churn on your code base, features constantly making it to production, and PMs constantly wordsmithing your pages during this project. I suggest making an i18n branch of your code and merging non-UI changes over during the project. Then close to the date of your i18n launch, plan for another round of translation. If your company is not in a rapid release cycle, I’m not sure why you’re taking advice from me ;).
  • Find a GOOD translation firm – Trust me. A good translation firm can go a long way in i18n efforts. At a minimum, they must understand basic HTML tags. I would recommend looking for firm that has translators that are experienced in whatever industry you’re in.

Extracting and Translate

  • Wrap all strings – The first task you need to do is to wrap all your display strings in tags so that the gettext parser will be able to make a .po file. This is the bulk of the work as you have to run through every code /template file. Strings in PHP are wrapped in _() and strings in Smarty are wrapped in {t}{/t} tags. It’s best practice to keep punctuation inside the wrapping while leaving as many HTML tags outside. However, do not break up whole sentences this will confuse your translators.If you have any screenshots, pictures, or logos with embedded strings, prepare to break those up or replicate them localized. Also be prepared to do something about your strings in javascript. We made a simple framework for handling translations in php and outputting localized versions on the fly. The alternative is to generate local specific .js files using your build step.
  • String extraction script – Spend sometime writing a shell script that you can run on your code branch that will pull out all the strings. The Smarty gettext package includes a script that will run through your template files and build a simple .c file. After that you will need to use xgettext -j to make one large .po file. The following script will do the trick../tsmarty2c.php ../code > smarty.cxgettext --no-wrap -j smarty.cfind ../code/. -iname "*.inc" -exec xgettext --no-wrap -j -L PHP {} \;find ../code/. -iname "*.php" -exec xgettext --no-wrap -j -L PHP {} \;
  • Test and test again – After you have a .po file go ahead and fill in the translations with random garbage like some random Japanese characters. Make your .mo file then QA the hell out of your site. I guarantee that you will be missing some strings. You want to avoid having to send your strings off to be translated, only to have to send off another batch.
  • Translate! – Send the .po file to the firm and wait. Also bake in sometime for linguistics QA with the same firm… If you have any native speakers in house, plan some time to sit down with them to make sure the translations you are getting back make sense.
  • Launch – If you’re using Capistrano to deploy your site, prepare a script to deploy your branch. Keep in mind when there are changes to the .mo, it is highly recommended to restart apache.

Maintenance

  • Make i18n low impact – once the initial launch has happened, you want the impact of maintaining it to be unobtrusive as possible. Everybody developing front-end features by now should be aware of _() and {t} tags, however try to avoid letting i18n impede development progress for your core site.
  • Make a translation schedule – Set up a schedule with your translations firm so they plan for when they’re going to get a new batch of strings from you. This will lessen turn around times and enable you to launch features in other languages as quickly as possible

I hope this provided some insight into internationalizing your website. I know when we started on the project, there wasn’t much info to be found online. Web 2.0 isn’t English only and I hope everybody designs their site keeping in mind the world is flat. As always, feel free to leave any questions in the comments.

|   

How to Use ignore_user_abort() to Do Processing Out of Band

Fresh from ZendCon, a technique I learned from Eli White

Ever wanted to process things out of band in your php scripts? The ignore_user_abort() function allows you just to do that.

Description

int ignore_user_abort ( [bool $setting] )

Sets whether a client disconnect should cause a script to be aborted.

Parameters

setting

If not set, the function will only return the current setting.

Basically, when you use ignore_user_abort(true) in your php script, the script will continue running even if the user pressed the esc or stop on his browser. How do you use this? One use would be to return content to the user and allow the connection to be closed while processing things that don’t require user interaction.

The following example sends out $response to the user, closing the connection (making the browser’s spinner/loading bar stop), and then executes do_function_that_takes_five_mins();

ignore_user_abort(true);
header("Connection: close");
header("Content-Length: " . mb_strlen($response));
echo $response;
flush();
do_function_that_takes_five_mins();

It’s that easy! You can also redirect the user in the same fashion. In the example below I’ve added the line session_write_close() which forces the session to be written. If this is not done, any modifications to $_SESSION will not take affect to user’s other sessions until do_function_that_takes_five_mins(); has finished.

ignore_user_abort(true);
session_write_close(); // optional, this will close the session.
header("Location: $redirect_url");
flush();
do_function_that_takes_five_mins();

Also, if your max execution time is set to something low, you can increase the time limit for scripts by using set_time_limit(). Just keep in mind, the php process will still take up an apache process until it’s completed. This is a great technique to give the user immediate response and speed up your web apps!

|  

ZendCon 2007 Wrap Up

The past couple days I attend ZendCon 2007. I had low expectations for the event but planned to attend most of the break out sessions. My company, AdMob, also sent me to do some recruiting but with the job market so tight not many attendees were looking for jobs.

I tried to hit up every session about scaling web sites hoping to learn something new. However, all techniques end up being the same. Some combination of load balanced front end server, a memcached server pool, partitioned databases with purpose divided read only slaves, and if needed a jobs/queue server.

The most useful talk of the two days was done by Eli White on PHP Features You Didn’t Know Existed. (Slides available here.) Take aways for me were:

  • http_build_query – easy function to rebuild get parameters.
  • ignore_user_abort / connection_aborted – allows a php script to continue even after the user has ended the connection
  • register_shutdown_function – a destructor for your scripts, no matter how they end
  • set_time_limit – increase execution time on a script. Already a well known function, but didn’t realize it resets the counter every time, so you can throw this in a loop and you have N seconds from when the line gets executed
  • pspell – built in spell check library, great for English. Not sure about i18n support

Also Joel Spolsky of JoelOnSoftware.com fame gave Day 2’s afternoon keynote. Very entertaining speech, mainly because he showed numerous pictures of Angelina Jolie… and Brad Pitt so all the PHP women in the room wouldn’t feel left out. Then proceeded to BLAST Microsoft, from the Brown Zune to a hilarious Windows ME simulator. However, the main take away from his speech were his 3 tips to building great software.

  • Make People Happy – put people in control, even if it’s faux control. People that feel helpless or not in control are not happy. Software should do things for you, not ask you to do things (insert original Windows CD-ROM is a classic example).
  • Think About Emotions – Apple does this well. People connect to their devices because they evoke emotion. His example was SUVs make people feel safer due to small things such as cup holders and things being soft and round.
  • Obsess over Aethetics – Rounded corners are king. Mentioned French buildings which contain no fire escape stairs versus buildings in New York which all have them.

I’m obviously butchering his presentation. If you ever have a chance to hear Joel talk, take it.

Other than those 2 sessions, the rest of the conference was a waste of time in my opinion. If I didn’t work 3 exits away from the conference, I definitely wouldn’t have attended.