Zur deutschen Version wechseln…
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();
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');
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.
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
Based on these language and translation classes there are a couple of other classes to format numbers, dates, values… depending on the chosen locale:
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.
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.
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.
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.
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).
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()
.
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.
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