1. Oktober 2007 XSLT als Template Engine in PHP - Teil 7

in Kategorie PHP, XML & Co

Tags: , , , , ,


Eine große Stärke der Template Engine die im Laufe dieser Beitragsreihe aufgebaut wurde ist die Internationalisierung oder kurz I18N. Oft möchte man eine Website nicht nur in einer Sprache zur Verfügung stellen. Um die Mehrsprachigkeit einer Website herzustellen gibt es bereits einige Ansätze, doch keiner ist meiner Meinung nach so elegant wie der, den ich in diesem Beitrag vorstellen werde.

Ich werde wieder das letzte Beispiel aus dem 6. Teil weiterentwickeln. Wer die vorhergehenden Teile noch nicht gelesen hat sollte dies noch nachholen.

XSLT & XPath

Bisher wurde XPath in dieser Reihe nie angesprochen, da die Beispiele mit Absicht recht einfach gehalten waren. XPath — »XML Path Language« — ist eine relativ einfache Sprache um sich innerhalb eines XML–Dokumentes bewegen zu können. Der dazugehörige Wikipedia–Eintrag liefert eine ausreichende Einführung.

Interationalisierung

Um eine Website übersetzungfähig zu machen müssen alle Wörter und Sätze ausgelagert und auf Bedarf mit der jeweils gewünschten Sprache wieder eingesetzt werden. Hierzu legen wir ein neues XML–Dokument (i18n.xml) an:

<?xml version="1.0" encoding="utf-8"?>
 
<i18n>
    <general>
        <title>
            <de>Eine Website generiert durch XSLT</de>
            <en>A website generated with XSLT</en>
        </title>
    </general>
    <home>
        <title>
            <de>Dies ist die Startseite</de>
            <en>This is the home page</en>
        </title>
        <text>
            <de>und ein bisschen Text.</de>
            <en>and a little bit of text.</en>
        </text>
        <currentDate>
            <de>Das aktuelle Datum:</de>
            <en>The current date:</en>
        </currentDate>
    </home>
</i18n>

Der Aufbau kann sich nach belieben gestalten nur die Tags zu den Wörtern oder Sätzen müssen immer auch die selben Tags enthalten. In diesem Beispiel bieten wir zwei Übersetzungen an, Deutsch (de) und Englisch (en). Die übergeordneten Tags können zur Strukturierung und besseren Übersichtlichkeit dienen. Dieses XML–Dokument wird uns als eine Art Datenbank für alle zu übersetzenden Texte dienen.

Betrachten wir nun die Datei »index.php«. Dort fügen wir vor der Transformation folgende Zeile ein:

$xp->setParameter('', 'currentLang', 'de');

Der XSLTProcessor bietet die Möglichkeit über die Methode »setParameter« eine Variable für den späteren Gebrauch im XSL–Stylesheet zu setzen. Hier legen wir also die Sprache fest, die aktuell verwendet werden soll.

Nun muss auch das Root–Stylesheet angepasst werden indem wir vor dem ersten Template aber innerhalb des Stylesheet–Tags folgende Zeile einfügen:

<xsl:variable name="i18n" select="document('i18n.xml')/i18n"/>

Mit Hilfe von XPath ist es hier möglich auf ein weiteres XML–Dokument zuzugreifen und dort den obersten Tag »i18n« auszuwählen. Die Variable »$i18n« ist im folgenden quasi die Kurzschreibweise für diesen XPath–Ausdruck.

Im letzten Schritt werden nun alle Wörter und Sätze, die sich in den Templates befinden, durch einen xsl:value-of–Tag ersetzt, der diese aus unserer »Übersetzungsdatenbank« liest. Zum Beispiel sieht die Überschrift im home–Template dann so aus:

<h2><xsl:value-of select="$i18n/home/title/*[name()=$currentLang]" /></h2>

Dieser XPath–Ausdruck macht folgendes: Innerhalb unserer zuvor gesetzten Variable »$i18n« wählen wir den Tag »home« und dann »title«. Dort befindet sich der Text, den wir suchen. Um nun die richtige Sprache auszuwählen wählen wir den Tag anhand dessen Names aus. Hier finden wir auch den Parameter wieder den wir zuvor in PHP gesetzt haben.

