Localization with PHP

Place to place any code snippets, completed games, or even uncompleted games for IR users to use.
Post Reply
User avatar
Hamilton
Posts: 114
Joined: Tue Sep 11, 2012 7:11 am

Localization with PHP

Post by Hamilton »

This is a set of codes I use to localize. There are a number of ways to do this, but I prefer this way as it involves less programming and easier to update another language. This is based on "Localizing PHP Applications “The Right Way”" by Abdullah Abouzekry.
Link: http://phpmaster.com/localizing-php-applications-1/

What this code does is to allow the program to display in the default language (English for this one), while additional languages will use files generated from PoEdit. This allows for the original program files to remain as is, as well as not having the need to recreate a new site for another language. The code will also provide language changes for images and javascript variables.

Requirements:
- PHP 5.X
- GETTEXT Extension enabled
- POEDIT http://www.poedit.net

NOTE:
WAMP appears to not work correctly with this and I have given up on trying to figure out how to make it work.
XAMPP works fine.

To Enable GETTEXT (or to verify):
Open and make edit if needed in the php.ini file:

Code: Select all

# for Windows users
extension=php_gettext.dll
# for *nix users
extension=gettext.so
The Folder Structure would be as follows (English is the default, and thus no English folder is present. I will include Polish as an example):

Code: Select all

HOME
- local
-- pl_PL
--- LC_MESSAGES
Each language to be included will be located in the "local" folder.

Explanation:
HOME - This is the root folder of the game, or where the Index.php file would be located.
local - This is the folder to contain all of the language files. This name can be anything, but may want to keep it simple. I prefer to use as "loc".
pl_PL - Each language will have its own folder, which must be named with two lowercase letters, underscore, and two uppercase letters of the abbreviation of the language. Example: English is abbreviated to "EN", Polish is "PL", Spanish is "ES", etc. Thus if to use Spanish, the folder must be "es_ES."
LC_MESSAGES - This folder will contain two language files, listed as messages.mo and messages.po. The folder must be set to this name of "LC_MESSAGES"

Example:
If I was to include French as another language, the folder structure would then be as follows:

Code: Select all

HOME
- local
-- pl_PL
--- LC_MESSAGES
-- fr_FR
--- LC_MESSAGES

PHP Code used in the Index.php file:
1) The first command I use (in this set of codes) is to check if there was a GET (such as clicking on a language preference button), and if not, then use the browser for the default language. The prefer method is to use a database to check for an existing user account and if so, then the preferred language; if no account exists, then check for a GET or the browser setting.

Code: Select all

// Check if there is a GET, otherwise detect from browser
if (isset($_GET["lang"])) {
    $lang = $_GET["lang"];
} else {
    // Autodetect by grabbing the first two letters from the Browser's Language Setting
	$lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
}
This code first checks if there was a GET in the URL, such as "lang=pl"; if there was none, then there is a check of the Browser Language Setting from the Browser and takes the two first letters and stores into the $lang variable. The first two letters are the abbreviation of the language.
(In Firefox, the language can be changed by going to Tools > Options > Content)
For this example, Polish is used; $lang will be "pl".

2) The next step is to form the name of the language folder, which will be used by the GetText extension and other useful items:

Code: Select all

