Neos Workshop Teil 5 - Mehrsprachige Seiten mit Neos

Soll die Webpräsenz auch international Anklang finden, sollte sie auch in den Sprachen des Zielpublikums vorliegen. Dieser fünfte Teil des Neos CMS Tutorials beschäftigt sich daher mit Mehrsprachigkeit, einer Anwendung des mächtigen Konzepts der Node Dimensions.

Ich liebe es wenn ein Plan funktioniert!

Daniel Lienert
Daniel ist immer auf der Suche nach technologisch innovativen aber dennoch nachhaltig stablilen Lösungen für unsere Kunden.
Lesedauer: ca. 6 Minuten

Im Laufe des Tutorials haben wir ein eigenes Site Package mit eigenen Node Types erstellt, mit denen es möglich ist, domänenspezifischen Content, in unserem Beispiel den Content eines Weinladens, zu pflegen. In diesem Teil geht es nun darum, alle Elemente der Seite, allem voran den Content, in mehreren Sprachen verfügbar zu machen. Alle Änderungen, welche dazu vorgenommen werden müssen, können wie immer im Detail im zugehörigen Git Repository nachvollzogen werden.

Übersicht der bereits erschienene Workshop-Teile

Die folgenden Teile des  Neos-Workshops existieren bisher. Die dort erarbeitete Website dient als Grundlage für diesen Workshop-Teil:

Bevor wir mit der eigentlichen Arbeit an der Mehrsprachigkeit des Contents beginnen, ist es sinnvoll sich etwas mit der Theorie dahinter zu beschäftigen: den Content Dimensions.

Einführung in Neos Content Dimensions

Mit dem Konzept der Content Dimensions ist es möglich, eine Node innerhalb des Neos Content Repositories in mehreren Varianten zu speichern. Eine solche Variante oder Dimension kann beispielsweise die Sprache sein, eine andere, die Zielgruppe der Webseite. So lassen sich mehrere Repräsentationen des gleichen Contents in einem Single-Tree-Ansatz anbieten.

Das Konzept lässt sich am besten an einem Beispiel erklären, wozu die nebenstehende Grafik dienen soll. Angenommen der Standard-Text unserer Seite ist in Deutsch geschrieben und richtet sich an private Weingenießer. Um die Seite in mehreren Sprachen anzubieten, wird eine Dimension "Sprache" hinzugefügt. Diese Dimension kann beliebig viele "Dimension Presets" erhalten, in unserem Fall sind das die Sprachen "Englisch" und "Französisch". Sprache ist also eine Dimension, in welcher eine Content Node in unterschiedlichen Varianten vorliegen kann und sicher der am häufigsten anzutreffenden Anwendungsfall der Content Dimensions ist. 

Dabei ist die Anzahl der Dimensionen nicht begrenzt. Im Beispiel fügen wir eine zweite Dimension "Audience" hinzu, mit der wir unseren Content für verschiedene Zielgruppen anbieten können. Während sich unser Content standardmäßig an private Weingenießer richtet, können wir in einer weiteren Dimension speziellen Content für unsere Zielgruppe der professionellen Sommeliers anbieten. Und natürlich sind auch alle Kombinationen der Dimensionen möglich. So kann auch der englische Content in beiden Zielgruppenvarianten existieren.
 

Neos Content Dimensions

Fallbacks sind beliebig definierbar

Inhalte werden in Neos immer auf Basis eines Kontextes abgerufen und gerendert. Ein Kontext wäre hier zum Beispiel: "Französisch für Sommeliers". Für den Fall, dass ein angefragter Inhalt in diesem bestimmten Kontext nicht vorhanden ist, können beliebige Fallbacks definiert werden. Ist der Inhalt in diesem Kontext nicht vorhanden, kann zunächst nach Content in normalem französisch und dann in der Variante "Englisch für Sommeliers" gesucht werden.

Fallbacks können aber auch absichtlich weg gelassen werden. Ist eine Seite dann für einen bestimmten Kontext nicht übersetzt, wird diese Seite dann auch im Menü nicht aufgeführt. Im Folgenden werden wir uns auf eine Dimension beschränken und unsere Seite in den Sprachen Englisch und Französisch übersetzbar machen.

Content Repository für mehrsprachige Inhalte vorbereiten

