Code Styling Project

It’s not a bug, it’s always a feature.
  • Deutsch
  • English
  • rss
  • Home
  • Blog
  • Impressum
  • Entwicklungen
  • Fehlerbehebungen
  • Anleitungen

Chaos bei der WordPress Theme Übersetzung

codestyling | 13. August 2010 | 15:37

In letzter Zeit nehmen die Meldungen zu, daß dieses oder jenes Theme nicht übersetzt ist, von meinem Plugin nicht erkannt wird oder die entsprechende Sprachdatei trotz Erstellung nicht benutzt.

Das ist aber nicht verwunderlich, denn anders als bei Plugins, herrscht hier ein heilloses Chaos, wie man denn nun mit Sprachdateien umgehen soll. Jeder Theme Designer kocht sein eigenes Süppchen und obwohl WordPress sich sichtlich um einen Standard bemüht hat.

Um vorweg auch mal was positives zu sagen, ehe ich mit meinem Frust beginne, finde ich es sehr gut, daß endlich die Einsicht gekommen ist, Themes auch übersetzungsfähig zu machen und anzubieten.

Allerdings gibt es in diesem Spiel zwei Parteien, die beide gute Arbeit machen um sie sogleich wieder mit dem Ar… einreißen. Ich meine die Theme Designer/Coder und das Core Dev Team von WordPress (Automattic).

Fraktion 1 - Core Dev Team WordPress

Seit WordPress Version 2.9 hat sich das Core Team echt Gedanken gemacht, wie man denn nun mit der Problematik zurechtkommt, wenn ein Basis-Theme übersetzt ist und eine Sprachdatei lädt, aber nun davon ein Child Theme abgeleitet werden soll. Vorher gab es nur eine Variante die Theme Sprachdatei sauber zu laden:

load_theme_textdomain(….)

Dies wurde mit der WP Version 2.9 und höher dann ergänzt mit einer 2. Funktion:

load_child_theme_textdomain(….)

Wozu sind diese aber nun gedacht und wie funktionieren die? Das hab ich mich erstmal auch gefragt, eine Idee dazu hatte ich bereits. Also meine Arbeitshypothese lautete:

  • load_theme_textdomain wird im Basistheme benutzt und immer geladen, selbst wenn das aktive Theme ein Child Theme diese Basisthemes ist
  • load_child_theme_textdomain wird nur im Child Theme benutzt, um eine additive Sprachdatei zu der bereits geladenen Basissprachdatei hinzuladen zu können, falls das Child zusätzliche Texte benötigt, die im Parent nicht drin sind.

Nun ja, eine Analyse ergab ein wenig abweichendes Bild. Child-Themes werden vom Core ein wenig aufwendiger behandelt. Deshalb findet man auch folgenden Code-Ausschnitt:

PHP
1
2
3
4
5
// Load the functions for the active theme, for both parent and child theme if applicable.
if ( TEMPLATEPATH !== STYLESHEETPATH && file_exists( STYLESHEETPATH . '/functions.php' ) )
	include( STYLESHEETPATH . '/functions.php' );
if ( file_exists( TEMPLATEPATH . '/functions.php' ) )
	include( TEMPLATEPATH . '/functions.php' );
timing: 0.035s

Das heißt nun folgendes:
Schritt 1: wenn das aktive Theme ein Childtheme ist und dort eine “functions.php” existiert, dann lade sie.
Schritt 2: wenn eine “functions.php” im Basistheme existiert, lade auch diese an.

Somit kann man auch im Childtheme eine “functions.php” besitzen und ist trotzdem sicher, dass die vom Basistheme auch geladen wird.

