Eine Hand die einen Globus hält

Der "TranslationHelper" in Neos CMS

Kategorien:

Neos CMS verfügt über eine sehr leistungsfähige Übersetzungshilfe in der eigenen Rendering-Sprache Fusion. Er kann einfache Übersetzungen passend zur Sprache des Benutzers bearbeiten, aber auch komplexere Lokalisierungsoptionen für Pluralformen und zum Ausfüllen von Platzhaltern.

Alle Funktionen leiten sich direkt aus dem i18n & l10n Framework von Neos zugrunde liegendem PHP Framework Flow ab.

[Globe picture copyright Pexels.com / Porapak Apichodilok]

Die Übersetzungsmethode

Normalerweise wird der Helfer in seiner Kurzform wie folgt aufgerufen:

myLabel = ${I18n.translate('Vendor.Package:Main:component.label')}

Dieser Aufruf bedeutet, dass im Paket Vendor.Package ein Ordner Resources/Private/Translations/en existiert. Wobei "en" für eine englische Variante steht und ich nur als Beispiel verwendet habe. Wenn Sie eine andere oder mehrere Sprachen (und deren Varianten) verwenden, sollten Sie für jeden Sprachcode einen eigenen Ordner haben.

Für unser Beispiel würde jeder Sprachordner mindestens eine Datei namens Main.xlf enthalten, die einen Eintrag für die id component.label enthält.

Der Text für diesen Eintrag wird abgerufen und myLabel wird ihn enthalten. Wenn der Benutzer eine andere Sprache verwendet und eine Übersetzung existiert, wird das richtige Label abgerufen. Wenn nicht, wird eine Fallback-Kette ausgelöst.

Die Datei ist wie folgt aufgebaut:

<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file original="" product-name="Vendor.Package" source-language="en" datatype="plaintext">
        <body>
            <trans-unit id="component.label">
                <source>My component</source>
            </trans-unit>
        </body>
    </file>
</xliff>

Jede trans-unit ist eine Übersetzungseinheit, die in Fusion abgerufen werden kann.

Wenn mehrere Sprachen definiert sind, wird das von Neos verwendete Flow Framework die richtige Übersetzung basierend auf der aktuellen Frontend-Sprachdimension und der definierten Fallback-Kette bereitstellen.

Die Übersetzungsdatei in einer anderen Sprache als der Standardsprache würde wie folgt aussehen:

<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file original="" product-name="Vendor.Package" source-language="en" target-language="de" datatype="plaintext">
        <body>
            <trans-unit id="component.label">
                <source>My component</source>
                <target>Meine Komponente</source>
            </trans-unit>
        </body>
    </file>
</xliff>

Sie definiert die Zielsprache und hat für jede Einheit einen Zieleintrag.

Verwendung von Argumenten und Mengen

Die Übersetzungshilfe ermöglicht es Ihnen auch, benannte und unbenannte Argumente anzugeben. Dadurch können Sie Platzhalter in Ihrem Text verwenden. Für benannte Argumente könnten Sie einen Platzhalter wie {amount} verwenden, um die Anzahl der Elemente in der Mitte eines Textes anzuzeigen. Und in einer anderen Sprache könnte der Platzhalter am Ende stehen.

Unbenannte Argumente können über ihren Index eingefügt werden. So würde das erste Element über {0} zugewiesen werden und so weiter. Ich empfehle immer, benannte Argumente zu verwenden, da es die Verwaltung dieser Übersetzungen so viel einfacher macht. Vor allem, wenn es mehrere Argumente gibt.

Sie können auch eine Menge angeben. Dies würde es ermöglichen, nicht nur wie oben beschrieben eine Variable in unserem Text zu haben, sondern auch Textvarianten basierend auf der Zahl selbst. Viele Sprachen haben Varianten für 0, 1 oder mehr Elemente. Einige Sprachen haben sogar noch mehr Differenzierungen.

Hier ist ein Beispiel für eine xliff-Datei mit Mengen und Argumenten:

<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file original="" product-name="Vendor.Package" source-language="en" target-language="de" datatype="plaintext">
        <body>
			<group id="some.label" restype="x-gettext-plurals">
	            <trans-unit id="component.label[0]">
	                <source>You see {amount} item</source>
	                <target>Du siehst {amount} Element</source>
	            </trans-unit>	            
				<trans-unit id="component.label[1]">
	                <source>You see {amount} items</source>
	                <target>Du siehst {amount} Elemente</source>
	            </trans-unit>
			</group>
        </body>
    </file>
