7 Internationalization
Web-Applikationen sollen oft mehrsprachig sein, oder sie sollen möglichst einfach an eine Sprache oder Lokalisierung anzupassen sein. Zwar sieht PHP dafür schon einige Funktionalitäten vor, aber die Möglichkeiten sind doch recht eingeschränkt. Die Pakete in dieser Kategorie bieten eine gute Unterstützung, wenn Sie Ihre Applikationen möglichst flexibel gestalten wollen.
Eine Applikation an unterschiedliche Sprachen und somit auch an unterschiedliche Kulturräume anzupassen ist nicht so einfach, wie man im ersten Augenblick denkt. Neben den sprachlichen Aspekten müssen Sie auch Datums- und Zahlenformate beachten.
Wenn Sie sich mit dieser Thematik beschäftigen, werden Sie oft den Abkürzungen i18n und l10n begegnen. Die erste steht für internationalization und die zweite für localization. Es werden also immer nur der erste und der letzte Buchstabe genutzt, und die Zahl sagt aus, wie viele Buchstaben dazwischen fehlen.
7.1 I18Nv2 

Besprochene Version: 0.11.3 | Lizenz: PHP-Lizenz 3 |
Klassendatei(en): I18Nv2.php; I18Nv2/Country.php; I18Nv2/Currency.php; I18Nv2/ Language.php; I18Nv2/AreaCode.php |
I18Nv2 ist der Nachfolger des Pakets I18N und stellt Ihnen eine große Bandbreite an Funktionen zur Verfügung, mit denen Sie Informationen landesspezifisch aufbereiten können.
Das Paket enthält eine Vielzahl von landesspezifischen Informationen. Welche Länder und Sprachen aktuell unterstützt werden, entnehmen Sie bitte der Homepage des Pakets unter http://pear.php.net/package/I18Nv2/. Alle »gängigen« Sprachen werden unterstützt.
In diesem Paket sind verschiedene Klassen definiert, die alle einen speziellen Zweck erfüllen. Objekte der entsprechenden Klasse können über ihren eigenen Konstruktor abgeleitet werden, wobei für zwei Klassen auch eine Factory-Methode in der Klasse I18Nv2 vorgesehen ist.
Die erste Klasse, die ich Ihnen vorstellen möchte, ist I18Nv2_Locale. Sie stellt Methoden zur korrekten Formatierung von Daten und Zahlen zur Verfügung.
Die Methode createLocale(), die ein I18Nv2_Locale-Objekt zurückliefert, bzw. der Konstruktor benötigen eine gültige Lokalisierungsangabe, die in der PHP-typischen Formatierung aufgebaut ist (also de_DE, de_AT etc.). Des Weiteren können Sie danach einen booleschen Wert übergeben, mit dem Sie den »Paranoia-Modus« einschalten können. Nutzen Sie diesen Modus, wird die Lokalisierung vor der Ausführung jeder Methode erneut eingeschaltet. Dies soll nur sicherstellen, dass die Einstellungen nicht aus Versehen durch Seiteneffekte überschrieben werden, und ist normalerweise nicht nötig.
Diese beiden Einstellungen können Sie auch nachträglich mit den Methoden setLocale() und setParanoid() verändern.
Um eine normale Zahl für die entsprechende Lokalisierung zu formatieren, ist die Methode formatNumber() vorgesehen. Sie bekommt die Zahl als Parameter übergeben, formatiert sie und gibt sie zurück.
require_once('I18Nv2.php'); $loc = I18Nv2::createLocale('de_DE'); echo $loc->formatNumber(1000.20); // Ausgabe: 1.000,20
Listing 7.1 Formatierung einer Zahl mit I18Nv2
Auch Zahlen mit Angabe einer Währung können entsprechend formatiert werden. Hierzu müssen Sie allerdings die Methode formatCurrency()nutzen. Die Währung selbst wird dabei nicht mit einem Sonderzeichen wie dem Euro-Zeichen dargestellt. Die Währung wird in Form des dreistelligen ISO-Codes der Zahl vorangestellt (z. B. EUR 1.000,20).
Bevorzugen Sie eine Darstellung in der Schreibweise, wie sie in dem Land üblich ist, übergeben Sie der Methode als zweiten Parameter die Konstante I18Nv2_CURRENCY_LOCAL. In diesem Fall wird das Währungssymbol (E , $ etc.) mit ausgegeben und entsprechend der lokal üblichen Formatierung vor oder hinter die Zahl gesetzt.
Allerdings können Sie mit der Methode setCustomFormat() selbst eine Formatierung definieren. An erster Stelle wird eine Zahl übergeben, über die das Format später angesprochen werden kann. An zweiter Stelle folgt ein recht umfangreiches Array, das den Aufbau des Formats definiert. Das Array besteht aus zwölf Feldern. Im ersten Element ist das Währungssymbol zu übergeben. Danach folgen die Anzahl der gewünschten Nachkommastellen, das Dezimaltrennzeichen und das Tausendertrennzeichen. Darauf folgend können Sie in den nächsten beiden Feldern angeben, mit welchem Zeichen die negativen und die positiven Zahlen gekennzeichnet werden. Um festzulegen, ob das Währungssymbol bei negativen und positiven Zahlen vorn oder hinten steht, können Sie in den nächsten beiden Feldern einen booleschen Wert übergeben. Nutzen Sie ein true, wird die Währungseinheit vor der eigentlichen Zahl ausgegeben, und bei einem false dahinter. Auch bei den nächsten beiden Elementen handelt es sich um boolesche Werte. Sie legen fest, ob das Währungssymbol bei negativen bzw. bei positiven Zahlen mit einem Leerzeichen abgetrennt werden soll.
Die letzten beiden Felder können eine Zahl enthalten, die definiert, wo das Positiv- bzw. Negativ-Zeichen ausgegeben werden soll. Welche Zahl welche Formatierung nach sich zieht, können Sie Tabelle 7.1 entnehmen.
Haben Sie das Format so definiert, müssen Sie es noch der Methode formatCurrency() zuweisen. Das erledigt die Methode setCurrencyFormat() für Sie, die den Integer-Wert übergeben bekommt, den Sie bei der Definition des Formats zur Identifizierung genutzt haben. Der zweite Parameter muss true sein, um der Methode mitzuteilen, dass es sich um ein selbst definiertes Format handelt.
require_once('I18Nv2.php'); define('MY_CURRENCY',0); $loc = I18Nv2::createLocale('de_DE'); $cust_format = array ( 'EURO', // Waehrungssymbol '3', // Nachkommastellen ',', // Abtrennung der Nachkommastellen ' ', // Abtrennung der Tausender 'S ', // Zeichen fuer negative Zahlen 'H ', // Zeichen fuer positive Zahlen false, // Waehrungssymbol vorne? (negative Zahlen) true, // Waehrungssymbol vorne? (positive Zahlen) true, // Waehrungssymbol abtrennen? (Negative Zahl) true, // Waehrungssymbol abtrennen? (Positive Zahl) 1, // Formatierung negativer Zahlen 0 // Formatierung positiver Zahlen ); $loc->setCustomFormat(MY_CURRENCY,$cust_format); $loc->setCurrencyFormat(MY_CURRENCY,true); echo $loc->formatCurrency(12111.3116).'<br />'; echo $loc->formatCurrency(-10000).'<br />'; //Ausgabe: // EURO (12 111,312) // S 10 000,000 EURO
Listing 7.2 Währungsausgabe mit selbst definiertem Format
Für die korrekte Formatierung von Datum und Uhrzeit sind drei Methoden definiert: formatTime(), formatDate() und formatDateTime(). Allen Methoden können Sie einen Timestamp übergeben. Machen Sie das nicht, wird die aktuelle Systemzeit genutzt.
require_once('I18Nv2.php'); $loc = I18Nv2::createLocale('de_DE'); echo $loc->formatDate(0).'<br />'; echo $loc->formatTime().'<br />'; echo $loc->formatDateTime().'<br />'; // Ausgabe: // 01.01.1970 // 15:50:12 // 05.03.2005, 15:50:12
Listing 7.3 Formatierung von Zeitangaben
Damit Sie nicht auf die Standardformatierungen angewiesen sind, können Sie noch einen weiteren Parameter übergeben, um die Ausgabe zu beeinflussen. Für formatDate() und formatTime() sind die Konstanten aus Tabelle 7.2 definiert. Die Konstanten sind für beide Methoden identisch, nur wirken sie sich natürlich unterschiedlich aus. Intern wird für die Formatierung die Funktion strftime() genutzt, so dass Sie in I18Nv2/Locale/de.php die Strings für die deutsche Lokalisierung finden. Für andere Lokalisierungen finden Sie die Strings in den Dateien, die im Unterverzeichnis I18Nv2/Locale enthalten sind.
Möchten Sie als zweiten Parameter eine dieser Konstanten nutzen, aber trotzdem die aktuelle Uhrzeit verwenden, übergeben Sie als ersten Parameter null.
Aber auch hier können Sie die Formatierung selbst definieren. Das Format wird auch hier mit setCustomFormat() festgelegt. Der erste Parameter ist wiederum eine Zahl, mit der das Format später angesprochen werden kann. Mit dem zweiten Parameter, einem String, formatieren Sie die eigentliche Ausgabe. Der String kann entsprechend den Möglichkeiten von PHPs strftime() aufgebaut werden, die Sie unter http://de.php.net/strftime nachlesen können.
Nach der Deklaration des Formats können Sie es mit der Methode setDateFormat() für die nächsten Aufrufe von formatDate() festlegen:
require_once('I18Nv2.php'); $loc = I18Nv2::createLocale('de_DE'); define('MY_DATE',0); $loc->setCustomFormat(MY_DATE,'%m %Y'); $loc->setDateFormat(MY_DATE, true); echo $loc->formatDate().'<br />'; // Ausgabe // 03 2005
Listing 7.4 Formatierung eines Datums
Natürlich können Sie auch für die Methoden formatTime() und formatDateTime() eine entsprechende Formatierung festlegen. Die Festlegung des Formats erfolgt auch hier mit setCustomFormat(). Um das Format dann aufzurufen, müssen Sie allerdings auf die Methoden setTimeFormat() und setDateTimeFormat() anstatt auf setDateFormat() zurückgreifen.
Um den Namen des Tages und des Monats als Text auszulesen, können Sie die Methoden dayName() und monthName() nutzen. Beide erwarten als ersten Parameter eine Zahl, die definiert, welchen Tag bzw. Monat Sie zurückgeliefert haben wollen. Beachten Sie hierbei bitte, dass die Angabe nullbasierend erfolgt. Die Zahl 0 entspricht also dem Sonntag bzw. dem Januar und die 1 dem Montag bzw. dem Februar.
require_once('I18Nv2.php'); $loc = I18Nv2::createLocale('fr_FR'); echo $loc->dayName(0).'<br />'; echo $loc->monthName(1).'<br />'; // Ausgabe: // dimanche // février
Listing 7.5 Ausgabe der Namen von Tag und Monat
Beide Methoden akzeptieren als zweiten Parameter noch einen booleschen Wert, über den Sie der Methode mitteilen können, ob der jeweilige Name abgekürzt werden soll (true) oder nicht (false).
Die Klasse I18Nv2_Country hilft Ihnen dabei, die ISO-Ländercodes in die Namen der Länder umzuwandeln. Der Konstruktor der Klasse bekommt als Parameter die Sprache übergeben, in der die Ausgabe erfolgen soll. Der zweite Parameter ist der Zeichensatz, der genutzt werden soll, um die Werte zurückzugeben.
Um zu erfahren, welche Abkürzungen dem System bekannt sind, können Sie die Methode getAllCodes() nutzen. Die Methode gibt ein Array zurück, bei dem der Schlüssel immer dem ISO-Code entspricht und der Wert des Elements der Name des Landes ist.
Um den Namen eines einzelnen Landes auszulesen, ist die Methode getName() definiert, die den Ländercode übergeben bekommt und den ausgeschriebenen Namen des Landes zurückgibt.
require_once ('I18Nv2/Country.php'); $country = new I18Nv2_Country('de', 'iso-8859–1'); echo "US steht für: ".$country->getName('us'); $country->setLanguage('en'); echo "<br />DE stands for: ".$country->getName('de');
Möchten Sie die Sprache für die Ausgabe ändern, können Sie die Methode setLanguage() nutzen, und für die Selektion eines anderen Zeichensatzes ist setEncoding() vorgesehen.
Die Klassen I18Nv2_Language, I18Nv2_Currency und I18Nv2_AreaCode leisten Ähnliches. Die Nutzung ist identisch mit der von I18Nv2_Country, wobei Sie natürlich die korrekten Klassen-Dateien einbinden müssen. I18Nv2_Language gibt Ihnen anstelle von Ländernamen allerdings die Namen von Sprachen zurück.
I18Nv2_Currency leistet das Gleiche für Währungen. Da die Währung allerdings nicht von der Sprache abhängt, muss der Methode getName() hier der dreistellige ISO-Code einer Währung übergeben werden. Die letzte Klasse in diesem Zusammenhang ist I18Nv2_AreaCode, die Ihnen die Vorwahl eines Landes zurückgibt, wenn Sie der Methode getName() einen Landescode übergeben. In dieser Klasse ist noch eine zusätzliche Methode namens mergeCountry() definiert. Ihr wird ein I18Nv2_Country-Objekt übergeben. Sie kombiniert die Landesvorwahlen mit den Ländernamen und gibt ein neues Objekt zurück. Rufen Sie aus dem neuen Objekt heraus die Methode getName() mit einer Landesvorwahl auf, erhalten Sie den ausgeschriebenen Namen des Landes zurück.
require_once ('I18Nv2/Country.php'); require_once ('I18Nv2/Currency.php'); require_once ('I18Nv2/Language.php'); require_once ('I18Nv2/AreaCode.php'); $lang = 'es'; $country = new I18Nv2_Country($lang, 'iso-8859–1'); $currency = new I18Nv2_Currency($lang,'iso-8859–1'); $language = new I18Nv2_Language($lang,'iso-8859–1'); $area = new I18Nv2_AreaCode($lang,'iso-8859–1'); echo "Alle Ausgaben auf Spanisch für Deutschland:<br />"; echo 'Name: '.$country->getName('de').'<br />'; echo 'Sprache: '.$language->getName('de').'<br />'; echo 'Währung: '.$currency->getName('eur').'<br />'; echo 'Vorwahl: '.$area->getName('de').'<br />'; // Landesname in Abhaengigkeit von der Vorwahl $merged = $area->mergeCountry($country); echo 'Land zu Vorwahl 49: '.$merged->getName ('49'); /** Ausgabe: ** Alle Ausgaben auf Spanisch für Deutschland: ** Name: Alemania ** Sprache: Alemán ** Währung: euro ** Vorwahl: 49 ** Land zu Vorwahl 49: Alemania **/
Listing 7.6 Nutzung der Listen-Klassen
Die Klassen I18Nv2_Language, I18Nv2_Language, I18Nv2_Currency und I18Nv2_AreaCode eignen sich natürlich wunderbar zur Ausgabe von Optionslisten. Um Ihnen hier die Arbeit zu erleichtern, sind die DecoratedList-Klassen vorgesehen.
Möchten Sie eine Optionsliste mit den Namen aller Länder ausgeben, könnte das so aussehen wie in Listing 7.7.
require_once 'I18Nv2/Country.php'; require_once 'I18Nv2/DecoratedList/HtmlSelect.php'; require_once 'I18Nv2/DecoratedList/HtmlEntities.php'; // Neues I18Nv2_Country-Objekt $country = new I18Nv2_Country('es', 'iso-8859–1'); // Sonderzeichen in Entitaeten konvertieren $country_entity = new I18Nv2_DecoratedList_HtmlEntities($country); // In HtmlSelect-Objekt konvertieren $country_select = new I18Nv2_DecoratedList_HtmlSelect($country_entity); // Attribute setzen $country_select->attributes['select']['name']='CountrySelect'; $country_select->attributes['select']['onchange']= 'this.form.submit()'; $country_select->attributes['option']['style'] = 'font-family:Courier;'; // Eintrag vorselektieren $country_select->selected['DE'] = true; // Select-Box ausgeben echo $country_select->getAllCodes();
Listing 7.7 Ausgabe aller Länder als Optionsliste
Das neu generierte Country-Objekt wird zuerst an den Konstruktor der Klasse I18Nv2_DecoratedList_HtmlEntities übergeben. Hierbei werden die enthaltenen Sonderzeichen in HTML-Entitäten konvertiert. Alternativ könnten Sie an dieser Stelle auch ein Objekt der Klasse I18Nv2_DecoratedList_HtmlSpecialchars nutzen. In dem Fall werden dann nur die Zeichen <, >, ' und " in Entitäten konvertiert.
Das resultierende Objekt wird dann an den Konstruktor der Klasse I18Nv2_DecoratedList_HtmlSelect übergeben. Dieser generiert ein Objekt, das automatisch eine komplette Optionsliste ausgeben kann.
Möchten Sie Attribute für die Ausgabe der Optionsliste definieren, können Sie auf die Eigenschaft attributes zugreifen. Hier sind zwei Schlüssel, select und option, definiert, unter denen Sie neue Optionen einordnen können. Elemente, die Sie unterhalb von select einfügen, werden dem <select>-Tag zugeordnet, und Felder, die Sie unterhalb von option einfügen, werden jedem <option>-Tag hinzugefügt.
Um einen bestimmten Eintrag vorzuselektieren, ist die Eigenschaft selected vorgesehen. Hierbei handelt es sich um ein Array, bei dem Sie dem gewünschten Country-Code einfach den Wert true zuweisen können.
Zur Ausgabe können Sie wiederum die Methode getAllCodes() nutzen.
Da die Inhalte der Optionsliste leider nicht sortiert sind und zurzeit auch keine Methode deklariert ist, um das zu tun, würde ich Ihnen empfehlen, sie manuell zu sortieren, wenn das gewünscht ist. Hierzu können Sie die Eigenschaft codes des I18Nv2_Country-Objekts an die Funktion asort() oder arsort() übergeben.
$country = new I18Nv2_Country('es', 'iso-8859–1'); asort($country->codes); $country_entity = new I18Nv2_DecoratedList_HtmlEntities($country);
Es wird sicher eher selten sein, dass Sie alle Länder ausgeben wollen. Daher sind auch noch Filter-Klassen definiert. So ist in der Datei I18Nv2/DecoratedList/EuropeanCountries.php die Klasse I18Nv2_DecoratedList_EuropeanCountries definiert. Übergeben Sie dem Konstruktor dieser Klasse ein I18Nv2_Country-Objekt, so liefert er Ihnen ein Landesobjekt zurück, das nur die europäischen Länder beinhaltet. Dieses können Sie dann genauso weiterverarbeiten wie ein I18Nv2_Country-Objekt.
$country = new I18Nv2_Country('es', 'iso-8859–1'); $country_europ= new I18Nv2_DecoratedList_EuropeanCountries($country); $country_entity = new I18Nv2_DecoratedList_HtmlEntities($country_europ);
Neben der europäischen Variante finden Sie im selben Unterverzeichnis auch noch die Dateien AfricanCountries.php (afrikanische Länder), AsianCountries.php (asiatische Länder), NorthAmericanCountries.php (nordamerikanische Länder), OceanianCountries.php (ozeanische Länder) und SouthAmericanCountries.php (südamerikanische Länder).
Natürlich stellt sich bei der Lokalisierung immer die Frage, welche Sprache bzw. welcher Zeichensatz vom Client verstanden wird. Der sicherste Weg ist immer noch, wenn Sie ein Formular vorsehen, in dem der Benutzer eine Sprache selektieren kann. Das Paket kennt auch eine Klasse namens I18Nv2_Negotiator. Mithilfe der dort enthaltenen Funktionen können Sie automatisch abgleichen, welche von den Sprachen, die Ihre Applikation unterstützt, auch vom Client unterstützt werden. Da diese Klasse zurzeit aber nicht das gewünschte Ergebnis liefert, werde ich nicht weiter darauf eingehen und empfehle Ihnen, den Benutzer zu fragen, welche Sprache er wünscht.