WordPress Multisite: Nachdem im ersten Teil berichtet wurde, welche verschiedenen Varianten es gibt, eine WordPress-Webseite z.B. für verschiedene Sprachvarianten einzurichten, geht es jetzt um die konkrete Umsetzung. Wie bereits vorher beschrieben, war für eproi eine WordPress Multisite die am besten funktionierende Variante.
WordPress Multisite: Vorbereitende Maßnahmen
WordPress wurde ursprünglich als Blog-Software konzipiert. Demzufolge ist es in einer Grundinstallation nicht dafür vorbereitet, als „Multisite“ Software mithilfe ein und derselben Installation für verschiedene Seiten Daten vorzuhalten und bearbeitbar zu machen. Im ersten Schritt muss also WordPress erst mal grundsätzlich darauf vorbereitet werden.
Hierfür gibt es aber bereits viele verschiedene Artikel im Internet. Dadurch liegt hier nicht der Fokus dieses Artikels. Wer dafür Hilfe braucht, kann direkt in der Doku von WordPress die notwendigen Dinge dafür durchlesen. Nur so viel: Eine Einstellung in der config-Datei, anschließend die Einrichtung des Netzwerks und ein paar kopierte Anweisungen in die config-Datei bzw. .htacces-Datei aktivieren erst mal WordPress für multiple Seiten aus einer Installation heraus.
Anschließend kann im WordPress Backend die Anlage der entsprechenden Seite etc. vorgenommen werden. Für Plugins gibt es dabei zwei Möglichkeiten. Entweder aktiviert man sie netzwerkweit. Einstellungen können dann je nach Plugin aber auch nur netzwerkweit vorgenommen werden. Und sie sind dann quasi immer für alle Seiten aktiviert. In der zweiten Option aktiviert man die Plugins nur jeweils nur für die Sprachvariante, die dieses auch nutzt. Vorteil: Wird ein Plugin in einer Sprachvariante gar nicht verwendet, „belastet“ es auch nicht deren Ladezeit. Theme und Inhalte der Seiten werden dann über die jeweilige Seite im Netzwerk direkt eingegeben.
Subfolder oder Subdomain?
Auf einigen Seiten und Artikeln wird zum Teil sehr kontrovers diskutiert, ob die Struktur einer Multisite mit nur einer Domain eher über Subdomains oder über Subfolder realisiert werden sollte. Grundsätzlich ist natürlich beides denkbar und beides bietet Vor- und Nachteile.
Suchmaschinen haben wohl mit keiner der beiden Varianten ein Problem. Schließlich ist das eine wie das andere ja erst mal nur ein Hilfsmittel, um eben den einen Inhalt vom anderen abzugrenzen. Scheinbar hat eine Subdomain einen kleinen Vorteil, da es ja eben eine eigene „neue“ Domain ist. Da die Suchmaschinen aber jeweils auch analysieren, was für ein Inhalt hinter der Domain steckt, dürfte der Vorteil eher marginal sein. Letztlich wird ja auch ein duplicate content angenommen, wenn ein und derselbe Inhalt auf zwei vollkommen unterschiedlichen Domains läuft. Daraus lässt sich wohl umgedreht schlussfolgern, dass ein entsprechender Content wichtiger ist als die Frage, auf welcher Domain er läuft.
Bleibt also nur die Frage, ob man jeweils ein eigenes Zertifikat bzw. Multi-Domain- oder Wildcard-Zertifikat erwerben möchte. Zu dem Thema haben wir aber schon im ersten Teil dieses Artikels die Vor- und Nachteile diskutiert.
Alles in Allem: Subfolder-Angabe macht das Rennen
Für eproi hat sich jedenfalls die Variante Subfolder angeboten. Einstellungstechnisch ist diese auch nicht viel anders als bei der Subdomain-Variante. Lediglich define(‚SUBDOMAIN_INSTALL‘, false); ist dann eben false statt true. Um keine Überraschungen zu erleben oder hinten dran erst nochmal alles umstellen zu müssen etc. stellt man dies am besten gleich bei der Netzwerkeinrichtung ein. Den Rest übernimmt dann WordPress. Hat man es am Anfang vergessen einzustellen, kann man das natürlich auch nachträglich umstellen, muss dann aber darauf achten, auch die „Grund-URLs“ der jeweiligen Seiten anzupassen. Das kann dann natürlich schwer werden, weil man nicht gleichzeitig die Änderungen im WordPress selbst und in der config-Datei einspielen kann und man ggf. durch Cookies und Co. dann erst mal soviel wie ein HTTP 404 oder ein permanentes Zurückspringen auf die Loginseite trotz korrekter Logindaten erlebt. Bei letzterem Fehler kann es übrigens helfen, die Cookies zu löschen.
SEO-Anpassungen für die verschiedenen Webseiten innerhalb der WordPress Multisite
Ein paar Probleme bleiben aber – auch wenn man jetzt sehr individuelle Inhalte eingeben und z.B. auch verschiedene grafische Elemente für verschiedene Sprachvarianten problemlos einstellen kann. Erst mal müssen die Sprachvarianten noch miteinander verlinkt werden. Hierfür haben wir bisher auch nur die absolut einfachste Variante eingesetzt. Auf jeder Webseite haben wir im Hauptmenü einen weiteren Menüpunkt eingebaut und dort die jeweils andere Sprache eingeben und auf die „home“-Seite der jeweiligen Sprachvariante verwiesen. Aktuell reicht das vollkommen aus, weil wir nur deutsch und englisch bereitstellen und die englische Sprachvariante aktuell auch (noch) nicht für alle Inhalte im Deutschen eine passende Entsprechung anbietet. Ziel ist aktuell vor allem, internationalen Besuchern einen Eindruck zu geben und damit die hohe Abbruchrate zu verringern.
Später wird mit Sicherheit noch eine komfortable Variante eingebaut werden, womit dann eine konkrete Verlinkung von einzelnen Inhalten ermöglicht wird.
WordPress Multisite aus SEO-Sicht
Trotzdem hat die Multisite-Option von WordPress einen entscheidenden Nachteil aus SEO-Sicht: im HTML-Tag einer Seite wird heute i.d.R. mittels lang-Attribut die Sprache des Inhalts hinterlegt. Das ist zwar nicht zwingend notwendig, hilft aber bei manchen Dingen. Und wenn es schon angegeben wird, sollte es natürlich auch mit dem Inhalt übereinstimmen. Bei WordPress ist das in dem Fall die ursprüngliche Sprache der ursprünglichen Seite (unter der Annahme, dass man wie bei eproi zunächst eine Zeit lang schon eine Sprachvariante aufgebaut hat). In einer Neuinstallation wird das aber vermutlich auch die von der Installation her festgelegte Sprache sein. Eproi lieferte naturgemäß de-DE für die deutsche Sprache aus.
Das ist natürlich die vollkommen falsche Sprachangabe für nicht-deutsche Inhalte – also beispielsweise unsere englische Version. Um dies auszugleichen, haben wir eine kleine Funktion erstellt. (Ein bisschen Inspiration haben wir uns dabei von einem Artikel auf stackexchange geholt.) Anhand der aufgerufenen Seite wird entsprechend das Language-Attribut befüllt:
function __language_attributes($lang)
{
$langs=array("de-DE","en-EN");
if (stripos($_SERVER[REQUEST_URI],"en")!== false )
{
return 'lang="'.$langs[1].'"';
}
return 'lang="'.$langs[0].'"';
}
add_filter('language_attributes', '__language_attributes');
Und wohin mit der Funktion?
Die Funktion entweder in eine eigene Datei packen und per require/include ins WordPress einbauen. Oder aber, was auch in verschiedenen Artikeln immer wieder angedacht wird: Direkt in die functions.php packen. Wichtig dabei wäre nur, darauf zu achten, dass die Funktion irgendwo da liegt, wo sie zentral in jeder Version aufgerufen wird. Denn nur dann kann sie natürlich wirken. Auch wichtig: Bitte dran denken, wenn die Funktion nicht in einem eigenen „WordPress“-Tool liegt, muss sie nach einem Update der entsprechenden Komponente wieder eingespielt werden…
Ein eigenes Plugin dafür bereitzustellen, empfiehlt sich aber trotzdem eher nicht. Einerseits ist der Code-Schnippsel viel zu kurz für ein Plugin. Andererseits müsste man für ein Plugin noch reichlich Overhead einfügen, damit WordPress es als Plugin ordentlich einbindet. Wäre also in dem Sinne auch zu „oversized“. Wer aber schon ein eigenes Plugin am Start hat, kann das natürlich problemlos auch dort mit einfügen – so haben wir das für eproi auch gelöst.
Was passiert in der Funktion?
Über ein Array werden die passenden Language-Attribute aufgelistet. Aktuell wäre das nicht zwingend notwendig dafür ein Array zu bauen, aber so ist die Funktion zum Teil schon vorbereitet, falls später mal eine größere Menge an verschiedenen Sprachen dargestellt werden soll.
Anschließend wird geschaut, ob in der angefragten URL die Zeichenkette en vorkommt – falls dem so ist, wird der Language-Attribut-String für Englisch ausgegeben (en-EN), sonst de-DE. Da es aktuell nur zwei Sprachen gibt, genügt das erst mal.
Über den zugefügten Filter wird dafür gesorgt, dass diese „Änderung“ der Ausgabe passiert, bevor der fertige HTML-Code erzeugt und an den Browser gesendet wird.
Jetzt stimmt dann die Angabe der Sprache mit dem Content jeweils überein. Aus SEO-Sicht ist das dann schon mal ein guter Punkt. Wie die einzelnen Sprachvarianten dann in Relation gestellt werden, packen wir dann in einen (eigentlich ungeplanten) dritten Teil dieser „Serie“.
Usability
Ein weiteres Problem ist die Usability. Wird von einem User eine Sprache bevorzugt, soll diese auch geliefert werden. Sofern die Sprache vorhanden ist natürlich. Und bestenfalls schon in dem Moment, in dem die Seite aufgerufen wird und nicht erst über den Klick auf einen Menüpunkt. Auch das schafft WordPress nicht von Hause aus. Es wird immer die Sprache ausgeliefert, die explizit angefordert wird. (Soll im Falle von eproi heißen: klemmt ein /de/ am Ende, gibt es die Seite auf deutsch, klemmt ein /en/ am Ende, gibt es das, was auf Englisch bereits eingestellt wurde).
Ganz ohne Hilfe eines Plugins oder mithilfe einer kleinen Programmierung lässt sich das WordPress nicht beibringen. Denn es gibt leider keinen expliziten Schalter, den man einfach nur einstellt (wie beispielsweise bei der Einstellung, dass WordPress als Netzwerkseite laufen soll). Und weil verschiedene Browser verschiedene Varianten für die bevorzugte Sprache senden können und diese Angabe von versierten Benutzern auch noch verändert werden könnte, kann man nicht einfach sagen „Wenn der Benutzer Sprache x anfordert, gib ihm diese, falls verfügbar“. Auch gibt es in der entsprechenden Angabe, die die Browser senden, womöglich mehrere Angaben, die zudem nicht zwingend chronologisch geordnet sein müssen (dafür gibt es in der übermittelten Zeichenkette die Angabe eines Parameters „q“, der dann angibt, wie bevorzugt die Sprache ist). Insofern ist es mitunter schwierig herauszufinden, welche Sprache in welcher Reihenfolge „am bevorzugtesten“ ist und auch noch vom Webserver angeboten wird.
Für eproi verwenden wir zur Zeit folgende zwei Funktionen:
function __redirectOnLang()
{
if (strpos($_SERVER['REQUEST_URI'], "wp-admin") === false && strpos($_SERVER['REQUEST_URI'], "en") === false && strpos($_SERVER['REQUEST_URI'], "de") === false)
{
$lang= GetBestLanguage();
if (stripos($lang,"en")!== false)
{
header("Location: https://eproi.eu/en/",302);
exit();
}
}
}
add_action('registered_taxonomy',"__redirectOnLang");
function GetBestLanguage()
{
$installedlangs=array("de-DE","en-US","en-EN","en");
$langs=array();
$splittedonce=explode(",",$_SERVER["HTTP_ACCEPT_LANGUAGE"]);
foreach ($splittedonce as $langprefs)
{
if (stripos($langprefs, ";")>0)
{
$splittedprefs=explode(";",$langprefs);
$q= floatval(str_ireplace("q=", "", $splittedprefs[1]));
$lang=$splittedprefs[0];
}
else
{
$q=1;
$lang=$langprefs;
}
$langs[$lang]=$q;
}
natsort($langs);
foreach (array_reverse($langs) as $key => $val)
{
if (in_array($key,$installedlangs))
{
return $installedlangs[array_search($key,$installedlangs)];
break;
}
}
return $installedlangs[0];
}
Was passiert in den Funktionen?
Es wird der durch die Anfrage übermittelte Accept-Language-String ausgewertet auf die am besten passende Sprache. Die PHP-eigene Funktion local_accept_from_http hilft uns hier aber nicht besonders viel weiter. Zwar kann diese Funktion die Variable HTTP_ACCEPT_LANGUAGE korrekt auswerten, aber die Auswertung bezieht sich hier auf die auf dem System installierten locales. Wir brauchen aber die Aussage, welche der in der Variablen enthaltenen Sprachen von unserer WordPress-Installation unterstützt wird. Die Funktion GetBestLanguage() nimmt daher den übermittelten String auseinander und prüft dann der Reihe nach, welche der Sprachen verfügbar ist. Die Erstbeste wird zurückgegeben.
Die Variante kann auf eine beliebige Anzahl von Sprachen angewendet werden. Dies ist ein großer Vorteil. Der Fallback return $installedlangs[0] kommt dann zum Tragen, wenn keine der akzeptierten Sprachen von der Seite bereitgestellt wird. Daher sollte in $installedlangs auch immer die „Hauptsprache“ als erstes hinterlegt sein. Für eproi ist dies de-DE. Dies lässt sich aber in jedem Fall auch rasch ändern, indem man die Reihenfolge der Sprachen in $installedlangs ändert.
optimierte Funktionen
In der ersten Variante unserer Funktionen, haben wir noch in einer Session festgehalten, das und wohin wir bereits redirected haben. Dies ist in der jetzigen Variante entfallen. Die jetzige Variante prüft einfach, ob in der aktuell angeforderten URL ($_SERVER[„REQUEST_URI“]) bereits eine der beiden Sprachen explizit vorkommt. Die Variante hat mehrere Vorteile. Zunächst entfällt die Einbindung einer weiteren Session- oder Cookie-Variable. Das ist aus Sicht einer ressourcensparenden Programmierung sehr sinnvoll und auch aus „Datenschutzgründen“. Unabhängig davon, wie lange das ein oder andere dann tatsächlich wirkt: Was wäre, wenn der User sämtliche Cookies ablehnt (z.B.)? Dann hätte man auch dort schon ein Problem – zumindest in der Theorie.
Folgendes ist noch wichtig zu erwähnen. Neben der Frage, ob eine Sprache bereits in der angeforderten URL vorhanden ist, wird zu allererst abgefragt, ob in der URL der String wp-admin vorkommt. Warum? Weil sonst die Administration der Netzwerkseite scheitert. Die übergeordnete Administration hat nämlich weder /de/ noch /en/ in der URL. Auf Grund der „lazyness“ von PHP wird hier gestoppt, sobald einer der drei Varianten vorkommt.
Weil jede einzelne Seite bereits in einer konkreten Sprache erstellt ist, reicht aktuell diese simple Prüfung aus. D.h. wir müssen nur die Besucher weiterleiten, die offenbar noch keine konkrete Seite angesteuern. Und für diese Besucher „genügt“ uns erst mal, sie auf die „Startseite“ der jeweiligen Sprache zu verweisen.
Und jetzt das Kernelement: der Redirect auf die jeweilige Sprachvariante der WordPress Multisite
Dem aufmerksamen Beobachter dürfte aber noch etwas aufgefallen sein. Wir verwenden für den Redirect nicht das wordpress-eigene wp_redirect sondern das php-eigene header mit entsprechender Location-Angabe. Grund dafür ist, dass zu dem Zeitpunkt, wo wir den Redirect anfordern, dieser noch nicht als Funktion bereitsteht. Das sieht man ein paar Zeilen weiter darunter. Add_action enthält „registered_taxonomy“ als Angabe, nach welcher Systemfunktion unsere Funktion eingebunden werden soll. Wir haben hier auf eine möglichst frühe Umleitung gesetzt. WordPress soll nicht erst die gesamte Seite bearbeiten, um sie dann durch den Redirect zu verwerfen. Eine ressourcenorientiere Nutzung und damit ein potentieller Zeitgewinn vom Laden ist hierbei der Fokus. Wer ansonsten gern selbst testen möchte, welche interne Action als erstes und am sinnvollsten dafür zu verwenden ist, findet in diesem Artikel auf stackexchange eine Inspiration dazu.
Nun ist unser WordPress als WordPress Multisite vorbereitet für die Anlage und Ausgabe verschiedener Sprachen und wir können uns jetzt darum kümmern, unsere Englische Seite mit Leben zu füllen.