i18n Logo

Switch to english version…

Erste Schritte…

Inhalt

Intro

Das i18n Package ist eine kleine Sammlung von Klassen zur International­isierung von PHP Web-Projekten. Mit deren Hilfe ist es möglich mehr­sprachige Seiten ein­facher zu ver­walten als mit den Standard PHP-Funktionen. Die Übersetzungs­texte können ent­weder in normalen Text­dateien, speziellen vor­kompilierten Gettext Dateien oder in einer MySQL Tabelle gespeichert werden. Alles funktioniert un­abhängig von den setlocale Ein­stellungen in PHP.

Um Probleme mit erzeugten Cookies und Sessions zu ver­meiden sollte man zuerst die Aus­gabe­pufferung bei jeden Script aktivieren indem man folgende Zeilen am Beginn einfügt:

ob_start();
session_start();

und am Ende:

ob_end_flush();

Die Sprache raus­bekommen

Fangen wir mit einer der Basis­klassen, der Language Klasse, an. Auf­gabe dieser ist es die vom Benutzer be­vorzugten Ein­stellungen bezüglich Locale, Sprache und Land aus dem HTTP_ACCEPT_LANGUAGE Header zu ermitteln. Sollten hier keine brauch­baren Informationen gefunden werden, so wird versucht die IP Adresse des Benutzers auf­zulösen und so zu­mindest ein Land und eventuell eine Sprache zu er­mitteln.

Zuerst erzeugen wir ein neues Objekt:

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

Nehmen wir an der Be­nutzer ist aus Österreich und hat Deutsch als seine be­vorzugte Sprache im Browser ein­gestellt (de-at). Um an diese Informationen zu kommen können folgende Methoden ver­wendet werden:

$lg->getLocale() gibt de_at aus (Binde­striche werden durch Unter­striche ersetzt)
$lg->getLang() gibt de aus
$lg->getCountry() gibt at aus

Da der Benutzer ja auch mehrere bevorzugte Locales haben kann, können diese Informationen natürlich auch abgerufen werden:

$lg->getUserRawArray() gibt einen Array mit allen bevorzugten Locales aus
$lg->getUserLangArray() gibt einen Array mit allen bevorzugten Sprachen aus
$lg->getUserCountryArray() gibt einen Array mit allen bevorzugten Ländern aus

Was passiert aber falls der Benutzer hier keine Informationen ein­gestellt hat oder preis­geben will? In diesem Fall wird auf die Standard­ein­stellungen der Klasse zurück­gegriffen. Diese befinden sich alle in der Datei i18n_settings.ini:

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

Um diese Standard­ein­stellungen un­abhängig von den Benutzer­ein­stellungen auslesen zu können kann man folgende Methoden ver­wenden:

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

In der i18n_settings.ini Datei kann aber noch mehr ein­gestellt werden:

Bei der Erzeugung von Language Objekten können aber auch alle ermittelten Daten ganz einfach über­gangen werden und ein bestimmter Locale erzwungen werden indem man ihn einfach als Argument übergibt:

$lg_gb = new Language('en_gb');

Texte über­setzen

Das war die Language Klasse. Jetzt verwenden wir die daraus gewonnen Informationen zum Über­setzen von Texten. Dafür nehmen wir die Translator Klasse her die die Language Klasse erweitert.

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