Nachdem wir uns die Theorie hinter den den Content Dimensions angesehen haben, können wir nun mit der Konfiguration einer Dimension für die Sprache beginnen. Dazu fügen wir in der Settings.yaml im Namespace Neos.ContentRepository.contentDimensions die Dimension language hinzu.

Wir gehen in diesem Beispiel von Deutsch als Standardsprache aus, daher setzen wir den Wert default und defaultPreset auf "de". Die Werte label und icon dienen der Gestaltung des Sprachwählers im Neos Backend.

Danach werden die presets für die Sprachdimension konfiguriert. Jedes Preset besteht aus einem label zur Identifizierung, dem uriSegement, welches in der URL der Seite den Kontext identifiziert und den values, den gültigen Werten für einen gegebenen Kontext. Die Liste der values gibt dabei an, in welcher Reihenfolge nach Content im Repository gesucht wird. 

Für unserer Konfiguration bedeutet das konkret: Soll eine Seite im Kontext "en" gerendert werden, wird im Content Repository nach Daten für dieses Preset gesucht. Ist keine Übersetzung für Englisch vorhanden, wird die Seite nicht angezeigt. Für Französisch ist "en" als Fallback konfiguriert. Wird eine angefragte Seite also nicht in der französischen Übersetzung gefunden, wird als nächstes nach der englischen Version gesucht.

	Neos:
	  ContentRepository:
	    contentDimensions:
	      language:
	        label: Language
	        icon: icon-language
	        default: de
	        defaultPreset: de
	        presets:
	          de:
	            label: Deutsch
	            values:
	              - de
	            uriSegment: deutsch
	          en:
	            label: English
	            values:
	              - en
	            uriSegment: english
	          fr:
	            label: Français
	            values:
	              - fr
	              - en
	            uriSegment: francais

Konfiguration der Content Dimensins in der Settings.yaml

Sobald die Content Dimensions mit Presets konfiguriert wurden, erscheint im oberen Bereich im Inhaltsmodul des Backends ein Dropdown zur Auswahl der Presets der konfigurierten Dimensionen – in unserem Fall der Sprache. Erscheint das Dropdown nicht, liegt es meist am Browsercache. Das Leeren des kompletten Inhaltscaches des Browsers löst dann das Problem.

Durch Auswahl einer anderen Sprache, beispielsweise Englisch, werden zwei Optionen zur Auswahl angeboten, um die Seite zu übersetzen. "Leer erstellen" legt die Seite ohne Inhalte an, während "Erstellen und kopieren" die Seite mit allen Struktur- und Inhaltselementen kopiert, so dass diese danach übersetzt werden können.

Dimension Selector
Leer erstellen oder Elemente kopieren

Existierende Inhalte migrieren

Wer das Tutorial bis hierher verfolgt hat, hat bereits einige Seiten und Inhalte angelegt. Vor der Konfiguration der Content Dimensions erstellte Nodes im Content Repository müssen nun mit dem konfigurierten Default Preset markiert werden.

Dies passiert durch eine spezielle Node Migration, welche mit dem Befehl:

./flow node:migrate 20150716212459

Befehl zur Migration vorhandener Nodes in die Standardsprache.

durchgeführt wird. Alle vorhandenen Nodes ohne Dimension Konfiguration werden nun unserem definierten Default "de" zugeschrieben.

Sprachmenü integrieren

Als nächstes benötigen wir das Sprachmenü, damit der Besucher eine alternative Sprache wählen kann. Neos bietet für so ein DimensionMenu einen Fusion Prototyp, der eine fertige Linkliste der verfügbaren Dimension Presets rendert. Das DimensionMenu lässt sich genauso einfach wie das Seitenmenü in das Template integrieren.

languageMenu = Neos.Neos:DimensionsMenu {
    dimension = 'language'
}

Instanziierung des Sprachmenüs in der Datei Root.fusion

Durch die Angabe der Dimension, wird nur diese gerendert, auch wenn weitere Dimensionen definiert wurden. Weitere Parameter stehen zur Verfügung, um das Rendering des Menüs zu beeinflussen. So können beispielsweise die zu verwendenden Presets auch explizit angegeben werden.

Die komplette Referenz des DimensionMenu Fusion objects lässt sich in der Dokumentation nachlesen.

Statische Template-Teile übersetzen

