Inhaltsverzeichnis

Zentrale Verwaltung der Übersetzungen

Translate ist ein Paket aus mehreren Klassen, das ab WB-2.10.0 bereits weitgehend das komplette Handling der bisher als 'Sprachdateien' bekannten Übersetzungen übernimmt.

Allgemeiner Aufbau der Übersetzungs-Struktur

Der Einfachheit halber rede ich hier immer nur von 'Sprachdateien'. Real müssen die Übersetzungen jedoch längst nicht mehr zwingend in PHP-Dateien als Arrays definiert werden. Es ist jetzt jedes denkbare, geeignete Dateiformat möglich. Die Daten müssen nicht einmal auf dem selben Server liegen. Auch könnten die Daten durch einen WebService zur Verfügung gestellt oder in einer Datenbank abgelegt werden. Dazu ist einzig und allein jeweils ein kleiner, spezialisierter Treiber nötig, der jederzeit, auch temporär sogar für ein einzelnes Addon oder Template, eingebunden werden kann.

Aufbau der Sprachdateien (EN.php)

Dies ist die bevorzugte und auch dringend empfohlene Art:
(übersichtlich und schon im Texteditor einfach zu sortieren)

//Modul Description
$module_description = 'Enter here a really short description of your module. About 200-250 characters should suffice.';
 
$MOD_MyModule['PRINT']                       = 'Please print this page, if a copy is desired for your records.';
$MOD_MyModule['LOAD_LAYOUT']                 = 'Load Default Layout';
$MOD_MyModule['TEXT_GUEST']                  = 'Guest';
$MOD_MyModule['TEXT_UNKNOWN']                = 'unknown';
$MOD_MyModule['MESSAGE_ARCHIVE_DELETED']     = 'Zip(s) deleted successfully.';
$MOD_MyModule['MESSAGE_ARCHIVE_NOT_DELETED'] = 'Cannot delete the selected Zip(s).';

manche schreiben die Einträge auch auf diese Art:
(deutlich schwieriger zu lesen und nicht exakt sortierbar)

$MOD_MyModule['PRINT']                       = 'Please print this page, if a copy is desired for your records.';
$MOD_MyModule['LOAD_LAYOUT']                 = 'Load Default Layout';
$MOD_MyModule_TEXT['GUEST']                  = 'Guest';
$MOD_MyModule_TEXT['UNKNOWN']                = 'unknown';
$MOD_MyModule_MESSAGE['ARCHIVE_DELETED']     = 'Zip(s) deleted successfully.';
$MOD_MyModule_MESSAGE['ARCHIVE_NOT_DELETED'] = 'Cannot delete the selected Zip(s).';

Das sieht nicht soo gut aus, aber es funktioniert. Beide Schreibweisen erzeugen jedenfalls die selben Translate-Schlüsselwörter.

echo $oTrans->MOD_MyModule_PRINT;
echo $oTrans->MOD_MyModule_LOAD_LAYOUT;
echo $oTrans->MOD_MyModule_TEXT_GUEST;
echo $oTrans->MOD_MyModule_TEXT_UNKNOWN;
echo $oTrans->MOD_MyModule_MESSAGE_ARCHIVE_DELETED;
echo $oTrans->MOD_MyModule_MESSAGE_ARCHIVE_NOT_DELETED;