Diese Klasse nimmt nun bereits zwei Argumente an. Das erste kann wieder zur Erzwingung eines bestimmten Locales ver­wendet werden. Das zweite wird speziell dann benötigt wenn man mit Text- oder Gettext-Dateien arbeitet.
Hier wird eine komma­unterteilte Liste mit Datei­namen ohne die Datei­namen­erweiterung ein­getragen (Die stellt man in der i18n_settings.ini Datei ein). Diese Dateien müssen sich im jeweiligen Sprach-Unter­verzeichnis des „locale“ Ver­zeichnisses befinden falls man reine Text­dateien ver­wendet oder dann noch einmal ein Unter­verzeichnis „LC_MESSAGES“ tiefer falls man Gettext Dateien ver­wendet.
Die Sprach-Unter­verzeichnisse müssen der ISO Norm für Locales ent­sprechen mit der Aus­nahme dass halt ein Unter- statt einem Binde­strich ver­wendet wird. (Zum Bei­spiel: „it“, „de_at“, „en_gb“,…).
Falls man den MySQL Modus ver­wendet ist das zweite Argument eine Liste von Name­spaces. Alle Über­setzungs­texte die diesem Name­space ange­hören werden dann auto­matisch geladen um nicht für jeden Text einzeln einen Daten­bank­zugriff machen zu müssen.
Außer im MySQL Modus ist es möglich auch Alias Sprachen anzu­legen. Falls man bereit ein Ver­zeichnis „en“ mit allen Über­setzungen hat und man nicht alles noch mal für „en_gb“ zum Bei­spiel über­setzen und Warten will, so legt man in dieses weitere Ver­zeichnis einfach eine Datei namens „redirect“ (ohne Datei­namen-Erweiterung) die als Text die eigentliche Sprache (in diesem Fall „en“) ent­hält. Alias Sprachen funktionieren im MySQL Modus nicht!

Im Package sind bereits ein paar Bei­spiel­sprachen ent­halten um so den Ein­stieg ein bisschen zu erleichtern und um zu zeigen wie die Über­setzungs­dateien aus­zu­sehen haben. Auf die Er­zeugung von Gettext Dateien wird hier aber nicht einge­gangen. Dazu gibt es genug Tutorials im Web…

Wenn ein neues Translator Objekt erzeugt wird, dann wird die Liste der bevorzugten Locales des Benutzers aus der Language Klasse mit den vor­handenen Sprach-Locales aus der Translator Klasse ver­glichen und der bestmögliche Locale gewählt. Auch geben die Methoden getLocale(), getLang() und getCountry() nicht mehr die bevorzugten Benutzer­ein­stellungen wieder sondern die Informationen aus dem auto­matisch ausge­wählten Über­setzungs-Locale.

Wenn man dann also mal die MySQL Tabelle oder die Über­setzungs­dateien an­gelegt hat kann man mit dem Über­setzen von Texten beginnen indem man die translate() Methode verwendet.

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

no_records_found“ ist der String nach dem in den Dateien oder der Tabelle gesucht und aus­gegeben wird. Falls man Um­laute und sonstige Sonder­zeichen auch gleich codiert ausgeben will kann man folgende Methode verwenden:

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

Um da­rüber hin­aus noch weitere Zei­chen, wie zum Bei­spiel „ד, „’“, „‡“… zu codieren kann man die translateEncodePlus Methode ver­wenden:

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

Um sich Schreib­arbeit zu ersparen kann man auch folgende Kurz­versionen ver­wenden:

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

Falls für den String keine Über­setzung gefunden wurde wird folgende Fehler­meldung bei der Aus­gabe auftauchen:

ERROR TRANSLATING: »» no_records_foun ««

Diese Meldung kann in der i18n_settings.ini Datei de­aktiviert werden. Dann wird nur der nicht über­setze String aus­gegeben.

Wenn man mit Gettext Dateien arbeitet ist die Situation ein bisschen kompli­zierter falls man mehr als nur eine Über­setzungs­datei bei der Objekt­erzeugung an­gegeben hat. In diesem Fall muss der ent­sprechende Datei­name für jeden zu über­setzenden String (außer der Erste in der Liste) als zweites Argument an die Translate() Methode über­geben werden:

echo $translation->Translate('another_string', 'translationfile2');
echo $translation->TranslateEncode('another_string', 'translationfile2');
echo $translation->_('another_string', 'translationfile2');
echo $translation->__('another_string', 'translationfile2');

Falls benötigt, kann die Sprache für ein Objekt auch dynamisch geändert werden:

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

Zuerst würde hier Guten Morgen aus­gegeben werden, bei der zweiten Ausgabe Good Morning.

Andere Methoden in der Klasse:

$translation->getLanguageFilesArray() gibt einen Array mit allen gefundenen Über­setzungs-Locales aus.
$translation->getModus() gibt den Modus aus („gettext“, „inc“ or „mysql“)
$translation->getRealLocale() Gibt das tat­sächlich ver­wendete Locale zurück falls ein Alias verwendet wird.