Nun zu einem Huhn und Ei Problem dabei. Da das Childtheme hier Vorrang vor dem Basistheme hat, kann man auf keinerlei Funktionalität des Basisthemes zugreifen, da dessen Funktionen noch nicht geladen sind! Allerdings könnte man so Funktionen des Basisthemes mit eigenen Implementationen ausser Kraft setzen, sofern dieses das Basistheme erlaubt. Bei Themes auf Framework Basis hat man damit allerdings keine Chance!
Würde man den Spieß umdrehen, dann könnte man im ChildTheme bereits Funktionen oder Klassen des Basisthemes benutzen, mit dem Überschreiben von Funktionen ist es dann aber definitiv vorbei. Dies wiederum würde allerdings das Basistheme gegen Modifikationen härten und den Supportaufwand des Authors drastisch reduzieren.
Hierzu hätte es meiner Meinung nach eine Abstimmung geben sollen, denn beide Varianten haben Vor- und Nachteile, die es abzuwägen gilt.

Was hat das nun mit Sprachdateien zu tun?
Wie schon gedacht, lädt die Funktion load_theme_textdomain die Sprachdatei des Basisthemes, unabhängig davon ob man ein Childtheme aktiv hat.
Ebenfalls wie vermutet, lädt load_child_theme_textdomain die Sprachdatei des Childthemes, sofern diese eines besitzt. Hat man also in der “functions.php” des Basisthemes die load_theme_textdomain drin und in der “functions.php” des Childthemes die load_child_theme_textdomain stehen, dann werden 2 Sprachdateien geladen, eine aus dem Basistheme Sprachen-Ordner und eine aus dem Childtheme Sprachen-Ordner. Wenn beide auch noch die gleiche Textdomain benutzen, dann werden beide gemixt.
Genau dieses Mixing führt nun unter der aktuellen Lade-Priorisierung der beiden “functions.php” Dateien zum Verlust der Einträge der Child-Spachdatei, wenn diese einige Begriffe des Basisthemes komplett anders übersetzt hat (z.B. im Kundenauftrag) !

PHP
1
2
3
4
5
function merge_with(&$other) {
		foreach( $other->entries as $entry ) {
			$this->entries[$entry->key()] = $entry;
		}
	}
timing: 0.031s

Wenn also erst das Childtheme geladen und danach erst das Basistheme, wird der mühevoll per Sprachdatei geänderte Text des Childthemes wieder mit dem aus der Basis-Sprachdatei überschrieben! Ich denke, daß dieser Zustand nicht gewünscht ist und ich sehr wohl eine eigene Sprachdatei in meinem eigenem Childtheme habe will, die zwar 90% aller Texte des Basisthemes benutzt, aber 10% manuell geänderte Texte enthält. Dies funktioniert aber derzeit nicht so! Nur eine geänderte Ladereihenfolge könnte das ermöglichen.

Fraktion 2 - Theme Designer

Sehr viele Theme-Designer setzen auf Theme-Frameworks. Damit ist es schlicht nicht mehr zu erkennen, wann eine Sprachdatei geladen wird. Somit kann man nie genau sagen, welche der beiden Sprachdateien (Child oder Basis) nun der “Gewinner” sein wird. Außerdem sind viele Themeauthoren vollkommen von den standardisierten Sprachdatei-Aufrufen abgewichen und kochen ein nicht mehr maschinell unterstützbares eigenes Süppchen.

Als Beispiel führe ich hier das Hybrid WordPress Theme (Framework) von Justin Tadlock an, das gleich mehrere “Fehler” enthält bzw. enthielt:

Anstatt die für Themes vorgesehenen Funktionen zum Laden der Sprachdateien zu benutzen, griff er auf die Funktion zu laden von Kontext unabhänigen Sprachdateien zurück load_textdomain. Damit umging er mehrere WordPress Filter und Actions die beim Laden von Theme Sprachdateien normalerweise ablaufen und schoss am Standard vorbei. Im Trunk ist das nun nach Diskussion mit mir bereits geändert und sollte in 0.9 zur Verfügung stehen. Aus diesem Grund findet auch mein Lokalisierungsplugin dieses Theme nicht, denn es unterstützt nicht eine der beiden Sprachdatei-Ladefunktionen für Themes. Nach seinem Patch habe ich mir das nochmal aus seinem Trac angesehen und mußte feststellen, daß er den Dateinamen immer noch mutwillig selbst generiert.
Normal wäre “de_DE.mo” für ein Theme aber seine Datei wird als “hybrid-de_DE.mo” erwartet, was mein Plugin ebenfalls nicht unterstützt. Es legt korrekt eine “de_DE.mo” an, die dann logischweise sein Theme nicht findet und auch nicht lädt.

