Drupal: Übersetzungen von Contrib-Modulen laden

Die letzte Zeit über hat mich ein Problem beschäftigt, das mich ein klein wenig verrückt gemacht hat und dessen Lösung so versteckt war, dass ich das auf jeden Fall für den Rest der Welt aufschreiben wollte. Ich arbeite hier mit einer Drupal-Installation, und in dieser wurden schlicht deutsche Übersetzungen der Contrib-Module nicht geladen!

Das Szenario

Eine Drupal-Installation (Version 9.3.14) mit verschieden Contrib-Modulen und zwei Sprachen, English als Default und Deutsch. Contrib-Module sind die Module, die nicht im Drupal-Core enthalten sind und die man auch nicht selber schreibt, sondern die von der Community bereitgestellt werden. Ohne diese läuft im Prinzip keine einzige Drupal-Installation. Natürlich gibt es zu diesen Modulen auch Übersetzungen, welche ebenfalls von der Community bereitgestellt werden. Den Status der Übersetzungen kann man sich unter https://localize.drupal.org/ anschauen.

Die deutsche Drupal-Community ist vergleichsweise groß, also kann man für bekannte Module wie z.B. „webform“ erwarten, dass zumindest die meisten Texte auf Deutsch übersetzt sind. Die Übersetzungen sind nicht immer korrekt und manchmal fehlen sie für ganz neue Strings auch ganz. Aber nach dem Aktivieren des webform-Moduls fiel mir dann doch recht schnell auf, dass fast alles auf Englisch ausgegeben wurde, außer so ganz einfachen Texten wie „Speichern“ und „Abbrechen“. Aber wenn man nachschaut, dann sieht man, dass von mehreren tausend Übersetzungs-Strings (es ist ein sehr großes Modul) nur 76 unübersetzt sind – der Übersetzungs-Status liegt bei fast 100%.

Noch ein bisschen testen und wundern und prüfen später ist klar: Für keines der Contrib-Module werden Übersetzungen geladen! Dies geschah nur für den Drupal-Core. Außerdem klappt das Laden der .po-Dateien aus unseren eigenen Custom-Modulen.

Die Fehlersuche

Meine erste Frage war, ob die Übersetzungs-Funktion denn überhaupt auch für Contrib-Module Übersetzungen laden soll. Der Drush-Befehl dafür sieht so aus:

ddev drush locale-check && ddev drush locale-update

Spoiler: Natürlich soll das so funktionieren, wozu pflegt man das sonst unter localize.drupal.org ein! Aber witzigerweise fand sich nirgends die konkrete Information, dass das so gehen soll! Unter Drupal 7 gab es ein Modul, welches diese Aufgabe übernahm, und seit Drupal 8 ist es nun Core-Funktionalität. Der Zustand der Doku ist aber beklagenswert veraltet. Auch auf Stack Overflow fand sich niemand, der unter meinen längeren Eintrag zum Thema wenigstens mal schrieb „Keine Ahnung, woran es bei Dir liegt, aber das sollte eigentlich gehen“.

Die Ausgabe von drush locale-update lautete als Fazit immer:

Message: Checked available interface translation updates for 25 projects.

25 ist die Summe aus Drupal-Core, 23 Custom-Modulen und dem Theme. Die Anzahl der Contrib-Module liegt bei 64, auch wenn nicht alle davon Übersetzungen enthalten mögen.

Im Netz fand sich nun noch oft der Hinweis auf die Seite „Berichte > Verfügbare Aktualisierungen für Übersetzungen“. Die Ausgabe dort war aber denkbar einfach:

Verfügbare Aktualisierungen für Übersetzungen

Hier wurden mir beharrlich keine Module aufgelistet. Drupal ist leider kein ganz einfach zu debuggendes System, so dass ich auch an dieser Front nicht weiter kam.