Neben redaktionell gepflegten Inhalten, enthalten die Templates der Seiten- und Content-Elemente fest konfigurierte Labels und Textbausteine. Zur Übersetzung werden diese Textbausteine aus den Templates in eigene Übersetzungskataloge ausgelagert.

Neos Flow verwendet für Übersetzungen den Standard "XML Localisation Interchange File FormatXLIFF
Die .xlf Dateien werden in unserem Beispiel unter Resources/Private/Translations  im Site Package abgelegt. In Translations wird für jede Sprache ein eigener Ordner angelegt, welcher eine Datei "Main.xlf" zur Aufnahme der Übersetzungen enthält.

Übersetzungen können zur besseren Strukturierung in beliebig viele Dateien aufgeteilt werden und passend benannt werden. Da das Flow Localization Framework aber ohne weitere Konfiguration nach der Datei Main.xlf sucht, halten wir uns an diese Konvention.

Die Verzeichnisstruktur sieht im gesamten aus wie hier gezeigt.

Resources/
  Private/
    Translations/
      en/
        Main.xlf
      de/
        Main.xlf

Die Verzeichnisstruktur der Übersetzungsdateien

Die zu übersetzenden Zeichenketten werden in <trans-unit> Tags in diesen Dateien abgelegt. Jedes <trans-unit> Element enthält ein <source> Element, welches den Text in der Quellsprache enthält und optional ein <target> Element für die Zielsprache.

Die komplette XLIFF Datenstruktur für die Quellsprache, in unserem Fall Deutsch, sieht so aus:

<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
        <file original="" source-language="de" datatype="plaintext">
                <body>
                        <trans-unit id="wine.grape">
                                <source>Traube</source>
                        </trans-unit>
                </body>
        </file>
</xliff>

Datei Main.xlf im Verzeichnis Resources/Private/Translations/de/

"wine.grape" ist der XLIFF Identifikator, welcher innerhalb des Packages eindeutig sein muss und in den Templates verwendet werden kann. Es lohnt sich hier auf aussagekräftige Identifikatoren zu achten. Die Trennung mit Punkten ist Best Practice. In unserem Fall beschreiben wir das label "grape" im Node Type "wine".

Die Datei für eine Zielsprache enthält neben der Angabe der Source-Zeichenkette die Übersetzung im Feld <target>.

<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
        <file original="" source-language="de" target-language="en" datatype="plaintext">
                <body>
                        <trans-unit id="wine.grape">
                                <source>Traube</source>
                                <target>Grape</target>
                        </trans-unit>
                </body>
        </file>
</xliff>

Datei Main.xlf im Verzeichnis Resources/Private/Translations/en/

Das Neos Flow Internationalization & Localization Framework bringt neben dem hier gezeigten noch einige weitere Möglichkeiten mit, wie Platzhalter in übersetzten Texten oder die Unterstützung für Singular- und Pluralformen. Die komplette Dokumentation findet ihr hier

Sind die Dateien an Ort und Stelle, können wir nun in Fluid und Fusion auf diese Übersetzungstabelle zugreifen.

In Fluid Templates mit dem "f:translate" Viewhelper:

{f:translate(id: 'wine.grape', package: 'WL.WeinLaden')}

Beziehungsweise in Fusion mit dem entsprechenden EelHelper:

grapeLabel = ${Translation.translate('wine.grape', null, [], 'Main', 'WL.WeinLaden')}

Nun haben wir alle Bauteile zusammen, um die Seite komplett zu übersetzen. Das kann beispielsweise so aussehen:

Screenshot der Übersetzung der Seite auf nglisch

Ich hoffe dieser Workshop-Teil konnte vermitteln, wie sich mehrsprachige Websites mit Neos umsetzen lassen. Wie bereits erwähnt, befindet sich der dafür nötige Code wie immer auf unserem github Repository

Für Fragen und Anmerkungen zu diesem Teil des Tutorial kann das untere Kommentarfeld gerne genutzt werden. 

Dann noch viel Spaß beim Umsetzen! 

Das war noch nicht genug Input? Du möchtest noch mehr über das Neos CMS lernen? Wir bieten auch Vor-Ort Schulungen an. 

zu den Neos-Workshops
Teilen:

Weitere Beiträge

router bgp 16188
Patrick M. Hausen, Technik bei punkt.de
Arbeiten bei punkt.de