Der Benutzer soll entscheiden

Jetzt wissen wir wie wir multi­linguale Scripts machen können. Aber wie kann der Benutzer eine andere Sprache aus­wählen?
Dafür brauchen wir die ChooseLanguage Klasse die die Translator Klasse erweitert:

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

Damit wird ein <select> Formular­element mit allen möglichen Sprachen in der jeweiligen Sprache aus­gegeben. Die anderen Formular­elemente wie Senden Knöpfe usw. müssen dann im Script manuell hinzu­gefügt werden. Auf jeden Fall muss das Ziel des Formulars eine Seite sein die die ChooseLanguage Klasse wieder aufruft. Falls einem ein Dropdown-Menü nicht liegt kann man die Formular­elemente auch manuell erstellen. Der Name dieser muss aber “locale” lauten und muss als Wert dann ein, der ISO Norm ent­sprechendes, Locale besitzen. Bei­spiel:

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

Andere Methoden in der ChooseLanguage Klasse:

$translation3->countStrings(); gibt die Anzahl gefundener Über­setzungs­texte in den ange­gebenen Dateien, bzw. Name­spaces der Über­setzungs­tabelle zurück
$translation3->countLanguages(); gibt die Anzahl vor­handener Über­setzung-Locales zurück

Andere sprach­basierende Klassen

Basierend auf diesen Klassen gibt es noch ein paar andere Klassen um Datums­angaben, Zahlen, Währungen usw. zu im Kontext der Sprache zu formatieren:

Formatieren von Datums­angaben

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

Dies würde das ISO Datum '2003-12-24 13:45:00' formatieren. Falls das ein­gestellte Locale „de“ ist, würde die Ausgabe folgender­maßen aus­sehen:

Mittwoch, 24. Dezember 2003 – 13.45 Uhr

Beim Locale „it“ würde es folgendermaßen aussehen.

Mercoledì, 24 dicembre 2003 – 13.45

Es stehen folgende Methoden für die Formatierung zur Ver­fügung: shortDate(), shortTime(), shortDateTime(), middleDate(), middleTime(), middleDateTime(), longDate(), longTime(), longDateTime(). Diese brauchen als Argument alle einen UNIX Timestamp

Um dem Benutzer mehr Auswahl bieten zu können ist es möglich mit Hilfe der ChooseFormatDate Klasse ein <select> Dropdown­menü, ähnlich dem der ChooseLanguage Klasse, auszugeben. Zur Aus­wahl stehen dann das klassische Datums­formatierung in der jeweiligen Sprache, das ISO Format und das swatch Format.

Angaben zur Formatierung können in der l10n.ini Datei gemacht werden.

Formatieren von Zahlen

Zahlen werden auch sprach­abhängig unter­schiedlich formatiert. Die englisch geschriebene Zahl 123456.789 würde in der deutschen Schreib­weise 123 456,789 lauten.

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

Mit dieser Methode werden Zahlen formatiert. Das zweite Argument gibt die Anzahl an Nach­komma­stellen an die ange­zeigt werden sollen. Die percent() Methode arbeitet auf die gleiche Weise.
Bei der Formatierung von Währungs­ein­heiten gibt es mehr Argumente:

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

Dies würde in der deutschen Schreib­weise 99,90 Pfund aus­geben und in der Englischen 99.90 Pound. Das erste Argument ist die zu formatierende Zahl, das Zweite definiert die Schreib­weise der Währungs­einheit: full bedeutet das der aus­geschriebene Name der Währung ver­wendet wird (zum Bei­spiel „Pound“), short würde das Kürzel aus­geben (hier „GBP“) und symbol würde das Währungs­symbol aus­geben (hier „£“).
Das dritte Argument gibt das Land der Währung an (hier „England“) und das letzte Argument gibt an ob nur die „große“ Währungs­angabe ver­wendet werden soll oder nicht. Bei­spiel:

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

Angaben zur Formatierung können in der l10n.ini Datei gemacht werden.

