i18n Logo

Zur deutschen Version wechseln…

Getting started…

ToC

Intro

The i18n package is a punch of classes for internationalization. It gives you the possibility to maintain multilanguage webpages more easily. The translation strings are stored in flat text files, special Gettext files which are basically precompiled translation files or in a MySQL database. And it works independently from PHP’s setlocale function.

First, to avoid problems, make sure that for all pages that use the package you start and end your scripts with:

ob_start();
session_start();

and

ob_end_flush();

Getting the language

Let’s start with one of the base classes, the language class. It’s propose is to determinate the preferred locale of the user, by looking at the HTTP_ACCEPT_LANGUAGE header an the users IP address.

First create a new object:

include('class.Language.inc.php');
$lg = new Language();

Let’s say the user has set his browser to “German – Austria” (de-at). Now get your information with those methods:

$lg->getLocale() outputs de_at (the hyphen is replaced with an underscore)
$lg->getLang() outputs de
$lg->getCountry() outputs at

Of course some users have set more than one locale. To get those informations use:

$lg->getUserRawArray() to get an array with all locales accepted by the user
$lg->getUserLangArray() for all Languages
$lg->getUserCountryArray() for all countries

But what if the user has set no specific country code or no locale information could be found at all? That’s where the default values kick in. Stuff like this is saved in the i18n_settings.ini file:

[Language]
default_locale = "en"
default_language = "en"
default_country = "us"

To retrieve those settings in your script use

$lg->getDefaultLocale()
$lg->getDefaultLanguage()
$lg->getDefaultCountry()

But there are much more settings for the package you can change in the i18n_settings.ini file:

You can also force a specific locale to overrule all other locale sources when you create a language object:

$lg_gb = new Language('en_gb');

Translating strings

Now that was the language class. Now we want to use the information from it for translations. For this use the Translation class which extends the language class.

include('class.Translator.inc.php');
$translation = new Translator('','frontpage, everypage');

Now this class takes two arguments. The first on is the same like in the language class to overrule the locale settings and force a specific locale. The second argument is needed if you work with flat translation files or Gettext files.
The second argument is a comma-separated list of one ore more filenames without the extension (you set this in the i18n_settings.ini file). This files have to be placed in the language subdirectory under the “locale” directory if you use flat text files or in the “LC_MESSAGES“ subdirectory of the language subdirectory if you work with Gettext files.
The language directory itself has to be named properly like a locale or language according to the ISO standard. (examples: “it”, “de_at”, “en_gb”,…).
If you use MySQL then the second argument is the namespace column in the translation table. Whenever you create a new translation object it preselects all translations with that namespace into an array to prevent firing a SELECT statement at the database for every translation.
You can also create some alias languages. If you have a folder “en” with all your translations and you want to add a new folder “en_gb” which uses the same translations, just place a file called “redirect” (without an extension) there which contains the actual language where the translations can be found (in this case “en”). Remember, alias languages don't work in MySQL modus!

There are already a couple of example files in the package, so take a look at them to see how your translation files have to look like. If you want to use Gettext files, search the web for tutorials on how to create them. I’m not going into this here…

When creating a new translator object the list of preferred user locales from the language class and available translations in the locale folder are compared and the most likely translation is chosen. Also the getLocale(), getLang() and getCountry() methods are not returning the preferred user values anymore, but those of the most likely translation available.

Now that you have created a MySQL table or translation file (and placed it in the correct directory) you can start translating in your scripts using the translate() method.

echo $translation->translate('no_records_found');

“no_records_found” is the translation string to look for in the MySQL table or translation file. If you not only want to translate the string, but also html encode it at the same time use:

echo $translation->translateEncode('no_records_found');

To encode even more special characters, like “×”, “„”, “‡”… you can use the translateEncodePlus method:

echo $translation->translateEncodePlus('no_records_found');

There are also some shortcuts so you don’t have to write the long method name all the time:

echo $translation->_('no_records_found'); = translate
echo $translation->__('no_records_found'); = translateEncode
echo $translation->___('no_records_found'); = translateEncodePlus

If no translation string was found you will see an error message in your script:

ERROR TRANSLATING: »» no_records_foun ««

You can turn off error reporting in the i18n_settings.ini file. In this case only the untranslated string will be returned.

If you use Gettext files, using the translate methods can be a bit more complicated if you use more that one translation file in one script. you have to pass the name of the translation file as a second value if the string is not located in the first language file from the file list you have used when creating the object:

echo $translation->translate('another_string', 'translationfile2');
echo $translation->translateEncode('another_string', 'translationfile2');
echo $translation->_('another_string', 'translationfile2');
echo $translation->__('another_string', 'translationfile2');

You can also change the language of the translator object during use if you like:

$translation2 = new Translator('de','frontpage, everypage');
echo $translation2->_('good_morning');
$translation2->changeLocale('en');
echo $translation2->_('good_morning');

first it would output Guten Morgen, the second time Good Morning.

Other methods in this class:

$translation->getLanguageFilesArray() returns an array with all available valid languages found under the “locale” folder.
$translation->getModus() returns “gettext”, “inc” or “mysql” (whatever you use)
$translation->getRealLocale() returns the real locale if the chosen one is just an alias.

Let the user decide

Now we know how to make our scripts multilingual, but how can the user choose a different language?
For this you need the ChooseLanguage class which extends the Translator class by a couple of methods:

include('class.ChooseLanguage.inc.php');
$translation3 = new ChooseLanguage('','frontpage, everypage');
echo $translation3->returnDropdownLang();