Zusammenfassung und Einfluß auf mein Lokalisierungsplugin

Wie man hier sehen kann, ist es einfach ein Graus, mit all den verschiedenen Sachen zurechtzukommen und dies in ein Übersetzungssystem zu verpacken. Da sich hier viele nicht an Standards halten oder logische Fehler im Core einem das Leben schwer machen, kann ich derzeit nicht garantieren, daß alle Themes (Childthemes) richtig erkannt oder aber nach Übersetzung auch die Sprachdatei überhaupt laden. Ich kann nur eine Software bereitstellen, die mit dem Standard konform geht.
Eine Anpassung meines Lokalisierungsplugins im Bereich Themes wird es mit dem nächsten Update (vermutlich dann v1.98.1) geben, allerdings weiche ich da nicht vom Standard ab. Ich kann leider nicht für jede Theme-Schmiede eine Sonderwurst in mein Plugin schreiben, denn dann würde ich nie fertig sein.

Ich appelliere an alle Theme-Designer, sich mit den Standards auseinanderzusetzen und der Community die Möglichkeit zu eröffnen, sich ihre Themes so einfach wie möglich auch im Sinne der Lokalisierung anzupassen. Mich würde interessieren, was eure Meinung dazu ist, ich hab eine Menge graue Haare bekommen, als ich mich intensiver mit Themes auseinandergesetzt habe.

Kategorien
Deutsch, WordPress (DE)
RSS Kommentare
RSS Kommentare

« Codestyling Localization beherrscht jetzt BuddyPress und bbPress Codestyling Localization und PoEdit sind wieder kompatibel »

12 Antworten    Schreib einen Kommentar

Thomas Scholz

Thomas Scholz

13.08.2010 | 20:12

Könnte man das Laden der Sprachdatei für das Childtheme nicht an eine bestimmte action binden, die erst ausgeführt wird, wenn das Basistheme seine Sprachdatei geladen hat? Damit umginge man zumindest das Überschreiben, oder?

Antworten »

codestyling

codestyling

13.08.2010 | 20:45

Wäre zumindest eine Variante, aber das Problem ist größer, als man denkt. Man kann zum Beispiel im Child eine vollständige *.mo haben und will die Basis damit komplett ersetzen. Oder aber man will nur den Delta zur Basis *.mo haben, den man selbst eingebaut hat aber mit der Basis mischen lassen. Oder man hat 50% geändert, auch nur ein delta *.mo und will das auch korrekt mischen lassen. Warum muß ich beim Vollersatz 2 Monster-Mo’s laden, wenn ein mal laden reichen würde?
Die Sache ist noch beliebig komplexer als ich hier darstellen kann, denn wenn Vererbung habherzig eingebaut wurde (bei Themes als Beispiel) ist das ein Problem.
PS: Nur am Rande, ich würde auch erwarten, das man eine Theme Kette bauen kann, was WordPress aber komplett nicht versteht: Basis -> Child 1 -> Child 2 -> Child 3
Logisch gesehen ist dies bei gescheitem Konzept kein Problem, WP kann aber nur Basis -> Child.

Antworten »

Thomas Scholz

Thomas Scholz

13.08.2010 | 23:23