Zwingend ist derzeit jedoch noch der Präfix „$MOD_MyModule_“ bzw.„$MOD_MyModule[“. Nur so lassen sich ungewollte Überschneidungen der Schlüsselwörter verhindern.

Aufbau der DB-Sprachtabelle

FIXME

Historisches

Fangen wir beim Stand vor der 2.10.0. Im zentralen Sprachverzeichnis des Core existieren eine große Anzahl von Sprachdateien (DE.php | EN.php | usw.). Ja, es waren noch reale Dateien!. Jede dieser Dateien enthält eine schier unüberschaubare Anzahl von Sprachvariablen (genauer gesagt, derzeit etwas über 600!!). Das benutzte System erfordert, dass in jeder Sprachdatei jeder Eintrag vorhanden ist. Fehlt einer, löst das im Benutzungsfall häufig einen Laufzeitfehler aus. Sobald WebsiteBaker startet, wird zwangsweise eine dieser Sprachdateien mit ihren vollen 600 Einträgen geladen. völlig unabhängig davon, ob auch nur ein einziger davon benötigt wird! Selbstverständlich laden dann verschiedene Addons ihre eigenen Sprachdateien noch zusätzlich dazu. Bei mehreren verschiedenen Addons auf einer Seite kann da zusätzlich noch eine ganz erkleckliche Anzahl zusammenkommen.
(Aus historischen Zeiten sind da auch die längst veralteten Übersetzungen von sehr vielen uralten Modulen enthalten und eine wirkliche Pflege der Dateien ist, mit vernünftigem Zeit-/Arbeitsaufwand praktisch unmöglich. Wer was anderes behauptet, darf gerne ab sofort die Pflege dieser fossilen Dateien übernehmen.;-))

Die Sprachdateien sowohl im Core als auch in Modulen mussten mit solchen und ähnlichen, abenteuerlichen Konstrukten 'eingebaut' werden:

// Load Language file
if(is_readable(WB_PATH.'/modules/MyModule/languages/EN.php')) {
    require_once(WB_PATH.'/modules/MyModule/languages/EN.php');
}    
if(is_readable(WB_PATH.'/modules/MyModule/languages/'.DEFAULT_LANGUAGE.'.php')) {
    require_once(WB_PATH.'/modules/MyModule/languages/'.DEFAULT_LANGUAGE.'.php');
}    
if(is_readable(WB_PATH.'/modules/MyModule/languages/'.USER_LANGUAGE.'.php')) {
    require_once(WB_PATH.'/modules/MyModule/languages/'.USER_LANGUAGE.'.php');
}    
if(is_readable(WB_PATH.'/modules/MyModule/languages/'.LANGUAGE.'.php')) {
    require_once(WB_PATH.'/modules/MyModule/languages/'.LANGUAGE.'.php');
}    

Die Ausgabe erfolgte nach dem rudimentären Muster:

// Syntax Typ 1
global $MOD_MyModule;
echo $MOD_MyModule['PRINT'];
echo $MOD_MyModule['TEXT_GUEST'];
echo $MOD_MyModule['MESSAGE_ARCHIVE_DELETED'];
// oder Syntax Typ 2
global $MOD_MyModule, $MOD_MyModule_TEXT, $MOD_MyModule_MESSAGES;
echo $MOD_MyModule['PRINT'];
echo $MOD_MyModule_TEXT['GUEST'];
echo $MOD_MyModule_MESSAGE['ARCHIVE_DELETED'];

Nach dieser alten Prä-Translate-Methode sind leider noch die teils ellenlangen Präfixes notwendig.

Die Benutzung der alten Sprachvariablen ist deprecatet!
Die alten Sprachvariablen werden mit Abschluss der Umstellung auf Translate ohne extra Ankündigung aus dem System entfernt, was auch die obigen Ausgabemethoden entfernt.

Die Zukunft

hat bereits begonnen.

Ab 2.10.0 beginnt die schrittweise Umstellung auf Translate. Bis diese komplett abgeschlossen ist, bleiben parallel dazu die 'alten' Sprachvariablen funktionsfähig.

Wo die Umstellung schon erfolgte, genügt ab jetzt ein einfaches: <php>$oTrans→enableAddon('modules\\MyModule');</php> und schon ist alles erledigt.

Wenn jetzt die berechtigte Frage kommt: 'was ist dann alles erledigt?', dann einfach weiterlesen, wie Translate funktioniert.

Das Grundsystem hinter Translate