Formatieren von Maß­einheiten

Diese Klasse beinhaltet nur die US Maß­einheit und das metrische System.

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

Hier wurde ein neues Objekt erzeugt welches als Eingabe Werte in metrischen Angaben her­nimmt und Diese umge­rechnet dann in US Ein­heiten ausgibt. Falls das zweite Argument nicht über­geben wurde, wird versucht wiederum an­hand der Sprache das richtige System zu wählen.

Die Methoden die zur Ver­fügung stehen sind linear(), surface(), capacity(), cooking(), liquid() und temperature(). Die meisten davon benötigen drei Argumente. Das Erste ist die Zahl, das Zweite und Dritte sind die Ein- und Ausgabe­einheit. Zum Bei­spiel würde dies bei der Methode linear() so aussehen:

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

Alle Maß­einheiten sind in der Klassen­dokumentation zu finden.

Formatieren von Texten

Im Kontext der Sprache können zwei Filter-Methoden auf Texte an­gewendet werden. Die Methode wordFilter() ersetzt „böse“ Wörter, wie zum Bei­spiel Schimpf­wörter, mit „*“ Sternchen. Eine komma­getrennte Liste kann für jede Sprache separat in der l10n.ini Datei an­gegeben werden.

Die Methode filterSpecialWords() sucht nach Schlüssel­wörtern und formatiert diese mit abbr, acronym oder dfn HTML Tags. Die Wörter und dazu­gehörigen Texte können in der words.ini Datei ge­ändert werden, die in jedem Sprach­verzeichnis liegt.

An­merkungen

Ver­schiedenes

Shared Memory Funktionen

Mit der Shared Memory Funktion­alität können alle ini Dateien bei erst­maligen Lesen auch in den ge­meinsamen Speicher des Servers ge­schrieben werden. Auf diese Weise er­spart man sich ein einlesen der Dateien mit jedem Seiten­zugriff. Standard­mäßig ist dies jedoch de­aktivert. Zur Aktivierung muss die Klassen­variable $use_shared_mem in der I18N Klasse auf TRUE gesetzt werden.
Aller­dings werden Änderungen an einer der Dateien nicht auto­matisch über­nommen. Dazu kann der Speicher aber manuell ge­löscht werden indem man die $flush_sm Variable (eben­falls in der I18N Klasse) auf TRUE setzt.

Ver­wenden einer anderen Daten­bank

Wer eine andere Daten­bank als MySQL ver­wenden will muss wohl oder übel die betreffenden Methoden manuell um­schreiben. Es ist nicht geplant irgendeine Art von DB Abstract Layer zu ver­wenden um die Klassen un­abhängig von irgendeinem Frame­work halten zu können.
Da ich noch nie PEAR verwendet habe, kann ich auch nicht sagen wie schwer es wäre die Klassen dort mit der DB Klasse zu implementieren, aber wer will kann es ja mal probieren und es mir dann sagen :-)

Daten­bank­code kommt in folgenden Methoden der Klassen Translator und ChooseLanguage vor: setConnection(), checkLocale(), languagesFilesList(), readLanguageFile(), Translate(), getLastUpdateDate() und countStrings().

Seiten cachen

Falls man irgendeine Art von Cache-Mechanis­mus verwendet so ist seit Version 1.054 in der Klasse Translator eine Methode namens getLastUpdateDate() zu finden. Diese gibt das aktuellste Änderungs­datum (als UNIX Timestamp) der aus­gewählten Über­setzungs­dateien bzw. Name­spaces zurück. So kann man über­prüfen ob irgendwelche Über­setzungen neuer sind als die gecachte Seite und gegebenen­falls Diese neu erzeugen lassen.

Smarty

Die Klassen wurden noch nicht mit smarty ge­testet, sollten aber ohne Probleme funktionieren wenn man die Design­vorlage folgender­maßen aufruft:

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>

Auf diese Weise sollte bei ein­geschaltetem Caching für jede Sprache eine eigene kompilierte Seite ge­speichert werden.


Autor: Flaimo
Datum: 2003-06-13
URLs:
Projekt­seite
Beispiel­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!