In meinem eigenen Framework benutze eine separate Config-Klasse, über die das Child-Theme bestimmte Optionen komplett abschalten kann. Hierüber ließen sich auch die zu ladenden Sprachdateien steuern, also was geladen wird und in welcher Reihenfolge. Bisher war das nicht relevant, aber ich werde das mal einbauen.

Als allgemeine Lösung wäre ein zusätzlicher Header in der style.css vielleicht hilfreich, der das regelt. Etwa so:

Load Parent Language: first|second|never

Willst du das mal im Trac oder auf wp-hackers diskutieren?

Antworten »

codestyling

codestyling

14.08.2010 | 00:06

Momentan hab ich mehrere Ideen, eine davon ist ähnlich der von dir vorgeschlagenen Idee. Allerdings hab ich dazu schon eine private Discussion mit Justin Tadlock begonnen, deren Ziel ein möglichst umfassender Patch für den WP core sein soll. Das würde auch ein Redesign der aktuellen Theme Load Funktionen nach sich ziehen müssen bei gleichzeitiger Abwärtskompatibilität.
Ich mag keine Schnellschüsse, was dabei rauskommt, sieht man ja an WordPress selbst. Erst wenn es einen Pool von “Prove of Concept” Lösungen gibt, der diskussionswürdig ist, wäre ein Trac Eintrag gerechtfertigt. Frickel-Lösungen mit der heißen Nadel liegen mir eben nicht, sah man ja an der Datei für das laden von *.mo Files, die Speicher sparen kann, wenn man sich ein wenig mehr den Kopf zerbricht.

Antworten »

wemaflo

wemaflo

15.08.2010 | 22:30

Du kannst in der functions.php deines Child Theme Funktionen der functions.php im Parent Theme deaktivieren. Wenn du also das laden der Sprachdatei des Parent Theme deaktivierst, diese ins Child Theme kopierst und die Änderungen vornimmst, sollte alles nach deinen Wünschen funktionieren.
Um das ganze besser zu gestalten, könnte man eine neue Funktion schreiben, die erst die Sprachdatei des Parent Theme läd und dann mit der Sprachdatei des Child Theme überschreibt, wo Überlagerungen auftreten.

Das ist jetzt nur so ein kleiner Denkansatz ;)

Antworten »

codestyling

codestyling

15.08.2010 | 22:56

Machen wird mal ein Beispiel mit einem Basistheme, das eine Sprachdatei innerhalb einer Klassen-Architektur lädt (functions.php des Basis-Themes):

PHP
1
2
3
4
<?php
require('my_theme_core.php');
$my_theme = new my_theme_class();
?>
timing: 0.034s

Wie überschreibst du jetzt gefahrlos die Klassenmethode, die die Sprachdatei lädt in der functions.php deines Child-Themes?
Warum sollte ich 2 mal ein *.mo File laden müssen (aus Speicher- und Performancegründen nicht akzeptabel), wenn es mit einmal laden auch getan wäre ?
Wie du siehst, sind die Probleme nicht kleiner sondern sehr viel komplexer. Viele Theme-Frameworks lassen sich schlicht nicht aushebeln und geben dir als Child-Theme Writer auch keine Möglichkeiten, das zu beeinflussen. Deshalb tüftel ich an einer Lösung, die sich einfach in den WP Core als Patch beantragen lassen sollte und mit Bestandssoftware korrekt weiterarbeitet.

Antworten »

Justin Tadlock

Justin Tadlock

19.08.2010 | 13:16

Excuse me for responding in English, but it’s been a while since I’ve used my German language skills. And, sorry if I misread anything from your post.

I definitely understand your frustration. I have the same frustrations with WordPress. My theme users want to keep their parent theme translations in their child theme. Otherwise, their translation is lost when the parent theme is updated. Because so many of them asked about this, I created a solution that would fix this problem for them.

If I had $5 for every time I was asked for this solution, I’d be a wealthy man. I’ve only been asked about integration with your plugin one time. So, I have to look at which solution is currently the best for the majority of my users, even if that means not working with some plugin.

