Why Your PHP Website Should Speak Different Languages?
\n\nImagine: you've created the perfect product — fast, convenient, solving problems. But there's one catch: your interface only speaks Russian. Potential clients from Germany, Japan, or Brazil simply close the tab, not understanding a single button. This is not just lost profit — it's losing entire markets.
\n\nInternationalization (i18n — from "internationalization", where 18 letters between i and n) and localization (l10n) are not just a "checkbox" for a startup, but a strategic tool. Many developers fear this word, thinking they'll have to rewrite all the code. In reality, well-organized translations in PHP is an elegant process that makes code cleaner and business more global.
\n\nIn this article, we'll break down three real approaches to internationalizing PHP applications: from simple arrays to the powerful intl extension and the classic gettext. You'll learn not just how to translate text, but how to correctly work with dates, numbers, and plural forms — because Arabic, Polish, or Russian have their own declension rules.
Main Approaches to i18n in PHP
\n\nBefore writing code, you need to choose an architecture. Conventionally, all methods fall into three categories. Which one to choose depends on the project's scale and performance requirements.
\n\n1. Arrays and ini-files (DIY Approach)
\n\nThe simplest way is to store translations in associative arrays or .ini files. Perfectly suited for small projects or MVPs.
// lang/ru.php\nreturn [\n 'welcome' => 'Welcome to the website!',\n 'logout' => 'Logout',\n 'cart_empty' => 'Cart is empty'\n];\n\n// lang/en.php\nreturn [\n 'welcome' => 'Welcome to the website!',\n 'logout' => 'Logout',\n 'cart_empty' => 'Cart is empty'\n];\n\nUsing it is simple: include the required file based on the user's session or cookie.
\n\nfunction __($key) {\n static $lang = null;\n if ($lang === null) {\n $locale = $_SESSION['lang'] ?? 'ru';\n $lang = include "lang/{$locale}.php";\n }\n return $lang[$key] ?? $key;\n}\n\necho __('welcome'); // Welcome to the website!\n\nPros: simplicity, requires no extensions, easily cacheable (can even be stored in Redis).
\nCons: no support for plural forms (you'll have to write a workaround), inconvenient for large projects (100500 keys in one file).
2. Gettext — The Classic
\n\nGettext is the de facto standard for internationalization in the Unix world. In PHP, it's available through the gettext extension. It's a powerful tool used in WordPress, Drupal, and Symfony.
First, create .po files (e.g., for Russian):
# ru_RU/LC_MESSAGES/messages.po\nmsgid "Hello, %s!"\nmsgstr "Hello, %s!"\n\nmsgid "You have %d new message"\nmsgid_plural "You have %d new messages"\nmsgstr[0] "You have %d new message"\nmsgstr[1] "You have %d new messages"\nmsgstr[2] "You have %d new messages"\n\nThen compile it into a .mo file (using the msgfmt tool). In PHP code, it looks like this:
putenv('LC_ALL=ru_RU.UTF-8');\nsetlocale(LC_ALL, 'ru_RU.UTF-8');\nbindtextdomain('messages', './locale');\ntextdomain('messages');\n\necho gettext('Hello, %s!', 'Alexey'); // Hello, Alexey!\n\n// Plural forms — magic:\necho ngettext('%d message', '%d messages', 3); // 3 messages\n\nBy the way, to speed up development, use Poedit — it will highlight untranslated strings.
\n\n3. Intl Extension (PHP 5.3+)
\n\nIf you need not just to translate text, but to correctly format numbers, dates, or currency according to a country's standards — use Intl. It's a wrapper over the ICU (International Components for Unicode) library.
// Number formatting