© TYPO3 Association, typo3.org

Bei der Entwicklung mehrsprachiger Extensions für TYPO3 kommt man früher oder später in die Verlegenheit die richtigen (also in der benötigten Sprache) Datensätze aus der Datenbank zu holen. Viele Entwickler nutzen dazu eigene WHERE Anweisungen mithilfe derer sie den richtigen Datensatz ermitteln. Das sieht dann oftmals so aus:

// don't do this!
$where = 'uid = '.$uid.' AND  sys_language_uid = '.$GLOBALS['TSFE']->sys_language_uid.' AND hidden = 0 AND deleted = 0';
Schlechtes Beispiel für eine TYPO3 Datenbankabfrage

Die benötigte Sprache wird direkt gesetzt was zur Folge hat das man evtl. keinen Datensatz geliefert bekommt. Dies ist logischerweise dann der Fall wenn Der Datensatz noch nicht in die betreffende Sprache übersetzt wurde. Hört sich doch gar nicht so übel an?

 

Nicht ganz. Einerseits  bricht man mit den Coding Guidelines und andererseits macht man ein tolles TYPO3 Feature zunichte: den localization mode. Mit dem localization mode kann das Verhalten von TYPO3 bei Mehrsprachigkeit, oder besser das Verhalten von TYPO3 bei fehlenden Sprachdatensätzen, konfigurieren.

 

TYPO3 und Mehrsprachigkeit

TYPO3 bietet ja bekanntlich sehr flexible Möglichkeiten um die Internationalisierung (gerne mit i18n abgekürzt) umzusetzen.

 