Admittedly, I should’ve used a filter in version 0.8, but as you’ve seen, I’ve added this to the next update. Obviously, this doesn’t fix the problem with your plugin. Continuing to call the theme code non-standard and incorrect does nothing to help solve the issue. Using a filter hook available in WordPress is “standard” practice. If the plugin doesn’t recognize this, then there’s an obvious problem there.

The default in WordPress should be $textdomain-de_DE.mo for themes. This would help solve the issues with child themes and parent themes by allowing users to have multiple theme translations inside of the child theme. It should also satisfy your plugin’s need to scan for files that aren’t loaded. I will support any ticket you bring up on Trac with a good patch/solution.

Antworten »

Christian Land

Christian Land

01.09.2010 | 13:12

Was ich eigentlich noch viel schlimmer finde ist die generelle Naivität mit der viele PlugIn und Theme-Autoren an das Thema Mehrsprachigkeit rangehen.

Ich hab in letzter Zeit mehrfach Premium Themes (primär bei Themeforest) gesehen die einfach nur auf englisch waren und keinerlei Gebrauch von den WP-Funktionen zur Mehrsprachigkeit gemacht haben. Genauso “toll”: ich bastel momentan an “The Morning After” rum das ja seit einiger Zeit von WooThemes betreut wird. Nicht nur, dass deren Framework zum Teil nicht übersetzbar ist… nein… zumindest bei TMA gibt es einen wilden Mix von übersetzbaren und nicht übersetzbaren Strings, fehlender Nutzung von _n() wo es angebracht wäre und totaler Ignoranz gegenüber Argumenten und Argument Swapping was dann zu so Konstrukten führt wo erst ein Teil eines Satzes ausgegeben wird, dann eine Zahl und dann der Rest des Satzes. Schön aufgeteilt in seperate Aufrufe von _e() und echo.

Und bei PlugIns sieht die Sache nicht besser aus. Sie wird höchstens noch durch sinnfreies laden von CSS und JS-Dateien verschärft (mein Favorit im Moment: ein Syntax-Highlighter PlugIn welches ca. 20 JS-Dateien in die Seite klatscht - egal ob das PlugIn überhaupt auf der jeweiligen Seite etwas zu tun hat oder ob nicht).

Antworten »

Roman

Roman

07.09.2010 | 15:06

Danke Heiko für die Erläuterung. Wie dir ja vll. noch bekannt ist, hatte auch ich einige Probleme mit meinem Themeframework und der Mehrsprachigkeit.

Zum Huhn und Ei Problem: Seh ich genau so wie du. Der Entscheid vom WP Dev Team, zuerst das Child anstelle des Parent zu laden, ist aus OO sicht völliger quatsch. Verstehe nicht, weshalb dies so entschieden wurde. Auch wenn WordPress für PHP4 geschrieben wurde, sollte man von dem Ansatz wegkommen und etwas objektorientierter denken.

Mein eigenes Framework setzt auf PHP5 auf. Da ich jedoch keine Hooks im Child-Theme benutzen wollte, sondern das Child-Theme anschliessend gewisse Basis-Klassen überladen sollte (sofern erlaubt natürlich), laufe ich dank der “falschen” Ladereihenfolge nun in ein Problem. Dem Child sind alle Klassen des Basis Themes nicht bekannt. Ein nachträgliches includen hilft auch nicht, da dies erst zu einem späteren zeitpunkt durchgeführt wird. Ich suche und hoffe immer noch nach einem übersehen action hook, doch bisher leider Fehlanzeige.

Gruss
Roman

Antworten »

Roman

Roman

23.02.2011 | 17:11

Kleiner Nachtrag was die Ladereihenfolge der Themes betrifft.

Wie von Heiko bereits erwähnt, lädt WordPress zuerst das functions.php des Child- und anschliessend das des Parent-Themes (Sofern eine solche Datei existiert).

