Ale×: / (PHP) Sprachdateien im Template oder im Backend verarbeiten?

Hallo,

gegeben ist folgendes:
Eine Webanwendung ist in Backend, Templates uns Sprachdateien aufgeteilt. In den Sprachdateien sind die Strings folgendermaßen als Array gespeichert:

<?php  
$lang['back'] =       'zurück';  
$lang['back_page'] =  'zurück zu Seite [page]';  
$lang['time'] =       '%d.%m.%Y, %H:%M'  
// etc ...  
?>

Im Template könnte man es einfach so notieren:

<a href="..."><?php echo $lang['back']; ?></a>

Die Frage ist nun: wo ersetze ich die Platzhalter (z.B. "[page]") oder Zeit-Formatierungen - im Backend oder im Template? Einerseits haben die Sprachdateien nichts im Backend verloren (und müssten durch Funktionen/Klassen mitgeschleift werden), andererseits hätte ich auch gerne so wenig PHP-Code (str_replace, printf, strftime...) wie möglich im Template. Wo gehört sowas also hin?

Ale×

  1. Wo gehört sowas also hin?

    Weder ins Template noch ins Backend - es gehört eindeutig in den Code der das Template verarbeitet.

    Das Template ist nur die Vorlage mit Platzhaltern, das Backend dient idR. zum Bearbeiten von Inhalten oder zum Verändern von Umgebungsvariablen usw.

    Die Routine die die Inhalte aus der Datenbank (oder einem Sprachkonfigurationsfile nimmt), sollte auch die aktuelle Sprache prüfen (Accept-Language bzw. eine manuell gewählte Sprache) und dann die entsprechende Sprache ausgeben.

    1. Hallo suit,

      Weder ins Template noch ins Backend - es gehört eindeutig in den Code der das Template verarbeitet.

      Das hört sich schon mal vernünftig an. Werde ich mir durch den Kopf gehen lassen!

      Das Template ist nur die Vorlage mit Platzhaltern, [...]

      Ja, theoretisch. Aber es fängt schon damit an, dass man z.B. nicht um Schleifen herumkommt.

      Ale×

      1. Hello,

        Weder ins Template noch ins Backend - es gehört eindeutig in den Code der das Template verarbeitet.

        Das hört sich schon mal vernünftig an. Werde ich mir durch den Kopf gehen lassen!

        Aber es handelt sich doch um Daten. Und wenn die vom Programm (Controller) angefordert werden, hat sich das Datenbankmodul (Model) um die Bereitstellung zu kümmern. Ins Template gehört die Verarbeitung schon gar nicht, das bekommt nur fetig vorgekautes Futter und auch die Schnittstelle fürs Template muss sich darauf verlassen können, nicht noch selbst aktiv zu werden. Die darf sich darum kümmern, wie die Daten rot oder grün dargestellt werden können, wenn dies verlangt wird.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. Hallo Tom,

          Aber es handelt sich doch um Daten. Und wenn die vom Programm (Controller) angefordert werden, hat sich das Datenbankmodul (Model) um die Bereitstellung zu kümmern.

          Hört sich auch gut an, scheint aber nicht ganz eindeutig zu sein. Die Wikipedia meint dazu: "Auch für die Formatierung der Rohdaten und die Internationalisierung ist nicht definiert, wo diese erfolgen."

          Ale×

          1. Hello,

            Aber es handelt sich doch um Daten. Und wenn die vom Programm (Controller) angefordert werden, hat sich das Datenbankmodul (Model) um die Bereitstellung zu kümmern.

            Hört sich auch gut an, scheint aber nicht ganz eindeutig zu sein. Die Wikipedia meint dazu: "Auch für die Formatierung der Rohdaten und die Internationalisierung ist nicht definiert, wo diese erfolgen."

            Das ganze MVC-Gedöns ist nicht eindeutig und wohl eher am Biertisch entstanden. Denn sonst hätte man sich beim "Controller" etwas mehr Mühe gegeben und den zerlegt in mindestens drei Teile:

            Manipulativer Teil     (hier haben Benutzereingriffe Auswirkung)
              Optionaler Teil        (das sind gerade bei OOP die interessanten Teile)
              Kerncontroller         (im Grenzfall besteht der nur noch aus einer starren Kopplung
                                      aus Model und View und ist damit eigentlich überflüssig)

            Darüber habe ich mich gestern mit Dedlfix schon in einem anderern Thread ge.....

            Liebe Grüße aus dem schönen Oberharz

            Tom vom Berg

            --
            Nur selber lernen macht schlau
            http://bergpost.annerschbarrich.de
            1. Das ganze MVC-Gedöns ist nicht eindeutig und wohl eher am Biertisch entstanden. Denn sonst hätte man sich beim "Controller" etwas mehr Mühe gegeben und den zerlegt in mindestens drei Teile:

              Es gibt ja auch nicht _den_ Controller.

              Deine Auflistung entspricht in etwa dem, wie man das wohl in der Praxis umsetzt. Der Kernkontroller (oder Frontkontroller), startet die wichtigsten Prozesse. z.b. auch Plugins, was bei dir der optinale Teil ist und dann wird weitergeleitet auf die eigentliche Aktion oder wie du es nennst manipulativen Teil. Das halte ich für eine gelungene Umsetzung des MVC Controller Konzept.

              Struppi.

  2. echo $begrüßung;

    Die Frage ist nun: wo ersetze ich die Platzhalter (z.B. "[page]") oder Zeit-Formatierungen - im Backend oder im Template? Einerseits haben die Sprachdateien nichts im Backend verloren (und müssten durch Funktionen/Klassen mitgeschleift werden), andererseits hätte ich auch gerne so wenig PHP-Code (str_replace, printf, strftime...) wie möglich im Template. Wo gehört sowas also hin?

    Mit einer Übersetzung in diverse Sprachen allein ist es nicht immer getan. Benutzer senden lokalisierte Werte, die erst einmal normalisiert werden müssen, bevor man mit ihnen Datenverarbeitung betreiben kann. Anschließend müssen diese Daten wieder lokalisiert werden. Dabei können sich auch Inhalte ändern, wenn zum Beispiel zwischen dem US-amerikanischen und dem metrischen System umgerechnet werden muss. Die Normalisierung der Eingabedaten kann nicht vom Template vorgenommen werden, denn das kommt erst viel später zum Einsatz. Es wäre dann eigentlich praktisch, wenn der Normalisierer am Ende auch wieder lokalisiert, denn die Regeln dazu kennt er ja bereits. Sie müssen dann nicht zweimal implementiert werden.

    Die Programmflusssteuerung greift also vor und nach der Datenverarbeitung auf die Dienste des Lokalisierers zu. Genauso außenstehend könnte man den Textübersetzer implementieren. Wann immer ein Text zu übersetzen ist, fragt man ihn danach. Am Anfang bekommt er einen Default-Wert gesetzt, welche Sprache er zu liefern hat. Per optionalem Parameter kann man aber auch gezielt eine Phrase in einer bestimmten Sprache abfragen. Die Sprachdateien verwaltet der Übersetzer selbständig. Mitgeschleift muss da gar nichts werden. Die Programmflusssteuerung (oder wer auch immer) muss nur wissen, welcher Text auszugeben ist, der Übersetzer liefert ihn. (Eigentlich ist der Übersetzer in dem Szenario nur ein Lagerverwalter, denn er sucht ja nur vorgefertigtes Zeug aus seinem Bestand.)

    Eine Implementierung einer Texteverwaltung ist gettext, das auch Einzug in PHP gehalten hat. Auch wenn du gettext vermutlich nicht verwenden wirst, kannst du dir daran ein Beispiel nehmen. Die bekannteste Funktion von gettext heißt gettext() und sie hat ein Alias: _(). Kurz und knackig. So ein Unterstrich macht den Quelltext nur unwesentlich länger, eigentlich fällt nur der Text in der Default-Sprache oder ein Platzhaltername ins Auge. Und auch Tippfaule dürften kaum einen Ablehnungsgrund finden.

    Welche Texte und Daten in das Template einzufügen sind, ermittelt die Programmflusssteuerung. Der Übersetzer arbeitet kaum merklich nebenbei mit und das Template sowie die dazu gehörende Logik können sich ganz auf ihre Darstellungsaufgaben konzentrieren.

    Selbst wenn die Text-Platzhalter schon im Template eingearbeitet sind und die Steuerung keinerlei Gedanken mehr daran verschwenden muss, kann man immer noch den Übersetzer-Aufruf kaum sichtbar hinzufügen und bleibt sauber, was die Zuständigkeiten angeht.

    echo "$verabschiedung $name";

    1. Hallo dedlfix,

      danke für die ausführliche Antwort! In der Theorie ist das auch ganz einleuchtend. Wenn es nur um feste Zeichenketten ginge, wäre auch alles kein Problem. Ich weiß nur immer noch nicht, wo ich am besten die Platzhalter (z.B. "[number] Kommentare" oder "%d Kommentare") ersetze.
      Angenommen, die Anzahl der Kommentare wird in einer Methode einer Klasse berechnet. Dort könnte ich nun ganz einfach den Platzhalter durch die Zahl ersetzen. Die Sprach-Strings müssten dazu aber "global" sein, was ja nicht Sinn der Sache sein kann. Selbst wenn die Strings nicht verändert werden müssen, brauche ich z.B. das Datumsformat mindestens als Parameter (das meinte ich mit "mitschleifen").
      Alle Ersetzungen und Formatierungen außerhalb der Klasse durchzuführen, kommt mir wegen der Performance recht ungünstig vor (nochmal alle Kommentare durchlaufen...).

      gettext

      Ja, damit hatte ich auch schon mal das Vergnügen. Ich fand das allerdings etwas unhandlich, da es sich hier nicht um Text-Dateien handelt.

      Ale×

      1. echo $begrüßung;

        Wenn es nur um feste Zeichenketten ginge, wäre auch alles kein Problem. Ich weiß nur immer noch nicht, wo ich am besten die Platzhalter (z.B. "[number] Kommentare" oder "%d Kommentare") ersetze.

        Wer ermittelt denn die variablen Werte? Wer sorgt dafür, dass die passenden Texte geholt und Anpassungen (unterschiedliche Schreibweisen bei Einzahl und Mehrzahl) vorgenommen werden? Das ist Teil der Geschäftslogik und des Lokalisierers. Die Anzeigelogik bekommt die Texte fertig berechnet übergeben.

        Angenommen, die Anzahl der Kommentare wird in einer Methode einer Klasse berechnet. Dort könnte ich nun ganz einfach den Platzhalter durch die Zahl ersetzen. Die Sprach-Strings müssten dazu aber "global" sein, was ja nicht Sinn der Sache sein kann. Selbst wenn die Strings nicht verändert werden müssen, brauche ich z.B. das Datumsformat mindestens als Parameter (das meinte ich mit "mitschleifen").

        Das Wissen um die lokal richtige Formatierung von Werten muss der Lokalisierer mitbringen. Alternativ kann man den Platzhalter mit Zusatzinformation ausstatten.

        "Heute ist der {0:dd.MM.yyyy}. Die Temperatur beträgt {1} °C."
          "Today is {0:dd/MM/yyyy}. The temperature is {1} °C."

        So sehen beispielsweise Platzhalterstrings mit Formatierinformation (für den Platzhalter 0) unter .NET aus. Wer auch immer den Platzhalter ersetzt muss beim Einsetzen des Wertes die Formatierinformation extrahieren und berücksichtigen.

        Alle Ersetzungen und Formatierungen außerhalb der Klasse durchzuführen, kommt mir wegen der Performance recht ungünstig vor (nochmal alle Kommentare durchlaufen...).

        Für Performance zählen nur Messergebnisse, am besten vor und nach einem Optimierungsversuch.

        Wenn du Werte erst zusammentragen musst, die später in die Ausgabe eingearbeitet werden sollen, kannst du doch dabei gleich die Lokalisierung erledigen lassen.

        » gettext
        Ja, damit hatte ich auch schon mal das Vergnügen. Ich fand das allerdings etwas unhandlich, da es sich hier nicht um Text-Dateien handelt.

        Mein Vorschlag ging auch eher dahin, sich an der allgemeinen Arbeitsweise und an _() ein Beispiel für eine eigene Implementation zu nehmen.

        echo "$verabschiedung $name";

    2. Hallo dedlfix,

      Die bekannteste Funktion von gettext heißt gettext() und sie hat ein Alias: _(). Kurz und knackig.

      Wie könnte ich denn in einer Template-Klasse ein Alias für so etwas definieren?

      <?php $this->echo_local_string('back_home'); ?>

      Also, dass ich im Template nur noch z.B.

      <?php $str('back_home'); ?>

      notieren muss.

      Ale×

      1. Hello,

        <?php $this->echo_local_string('back_home'); ?>

        Also, dass ich im Template nur noch z.B.

        <?php $str('back_home'); ?>

        notieren muss.

        Habs nicht ausprobiert, aber so wie ich PHP einschätze:

        $str = 'this->echo_local_string';
          $str('back home');

        Mit "normalen" Funktionen geht es so.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. Hallo Tom,

          $str = 'this->echo_local_string';
            $str('back home');

          Das hatte ich auch schon ausprobiert, geht leider nicht (auch nicht mit $str = '$this->echo_local_string'):
          Fatal error: Call to undefined function this->echo_local_string()

          Ale×

      2. echo $begrüßung;

        Wie könnte ich denn in einer Template-Klasse ein Alias für so etwas definieren?
        <?php $this->echo_local_string('back_home'); ?>

        Es geht nicht. $this lässt sich nicht in eine variable Variable packen und so eine Kombination gleich gar nicht. Referenzen auf Funktionen/Methoden gibt es auch nicht.

        echo "$verabschiedung $name";