</xliff>

Verwendung der Methode

Die Übersetzungsmethode sieht in ihrer vollständigen Form wie folgt aus:

translate($id, $originalLabel = null, array $arguments = [], $source = 'Main', $package = null, $quantity = null, $locale = null)

Sie können es in Fusion wie folgt verwenden:

amount = 5
myAmountLabel = ${I18n.translate('component.amount', '{amount} Items', { amount: this.amount }, 'Main', 'Vendor.Package', this.amount)}

Jedes Argument der Methode, das einen Standardwert hat, kann übersprungen werden, wenn es nicht benötigt wird. Aber normalerweise müssen Sie die meisten setzen, um sicherzustellen, dass die Übersetzung aus dem richtigen Paket geholt wird. Da das Framework Neos.Neos als Standard-Paketname annimmt (im Neos CMS-Kontext).

Eine weniger überladene Variante

Die Übersetzungshilfe hat auch eine weitere Methode, die ein TranslationParameterToken bereitstellt.

Mit diesem Aufruf können Sie einen generieren:

myLabel = ${I18n.id('component.label')}

Oder diesem:

myLabel = ${I18n.value('My component')}

Dies wird aber noch keine gültige Übersetzung zurückgeben. Das Token, das zurückgegeben wird, bietet weitere Methoden, die verkettet werden können. Damit es vollständig funktioniert, müssen wir wieder die gleichen Parameter bereitstellen wie bei den vorherigen Aufrufen:

myLabel = ${I18n.id('component.label').package('Vendor.Package').source('Main').translate()}

Was haben wir also gewonnen? Noch nichts.

Aber wenn wir am Ende nicht translate() aufrufen, können wir unser Token wiederverwenden:

i18n = ${I18n.id('').package('Vendor.Package').source('Main')}

myLabel = ${this.i18n.id('component.label').translate()}
myOtherLabel = ${this.i18n.value('Other label').translate()}

Wie Sie sehen, können wir nun die Angabe des Pakets und der Quelle immer wieder überspringen. Dieser Ansatz ist recht hilfreich, wenn Sie den Helfer mit AFX verwenden und der Code ist weniger unübersichtlich.

Hinweis: Wenn Sie das translate() am Ende weglassen, wird es in den meisten Fällen trotzdem korrekt funktionieren, da das Token eine toString()-Implementierung hat. Aber explizit zu sein, kann unvorhergesehene Fehler verhindern.

Es ist immer gut, einen Blick auf die Implementierung des Helfers zu werfen. Vielleicht entdecken Sie noch ein paar nette Features, die Ihnen in Ihrem Projekt helfen.

Bitte kontaktieren Sie mich, wenn Sie das tun, und ich kann diesen Beitrag um weitere Hinweise ergänzen.

Übersetzung anhand des Textes

Sie haben vielleicht gesehen, dass Sie die Übersetzung auch anhand ihres Textes anstelle einer ID abrufen können. Meine persönliche Erfahrung ist, dass dies zu Problemen führen kann, wenn sich der Text ändert, sich Bedeutungen ändern oder Duplikate auftreten. Daher verwende und empfehle ich nur eindeutige Ids. Und Sie können immer noch die Bedeutung im Code ableiten, indem Sie Fallback-Werte bereitstellen, wenn eine Übersetzung nicht verfügbar ist.

Verwenden der Hilfsfunktion ohne Fusion

Wenn Sie Fluid-Vorlagen haben, gibt es dort einen ähnlichen Helfer. Er sieht so aus:

<f:translate id="label.id"/>

Wenn Sie JavaScript für benutzerdefinierte Neos UI-Plugins oder Backend-Module schreiben, können Sie auch die JS-Version des Helfers verwenden.

Sie ist in der Neos.UI über die i18nRegistry verfügbar. Und in Backend-Modulen als globales Objekt:

window.NeosCMS.I18n.translate()

Zusammenfassung

Das Übersetzen von Texten in Neos ermöglicht mehr Flexibilität, als Sie vielleicht bereits wissen. Verwenden Sie die Token-Form, Mengen und benannte Argumente, um Ihren Code einfacher zu pflegen und bessere Übersetzungen & Lokalisierungen zu erstellen, die den Benutzer berücksichtigen.