Grundlagen-Tipp: Wenn man mit 10 Minuten Fehlersuche nicht weiterkommt, sollte man das Problem immer auf seinen einfachsten Zustand versuchen zu vereinfachen. Das heißt in diesem Fall eine frische Drupal-Installation ohne alles! Und zwar ohne alles, also auch ohne Development-Module, Core-Patches etc. Hier habe ich anfangs einen Fehler gemacht und einiges aus der eigentlichen Installation übernommen. Erst als ich eine komplett frische Installation ohne alles ausprobierte, ging es plötzlich.

Dem Problem habe ich mich letztlich über die Modul-Installation genähert. Dazu habe ich jede Menge Module per Composer hinzugefügt, welche auf localize.drupal.org Übersetzungen vorliegen haben. In der alten Installation sah die Installation eines Moduls so aus:

Modul-Installation mit Bug

Unaufällig, aber man sieht, dass nichts zu Übersetzungen steht, auch wenn diese vorher geladen wurden. Das ist offenbar ein Installationsschritt mit Ladebalken und allem. In der frischen Installation sah das dagegen so aus:

Modul-Installation ohne Bug

Plötzlich steht da „Übersetzungsdatei wurde importiert“. Die Übersetzungen finden sich in der Datenbank und die Datei liegt als Backup auch unter „web/sites/default/files/translations“.

Der Fehler

Die neue ausgegebene Infozeile ermöglichte es mir letztlich, die Codestelle zu finden, welche die Übersetzungen lädt. Noch ein bisschen debuggen später war klar: Drupal kennt die Contrib-Module alle, ignoriert sie aber, weil in ihrer info-Datei kein Projektname steht. Normalerweise hätte ich jetzt erst mal recht doof geschaut, aber mein oben erwähnter Fehler führte dazu, dass ich eine komplett neue Installation hatte, in welcher der Download ging, und eine weitere neue Installation, in welcher ich ein paar ganz grundlegende Dinge übernommen hatte und in der es nicht ging. Die Unterschiede beider Installationen waren so gering, dass ich es mit Trial&Error auf eine einzige Zeile in der composer.json zurückführen konnte:

   "config": {
        "discard-changes": true,
        "preferred-install": "source",
        "sort-packages": true
    },

Die mittlere der drei Zeilen ist der Verursacher. Was heißt „preferred-install: source“? Ganz einfach, wenn ein Modul wie „drupal/coffee“ diese Angaben enthält:

            "name": "drupal/coffee",
            "version": "1.2.0",
            "source": {
                "type": "git",
                "url": "https://git.drupalcode.org/project/coffee.git",
                "reference": "8.x-1.2"
            },
            "dist": {
                "type": "zip",
                "url": "https://ftp.drupal.org/files/projects/coffee-8.x-1.2.zip",
                "reference": "8.x-1.2",
                "shasum": "3c3f251004ec0d0995a86407592f7f54872ebe87"
            },

Dann zieht Composer mit dieser Zeile das Modul aus Git, ohne die Zeile (=Standard-Verhalten) lädt Composer die zip-Datei vom Drupal-Server herunter. Die Unterschiede dürften marginal sein, aber der wichtige Unterschied steckt in der Info-Datei des Moduls (hier coffee.info.yml). Per git-Checkout sieht sie so aus:

name: Coffee
description: Provides an Alfred like search box to navigate within your site.
core_version_requirement: ^8 || ^9
type: module
configure: coffee.configuration

Wenn man das Modul aus der zip-Datei bekommt, sieht sie dagegen so aus:

name: Coffee
description: Provides an Alfred like search box to navigate within your site.
core_version_requirement: ^8 || ^9
type: module
configure: coffee.configuration

# Information added by Drupal.org packaging script on 2021-04-02
version: '8.x-1.2'
project: 'coffee'
datestamp: 1617351415

Und damit ist das Modul einem Projekt zugeordnet! Dazu muss man wissen, dass in Drupal Projekte übersetzt werden und nicht einzelne Module. Wenn man ein Modul wie „webform“ aktiviert, welches gefühlte 50 Untermodule hat, dann werden einmal die gesammelten Translations gezogen. Alle Untermodule sind dem gleichen Projekt zugeordnet.