Fazit

Eventuell mag diese Möglichkeit der Internationalisierung auf den ersten Blick kompliziert erscheinen. Sie ist es aber nicht. Die Auswahl der passenden Tags über XPath bietet zudem sehr flexible Möglichkeiten auch für große Webseiten die Wartbarkeit zu gewährleisten. Die Datei i18n.xml kann auch durchaus anders aufgebaut werden. Ich möchte nicht behaupten, dass der hier beschriebene Weg immer der optimalste ist. Für mich hat er sich aber bewährt.

Das vollständige Beispiel aus diesem Beitrag gibt es wie immer hier zum Download:

Ähnliche Artikel


Der Beitrag wurde am Montag, den 1. Oktober 2007 um 16:32 Uhr veröffentlicht und wurde unter PHP, XML & Co abgelegt. du kannst die Kommentare zu diesen Eintrag durch den RSS 2.0 Feed verfolgen. du kannst einen Kommentar schreiben, oder einen Trackback auf deiner Seite einrichten.

Kommentare

  1. Am 9. Oktober 2007 um 20:33 Uhr
    Mark Raddatz gravatar

    Hallo Mario,
    grossartig! Du beschreibst genau das, wonach ich schon lange suche. Vielen Danke fuer die Artikelreihe.

  2. Am 9. Januar 2008 um 13:20 Uhr
    Joeles gravatar

    Hi Mario,

    sehr interessantes Tutorial.
    Das Ausführen von PHP Funktionen in den Templates macht mich aber stutzig.

    Wie schaffst Du dem sehr wahren Satz:
    -Traue nie deinem Template Designer- abhilfe?

    Ist das nicht ein potentielles Sicherheitsrisiko?
    Wie könnte man die Funktionen/Klassen, die der Template Designer nutzen kann/darf limitieren?

    Viele Grüße,

    Joeles

  3. Am 9. Januar 2008 um 16:58 Uhr
    Mario Volke gravatar

    Meines Wissens lassen sich die Funktionen nicht limitieren. Wenn der Template Designer PHP Funktionen also nicht nutzen soll, dann gäbe es zwei Möglichkeiten:
    Entweder man verzichtet komplett darauf und trennt wirklich sauber die Templates von der Logik oder es müsste auch möglich sein in XSLT quasi vom Entwickler vorgefertigte Templates an den Designer auszugeben, die der dann verwenden kann. In diesen Templates könnte sich der Entwickler ja dann austoben.

  4. Am 20. Januar 2009 um 13:59 Uhr
    Cedric gravatar

    Hi Mario,

    Ich muss dir noch mal einen Kommentar hinterlassen, da ich mittlerweile sehr begeistert von XSLT und XPath bin. :-)

    Ich arbeite derzeit an einer Template-Klasse auf XSLT-Basis und habe da schon ein paar Ideen im Kopf und auch mit der Entwicklung begonnen. Dabei orientiere ich mich derzeit sehr an der Struktur von Smarty.

    Meine Frage an dich: Hättest Du Lust die Template-Engine gemeinsam mit mir zu entwickeln? Ich denke du hast wesentlich mehr Erfahrung im Umgang mit XSLT als ich und weißt worauf es ankommt. Falls ja schreib mir doch einfach mal eine E-Mail, ich würde mich freuen.

    Viele Grüße,
    Cedric

  5. Am 7. Juni 2009 um 08:34 Uhr
    Chris gravatar

    Guten Morgen,

    erstmal vielen Dank für diese Tutorials. Auch wenn diese schon eine weile im Netz zu finden sind, hab ich sie erst jetzt gefunden. Naja, ich beschäftige mich aber auch erst jetzt mit xml/xslt.

    In Bezug auf die hier gezeigt Methode der Internationalisierung bin ich auf ein Problem gestoßen. Man kann leider kein die Tags nicht matchen, da der match-Befehl keine Variablen enthalten darf. Gibt es hier vielleicht einen Trick?

    Viele Grüße
    Chris

  6. Am 7. Juni 2009 um 10:45 Uhr
    Mario Volke gravatar

    Hi Chris,
    erstmal danke. Finde es immer wieder nett, wenn meine Artikel jemandem weiterhelfen.
    Zu deinem Problem:
    Für die reine Internationalisierung kann ich mir eigentlich gar nicht vorstellen, wo man einen match bräuchte.
    Aber du hast schon recht, das mit den Variablen dort ist ein Problem. Was du Versuchen könntest, wäre statt template-match,
    einfach for-each-select zu verwenden. Wenn dir das weiterhilft. Oder was auch immer möglich wäre:
    Du verwendest XSLT quasi zweimal: Erstens, um ein XSL-Stylesheet zu erzeugen welches dann in die richtige Sprache übersetzt ist
    und zweitens mit dem neuen XSL-Stylesheet das HTML-Markup erzeugen.
    Aber wie gesagt ich weiß nicht genau, was du überhaupt machen willst.

  7. Am 7. Juni 2009 um 20:32 Uhr
    Chris gravatar

    Naja, ich hatte mir gedacht, dass man in die Sprachfiles evtl. Formatierungen mit benutzen könnte, also sozusagen BBCode.

  8. Am 29. Juni 2009 um 15:37 Uhr
    Cedric gravatar

    Du kannst in den Lokalisierungen ohne weiteres HTML-Tags verwenden. Dafür musst du nur CDATA-Sektionen verwenden. Mache ich auch nicht anders. Aber trotzdem solltest du Quelltext von Design trennen und wirklich nur das Nötigste vorformatieren.

  9. Am 16. Juli 2009 um 00:16 Uhr
    Dominik gravatar

    Hallo,
    ich entwickle selber zur Zeit ein CMS, was bisher Smarty als Template Engine einsetzt. Ich plane schon länger diese rauszuwerfen da Sie doch schon allein auf Grund von PHP 4 nicht ganz in mein Konzept passt.
    Nun war ich am überlegen ob ich nicht eben XLST als Template nehmen könnte, da ich den Adminbereich eh in XLST (aber mit der Browser-Variante) umsetzen wollte.
    Nur stellen sich mir 2 Probleme in den Weg:

    1. Momentan werden einige Daten erst nach dem Funktionsaufruf aus dem Template geladen. Vorteil es werden eben echt nur Daten geladen die man wirklich brauch.
    Nur wie bringt man das mit XLST in Vereinbarung?

    2. Wie weit ist der XLST-Processor wirklich verbreitet, soweit ich das sehe ist er nämlich fast nie installiert. Und besonders bei Massenhostern ist es ziemlich schwer sowas nachinstallieren zu lassen.
    Lohnt es sich also überhaupt?

    mfG

  10. Am 16. Juli 2009 um 08:05 Uhr
    Mario Volke gravatar

    Hallo Dominik,
    dynamisches Nachladen von Daten kann evtl. schon funktionieren. Prinzipiell sehe ich da zwei Möglichkeiten:
    1) Wie in Teil 6 beschrieben kann mit select=”php:function(’MyClass::get’)” beliebige PHP-Funktionen bzw. Klassenmethoden aufrufen. Damit könnte man also einfache Strings nachladen.
    2) Du generierst für zusätzliche Daten separat ein XML-File und fügst es, wie in diesem Teil beschrieben, mit select=”document(’myfile.xml’)/…” ein. Evtl. ist hier aber Caching Pflicht.
    Prinzipiell solltest du auf solche Tricks aber verzichten und einfach den View sauber vom Controller trennen und wenn zusätzlich die Ausgabe gechached wird sollte es auch keinerlei Performanceprobleme geben.

    Zur XSLT-Processor: Ja die XSL-Library in PHP ist nicht standardmäßig aktiviert, dennoch ist sie auch auf vielen Massenhostern vorhanden (z.B. HostEurope).

    Ob es sich lohnt? Also ich kann mir vorstellen, dass es durchaus Probleme bereiten kann, wenn man eine bestehende Applikation auf XSLT umstellen will, wenn aber konsequent XML-Technologien verwendet werden, kann man vieles sehr elegant lösen.

  11. Am 16. Juli 2009 um 20:53 Uhr
    Dominik gravatar

    Hallo,
    die Trennung zum Controller besteht ja. Die Logik befindet sich vollkommen in den Controller Klassen.
    Nur der Aufruf einiger Funktionen (z.B. sowas wie getLink) findet dann eben im Viewer statt. Die genannte Methode stellt wahrscheinlich nicht mal das Problem da.
    Eher so Dinge wie das man den zu einem News Eintrag gehörenden Autor so $author = $newsEnty->getAuthor(); aus der Datenbank läd.
    select=”php:function(’MyClass::get’)” Bringt mir absolut gar nix, den mit statischen Funktionen kann ich in dem Zusammenhang absolut gar nix anfangen.
    Ich müsste irgendwie eine weitere XML-Datei haben die angibt wie die Modelle geladen werden. Irgendwelche Ideen, wie man so etwas aufbauen müsste?

    > Zur XSLT-Processor: Ja die XSL-Library in PHP ist nicht standardmäßig aktiviert, dennoch ist sie auch auf vielen Massenhostern vorhanden (z.B. HostEurope).
    Ja richtige Hoster für die man auch Geld ausgibt sind auch teilweise sogar dazubereit, Extensions nach zu installieren.
    Kostenlose Hoster wie z.B. funpic (welche sogar debug_backtrace() deaktivieren) … sind da aber anders. Ich denke wenn müsste ich eine alternative bieten: Also das Smarty Templates auch weiterhin funktionieren.

    mfG

  12. Am 16. Juli 2009 um 21:07 Uhr
    Mario Volke gravatar

    Ich verstehe nicht ganz, warum du erst im View weißt, welche Daten du benötigst, normalerweise könnte doch der Controller alles bereitstellen was für die Transformation benötigt wird. Und falls man mehr komfort haben möchte kann man eine zweite Schicht einbauen. Viele Frameworks machen das ähnlich z.B. Zend_Layout. Einen solchen 2-schichtigen View bräuchtest du dann vielleicht auch. Das würde dann so aussehen, dass einzelne XML-Daten aus den Controllern geladen werden, über eine Layout Klasse zusammengesetzt werden und erst dann zur Transformation gehen.

  13. Am 16. Juli 2009 um 23:29 Uhr
    Dominik gravatar

    Ich will eigentlich das die Module relativ einfach aufgebaut sind und nicht nur aus Seiten weise $this->assign(…) bestehen.
    Mal ein Beispiel, beim News Modul gebe ich ein Array ähnliches Objekt ans Template zurück in dem sich alle News Einträge befinden.
    Diese News Einträge sind wiederum Objekte. Diese Objekte bilden direkt die Datenbank ab, heißt sie haben Eigenschaften die den Datenbankfeldern entsprechen und Funktionen die die Beziehungen verkörpern. Ein MVC mit dahinterliegendem ORM eben.
    Wenn ich nun z.B. den Benutzer haben möchte der den aktuellen Newseintrag verfasst hat brauch ich nur die Funktion aufrufen die Beziehung zur Benutzertabelle abbildet und ich habe ein Objekt das eben den Benutzer verkörpert.
    Gut es geht auch anders, guckt man sich mal das Framework CakePHP an. Hier werden riesen große Arrays aus der Datenbank ausgelesen und an das Template übergeben.
    Und der Benutzer wundert sich dann wieso der Krams so langsam ist …
    Wenn ich jetzt auch solche Arrays erzeugen wollte, müsste ich (um beim Beispiel des News-Autors zu bleiben) alle Datensätze durchlaufen den Autor auslesen und das ganze als multidimensionales Array wieder zusammen backen. Finde ich aber nicht wirklich toll und ist vor allem langsam.
    Schöner wer es wenn das auslesen eben erst dann passiert wenn er das XML-Dokument per DOM-API zusammen baut.

    Das mit dem Zend_Layout sieht mir mehr nach dem aus, was bei mir eh bereits gegeben ist. Nämlich das die Seite in mehreren Phasen gerendert wird.
    Eben das es ein großes Basis Template gibt (welches eben das HTML-Grundgerüst enthält) und dann für die Navigation und jeden Seitentyp sein eigenes Unter-Template.

  14. Am 8. August 2009 um 00:09 Uhr
    Timo gravatar

    Servus.

    Wenn ich das hier so lese, insbesondere mit der Sprachdatei (oben), dann frage ich mich wie es mit der Performance aussieht.

    Ist das im annehmbaren Bereich, wenn man sehr viele Sprachen und viele Platzhalter für eine Seite hat oder sollte man da lieber auf was anderes zurückgreifen?

    Meines Erachtens nach ist das sehr langsam, da die Datenmenge exponentiell ansteigt und es doch sehr viel wäre.

  15. Am 8. August 2009 um 10:41 Uhr
    Mario Volke gravatar

    Hallo Timo,

    im prinzip lässt sich der Prozess wunderbar, sogar auf 2 Ebenen cachen.
    Zum einen kann man den generierten XML-Output und zum anderen das fertig transformierte HTML-Markup cachen. Das führt dann doch zu einer sehr guten Skalierbarkeit.

    Aber hier kommt noch etwas anderes zum tragen, denn im Gegensatz zu Smarty oder anderen üblichen PHP-Template-Engines läuft der XSLTProcessor in C-Code und ist damit sogar wesentlich schneller.

  16. Am 12. September 2009 um 13:06 Uhr
    Recherche: Multilinguale sprechende URL - Coders Talk @ tutorials.de: Forum, Tutorial, Anleitung, Schulung & Hilfe gravatar

    [...] durchaus Sinn machen, dann kann man das Dokument einfach transformieren, hier ein Tutorial dazu: http://www.webholics.de/2007/10/01/x…in-php-teil-7/ Die Verwaltung muss mit XML nicht unbedingt schwer sein,

  17. Am 8. März 2010 um 09:23 Uhr
    Nico gravatar

    Servus

    Die Engine sieht trotz ihrer Einfachheit relativ leistungsstark aus. Als XML NEuling versuche ich erstmal “alles” zu verstehen. Das klappt soweit auch ganz gut … nur :

    funktioniert bei mir irgendwie gar nicht. Daher mal ne Frage wie erreiche ich, dass die PHP funktionen in meinem XML testprojekt auch ausgeführt werden ?

  18. Am 8. März 2010 um 10:53 Uhr
    Mario Volke gravatar

    Hallo Nico,

    ich kann dir so ganz ohne Beispiel leider schlecht weiterhelfen, wenn du PHP-Funktionen vom Stylesheet aus aufrufen möchtest, solltest du den Teil 6 lesen, dort steht eigentlich alles beschrieben.

  19. Am 23. Oktober 2010 um 23:38 Uhr
    Chris gravatar

    Hi ich finde es sehr coll das es sowas gibt und nun ist meine Frage gibt es das gesammte script zum runterladen?

    bitte das scriot an meine e-mail senden:

    cmagerabronn110@web.de

  20. Am 24. Oktober 2010 um 09:58 Uhr
    Mario Volke gravatar

    Hallo Chris,

    direkt am Ende dieses Artikels findest du den Downloadlink zu den hier beschriebenen Features. Das sind natürlich alles nur Code-Snippets. Wenn du XSLT produktiv als Template-Sprache einsetzen möchtest, solltest du sowieso darüber nachdenken eine MVC-Struktur aufzubauen oder ein bestehendes Framework herzunehmen und dieses mit XSLT im View zu erweitern. Ich selbst habe das so bereits in Projekten gemacht. Viel mehr kann ich dir daher erstmal leider nicht zukommen lassen.

  21. Am 24. Oktober 2010 um 10:23 Uhr
    Chris gravatar

    ok, aber wie nutz man das script?
    kann man das auf phpinstalliieren?

  22. Am 24. Oktober 2010 um 10:26 Uhr
    Mario Volke gravatar

    Was meinst du mit installieren? In dem Zip-Archiv findest du eine Datein “index.php”. Dabei handelt es sich um ein ganz normales PHP-Script, welches du z.B. über PHP+Apache im Browser aufrufen kannst. Falls du dich mit PHP noch nicht auskennen solltest: Ich setze in dieser Artikelserie solches Basiswissen natürlich voraus.

  23. Am 26. Oktober 2010 um 15:34 Uhr
    Chris gravatar

    bei mir gehz das script ledier nicht

  24. Am 26. Oktober 2010 um 16:09 Uhr
    Mario Volke gravatar

    Welcher Fehler kommt?
    Welche PHP-Version? Welches System?

  25. Am 2. Juni 2011 um 09:08 Uhr
    Simon gravatar

    Hallo Mario,
    ich habe mich in letzter Zeit in dein Tutorial eingelesen. Es gefällt mir sehr gut und ist auch sehr hilfreich. Allerdings habe ich jetzt noch eine Frage, die ich mir von jemanden beantworten lassen möchte, der sich mit XSLT wirklich gut auskennt.
    Ich habe ein HMVC Framework in PHP entworfen und dort möchte ich XSLT einsetzen. Bei HMVC werden ja die Seiten in Module aufgeteilt. Es wird also im Controller die zuständige Viewklasse instanziert; Dort können dann Variablen hinzugefügt werden. Dann gibt es noch eine Methode render(), die das ganze dann ausgibt, aber je nach Ausgabeformat unterschiedlich ist. Oben wurde schon das mit der zweiteiligen View angesprochen. Wie kann ich aber jetzt konkret Teile einer XSLT-View rendern und nachher wieder zusammenbauen, weil ja der “Untercontroller” auch seine Daten über render() an den Obercontroller liefern soll. Wahrscheinlich habe ich mich etwas komisch ausgedrückt, aber ich hoffe, du verstehst mein Problem.
    Schöne Grüße,
    Simon

  26. Am 2. Juni 2011 um 13:27 Uhr
    Mario Volke gravatar

    Hallo Simon,

    ich bin mir nicht ganz sicher, wie sich dein HMVC-Framework genau zusammen setzt. XSLT ist ja erstmal nur eine Transformation von einem XML-Schema in ein anderes (oder auch HTML, etc.). Wenn du alles in einer Transformation abhandeln willst, dann musst du auch vorher alle Daten im XML-Format liefern. Was du aber evtl. machen könntest, wäre ein mehrstufiger Transformationsprozess. D.h. du renderst erst nur die Daten zusammen (XML -> XML) und in der obersten View erstellst du daraus das HTML-Markup. Ich weiß aber nicht, ob das für dich die optimale Lösung ist.

  27. Am 4. Juni 2011 um 11:21 Uhr
    Simon gravatar

    Danke für die Antwort. Ich glaub’, mir ist jetzt ein Licht aufgegangen. Probieren werde ich jetzt mal Folgendes: alle “Untercontroller” geben die dem View zugeordneten Daten/Variablen an den jeweiligen Obercontroller als XML zurück. So müsste dann am Ende eine XML-Datei mit allen Daten vorliegen. Dann muss ich diese XML nur noch durch den XSLT Processor jagen. Nun aber noch ein Problem, woher weiß XSLT, welche XSL-Stylesheets außer dem Default es für das Rendern verwenden soll? Sollen die dann für jede Seite per eingebunden werden? Am liebsten hätte ich es so, dass man schon beim hinzufügen des Contents entscheiden könnte, welches XSL Template auf den jeweiligen Knoten angewandt wird.

  28. Am 4. Juni 2011 um 12:07 Uhr
    Mario Volke gravatar

    XSL ist sehr mächtig. Prinzipiell gibt es viele Möglichkeiten dafür. Die einfachste wäre wahrscheinlich mit template match=”" zu arbeiten. Du kannst ja für alle möglichen Tags (XPath) ein Template erstellen.

Einen Kommentar schreiben