Why PHP Internationalization Is Not Just "Word Translation"
\n\nImagine: you wrote a cool PHP project. It works, delights users, and generates profit. But one day, you notice in your statistics that 30% of traffic is from Germany, 15% from Japan, and 10% from the UAE. Your site only speaks Russian. Users leave without finding the "Buy" button in their language.
\n\nDoes this sound familiar? Internationalization (i18n) of PHP applications is not just about replacing words in a template. It is an architectural approach that allows your code to "understand" culture, currency, dates, and even text direction (left-to-right or right-to-left).
\n\nIn this article, we will break down how to turn a regular PHP site into a multilingual platform using native tools and best practices. You will learn how gettext in PHP works, how localization differs from internationalization, and how to avoid pitfalls with pluralization.
\n\ni18n vs l10n: What's the Difference (and Why It Matters)
\n\nBefore writing code, let's agree on terms. Internationalization (i18n) is preparing the code for translation: extracting strings, working with number, date, and currency formats. Localization (l10n) is the actual process of translation and adaptation for a specific region.
\n\nA simple analogy: internationalization is building a house with empty rooms where you can place any furniture. Localization is arranging the furniture according to the tastes of a specific resident.
\n\nFor a PHP developer, this means: do not hardcode strings! Never. Even if the site is currently only in Russian. Every phrase should be wrapped in a translation function.
\n\nTechnology Stack for i18n in PHP
\n\nThe PHP ecosystem has several powerful tools. We will look at two main approaches: the classic gettext and the modern ICU MessageFormat via the intl extension.
1. Gettext — The Grandfather of Multilingualism
\n\nGettext in PHP is the de facto standard for many CMS (WordPress, Drupal). It uses .po and .mo files to store translations. Pros: simplicity, support for plural forms, integration with Poedit.
2. ICU MessageFormat — For Complex Logic
\n\nThe PHP intl extension (Internationalization) provides the MessageFormatter and NumberFormatter classes. This is the choice for projects that require complex templates with case, gender, and number selection.
3. Wrapper Libraries
\n- \n
- symfony/translation — a Symfony component, works with YAML/XLIFF as well as gettext. \n
- laravel-localization — for Laravel (mcamara/laravel-localization package). \n
- zend-i18n — for Laminas (formerly Zend Framework). \n
Practice: Setting Up gettext in PHP from Scratch
\n\nLet's say we have a simple script index.php that outputs a greeting. Let's make it multilingual.
Step 1. Prepare the Code
\n\nWrap all strings in the _() function (an alias for gettext()):
<?php\n// index.php\necho _("Welcome to our site!");\n\n$items = 5;\n// For plural forms, use ngettext\necho ngettext("%d item", "%d items", $items);\n?>\n\nStep 2. Generate the .pot File
\n\nInstall gettext on the server or use xgettext:
xgettext -o locales/messages.pot index.php --from-code=UTF-8\n\nStep 3. Create the Translation
\n\nCopy messages.pot to locales/ru_RU/LC_MESSAGES/messages.po and edit it:
msgid "Welcome to our site!"\nmsgstr "Добро пожаловать на наш сайт!"\n\nmsgid "%d item"\nmsgid_plural "%d items"\nmsgstr[0] "%d товар"\nmsgstr[1] "%d товаров"\n\n