Wenn man aufpasst, sieht man den Unterschied übrigens auch in PHPStorm, denn das Modul enthält einen “.git“-Unterordner, ist farblich anders dargestellt und nervt ggf dadurch rum, dass es als „VCS root“ mit auftaucht.

Die Lösung

Zum einen natürlich die „preferred_install“-Zeile ersatzlos aus der Datei streichen oder ggf. wie folgt ändern:

"preferred-install": {
    "drupal/*": "dist",
    "*": "source"
},

Das allein führt aber nicht dazu, dass die bereits installierten Module neu installiert werden. Hier muss man einmal den „vendor“-Ordner sowie alles unter „web/modules/contrib“ löschen und dann „composer install“ aufrufen. Das muss natürlich auch in den verschiedenen Live-Umgebungen passieren, erfordert also ggf. eine Script-Lösung.

Folgendes habe ich dazu gebastelt. Aufgrund der zusätzlichen Checks sollte das natürlich nicht dauerhaft Teil des Auslieferprozesses sein. Wenn es in allen Umgebungen gelaufen ist, kann das Script wieder weg.

#!/bin/bash

# Fix for installing Drupal packages from source instead of the Drupal zip file with added meta information.

# Get all Drupal package names.
DRUPAL_PACKAGES=`composer show --name-only --no-interaction | grep drupal/`
readarray -t DRUPAL_PACKAGE_ARRAY <<<"$DRUPAL_PACKAGES"
for name in "${DRUPAL_PACKAGE_ARRAY[@]}"
do
  # Remove spaces and drupal prefix from package name.
  PACKAGE_NAME=`echo "$name" | sed 's/ //g'`
  PACKAGE_NAME=`echo "$PACKAGE_NAME" | sed 's/drupal\///'`

  # Can be either installed under vendor/drupal or under web/modules/contrib.
  PACKAGE_DIR=`realpath "./vendor/drupal/$PACKAGE_NAME"`
  if [ ! -d "$PACKAGE_DIR" ]; then
    PACKAGE_DIR=`realpath "./web/modules/contrib/$PACKAGE_NAME"`
    if [ ! -d "$PACKAGE_DIR" ]; then
      continue
    fi
  fi

  # Check if a .git folder exists for this package.
  if [ -d "$PACKAGE_DIR/.git" ]; then
    # Check if the package has a dist source.
    HAS_NO_DIST=`composer show "drupal/$PACKAGE_NAME" | grep "dist     : \[\]"`
    if [ ! -z "$HAS_NO_DIST" ]; then
      continue;
    fi

    # Force a reinstallation of the package.
    composer reinstall --no-interaction --no-progress "drupal/$PACKAGE_NAME"
  fi
done

Fazit

Drupal lädt für aus git ausgecheckte Module nicht automatisch die Übersetzungen von localize.drupal.org und gibt auch keinen Hinweis darauf, wieso es das nicht tut!

2 Gedanken zu „Drupal: Übersetzungen von Contrib-Modulen laden

  1. Danke für den Hinweis! Das Modul ist sicher eine gute Lösung, falls man mal absichtlich lieber die git-Variante haben möchte. In meinem Fall war das aber ein Versehen, bzw. die Kollegen konnten mir nicht mehr sagen, warum das vor längerer Zeit mal so committet wurde. Aus irgendeine Tutorial übernommen, vermute ich.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Bitte beachte die Kommentarregeln: 1) Kein Spam, und bitte höflich bleiben. 2) Ins Namensfeld gehört ein Name. Gerne ein Pseudonym, aber bitte keine Keywords. 3) Keine kommerziellen Links, außer es hat Bezug zum Beitrag. mehr Details...

So, noch mal kurz drüber schauen und dann nichts wie ab damit. Vielen Dank fürs Kommentieren! :-)