In this tutorial, we are going to discuss how to implement PHP translation, localization, and internationalization. The PHP language is quite a stable and popular choice for web application development. Despite the existence of many other programming languages, such as Python, Ruby, and Node.js, a large number of applications are still built using flat PHP without any framework. Therefore, learning PHP localization techniques is undoubtedly useful for web developers.
First off, let’s briefly discuss what localization (l10n) is and why it is such a common task for developers. In software engineering, localization is the process of adapting the content of a system to a particular locale so the target audience in that locale can understand it. It is not just about changing content from one language to another. You have to take date formats, number formats, currencies, text direction, and proper pluralization into account as well. For more information, here is our ultimate guide to software localization.
As an example, you may know that pluralization is comparatively easy in English versus other languages. In some languages, such as Slavic ones, there are two plural forms for a given singular one. Likewise, the date, time, and number formatting in some languages can differ significantly from those used in English.
So, now you understand that we have to consider multiple things in PHP translation besides simple language switching.
Let’s move on to discover how we can implement PHP l10n – this tutorial will cover the following five ways to achieve this:
- PHP arrays
- Gettext
- Define functions in PHP
- Setlocale
- Frameworks
Huge thanks to my colleague Ondřej Frei for all the assistance and technical insights.
PHP arrays
This is the primitive method that has been used to implement PHP translation for a long time. For this, you need to create an associative array for each language. These arrays are used to map the source language or keys to localized content. When you need to view localized content, you simply have to select the array and key of the relevant language.
Plain text translation with PHP arrays
Let’s take a look at an example. In it, we will implement localization for three languages — English, Arabic, and Spanish — but of course you can choose any other locales. Please note that we are using English as the base language as it’s the primary language of most systems and applications.
Here we will use a separate PHP file for each array to increase readability. However, if you like, you can use the same file to store all the arrays.
Let’s see how we can display the sentence “Welcome to the tutorial” with PHP arrays in the languages mentioned above. First, you’ll need to create a project directory with an index.php
file inside, along with the locale
folder. Here’s the file structure:
index.php
locale/
arab.php
en.php
es.php
arab
, en
, and es
are the locale codes for Arabic, English, and Spanish, respectively.
In the index.php
file, create a simple form that includes a dropdown to ensure that the user can easily select languages:
<form name="langSelect" action="" method="get"> <select name="langID" id="langID"> <option>Select Language</option> <option value="arab">Arabic</option> <option value="en">English</option> <option value="es">Spanish</option> </select> <br><br> <button type="submit">Submit</button> </form>
Now we’ll create our translation files for the three languages inside the locale folder.
Here’s the en.php
file:
<?php declare(strict_types=1); return [ "welcome" => "Welcome to the tutorial", ];
The arab.php
:
<?php declare(strict_types=1); return [ "welcome" => "مرحبًا بكم في البرنامج التعليمي", ];
And the es.php
:
<?php declare(strict_types=1); return [ "welcome" => "Bienvenida al tutorial", ];
Next, we’ll go back to our index.php
file and display the “Welcome to the tutorial” line. The content should be translated according to the language the user selects from the dropdown. To do this, let’s add the following PHP code snippet to the index.php
file:
<?php declare(strict_types=1); $lang = $_GET['langID'] ?? 'en'; $langArray = require 'locale/' . $lang . '.php'; echo $langArray['welcome'];
So, here we’re fetching the selected language code and importing that particular translation file. Then finally, we’re reading the contents of the welcome
key from the imported file.
Now let’s check out how to perform pluralization with PHP arrays.
Pluralization with PHP arrays
Okay – assume you need to display sentences such as, “I bought 1 book”, “I bought 4 books”, “I bought 10 books”, etc. You can see that the word for the number of items is either “book” or “books”, depending on how many books we are adding. To achieve this change, we’ll create a text box to input the number of books as below:
<input type="text" id="numBooks" name="numBooks" placeholder="Add number of books.">
We can successfully perform pluralization by simply adding the following part to the index.php
file:
<?php // ... $numberOfBooks = (int)($_GET['numBooks'] ?? 1); if ($numberOfBooks === 1) { $sentence = sprintf($langArray['i_bought_single_book'], $numberOfBooks); } elseif ($numberOfBooks > 1) { $sentence = sprintf($langArray['i_bought_many_books'], $numberOfBooks); } else { $sentence = $langArray['i_bought_no_books']; } echo '<br/>'; echo $sentence;
If the number of books equals 1, it refers to the single_book
key in the langArray
array, and if not, it refers to the many_books
key.
Now let’s add these keys to the translation files.
en.php
:
return [ 'i_bought_single_book' => 'I bought a book.', 'i_bought_many_books' => 'Books bought: %d.', 'i_bought_no_books' => 'I didn\'t buy any books.', ];
es.php
:
return [ 'i_bought_single_book' => 'Compré un libro.', 'i_bought_many_books' => 'Libros comprados: %d.', 'i_bought_no_books' => 'No compré ningún libro.', ];
arab.php
:
return [ 'i_bought_single_book' => 'شتريت كتابًا', 'i_bought_many_books' => '%d شترى الكتب', 'i_bought_no_books' => 'م أشتري أي كتب', ];
Great job!
PHP translation with gettext
As you’ve seen in the first section, PHP translation is quite straightforward with the help of arrays. But as the system expands, it can be hard to maintain translation arrays.
The GNU gettext system is a widely used localization method in many programming languages such as PHP, Python, C++, and so on. In this section, we’ll look at how we can use the gettext
technique for PHP translation. With gettext
, it is very easy to sync locale files with changes to the codebase.
Now, let’s make another small application to illustrate PHP localization with gettext
. Before using gettext
, you need to follow a few steps, as mentioned in the manual, to set it up.
Firstly, create a separate folder for gettext
localization implementation to avoid any possible confusion. I have named the folder PHP-Gettext
here. Inside it, create a file named index.php
and a locale
folder. This folder should contain nested directories named after the corresponding locale codes. These folders should, in turn, contain messages.po
and messages.mo
files.
Here’s the folder structure:
index.php
locale/
ar_EG/LC_MESSAGES/
messages.mo
messages.po
en_US/LC_MESSAGES/
messages.mo
messages.po
es_CO/LC_MESSAGES/
messages.mo
messages.po
A .po
is a portable object file. These files are saved in a human-readable format. In the context of gettext
, all translations are stored in .po
files.
A .mo
is a machine object file that is generated with the help of a special tool. These files are not meant to be edited directly. When the actual translation process takes place, gettext reads data from these files.
Translate plain text
Now let’s make an HTML form that includes a dropdown to pick a language from:
<div class="col-md-6"> <form name="langSelect" action="" method="get"> <select name="langID" id="langID"> <option>Select Language</option> <option value="ar_EG">Arabic</option> <option value="en_US">English</option> <option value="es_CO">Spanish</option> </select> <br><br> <button type="submit">Submit</button> </form> </div>
Note that the value
attributes are set to the corresponding locale codes.
Next, we’ll need to create a PHP code in order to change the content between locales:
<?php declare(strict_types=1); $lang = $_GET['langID'] ?? 'en_US'; putenv("LANG=" . $lang); setlocale(LC_ALL, $lang); $domain = "messages"; bindtextdomain($domain, "locale"); textdomain($domain); echo gettext("welcome");
This code snippet contains some important things to discuss. First, we’re fetching the chosen locale or reverting to the default option.
The putenv()
the function sets the environment variable of the LANG
variable.
Then the setlocale()
method is employed to set the app locale.
Next, we use the bindtextdomain()
function and pass the folder and domain names as parameters. This function sets the path for a given domain. The textdomain()
function is used to set the default domain. Finally, we’ve employed the gettext()
function to look up a message in the given domain.
Let’s now discover how to create the translation files – it’s quite straightforward. All you have to do is add the msgid
and msgstr
key and value pairs to the file as shown below.
First, open the en_US/LC_MESSAGES/messages.po
file. Once again, make sure to edit the PO, not the MO file!
msgid "welcome" msgstr "Welcome to the tutorial" msgid "i_bought" msgstr "I bought " msgid "her_birthday" msgstr "Her birthday" msgid "now_time" msgstr "Current time"
ar_EG/LC_MESSAGES/messages.po
:
msgid "welcome" msgstr "مرحبًا بكم في البرنامج التعليمي" msgid "i_bought" msgstr " اشتريت" msgid "her_birthday" msgstr "عيد ميلادها" msgid "now_time" msgstr "الوقت الحالي"
es_CO/LC_MESSAGES/messages.po
:
msgid "welcome" msgstr "Bienvenida al tutorial" msgid "i_bought" msgstr " yo compré" msgid "her_birthday" msgstr "Su cumpleaño" msgid "now_time" msgstr "hora actual "
Now we should create the MO files, which are effectively the compiled versions of the PO. To do this, run the following command:
msgfmt messages.po -o messages.mo
That’s it!
Pluralization with gettext
Next, we’ll discuss how to perform pluralization with gettext
. Let’s start by creating our translation file contents by adding msgid
and msgid_plural
to the translation files as follows:
en_US/LC_MESSAGES/messages.po
msgid "single_book" msgid_plural "many_books" msgstr[0] "book" msgstr[1] "books"
ar_EG/LC_MESSAGES/messages.po
:
msgid "single_book" msgid_plural "many_books" msgstr[0] "كتاب" msgstr[1] "كتب"
es_CO/LC_MESSAGES/messages.po
:
msgid "single_book" msgid_plural "many_books" msgstr[0] "libro" msgstr[1] "libros"
And so, we are done with our translation files.
Open the index.php
file and create a text box to get the user’s input:
<input type="text" id="numBooks" name="numBooks" placeholder="Add number of books.">
Now add:
<?php if (!isset($_GET['numBooks'])) { $numberOfBooks = 1; } else { $numberOfBooks = $_GET['numBooks']; } echo $numberOfBooks . " " . ngettext('single_book', 'many_books', $numberOfBooks);
Nice job!
The define function in PHP
So far, we have talked about two different methods of PHP translation: PHP arrays and gettext. In this section, we are going to learn how to perform PHP localization with the define function. This is a simple and straightforward method — let’s find out how we can implement it.
First, create a new project with the following structure:
-
index.php
-
lo
-
arab.php
-
en.php
-
es.php
-
Next, add translations to the en.php
file:
<?php declare(strict_types=1); define("welcome", "Welcome to the tutorial");
The es.php
:
<?php declare(strict_types=1); define("welcome", "Bienvenida al tutorial");
And the arab.php
:
<?php declare(strict_types=1); define("welcome","مرحبًا بكم في البرنامج التعليمي");
Return to the index.php
file and add the following code snippet:
<?php declare(strict_types=1); $lang = $_GET['langID'] ?? 'en'; include('locale/' . $lang . '.php'); echo welcome;
Please note that use of the HTML code is similar to that mentioned in previous sections. This is a very clear method for PHP translation as we’re simply including the translation file based on the user’s language selection.
The PHP setlocale() function
setlocale
is an inbuilt function that supports PHP l10n. This assigns your system a geographical location and performs certain functions, such as content translation and currency and date formatting, based on the locale of that place. The syntax of this method is:
setlocale($category ,$locale)
As you can see, the method accepts two parameters.
The category
is a constant that specifies the category of the functions. There are multiple categories that you can use depending on the functionality you need. Let’s discuss a few of those categories.
-
LC_ALL
– for all of the below
-
LC_COLLATE
– for string comparison
-
LC_CTYPE
– for character classification and conversion
-
LC_MONETARY
– for monetary/currency formatting
-
LC_NUMERIC
– for numeric formatting
-
LC_TIME
– for date and time formatting with strftime()
-
LC_MESSAGES
– for system responses
Let’s discuss how we can use the setlocale
method in PHP for localization. To get started, create a new project with an index.php
file inside.
Date formats
As we know, date formats can change depending on locale. So, it is important to consider the locale when displaying date formats.
You can check the locales you currently have installed by using the following command:
locale -a
We need to change the locale according to the selected language:
$locale = match ($lang) { 'en' => 'en_US', 'es' => 'es_CO', 'arab' => 'ar_EG', default => 'en_US', }; setlocale(LC_ALL, $locale);
Please note that if the locale you need to use is not already installed on the machine, the setlocale
method returns false. You can use this return value to identify whether or not the given locale is installed.
Once we have set the required locale, we need to display the time and date. To do this, we will use the strftime
method. In this scenario, we will display two dates:
<?php declare(strict_types=1); setlocale(LC_ALL, "en_US"); echo "The time in US is"; echo(strftime("%m/ %d/ %Y , %X ")); echo ("<br/>"); setlocale(LC_ALL, "zh_CN"); echo "The time in China is "; echo(strftime("%m/ %d/ %Y , %X "));
Here we can use any date format by providing special placeholders. For example:
-
%m
— month (01 to 12).
-
%d
— day of the month (01 to 31).
-
%Y
— year including the century.
-
%X
— preferred time representation without the date.
So that’s how we can change date formats with setlocale
i10n in PHP.
Currency formatting
Currency formatting is an important feature that PHP offers. Let’s go through a simple code snippet that uses currency formatting based on locale in PHP.
<?php declare(strict_types=1); setlocale(LC_MONETARY, "en_US"); $loc = localeconv(); print_r($loc);
Here we have used the LC_MONETARY
category and the localeconv
function. This function is used to obtain numeric formatting information.
Now let’s see how to use the currency formatter:
$number = 12000; setlocale(LC_MONETARY, 'en_US'); echo money_format('%i', $number)
The output of this code is USD 1,200.00
.
Likewise, you can change the locale of your code and format currency as required. Please note that the money_format
method is only defined if the system has strfmon
capabilities. Therefore, you can’t use money_format
in Windows as it doesn’t have strfmon
capabilities.
Number formatting
Next, let’s discover how to perform number formatting in PHP with setlocale
. Take a look at the following code:
$num = 1314.15; setlocale(LC_ALL, 'en_US'); echo number_format($num, 1); echo "<br/>"; echo number_format($num, 2);
The number_format
function takes two parameters. The first one is the number
that should be displayed, and the other parameter is the number of digits after a decimal point. This is the output of the above code: 1,314.2
and 1,314.15
.
Frameworks
Nowadays, most developers use frameworks in order to make development quick and easy. So, let’s briefly discuss the localization support that is provided by frameworks.
Actually, almost all major PHP frameworks support PHP translation. Moreover, some even support features such as currency and date formatting. We are not going to have an in-depth discussion on how to implement PHP localization with frameworks here. So, we’ll briefly look at the methods these frameworks use for localization.
The CodeIgniter and Kohana frameworks use PHP arrays for localization. The CakePHP framework uses the gettext method for translation. Both PHP arrays and INI techniques are used in the F3 for localization. The Laminas Project uses several methods for translation such as PHP arrays, CSV, TBX/TMX, gettext, Qt, XLIFF, and INI. These methods as introduced by the frameworks can be easily used for l10n in PHP.
Note, however, that you may only load internationalization modules from your favorite framework and use it in your pure PHP application. So, ultimately the choice is yours.
What is the best method for PHP localization?
In this tutorial, we discussed multiple localization techniques that we can use in PHP. So, let’s see which one of them is the best.
There are so many open questions in Stack Overflow regarding the best technique for PHP localization. The majority of PHP developers are of the opinion that gettext is the best method of localization. In the gettext
method, you separate the translation text from the code. So, if you want to change the translation text, all you have to do is change the .po
file without touching the code.
Gettext is also very commonly used and has built-in support in PHP. Even WordPress uses the GNU gettext method to provide localized translations. However, it will not be easy to internationalize a whole application using gettext alone. You will need the other methods mentioned here and intl
as well. In any case, developing large applications without using a PHP framework is very rare these days. So, when you select a framework, make sure to consider the localization methods it uses.
Simplifying the translation process with Lokalise
When your app is localized, you might be wondering: What if I would like to invite professional translators to work on the texts? Should I send them the translation files directly? Well, of course it is possible but perhaps not too convenient. Moreover, if you have many people working on the same project it will quickly turn to chaos. On top of that, you might need to introduce quality assurance or take advantage of machine translation.
Is there a solution that can do the heavy lifting for you? Meet Lokalise, a translation management system for agile teams that is here to save the day! It offers all the basic translation features with the ability to work in teams, and advanced tools and integrations with dozens of third-party services including Amazon S3, GitHub, Figma, and many others.
To get started, navigate to app.lokalise.com/signup and grab your free 14-day trial.
Enter your details and follow the wizard to create a new project. A project is basically a collection of your keys and translations along with contributing users, settings, and other useful things.
Once you’ve navigated to the newly created project, click More > Settings and take note of the project ID.
Next, click on your avatar in the bottom left corner and choose Profile settings. Switch to the API tokens tab and generate a new read-write token.
Next, you can use the Lokalise CLI to easily upload and download translation files.
For example, to upload your translation file, it is:
lokalise2 file upload --lang-iso en --file "PATH_TO_PROJECT\PATH_TO_LOCALE\messages.po" --project-id PROJECT_ID --token YOUR_TOKEN
To download translation file back to the project, it is:
lokalise2 file download --unzip-to PROJECT_PATH\PATH_TO_LOCALE --format po --token YOUR_TOKEN --project-id PROJECT_ID
That’s it!
Summary
In this tutorial, we discussed several methods that we can use for PHP translation such as, PHP arrays, PHP define functions, and gettext and setlocale
methods. Also, we’ve seen that the majority of PHP frameworks support localization.
The PHP array and define function methods are quite simple and easy to work with. However, as the system grows it gets harder to maintain translation files. Because of that, those two methods are more suitable for small applications. Then we use the gettext method for PHP translation. But it does have the drawbacks of not supporting number, date, or currency formatting. Finally, we discussed the setlocale
function which is quite an easy method for performing localization.
After all this, you might be interested in using PHP frameworks. In that case, be sure to read our Laravel guide and the Symfony tutorial.