This outputs a <select> form element with all available languages. You have to add the other form elements in the scripts yourself. Just make sure that the form URL is points to a script which calls the ChooseLanguage class again. If you don’t want to use a dropdown menu you can make the form elements yourself. Just be sure that they are named “locale” and have the locale as the value. Example:

<input type="hidden" name="locale" value="en">

Other functions in the ChooseLanguage class:

$translation3->countStrings(); returns the number of strings found in the selected translations files or MySQL table
$translation3->countLanguages(); returns the number of languages available

Other language based classes

Based on these language and translation classes there are a couple of other classes to format numbers, dates, values… depending on the chosen locale:

Formatting dates

include('class.FormatDate.inc.php');
$fd = new FormatDate();
$timestamp = $fd->ISOdatetimeToUnixtimestamp('2003-12-24 13:45:00');
echo $fd->longDateTime($timestamp);

This would format the date '2003-12-24 13:45:00'. If you have chosen the locale “de” it would output:

Mittwoch, 24. Dezember 2003 – 13.45 Uhr

if the locale is “it” it would output

Mercoledì, 24 dicembre 2003 – 13.45

The methods shortDate(), shortTime(), shortDateTime(), middleDate(), middleTime(), middleDateTime(), longDate(), longTime(), longDateTime() all take a timestamp as a value and output the according strings.

To give the user more options on how dates/times should be displayed you can use the ChooseFormatDate class to output a dropdown <select> box just like the ChooseLanguage class does to give the user the possibility to select normal date format, ISO date format or swatch date format.

Formatting numbers

Numbers are also written differently depending on the language. In German the English written number 123456.789 would be written as 123 456,789

include('class.FormatNumber.inc.php');
$fn = new FormatNumber();
echo $fn->number(123456.789, 2);

This method formats a number. The second argument is the number of digits that should be displayed. The percent() methods works the same way.
When formatting currencies you have more arguments to pass to the method:

$fn->currency(99.90, 'full', 'gb', FALSE);

This would output 99,90 Pfund in German and 99.90 Pound in English. The first argument is the number, the second tell how the currency should be marked: full means that the full currency name is displayed (in this example “Pound”), short would output the short currency name (here “GBP”) and symbol would output the currency symbol (here “£”).
The thirds argument is the country of the currency (here “Great Britain”) and the last argument tells whether the major currency should be forced to display or not. Example:

$fn->currency(0.99, 'full', 'de', FALSE); outputs 99 Cent
$fn->currency(0.99, 'full', 'de', TRUE); outputs 0.99 Euro

Settings for this class can be changed in thel10n.ini file.

Formatting measure values

This class contains only the US measure system and the metric system.

include('class.FormatMeasure.inc.php');
$fn = new FormatMeasure ('si','uscs');

This created a new object where the input values are in the metric system and the output values are US measure system. If no second argument for the output is given, the class chooses it by looking at the locale set by the translator class.

There available Methods are linear(), surface(), capacity(), cooking(), liquid() and temperature(). Most of them in this class take three arguments. The first one is the number, the second and third are the input and output format. For example for the linear() method these formats would be:

0: mm|in,
1: cm|ft,
2: dm|yd,
3: m|fur,
4: km|mi,

Please see the class documentation for all the formats for the different methods. Settings for this class can be changed in thel10n.ini file.

Formatting Strings

Two language based methods are available. The wordFilter() method replaces “bad” words, like curses for example, with “*” signs. A commaseperated list can be defined in the l10n.ini file.

The method filterSpecialWords() searches for special words in a string and formats them with the HTML tags abbr, acronym or dfn. The words and related description strings can be changed in the words.ini file, which is placed in every language subdirectory.

Notes

Other Stuff

Shared Memory Functions

With the shared memory functions enabled, ini files are only read once on first access an are written to the servers memory. This way the files don’t have to be read with every page access. By default this is disabled. To enable it, you have to set the $use_shared_mem variable in the I18N class to TRUE.
Once in the shared memory, changes to one of the ini file won’t have any effect until you reset the shared memory blocks. You can do this by setting the $flush_sm variable to TRUE (also in the I18N class).

Using a DB other than MySQL

If you would like to use another database instead of MySQL you have to rewrite the code manually. I don’t plan to use some sort of DB abstract layer to keep the classes independent from any framework.
I also haven’t worked with PEAR yet, so I don’t know how hard it would be to implement they DB class. But you could try and let me know :-)

All the database code can be found in the Translator and ChooseLanguage class. The connection is created in the setConnection() method. SQL Queries are made in the methods checkLocale(), languagesFilesList(), readLanguageFile(), translate(), getLastUpdateDate() and countStrings().

Caching pages

If you use some sort of caching mechanism then you can use the getLastUpdateDate() method from the Translator class (available since version 1.54). It returns the most current modification date of the selected translation files. If you use MySQL then it returns the most current modification date of all the strings of the selected namespaces. You can compare the returned UNIX timestamp with the dates of your cached files to see if they need to be updated.

Smarty

The classes haven’t been tested with smarty yet, but if you do it this way smarty should cache each language version separately.

php file:

$smarty->assign('LANG_fist_name', $translator->__('first_name'));
$smarty->assign('LANG_last_name', $translator->__('last_name'));

$smarty->display('template_filename.tpl', $translator->getLocale());

template_filename.tpl:

<html><body><p>{$LANG_first_name} {$LANG_last_name}</p></body></html>


Author: Flaimo
Date: 2003-06-13
URLs:
Project homepage
Example script


Rate Our Script @ HotScripts.com

Rate Our Script @ PHP-Resource.de

Rate Our Script @ phparchiv.de (10 is best)


Valid XHTML 1.0! Valid CSS!