🌐 Auch auf: English · Français · Español

Fast zwei Jahre lang sprach dieser Blog genau eine Sprache. Englisch ist die Lingua franca der Sysadmins, also schien das in Ordnung. Aber ich bin Deutscher, ein guter Teil meiner Leser hat Englisch ebenfalls nicht als Muttersprache, und wer auf Deutsch, Französisch oder Spanisch nach „ZFS RAIDZ1 NAS Debian“ sucht, bekommt Ergebnisse auf Deutsch, Französisch oder Spanisch. Nicht meine.
Also hat der Blog diese Woche drei neue Sprachen gelernt. Jeden veröffentlichten Beitrag gibt es jetzt auf Englisch, Deutsch, Französisch und Spanisch, mit einem Sprachumschalter im Menü und einer kleinen Zeile „🌐 Auch verfügbar auf“ über jedem Beitrag. Dieser Beitrag ist das Making-of: das Plugin, die Pipeline, der Bug, der eine komplette Sprache auf 404 geschickt hat, und was ich darüber gelernt habe, eine kleine Armee von KI-Agenten parallel laufen zu lassen. Gleicher Workflow wie immer: Ich treffe die Entscheidungen, mein KI-Co-Admin (Claude) hat die Schwerarbeit erledigt.
🎯 Wozu der Aufwand? Mehr Leser, hoffentlich
Seien wir ehrlich, was die Motivation angeht: Ich erhoffe mir mehr Besucher über Suchmaschinen. Google, Bing und DuckDuckGo ordnen Suchanfragen Seiten in der Sprache des Suchenden zu. Ein deutscher Admin, der einen fail2ban-Fix sucht, tippt sehr viel wahrscheinlicher deutsche Suchbegriffe ein, und eine Seite, die es auf Deutsch gibt, hat dort eine echte Chance aufzutauchen. Mit sauberen hreflang-Tags weiß die Suchmaschine außerdem, dass die vier Versionen zusammengehören, und kann jedem die Version in seiner Sprache zeigen, statt sie als Duplicate Content zu behandeln.
Ob das klappt? Keine Ahnung, noch nicht. Ich habe vor einer Weile meine echten, menschlichen Leser gezählt (Spoiler: deutlich weniger, als die Request-Logs vermuten lassen), und in ein paar Monaten zähle ich noch einmal. Wenn die übersetzten Seiten Leute herbringen, die das englische Original nie gefunden hätten, hat sich das Experiment gelohnt. Wenn nicht, schreibe ich darüber eben auch einen Beitrag.
🧩 Das Plugin: Polylang, und warum
Ich habe mich für Polylang entschieden, in der kostenlosen Version. Meine Kriterien für die Shortlist waren simpel: kostenlos, Open Source (GPLv3), kein Account, kein Cloud-Dienst und keine maschinelle Übersetzung, die meine Inhalte irgendwohin schickt. Polylang übersetzt selbst gar nichts. Es weiß nur, welcher Beitrag in welcher Sprache ist und welche Beiträge zusammengehören, und speichert die Sprache dafür als ganz normale WordPress-Taxonomie. Keine zusätzlichen Tabellen, nichts Exotisches in der Datenbank.
Die Einstellungen, die ich gewählt habe, und warum:
- Englisch bleibt Standard, ohne
/en/in der URL. Jeder bestehende Link, jedes Lesezeichen und jeder Backlink funktioniert weiter. Die neuen Sprachen liegen unter/de/,/fr/und/es/. - Keine Erkennung der Browsersprache. Suchmaschinen-Crawler schicken keine bevorzugte Sprache mit, und ich will niemanden, der auf einen englischen Link geklickt hat, woandershin umleiten. Du bekommst, was du angeklickt hast; der Umschalter ist direkt da, wenn du eine andere Sprache willst.
- Medien werden nicht übersetzt. Screenshots sind Screenshots. Alt-Texte und Bildunterschriften in den Beiträgen werden übersetzt, die Bilddateien werden gemeinsam genutzt.
- Kategorien und Schlagwörter gibt es pro Sprache („IT Security“ wird zu „IT-Sicherheit“, „Sécurité informatique“ und „Seguridad informática“), damit auch die Archivseiten in jeder Sprache funktionieren.
Die SEO-Verkabelung gab es gratis dazu: Polylang ergänzt jede Seite um die hreflang-Alternativen, und Yoasts XML-Sitemap nimmt die Übersetzungen automatisch auf. Das Menü hat vier kleine Flaggen als Sprachumschalter bekommen, und ein kleines Must-use-Plugin (ausgerollt von meiner Ansible-Rolle, wie alles andere auf diesem Server) gibt die Zeile „Auch verfügbar auf“ aus, aber nur für Sprachen, in denen es den Beitrag wirklich gibt.