// Form the lower case _ upper case path for GetText to work right
$language = strtoupper($lang);
$language = $lang."_".$language;
// I18N support information here
putenv("LANG=" . $language); 
setlocale(LC_ALL, $language);
// Set the text domain as "messages"
$domain = "messages";
bindtextdomain($domain, "local"); 
bind_textdomain_codeset($domain, 'UTF-8');
textdomain($domain);
Comments (starts with //) I am ignoring for below descriptions.
1 - This statement takes the uppercase value of "pl", thus $Language = "PL"
2 - $Language is further modified to now have the folder name of, "pl_PL"
3 & 4 - These are required for GetText to work properly.
5 - $domain is the name for the language files, as named "messages." Supposedly these can be renamed to something else, but when I have done so, errors have occurred. My recommendation is to keep the name as is, "messages"
6 - This statement of bindtextdomain is of the Localization folder; which in this example is "local". You can use a different name, such as what I use "loc" or even "localization."
7 - This statement is the character set, which is UTF-8. I recommend to keep this the same as you can use the characters of other languages.
8 - This is a required statement for GetText.

PHP and HTML: Tricky Part
This is not really tricky to program, just tricky to explain this correctly.
When using text in HTML, the code is normally used in tags:

Code: Select all

<h3>Hello</h3>
But since we are using PHP for Localization, the text would then be done with PHP echoing within the HTML H3 tags:

Code: Select all

<h3><?php echo "Hello"; ?></h3>
HOWEVER...
In order for GetText to work, a leading underscore and parentheses are required around the quoted text.
EX: _("Hello")
Thus the PHP statement within the HTML H3 Tags would be:

Code: Select all

<h3><?php echo _("Hello"); ?></h3>
By using the _( ), this informs that GetText will be used to convert the text into another language.

What happens is that the characters of "Hello" are searched through the selected language file (IE: Polish). If "Hello" is not found, then the text remains as is, thus no English language file is needed. Otherwise "Hello" will be replaced by "Halo".

Images Files
Now let's say you have an image file with text in it (to have special effects or due to some legal situations or something). In this case there would need to be different image files made to a given language. Or a spritesheet could be done I image.

If to use HTML, the PHP code would be used as above, but to add in the $lang variable.
The image tags normally done for HTML:

Code: Select all

<img src="/images/en/header_en.jpg" height="35" width="760"> 
The code for localizing with use of the PHP variable $lang:

Code: Select all

<img src="/images/<?php echo $lang ?>/header_<?php echo $lang ?>.jpg" height="35" width="760"> 
Within the PHP 'tags' (for a lack of a better term), the variable $lang is used for both the folder path and the name of the file. Thus every image file which is used for a different language will have its own folder and file. With this example, I have two headers, one for English and one for Polish.
header_en.jpg
header_pl.jpg
For javascript (say with HTML5), this is done along the same lines; using PHP and the $lang variable:

Code: Select all

var headerImg 		= new Image();
headerImg.src 		= "/images/<?php echo $lang ?>/header_<?php echo $lang ?>.jpg";
And that is all there is for programming. 8-)

Note, the text used for messages to the user are case and character sensitive. Thus _("HELLO") will not display "HALO" or even "Halo" (if Polish is the selected language). Instead only, "HELLO" will be shown. Thus there would need to be an entry for "HELLO" to show "HALO". This will be explained of how to use PoEdit.

USING POEDIT
Using PoEdit is fairly easy, or least how it will be used for this.
1) First step by opening PoEdit (assuming it has already been installed)
2) Go To File > New catalog...
3) A Catalog Properties Window will pop-up, as shown below:
poedit2.png
4) The fields for the Project Name, Team and Email are not important. The Language field is, along with setting the Charset and Source code charset to "UTF-8 (recommended)". If anything, type in the language and set the Charsets.
5) Click on OK
6) A save dialog window will pop-up, in which the filename is "default.po". Rename the file to "messages.po".
7) Navigate and save the file into the specific language folder of your game's localization folder.
In this example, this language file is Polish, and thus would be saved at "HOMEFOLDER/local/pl_PL/LC_MESSAGES
8) An error may be encountered after saving, if so, ignore it.
9) Close PoEdit.
10) Using your favorite language or text editor, open up the newly created language file, "messages.po"; ignore the messages.mo for now.
11) Your messages.po file should look like similar to this:

Code: Select all

msgid ""
msgstr ""
"Project-Id-Version: Our Game\n"
"POT-Creation-Date: 2013-01-29 21:00+0430\n"
"PO-Revision-Date: 2013-01-29 21:01+0430\n"
"Last-Translator: \n"
"Language-Team: Me and You <team@email.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.4\n"
"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
"X-Poedit-Basepath: .\n"
"Language: Polish\n"
"X-Poedit-SourceCharset: UTF-8\n"
12) Ok... so here is the deal of making another language to work... so pay attention.

