Das Örtchen RSS-Feed
Kategorie
Kategorie: Blog
Neueste Kommentare

Drupal 8: Die Sache mit der Interface-Sprache

Seit dem Release von Drupal 8 gibt es die Möglichkeit, Inhalte (z.B. Artikel) in einer auswählbaren Sprache zu erstellen und für das Interface eine statische Sprache festzulegen. Dies ist ein Feature, welches ich in mehrsprachigen Drupal 6-Websites schmerzlichst vermisst habe, und in aktuellen CMS-Projekten wirklich lieben gelernt habe. Leider bringt dieses Feature bzw. eine damit zusammenhängende API-Entscheidung bei der Erstellung des Cores von Drupal 8 wirklich nervige Probleme mit sich.

 

So hatte ich bei einigen Erweiterungsmodulen (z.B. Linkit und Menu Breadcrumb) Probleme mit Ausgaben in der falschen (statischen Interface-) Sprache anstatt der gewünschten Sprache des Inhalts. Diese Probleme waren meist auf die Methode Drupal\Core\Language\LanguageManager#getCurrentLanguage() zurückzuführen.

public function getCurrentLanguage(
  $type = LanguageInterface::TYPE_INTERFACE
) {
  return $this->getDefaultLanguage();
}

Übergibt man also keinen Wert für den Parameter $type, so wird die Interface-Sprache als Rückgabewert ermittelt. Persönlich halte ich dies für nicht wirklich intuitiv, ich hätte jedenfalls die Inhaltssprache als Standard erwartet. Letztere erhält man jedoch nur als Rückgabewert, wenn man Drupal\Core\Language\LanguageInterface::TYPE_CONTENT als Argument an die Methode übergibt.

Das Problem mit den Status-Nachrichten

Eine ganze Weile dachte ich, daß ich nun alle Sprachprobleme im Griff hätte, doch dann fielen mir weitere auf. Sieht man sich nämlich beim Webform-Modul die serverseitigen Validierungsmeldungen an, so erscheinen auch diese in der Interface-Sprache. Doch dieses Problem betrifft nicht nur Erweiterungen von Dritten sondern auch den Core (z.B. die Suche). Genauer gesagt liegt das Problem darin, wie normalerweise Meldungen erstellt werden, die im Block Status-Nachrichten ausgegeben werden sollen. Im SearchController des Core-Search-Moduls findet sich beispielsweise folgende Zeile:

drupal_set_message($this->t('Please enter some keywords.'), 'error');

Hier wird also mittels der t-Methode aus dem Trait Drupal\Core\StringTranslation\StringTranslationTrait eine übersetzbare Meldung an die Funktion drupal_set_message() übergeben. Das gleiche Problem gibt es jedoch auch mit der Methode formatPlural und der t-Function in der bootstrap.inc. Bei jeder dieser Möglichkeiten, könnte man als zusätzliches Argument ein Array $options übergeben und so die gewünschte Sprache festlegen. Doch dies wird meist nicht gemacht und führt uns zu einem in der LanguageManager-Klasse versteckten Kommentar:

Interface language is the only configurable language type in core. It is used by t() as the default language if none is specified.

Doch wie kann man das Problem nun lösen bzw. die Status-Nachrichten in der Inhaltssprache ausgeben lassen?

Die Recherche

Ich wollte natürlich nicht den Code Dritter anpassen oder gar am Core Änderungen vornehmen. Gesucht wurde also eine Möglichkeit, die Meldungen nach der Übergabe an drupal_set_message() zu verändern. Schaut man sich die Funktion an, so wird dort die Methode Drupal#messenger() aufgerufen, welche wiederum ein Objekt der Klasse Drupal\Core\Messenger\LegacyMessenger instanziiert. Dort wird wiederum mittels des Aufrufs Drupal::service('messenger') der aktive Messenger-Service ermittelt und diesem die Meldung übergeben.