🤖 Offen zum KI-Anteil
Alle Übersetzungen sind mit KI erstellt. Das sage ich auf der Seite AI Usage Notice, die jetzt einen Abschnitt „Translations“ hat: Englisch ist das Original, die anderen Sprachen sind KI-Übersetzungen, geprüft von einem zweiten, unabhängigen KI-Durchgang. Wenn sich etwas auf Deutsch, Französisch oder Spanisch seltsam liest, gilt die englische Version, und ich freue mich über einen Hinweis.
🧪 Der Pilot und der 404, der eine ganze Sprache verschluckt hat
Bevor ich hundert Beiträge anfasse, habe ich genau einen übersetzt: meinen Beitrag über die Startseite auf dem Handy. Zum Glück, denn das erste Ergebnis war spektakulär, nur in die falsche Richtung: Jede deutsche Seite lieferte einen 404. Die Beiträge existierten, Polylang kannte sie, der Umschalter verlinkte auf sie, und WordPress sagte: „Nie gehört.“
Der Übeltäter: die Rewrite-Regeln. Ich hatte Polylang per wp-cli eingerichtet und die Rewrite-Regeln auf der Kommandozeile geflusht. Auf der CLI ist Polylangs Filter für das Sprachpräfix nicht vollständig aktiv, also hat WordPress fröhlich einen frischen Satz Regeln erzeugt, der von /de/ nichts wusste. Der Fix war fast schon peinlich: die gecachten Regeln löschen und sie vom nächsten normalen Frontend-Request neu aufbauen lassen, diesmal mit vollständig geladenem Polylang.
wp eval "delete_option('rewrite_rules');"
# then just load any page once – WordPress rebuilds the rules with the language prefixesLektion notiert: Alles, was vom Request-Kontext abhängt (Sprachen, Themes, manche Caching-Plugins), sollte seinen Zustand in einem echten Request neu erzeugen, nicht in einer CLI-Sitzung.
🏭 Die Pipeline: 111 Beiträge × 3 Sprachen
111 veröffentlichte Beiträge mal drei Sprachen macht 333 Übersetzungen. Das von Hand im Editor zu machen, stand nie zur Debatte, also wurde eine Pipeline daraus. Jeder Beitrag durchläuft fünf Schritte:
- Export. Ein wp-cli-Skript exportiert jeden Beitrag als Gutenberg-HTML. Codeblöcke werden herausgeschnitten und durch Platzhalter wie
<!--PLLCODE 3-->ersetzt; der Originalcode landet in einer separaten JSON-Datei. Ein Übersetzer kann einen Shell-Befehl nicht „verbessern“, den er nie zu sehen bekommt. - Übersetzen. Ein KI-Agent übersetzt das HTML ins Deutsche, Französische und Spanische und hält sich dabei an einen Styleguide (mehr dazu unten), dazu Titel, Auszug und Meta-Beschreibung pro Sprache.
- Prüfen, mechanisch. Ein kleines Python-Skript vergleicht jede Übersetzung mit dem Original: gleiche Abfolge der Gutenberg-Blockkommentare, gleiche Platzhalter, dieselbe Menge an Linkzielen und Bildquellen, gleich viele Tabellenzeilen und Listenpunkte. Passt etwas nicht, geht die Übersetzung zurück.
- Review, unabhängig. Ein zweiter Agent, der nie gesehen hat, wie die Übersetzung entstand, vergleicht Original und Übersetzungen Satz für Satz und schreibt seine Korrekturen als exakte Suchen-und-Ersetzen-Paare, jeweils mit Begründung. Jeder „alte“ String muss genau einmal vorkommen, sonst wird die Änderung abgelehnt.
- Import. Zuerst ein Datenbank-Dump, dann setzt ein Skript, das als
www-dataläuft, die Codeblöcke wieder ein, veröffentlicht die Übersetzungen mit dem Originaldatum, weist die Kategorien und Schlagwörter der jeweiligen Sprache zu, verwendet das Beitragsbild wieder, setzt die Yoast-Meta-Beschreibung und verknüpft die vier Versionen in Polylang miteinander.
Das Herzstück der mechanischen Prüfung ist ein Dutzend Zeilen. Es versteht kein einziges Wort irgendeiner Sprache, und genau darum geht es:
def feats(h):
return {
'blocks': re.findall(r'<!-- (/?wp:[a-z0-9/-]+)', h),
'code': sorted(re.findall(r'<!--PLLCODE \d+-->', h)),
'href': sorted(re.findall(r'href="([^"]*)"', h)),
'src': sorted(re.findall(r'src="([^"]*)"', h)),
'tr': h.count('<tr'), 'li': h.count('<li'), 'img': h.count('<img'),
'inline_code': len(re.findall(r'<code>', h)),
}
# original and translation must produce identical features📏 Ein Styleguide, wie bei einem echten Übersetzungsbüro
Konsistenz über 333 Texte hinweg entsteht nicht zufällig. Der Styleguide legt den Ton fest (Deutsch du, Französisch vous, Spanisch tú), ein Glossar („hardening“ ist immer „Härtung“, „durcissement“, „bastionado“), Zahlenformate (2,6 s statt 2.6 s), französische Typografie mit geschützten Leerzeichen vor : ; ? ! und eine Liste von Dingen, die nie angefasst werden: Befehle, Pfade, Hostnamen, Versionsnummern, Fehlermeldungen, Produktnamen. Links auf verwandte Beiträge behalten ihre englischen Titel, mit einem kurzen Hinweis „(auf Englisch)“, bis auch diese Beiträge übersetzt sind.
🔍 Hat sich das Review gelohnt?
Absolut. Die meisten Übersetzungen waren gut, aber „gut“ ist nicht „richtig“. Bei den ersten 26 Beiträgen fand der Reviewer etwa hundert Stellen zum Korrigieren, ungefähr gleichmäßig über die drei Sprachen verteilt; nur zwei Beiträge kamen ganz ohne Befund zurück. Meist ging es um stilistischen Feinschliff, aber ein paar waren echte Sinnfehler, die ein flüssig klingender Text perfekt versteckt:
- Aus „a longer memory“ (gemeint: längere Aufbewahrung der Logs) war im Spanischen „mehr RAM“ geworden.
- Aus „customer-facing service“ (ein Dienst, den Kunden direkt nutzen) war im Französischen „Kundendienst“ geworden.
- Eine Tabellenspalte „Focus“ hatte sich in „Priority“ verwandelt. Plausibel, falsch.
✏️ TODO (Raphael): final numbers when all 111 posts are through.
🐝 Subagenten: ein Dispatcher, viele Worker
Hier wurde es interessant. Einen Beitrag zu übersetzen kostet eine KI eine Menge Lesen und Schreiben: den Styleguide, das Original, drei Übersetzungen. 111 davon in einer einzigen Unterhaltung würden die Unterhaltung lange vor dem Ende unter Text begraben. Also wurde die Hauptsitzung zum Dispatcher und hat selbst fast nichts übersetzt:
- Er startet pro Beitrag einen Übersetzer-Agenten. Jeder ist eine frische Instanz mit sauberem Kontext: Er liest den Styleguide und genau einen Beitrag, schreibt die Dateien, lässt die Prüfung laufen und meldet sich mit drei Zeilen zurück.
- Meldet sich ein Übersetzer, startet der Dispatcher für diesen Beitrag einen Review-Agenten, der auf einem kleineren, günstigeren Modell (Sonnet) läuft, und sofort den nächsten Übersetzer.
- Kommt ein Review zurück, wendet der Dispatcher die Korrekturen an, lässt die Prüfung erneut laufen und lädt hoch.
Drei bis vier Übersetzer und ein paar Reviewer liefen gleichzeitig. Der Kontext des Dispatchers selbst blieb klein, weil er immer nur kurze Berichte zu sehen bekam. Schreiber und Reviewer zu trennen, hat neben der Parallelität einen zweiten Vorteil: Der Reviewer hängt nicht an der Übersetzung. Er hat sie nicht geschrieben, er „erinnert“ sich nicht, warum ein Satz so formuliert wurde, er vergleicht einfach.
✅ Wann sich parallele Agenten lohnen
- Unabhängige Aufgaben. Beitrag 1370 ist Beitrag 1373 völlig egal. Kein gemeinsamer Zustand, keine Probleme mit der Reihenfolge.
- Klarer Input, klarer Output. „Lies diese Dateien, schreib jene Dateien, lass diese Prüfung laufen.“ Ein Agent mit einem klaren Auftrag braucht kein Hin und Her.
- Eine mechanische Schranke. Weil die Python-Prüfung kaputte Struktur abfängt, muss ich nicht jedem Agenten vertrauen; ich vertraue der Schranke.
- Frischer Kontext spart Budget. Ein Worker, der nur liest, was er braucht, ist günstiger als eine lange Unterhaltung, die jeden bisherigen Beitrag mitschleppt.
❌ Wann nicht
- Verzahnte Schritte. Polylang einrichten, dem 404 hinterherjagen, das Import-Skript bauen: Jeder Schritt hing vom Ergebnis des vorherigen ab. Das war eine Unterhaltung, ein Kopf, kein Fan-out.
- Kleinkram. Jeder Agent startet kalt und muss erst den Styleguide lesen. Bei einem Einzeiler-Fix kostet das Hochfahren mehr als die Arbeit.
- Vermischte Anweisungen. Ein Reviewer bekam obendrauf einen Zusatzjob („stell bei diesem spanischen Titel auch gleich von vosotros auf tú um“). Das hat er gemacht und ehrlich gemeldet, dass er Deutsch und Französisch nur überflogen hatte. Ein zweites, vollständiges Review desselben Beitrags fand neun echte Fehler. Lektion: Halte den Job jedes Agenten sauber, und lies die Berichte, nicht nur die Zahlen.
🧱 Die echte Grenze: das Kontingent
Parallelität macht Tokens nicht billiger; sie bringt dich nur schneller an die Wand. Gegen Mittag am ersten Tag, nach etwa zehn fertigen Beiträgen und mit vier Übersetzern und drei Reviewern gleichzeitig im Einsatz, lief mein Abo ins Sitzungslimit. Alle sieben Agenten starben in derselben Sekunde und hinterließen halb geschriebene Dateien. Online war nichts kaputt, denn hochgeladen wird erst, was die Prüfung und das Review bestanden hat. Nach dem Reset bekamen die neu gestarteten Übersetzer die Anweisung, eine liegengebliebene Datei nur dann weiterzuverwenden, wenn sie anhand des Originals nachgewiesen hatten, dass sie vollständig ist, und sie andernfalls neu zu schreiben. Seitdem laufen weniger Agenten parallel, in gleichmäßigerem Tempo. Bei einem Nutzungslimit pro Sitzung und pro Woche entscheidet dieses Budget, wie schnell es vorangeht, nicht die Zahl der Agenten.
🚫 Was ich bewusst nicht gemacht habe
- Kein Chinesisch (oder Japanisch, oder …). Verlockend wegen der Reichweite, aber dann würde ich Text veröffentlichen, den ich nicht einmal stichprobenartig prüfen kann. Drei Sprachen, denen ich wenigstens grob folgen kann, fühlten sich wie die ehrliche Grenze an.
- Kein komplett unbeaufsichtigter Autopilot. Irgendwann stand die Idee im Raum, einen systemd-Timer zu bauen, der alle paar Stunden eine Headless-KI-Sitzung startet und weiter übersetzt, während niemand hinschaut. Die Sicherheitsprüfungen in meinem KI-Tooling haben sich geweigert, das einzurichten, und im Nachhinein zu Recht: Eine unbeaufsichtigte KI mit SSH-Schreibzugriff auf einen Produktivserver ist etwas, das ein Mensch bewusst einschalten sollte, nicht etwas, das sich als Komfortfunktion einschleicht. Also läuft die Pipeline, während ich da bin.
📝 Noch auf der Liste
- Die restlichen Beiträge fertig übersetzen. ✏️ TODO (Raphael): current state.
- Interne Links und die „Related posts“-Listen auf die übersetzten Versionen zeigen lassen und die Hinweise „(auf Englisch)“ entfernen.
- Die statischen Seiten übersetzen (About, Impressum, Datenschutzerklärung, AI Usage Notice) und jeder Sprache ihr eigenes Menü geben.
- In ein paar Monaten in die Suchstatistik schauen und nachsehen, ob überhaupt jemand gekommen ist.
Wenn du das hier auf Deutsch, Französisch oder Spanisch liest: willkommen, bienvenue, bienvenido. Und wenn ein Satz klingt, als hätte ihn ein Roboter geschrieben, dann deshalb, weil es so war. Sag mir Bescheid, und ein Mensch bringt es in Ordnung.