13) To add in a word or phrase that is to be translated, there will need to be two lines added after that above set of codes (think of those as a header for this file). The two lines, begin with these two words and paired quotes afterwards:

Code: Select all

msgid ""
msgstr ""
As you may tell; "msgid" is for Message Identifier, and "msgstr" is for Message String.

Then just add in your default language (example: English) into the quotes of the msgid; and the translated text into the msgstr.
Example"

Code: Select all

msgid "Hello"
msgstr "Halo"

msgid "HELLO!"
msgstr "HALO!"
The above is of two sets of translated text. The first one is to translate the English word of "Hello" with the Polish word "Halo". The second set, say for example, a character is shouting Hello, and thus there would need to be another translated text. Also note the added Exclamation Mark (!). If somewhere I accidentally type in _("HELLO"), then only HELLO. will appear and not the expected HALO!. Thus this is both case and character sensitive.

Another example of a translated text:

Code: Select all

msgid "Force of Arms"
msgstr "Sila Broni"
14) Continue adding in additional translated text, using the msgid"" and msgstr"" statements until you are done.
NOTE: You can make a template file which all of the msgid's have your native language text; but all of the msgstr's have empty quotes (""). Then all you would need to do is change the language field of the file and enter in all of the translated texts into their respective places.

15) When done, save the file.
16) Now, you will need to open PoEdit once again.
17) Click on the Open button, or go to File > Open catalog...
18) Navigate to the messages.po file you just saved.
You would have noticed there is also a messages.mo. This is a compressed poedit file, which you will not need to worry about editing. But you will need to have it in the folder along with the .po file, in order for the translation to work.
19) Select the messages.po file and open it.
20) Now, click on save.
21) No errors should have been encountered. If there was, most likely there is a typo somewhere. Look for the error and correct it.
22) The save, is not really of saving the .po file, but saving (or updating) the .mo file.
23) Close PoEdit

That is all there is to PoEdit.


ADDING IN MORE LANGUAGES
This is what makes this nice, is how to add in additional languages as well as updating.
For additional languages, just create the messages.po and messages.mo files via PoEdit and the text editor; along with the localized language subfolders.
Upload the files and folders to your game's local folder; and that is it. No changing of programming code needed.
For images, create the images with adding in the two letter abbreviation into the file name and upload them to the proper folders.

For updating a language:
Open the given language messages.po file with the text editor.
Add in the statements or make corrections and save the file.
Open the messages.po file with PoEdit and then save to update the messages.mo file.
Upload the files to your game's local language folder.



Still to add:
- Example index file.
Last edited by Hamilton on Tue Jan 29, 2013 5:09 pm, edited 2 times in total.
Sign off,
Hamilton
User avatar
hallsofvallhalla
Site Admin
Posts: 12023
Joined: Wed Apr 22, 2009 11:29 pm

Re: Localization with PHP

Post by hallsofvallhalla »

wow nice
User avatar
Jackolantern
Posts: 10891
Joined: Wed Jul 01, 2009 11:00 pm

Re: Localization with PHP

Post by Jackolantern »

Very nice! This should be useful, as we have several European and South American devs making games with multiple languages! 8-)
The indelible lord of tl;dr
User avatar
Hamilton
Posts: 114
Joined: Tue Sep 11, 2012 7:11 am

Re: Localization with PHP

Post by Hamilton »

Agreed... and to help improve the to reach of users, just need that sliver of a fraction of all of the world's users. Given how globalization is occurring, localization I believe is a requirement. Even during down times of a country's economy, there are other countries doing well.

And with this set of codes, the programming side of localizing is simple and easy to add in additional languages. The daunting task would be the translation itself... But at least there is Google (And other online) translators. Heck, may have a game go viral because of something similar to the classic "All You Base, Are Belongs To Us" mistake.
Sign off,
Hamilton
User avatar
Hamilton
Posts: 114
Joined: Tue Sep 11, 2012 7:11 am

Re: Localization with PHP

Post by Hamilton »

Updated with PoEdit and almost done...
Sign off,
Hamilton
Post Reply

Return to “Code Sharing”