Skip to Content
Course content

222: Translation Files and gettext

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was mentoring a junior dev who had built a beautiful SaaS dashboard. He had handled "internationalization" by creating a massive associative array in a file called lang.php. It worked fine for English and Spanish, but then the client demanded support for Polish and Arabic. Suddenly, he was drowning in a sea of $lang['welcome_message'] keys across forty different files. The real breaking point came when he realized that pluralization rules vary wildly between languages—some languages have three or four different plural forms depending on the count. He spent an entire weekend trying to write a complex if/else logic gate just to handle "1 item" versus "5 items" in Polish. That's when I sat him down and introduced him to gettext.

Moving Beyond Manual Language Arrays

When you're first starting out, it's tempting to just put your strings in an array. But that's not how professional software is localized. gettext is a battle-tested standard (originally from the GNU project) that separates the translation process from the coding process. Instead of your PHP code worrying about which language is active, you mark the strings in your code as "translatable," and you let a separate system of files handle the actual mapping.

In PHP, the most common way to use this is by wrapping your strings in the gettext() function. Since typing that out a thousand times is a nightmare, almost every developer defines a short alias—usually just an underscore _()—to make the code readable. It looks like this:


function _($text) {
    return gettext($text);
}

echo _("Your subscription expires in 3 days.");

The magic here is that the string "Your subscription expires in 3 days." acts as the key. You don't have to invent a key like sub_expiry_warn; the English text is the key.

The Lifecycle of PO and MO Files

Now, where do the translations actually live? This is where gettext differs from a simple PHP file. It uses two specific file formats: .po (Portable Object) and .mo (Machine Object).

  • .po files: These are plain text files that translators actually edit. They contain the original string (the msgid) and the translated version (the msgstr). They are human-readable and can be edited in tools like Poedit.
  • .mo files: These are binary versions of the .po files. PHP doesn't read the .po file directly because it would be too slow. Instead, it reads the compiled .mo file, which allows for lightning-fast lookups.

I'll be honest: setting up the directory structure for gettext can be a bit finicky the first time. You have to follow a strict convention, usually /locale/[lang]/LC_MESSAGES/messages.mo. If you miss one folder level, PHP will silently fail and just show the original English strings, which can be frustrating if you aren't aware of the requirement.

Telling PHP Which Locale to Use

To make gettext actually work, you have to configure the environment at the start of your request. You need to tell PHP where the translation files are located and which language the current user wants. I usually handle this in a bootstrap file or a middleware layer.


// Set the locale to French (France)
setlocale(LC_ALL, 'fr_FR');

// Tell gettext where the .mo files are stored
bindtextdomain('messages', '/var/www/html/locale');

// Set the domain to 'messages' (this matches the filename messages.mo)
textdomain('messages');

echo _("Welcome to your dashboard!"); 
// If fr_FR is set and the .mo file exists, this prints "Bienvenue sur votre tableau de bord !"

One thing to keep in mind: setlocale() depends on the locales installed on the actual server OS. If you're on a Linux box and fr_FR isn't installed in the system's locale settings, gettext won't find it, regardless of how perfect your PHP code is. I've spent more hours than I'd like to admit debugging "broken" translations only to find out the server admin forgot to run locale-gen.




📋 Practical Task

Exercise: Localizing a Customer Order Confirmation Page

You have been handed a simple order confirmation script that is currently hard-coded in English. Your task is to prepare this script for internationalization using gettext.

The Setup:
Imagine you have a directory structure: /locale/es_ES/LC_MESSAGES/orders.mo.

Requirements:

  1. Create a helper function _() that wraps gettext().
  2. Initialize the locale environment:
    • Set the locale to Spanish (es_ES).
    • Bind the text domain 'orders' to the /locale directory.
    • Set the active text domain to 'orders'.
  3. Rewrite the following output strings using your helper function so they can be translated:
    • "Thank you for your purchase!"
    • "Your order number is: "
    • "Estimated delivery: 5 business days."

Expected Code Structure:


// 1. Define helper function
// 2. Setup locale, bindtextdomain, and textdomain
// 3. Echo the translated strings
Rating
0 0

There are no comments for now.

to be the first to leave a comment.