Translate basiert auf der Vorgabe, dass Sprachdateien grundsätzlich jeweils nur die für den jeweiligen Bereich notwendigen Einträge enthalten. Im Gegenzug werden relativ viele, kleine Sprachdateien eingesetzt. Jedes Addon, jede ACP-Erweiterung muss und sogar jedes einzelne Template/Theme kann seine eigenen Sprachdateien mitbringen.
Diese Aufteilung vereinfacht die Arbeit eines Entwicklers. Er muss nicht mehr 'halbwegs passende' Einträge in der zentralen Datei suchen und dort schon gar keine neuen Einträge hinzufügen, die dann in ~25 verschiedenen Sprachen gepflegt/übersetzt werden müssen. Als absolute Mindestanforderung genügt es völlig, eine Addon-spezifische Datei in der Systemsprache 'englisch' mitzuliefern, die alle Texte eines Addons, abzüglich der zentralen Texte enthält.
Was sollen Sprachdateien in Templates??
Alle Sprachdateien sind 'Ausgabe-Global'. Das bedeutet, dass diese Texte grundsätzlich in allen ausgewählten Templates/Themes identisch ausgegeben werden. Hat jetzt ein Template z.B. nur Platz für relativ kurze Texte so ist das kein Problem mehr. Es werden einfach die angepassten Texte mit identischem Schlüssel in die Sprachdatei des Templates aufgenommen - Und schon sind die Texte speziell auf dieses Template angepasst. Alle anderen Templates bekommen wie gewohnt die globalen Texte.

Struktur der Translation Tabellen

WebsiteBaker Versionen der 2er Serie (also alle 2.x.x Versionen) können ausschließlich einfache 2-Zeichen Sprachcodes nach ISO 639-1 verarbeiten (auch wenn Translate selbst längst mehr könnte)!
Die Region- und Variant-Codes werden erst ab der Version 3.0.0 ausgewertet.

Sprachauswahl im FireFox

Translate arbeitet intern mit mehreren Übersetzungstabellen (TranslationTable). Jede dieser Tabellen setzt sich aus einer bis maximal 9!! verschiedenen Sprachdateien zusammen. Das hört sich zwar im ersten Moment etwas 'oversized' an, ist aber halb so wild, da durch die eingesetzte Technik die resultierend Übersetzungstabelle erstens nie größer sein wird, als die größte der geladenen Einzeldateien und zweitens durch das eingebaute Caching die Übersetzungstabellen nur nach Änderungen an den Sprachdateien neu aufgebaut werden müssen.

Wenn man sich die Tabelle links (Auszug aus der Sprachauswahl des FireFox) anschaut, erkennt man bereits verschiedene chinesische, mehrere deutsche und noch mehr verschiedene englische Sprachweisen. Die bisher genutzten, simplen 2-Buchstaben-Codes rein nach ISO 639-1 genügen längst nicht mehr um wenigstens die meisten Sprachen mit deren wichtigsten, regionalen Abwandlungen zu definieren. Die Nutzung von IETF language tag nach RFC 5646 ist daher zwischenzeitlich längst Standard. Translate unterstützt zumindest den wichtigsten Teil der IETF-Tags. Nicht unterstützt werden derzeit die optionalen script subtags, extension subtags und der private-use subtag.
Translate selbst berücksichtigt derzeit folgende Tags:

Mit diesem System können fast alle auf der Erde vorkommenden Sprachen und Sprachvariationen abgebildet werden. In den vollständigen IETF-Tags stünden noch weit mehr Informationen, bis hin zum verwendeten Zeichensatz(Script). Wir haben uns hier bewusst nur an den eigentlichen Sprachcode plus die Regionalzuweisung gehalten. Der Variant-Tag wird leicht modifiziert zur Definition von Unterschieden einer Sprache im selben Land genutzt. Z.B. gibt es ja chinesisch in China und davon dann wiederum Mandarin oder Cantonesisch, Singapur etc… Diese Sprachvielfalt kommt wesentlich häufiger vor, als man sich im ersten Augenblick vorzustellen vermag.