Aus meiner Sicht sprach also nichts dagegen diesen Service zu ersetzen und die erhaltenen Meldungen, welche Objekte der Klassen Drupal\Core\StringTranslation\TranslatableMarkup bzw. PluralTranslatableMarkup sein können, abzufangen und zu verändern. Dummerweise bieten beide Klassen zwar öffentliche Methoden, um die verwendeten Optionen auszulesen, aber man kann diese im Nachhinein nicht mehr verändern. Leider gibt es zudem bei der Klasse PluralTranslatableMarkup für manche Attribute keine Getter (z.B $count). Man kann also nicht einfach die Werte aller Attribute auslesen und mit diesen neue Objekte instanziieren.

Glücklicherweise erinnertete mich die Methode Drupal\Core\StringTranslation\PluralTranslatableMarkup#createFromTranslatedString() an einen Umstand im Zusammenhang mit geschützten Attributen, über den ich mich vor ein paar Wochen mit einem Kollegen unterhalten hatte.

public static function createFromTranslatedString(
  $count,
  $translated_string,
  array $args = [],
  array $options = []
) {
  $plural = new static($count, '', '', $args, $options);
  $plural->translatedString = $translated_string;
  return $plural;
}

Das Attribut translatedString ist mit dem Schlüsselwort protected versehen. Von außerhalb des Objekts kann also normalerweise nicht auf das Attribut zugegriffen werden. Dieser Schutz gilt aber nicht für Instanzen der gleichen Klasse (siehe Sichtbarkeit von anderen Objekten). Diese können problemlos schreibend (wie im Beispiel) oder auch lesend auf die geschützten Attribute zugreifen. Wichtig ist in diesem Zusammenhang auch, daß sich der Schutz auch aushebeln lässt, wenn man in einem Objekt einer Kindklasse auf die Attribute eines Objekts der Elternklasse zugreift.

Die Lösung

Man kann also den Messenger-Service ersetzen, um zentral an die Objekte mit den übersetzbaren Strings zu kommen. Diese Objekte kann man dann mittels Objekten einer Kindklasse von TranslatableMarkup bzw. PluralTranslatableMarkup auslesen und als Objekte der jeweiligen Kindklasse neu instanziieren. Diese Kindklassen können dann wiederum eine Möglichkeit bieten, im Array $options einen Wert mit dem Schlüssel langcode zu hinterlegen, der unserer gewünschten Sprache entspricht. Das fertige Drupal-Modul findet sich auf GitHub. Die derzeitige Version 1.0.0 kann man als zip-Datei herunterladen. Beim Entpacken den Ordner messenger_in-1.0.0 bitte einfach in messenger_in umbenennen und diesen dann im Ordner modules von Drupal 8 ablegen.

Bild von Jan Teriete Hallo! Bist du neu hier? Dann abonniere doch den RSS-Feed dieses mal mehr oder weniger stillen Örtchens, um über meine geistigen Ergüsse auf dem Laufenden zu bleiben. Du kannst aber auch gerne diesen Artikel in den sozialen Netzwerken teilen und mit Deinen Kontakten über das Thema diskutieren.

Neuen Kommentar hinzufügen

Der Inhalt dieses Feldes wird nicht öffentlich zugänglich angezeigt.
Der Inhalt dieses Feldes wird öffentlich zugänglich angezeigt, aber als rel="nofollow" markiert.
Hinweis

Die mit einem Stern markierten Daten werden zur Bearbeitung Deines Kommentars benötigt und zusammen mit Deiner IP-Adresse erfasst (siehe Datenschutzerklärung).

Kommentare beleben den Blog! Ich freue mich über jeden Kommentar. Du kannst hier offen Deine Meinung zum Artikel sagen, aber bitte beachte die Netiquette und vermeide es andere zu beleidigen.

Bitte unterlasst es die Kommentare zu SEO-Zwecken zu missbrauchen. Kommentare mit Links, die nicht zu Blogs führen (oder zu Blogs mit Grauzonen-Themen) und/oder Keywords als Namen verwenden, sind nicht erwünscht!

Möchtest Du mir einen Blog-Artikel schmackhaft machen, dann schreib die URL ohne HTML-Tag in den Kommentarbereich und ich werde diesen bei Gefallen verlinken.