Ich habe mich nun mittels dem Action-Hook “after_setup_theme” zuhelfen versucht. Die Ladereihenfolge der functions.php habe ich so zwar nicht umdrehen können, doch innerhalb der functions.php lade ich nun die jeweilige PHP-Klasse mit unterschiedlicher Prioität. So erreiche ich, dass zuerst der Code fürs Parent-Theme und erst anschliessend das Child-Theme abgearbeitet wird.

Im Action-Hook erstelle ich hierbei eine anaonyme Funktion, in welcher ich die init-Methode meiner Klasse aufrufe. Entscheidend hier ist die Prioritätsangabe als letzten Parameter. Diese muss höher sein, als der Standardwert von WordPress (10).

In der Init-Methode der Parent-Klasse, lade ich nun via der Funktion “load_theme_textdomain” die Sprachfiles.

Im Child-Theme mache ich prinzipiell das selbe, ausser das ich hier dem Action-Hook “after_theme_setup” nun eine niedrigere Priorität (10 statt 9) zuweise. Somit stelle ich sicher, dass die Init-Methode des Child-Themes erst nach der des Parent-Themes abgearbeitet wird.

In der Init-Methode des Child-Themes kommt dann die Funktion “load_child_theme_textdomain” zum Zug. Als Paremeter diesmal nicht TEMPLATEPATH sondern STYLESHEETPATH, da wir uns ja nun im Child befinden.

Das Schöne dabei, ich kann nun im Child auf Klassen im Parent-Theme zugreifen, von diesen erben und eigene Implementierungen schreiben. Änderungen am Parent haben somit ebenfalls Einfluss aufs Child, da ich hierbei nicht auf Action-Hooks sondern auf die OO-Technik von PHP5 setze.

Korrigiert mich wenn ich irgendwo einen Fehler drin habe resp. dieses Vorgehen in Zukunft zu grösseren Problemen führen könnte. Meine bisherigen Seiten laufen nun jedoch bereits einige Zeit mit zahlreichen 3th-Party Plugins ohne Probleme. Was ich selbst noch nicht testen konnte, war die Implementation in eienr WPMU Umgebung.

Gruss
Roman

Antworten »

Roman

Roman

23.02.2011 | 17:14

Leider scheint der Code hier etwas zerstückelt anzukommen, daher noch als Alternative über pastbin.com:
-> http://pastebin.com/fpKSEHF0

Antworten »

Jan

Jan

23.03.2011 | 22:30

Danke für die Problem-Beschreibung Deines Plugins mit hybrid-core. Bis es eine bessere Lösung gibt, verwende ich eine von Hybrid extendete Klasse, die eine geänderte Funktion load_textdomain nutzt. Diese sucht nun sowohl nach Justins beiden Schreibweisen, als auch den beiden Schreibweisen ohne die Domain.

Jan (interiete.net)

Antworten »

Du kannst diese Tags verwenden : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Navigation

  • Allgemein
  • jQuery in WordPress
  • Politik
  • WordPress (DE)

Suche

Neuere Beiträge ...

  • Server Quota’s mit WordPress anzeigen
  • Plugin WP System Health ist jetzt übersetzbar und aufpoliert
  • WPTouch iPhone Theme und die verkrüppelte Übersetzung
  • Die Zukunft von BuddyPress und bbPress
  • Codestyling Localization und PoEdit sind wieder kompatibel

Ältere Beiträge ...

  • Codestyling Localization beherrscht jetzt BuddyPress und bbPress
  • Wiederbelebung von Kubrick für WordPress 3.x Versionen
  • WordPress Sprachdateiverarbeitung Betatest - erste Analysen
  • WordPress 2.8 - Sprachdatei Speicherverbrauch minimieren
  • Sprache der Administration - wie hätten Sie es denn gern ?
rss RSS Kommentare valid xhtml 1.0 design by jide powered by Wordpress get firefox