Wie verarbeitet Translate jetzt diese Informationen?

Die komplette Struktur von Translate basiert auf komplexen Übersetzungstabellen die jeweils für den Core, ein Modul oder ein Template zuständig sind. Der Grundstruktur basiert auf den 3 Spracheinstellungen und dem FallBack-Mechanismus, den WebsiteBaker vorgibt:

  1. die Systemsprache (SYSTEM_LANGUAGE). Das ist die Sprache des Systems, auf deren Basis grundsätzlich programmiert wird. In der Regel einfaches Englisch [en].
  2. die Default-Sprache (DEFAULT_LANGUAGE), wird bei der Installation ausgewählt. Eine deutsche Website wird da deutsch [de] oder [de-DE] wählen.
  3. die Benutzersprache (USER_LANGUAGE), die sich der einzelne Besucher der Website aussucht.

Für jede Spracheinstellung wird jetzt eine Hilfstabelle aufgebaut, die sich jeweils wiederum aus bis zu 3 Sprachdateien zusammengemischt, die der Sprachcode vorgibt. Diese 3 Hilfstabellen werden nur in der Reihenfolge System→Default→User zu der eigentlichen Übersetzungstabelle zusammengemischt (in real etwas komplexer um Zeit und Speicher zu sparen), die dann auch automatisch gecacht wird. Beim nächsten Aufruf mit der selben Spracheinstellung entfällt also das ganze vorbeschriebene Prozedere und es wird einfach nur die fertige Tabelle aus dem Cache geladen.
So wie das jetzt für die zentrale Übersetzungstabelle beschrieben ist, exakt genau so geschieht es auch für jedes einzelne Modul und dessen Template, das Sprachdateien mitliefert und das auch tatsächlich geladen und benutzt wird.

Wie wird Translate benutzt?

Für Addon-Entwickler ist Translate sehr einfach einzusetzen. Die Grundinitialisierung wird immer automatisch vom Core vorgenommen und Addons haben damit überhaupt nichts zu schaffen. Bei älteren Addons, die noch PHP-Dateien enthalten, die direkt von außen aufgerufen werden müssen, sind 2 bis maximal 4 Translate-Methoden erforderlich (alle Kommandos werden in der Standalone-Form angegeben). Bedingung für den Einsatz von Translate ist, dass die Datei framework/initialize.php bereits geladen (included) ist:

Die folgenden Methoden stehen erst ab Template Version 1.0.0 zur Verfügung!

Einfacher noch geht es bei Addon-Dateien, die Core-gesteuert aufgerufen werden: Hier wird das Addon bereits vom Core aktiviert. Einzig das Template und/oder das Theme müssen derzeit noch vom Addon selbst aktiviert werden. Auch die Deaktivierung erfolgt automatisch durch den Core.

Jetzt müssen eigentlich nur noch die Übersetzungstexte von Translate abgerufen werden. Der einfachste Weg ist:

$oTrans = Translate::getInstance();
$sText = $oTrans->TEXT_CANCEL;
// oder auch 
$sText = Translate::getInstance()->TEXT_CANCEL;

entspricht dem früheren

$sText = $GLOBALS['TEXT']['CANCEL'];

Für ältere Addons existiert vorübergehend eine Methode, sämtliche Übersetzungstexte in einem Zug an die phplib Templateengine zu übergeben:

 $template->set_var(Translate::getInstance()->getLangArray()); 

eingebunden werden die Texte im Template mit {TEXT_CANCEL}. Das Problem bei dieser Methode ist, dass immer eine komplette Kopie der Übersetzungstabelle an die Templateengine übergeben wird.

Wesentlich einfacher und platzsparender ist die Verwendung in Verbindung mit Twig, da hier im Php-Code nur eine speichersparende Referenz auf das Translateobjekt übergeben wird:

$aTwigData['Trans'] = Translate::getInstance();

die Anzeige im Twig-Template erfolgt dann durch

{{ Trans.TEXT_CANCEL }}