Prinzipiell  kann auf Seitenebene mit der Option config.sys_language_mode gearbeitet werden. Diese gibt an wie mit der Seite umgegangen wird. So kann zum Beispiel per strict eine Fehlermeldung angezeigt werden wenn die Seite nicht verfügbar ist. content_fallback hingegen bietet die Seite dann in der konfigurierten default Sprache an.

    Auf Inhaltsebene werden per config.sys_language_overlay = 1 nach Übersetzungen gesucht, aber immer alle Elemente angezeigt -- notfalls eben in der default Sprache. Mit dem Wert  hideNonTranslated wird nicht übersetzter Inhalt hingegen versteckt.

     

    Das sind nur die beiden wichtigsten Einstellungen und selbst deren Funktionen habe ich hier nicht gänzlich abgedeckt. Es existieren noch viele weitere Möglichkeiten (wie etwa config.sys_language_softMergeIfNotBlank), aber der Teil hier langt zum Verständnis.

     

    Schaut euch die untenstehende Linksammlung an, dort erfahrt ihr mehr.

     

    SQL Anweisung für Mehrsprachige Extensions

    Um das oben beschriebene Verhalten auch in eigenen Extension möglich zu machen sollte man darauf achten mehrsprachige Datensätze korrekt aus der Datenbank holen.

     

    Zuerst holt man sich die Standardsprache aus der Datenbank. Das macht man normalerweise mit mit der WHERE Bedingung sys_language_uid = 0.

    Findige TYPO3 User schaffen es aber auch einen Datensatz anzulegen, ohne vorher die Default-Sprache anzulegen. Um diesem Umstand vorzubeugen ändern wir die SQL Anweisung auf folgende Bedingungen ab: Standard-Sprache ODER den übersetzten Datensatz (der dann aber kein parent Element haben darf).

      <?php
      // we need at last uid, pid, sys_language_uid
      $select = 'uid, pid, sys_language_uid, text, title';
      $table = 'tx_myext_data';    
      // we try to get the default language entry (normal behaviour) or, if not possible, currently the needed language (fallback if no default language entry is available)
      $where = '(sys_language_uid IN (-1,0) OR (sys_language_uid = ' .$GLOBALS['TSFE']->sys_language_uid. ' AND l18n_parent = 0))';  
      // always (!) use TYPO3 default function for adding hidden = 0, deleted = 0, group and date statements    
      $where  .= $GLOBALS['TSFE']->sys_page->enableFields($table);    
      $order = '';
      $group = '';
      $limit = '';
      $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($select, $table, $where, $group, $order, $limit);
        
      while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
        // check for language overlay if:
        // * row is valid
        // * row language is different from currently needed language
        // * sys_language_contentOL is set
        if (is_array($row) && $row['sys_language_uid'] != $GLOBALS['TSFE']->sys_language_content && $GLOBALS['TSFE']->sys_language_contentOL) {
          $row = $GLOBALS['TSFE']->sys_page->getRecordOverlay($table, $row,$GLOBALS['TSFE']->sys_language_content, $GLOBALS['TSFE']->sys_language_contentOL);
        }  
        if ($row) {
          // get correct language uid for translated realurl link    
          $link_uid = ($row['_LOCALIZED_UID']) ? $row['_LOCALIZED_UID'] : $row['uid'];
          
          // do stuff with the row entry data  like built HTML or prepare further usage
        }
      }
      ?>
      Mehrsprachige TYPO3 Extensions: Mehrsprachige Datensätze korrekt aus der DB holen.

      Es folgt eine normale TYPO3 DB Abfrage. Anschließend testen wir auf eine valides Array, ob wir eine andere Sprache überhaupt benötigen und ob die Konfiguration für den localization mode (wie er oben kurz erläutert wurde) vorhanden ist. Wenn ja, ereledigt TYPO3 den Rest: den Overlay-Datensatz mit der gewünschten Sprache ziehen, um damit den vorhandenen (alten) Datensatz ganz oder teilweise zu ersetzen bzw. die row zu löschen. Letzteres müsste evtl. bei gesetzter  hideNoneTranslated Option gemacht werden.

       

      So erhalten wir genau die Datensätze die wir brauchen und können die Ausgabe / Weiterverarbeitung angehen.

       

      Hinweise

      Gegebenenfalls muss sys_language_uid im select mit $TCA[$table]['ctrl']['languageField'] ersetzt werden (global $TCA; Anweisung nicht vergessen!). Ist aber im Normalfall nicht nötig.

       

      Eine Problematik bleibt bestehen: Wenn der Standard-Datensatz versteckt wurde, kann keine Übersetzung gefunden werden (auch wenn diese nicht versteckt ist und angezeigt werden soll).  Hier wäre generell das "mehrere Seitenbaumäume" Prinzip vorzuziehen.

      Eine andere Möglichkeit wäre es die enableFields Anweisung wegzulassen und erst die fertige (bereits mit getRecordOverlay bearbeitete) row manuell auf hidden, deleted, Gruppen und Zeit zu prüfen. 

       

      Referenzen:

       

       

      Wie immer freue ich mich über Feedback und Hinweise. Wenn ich kann helfe ich auch gerne, eine Antwort ist euch in jedem Fall sicher.

        Die Kommentarfunktion ist für diesen Artikel deaktiviert.

        5 Kommentare

        Danke für den sehr guten Artikel!


        Weil es eben diese Muster Extension nicht gibt habe ich diesen Artikel geschrieben. Ich hab mir das aus vers. Extensions zusammengesucht und wollte das mal notieren.

         

        Ich glaube das mit der Sortierung (wie auch das mit den versteckten Originaldatensätzen) kann bei dem Konzept nicht funktionieren.

        Eventuell wird das bei Extbase bald implementiert. Noch ist Mehrsprachigkeit und Extbase ja auch eine einzige Katastrophe. Guckst du hier:

        forge.typo3.org/issues/32072

        forge.typo3.org/issues/32216


        Danke für diesen Artikel!

         

        Wenn man nach getRecordOverlay sucht findet man in fast jedem Forum oder Maillinglist Artikel einen Link auf diese Seite.

         

        Ideal wäre, wenn es eine Mehrsprachige Muster Extension geben würde ( multilang_example, oder so ähnlich ) bei welcher man einen Titel und einen Text übersetzen kann.

         

        Ebenfalls müsste auch folgende Config korrekt funktionieren.

         

        config.sys_language_mode=content_fallback;1,2,3,0

         

        Und bei der Übersetzten Variante wir der Titel korrekt nach A-Z sortiert ( und nicht die Sortierung der Standardsprache verwendet )

         


        l18n_parent oder l10n_parent?

        Hey Markus,

         

        also wenn ich in die Tabellen einiger Extensions (tt_news, t3blog, aber auch tt_content) sehe steht da aber überall l18n_parent. I18N wird für gewöhnlich genutzt um die Gestaltung (die "Möglichkeit des Einpflegens von Sprachen") der Software zu beschreiben, während L10N meist den eigentlichen Übersetzungsvorgang beschreibt. Ich würde meinen das passt so, aber was da jetzt anerkannte Praxis ist, ka.


        Kleine Korrektur

        Die Spalte heißt nicht l18n_parent sondern l10n_parent

         

        (localisation, nicht internationalisation).

         

        Aber sonst: guter Beitrag, danke :)