Mehrsprachigkeit WordPress Plugins

Aus aktuellem Anlass (Erstellung meines ersten WordPress Plugins GarageSale) habe ich mich auch mit dem Thema Multilingualität und Übersetzungen auseinandergesetzt (Jetzt weiß ich auch woher I18N kommt – 18 Buchstaben zwischen i und n im Wort internationalization ;-)

Grundsätzlich muss die zu übersetzende WordPress Komponente natürlich die Funktionen __(„text“,“domain“) oder _e(„text“,“domain“) verwenden. Siehe hierzu I18N for WordPress Developers.

WordPress stellt für die Übersetzung einige Werkzeuge auf dem Subversion Host bereit. Aber zunächst einmal ein Schritt nach dem Anderen.

Schritt 1 – Download der Tools für Windows

Da ich auf den Clients Windows einsetze habe ich mich dazu entschlossen auch die Entwicklungswerkzeuge für Windows (in meinem Fall noch das gute alte Windows XP aber es sollte keinen Unterschied machen) einzusetzen  und zu beschreiben.

Wir beginnen also mit dem Download von folgenden Komponenten:

Und danach installieren wird zunächst einmal alle 4 Programme mit dem Windows Installer.

Schritt 2 – gettext und php einrichten

Damit die Gettext Werkzeuge funktionieren sollten Sie noch zum Windows Pfad hinzugefügt werden. Dies kann man entwededer dauerhaft machen unter Systemsteuerung -> System -> Erweitert -> Umgebungsvariablen oder aber direkt vor dem Aufruf des Übersetzungswerkzeugs in der Commandshell.

Ich habe gettext einfach in das Standardverzeichnis c:\Programme\GnuWin32 installiert. Da die Utilities im Unterverzeichnis bin liegen muss also der vollständige bin Pfad hinzugefügt werden:

c:\Programme\GnuWin32\bin

Wie gesagt die Alternative lautet, direkt vor dem Aufruf der Übersetzungstools den Pfad in der Commandline mittels set PATH=%PATH%;c:\Programme\GnuWin32\bin hinzufügen.

Analog zu gettext wird dies mit PHP gemacht. Also falls php in das Standard-Verzeichnis c:\Programme\php installiert wird, dann muss dieses ebenfalls zur Umgebungsvariable Path hinzugefügt werden.

Schritt 3 – Entwicklerwerkzeuge von WordPress aus SVN holen

Um mit der Übersetzung zu beginnen benötigt man jetzt die Entwicklerwerkzeuge von WordPress. Diese holt man sich mittels Subversion (in unserem Fall komfortabel mit dem Tool tortoiseSVN) von der Adresse http://i18n.svn.wordpress.org/tools/trunk

Hierzu erstellt man am Besten ein leeres Verzeichnis (z.B. c:\Work\wp-i18n\) und dort drückt man die rechte Maustaste und wählt SVN Checkout. Im darauffolgendem Tortoise Fenster gibt man unter URL of repository http://i18n.svn.wordpress.org/tools/trunk und drückt auf OK.

Schritt 4 – WP i18n tool starten

Nach dem Checkout befinden sich jetzt die WordPress i18n Übersetzungs Tools im Verzeichnis c:\Work\wp-i18n. Wir öffnen nun ein Commandline Fenster und wechseln in diesen Ordner (wichtig, falls oben in Schritt 2 die Methode mit der dauerhaften Pfad Anpassung gewählt wurde, darf ein jetzt bereits schon offenes Commandline Fenster nicht weiterverwendet werden, da sich die Umgebungsvariablen Änderung nicht auf bereits geöffnete Fenster auswirkt!

Eine Eingabe des Kommandos xgettext sollte nun eine Informationszeile auswerfen ebenso wie eine Eingabe des Befehlst php -v. Falls dies nicht funktioniert, nochmal zurück zu Schritt 2 und prüfen ob die Pfade auch wirklich korrekt gesetzt wurden.

Nun kann die Übersetzung eines Plugins gestartet werden. Zu diesem Zweck muss folgendes Kommando ausgeführt werden: php makepot.php wp-plugin <Pfad des WordPress Plugins>

wp-plugin steht hierfür für den Typ des zu übersetzenden Objekts (in diesem Fall ein WordPress Plugin).

In diesem Beispiel übersetze ich mein eigenes Plugin welches sich im Pfad y:\ktzv-fischamend.at\www\wp-content\plugins\garagesale befindet.

Nach dem Fehlerfreien Durchlauf des makepot.php Scripts (zu erkennen an keiner Ausgabe) befindet sich nun eine pot Datei mit dem Namen des Plugins im aktuellen Verzeichnis (in diesem Beispiel garagesale.pot).

Schritt 5 – Mit POEdit die POT Sprach Datei editieren

Nun starten wir das Programm POEdit und öffnen die pot Datei. Um sie zu öffnen muss der Dateityp Filter auf *.* gestellt werden.

Nach dem Laden der pot Datei kann sofort mit dem Übersetzen begonnen werden. In der linken Spalte sind immer die Original (englischen) Begriffe zu sehen und in der rechten Spalte die bereits übersetzten (deutschen) Begriffe. Man wählt einfach den zu übersetzenden Begriff aus und tippt die Übersetzung in das unterste der Eingabefelder.

Vor dem Speichern sollten jetzt noch die Katalog Optionen angepasst werden. Diese finden sich unter dem Menüpunkt Katalog -> Optionen. Wird auf Deutsch übersetzt sollte nicht nur bei Sprache „German“ sondern auch bei Land „GERMANY“ ausgewählt werden (der Ländercode für Deutsch lautet nämlich üblicherweise de_de und nicht de_at, weil man übersetzt üblicherweise nicht extra ins Österreichische – obwohl das sicher witzig wäre ;-)

Nach dem Speichern findet sich dann zusätzlich zur geänderten Quellsprachdatei garagesale.pot (die jetzt auch die deutschen Übersetzungen enthält) eine Datei namens garagesale.mo. Dies ist die Datei, die für WordPress notwendig ist um das Plugin dann in der korrekten Sprache anzuzeigen.

Wie in dem Screenshot zu sehen, muss diese .mo Datei mit dem richtigen Namen in das Plugin Verzeichnis kopiert werden: <PluginName>-<Länder-und-Sprach-Code>.mo (in diesem Beispiel garagesale-de_DE.mo).

Schritt 6 – Hinzugekommene Begriffe und erneute Übersetzung

Wie das so ist, kommt man nach der fertigen Übersetzung drauf, dass man doch noch eine Funktionalität vergessen hat. Zu diesem Zweck muss man natürlich nochmals das makepot.php Script aus Schritt 4 starten. ABER VORSICHT!

Vor dem erneuten Start muss unbedingt die bereits übersetzte .pot Datei umbenannt werden in z.B. garagesale-translated.pot – Das Extraktionsscript überschreibt kommentarlos eine eventuell bereits bestehende <pluginname>.pot Datei!

Nach erneuter Erstellung der garagesale.pot Datei (in der sich nun auch die zusätzlichen, noch nicht übersetzten Begriffe befinden, können nun mit dem gettext Tool msgmerge die beiden .pot Dateien gemerget werden:

msgmerge -N [bereits-übersetzte-pot-Datei.pot] [neu-generierte-pot-datei.pot] > neue-Pot-Datei.pot

Nun befindet sich im aktuellen Verzeichnis eine Datei namens garagesale_new.pot die wieder mit POEdit um die zusätzliche Begriffe ergänzt werden kann und anschließend muss wieder die .mo Datei entsprechend der oben genannten Namenskonvention in das Plugin Verzeichnis kopiert werden.

 

 

Ähnliche Artikel:

Leeren eines Webformulares

Das leeren eines Webformulars mitsamt einem löschen der Defaultwerte verlangt den Einsatz von Javascript, wie es geht wird in diesem Artikel beschrieben.

Ich denke der Großteil meiner Leser hat bereits das ein oder andere Webformular geschrieben, und wem es ein ebenso großes Mühsal ist wie mir, dem möchte ich hier eine kleine Hilfestellung bieten.

Möchte man dem User die Möglichkeit anbieten alle Daten die er in einem Formular eingegeben hat wieder zu löschen, erreicht man dies über einen zusätzlichen Button (siehe auch http://de.selfhtml.org/html/formulare/formularbuttons.htm):

<input type=”reset” value=”Abbrechen”>

Gleich mal als Beispiel zum selber ausprobieren:

 

Funktioniert auch bestens, solange man dem Besucher der Website nicht noch weiteren Komfort gönnt. Hat man z.B. Pflichtfelder in seinem Formular, und der User hat ein solches nicht ausgefüllt, gehört es eigentlich schon zum guten Ton, dass man dem User das Formular nochmal zum ergänzen anbietet, vorbefüllt mit den vorher von ihm angegebenen Daten. Möchte der User nun all seine Daten aus dem Formular löschen um nochmal von vorne anzufangen funktioniert ein Klick auf den Formular Button nicht mehr. Auch hier ein Beispiel zum gleich testen:

 

Denn der Klick auf den Button leert die Felder nicht, er setzt sie auf den Ursprungszustand zurück, der vom Server geliefert wurde, und damit auf die mitgelieferte Befüllung. Hier muss man sich anderer Wege behelfen, und wie so oft im Web heißt das Zauberwort JavaScript.

Hier eine entsprechende Lösung:

<a onclick="document.forms['default'].elements['nameOfInputField'].value='';” href="#">zurücksetzen</a>

Über einen damit erzeugten Link lässt sich jedes einzelne Feld per JS löschen indem es im Dom-Baum zum Element manövriert und den Value mit nichts ersetzt. Oder im Falle eines DropDowns mit dem 0 Element.

Das ganze funktioniert natürlich auch wunderbar als Button, der ebenfalls per onClick einen JavaScript Aufruf starten kann.

<input type="button" value="zurücksetzen" onClick="document.forms['third'].elements['input'].value='';"/>

Natürlich darf auch hier ein Beispiel zum direkt ausprobieren nicht fehlen:


zurücksetzen

Und schon kann der User alle Daten resetten und mit dem ihm angebotenen Formular von vorne anfangen.
Viel Spaß beim nachprogrammieren ;)

Ähnliche Artikel:

JavaScript Countdown vs JQuery Countdown

HOWTO: Ein Countdown auf einer Homepage, z.B. fürs geplante Release, selbstgeschrieben oder mit JQuery realisieren. Hier wird gezeigt, wie es geht.

Was haben Downloadsites die noch auf ihre Werbung aufmerksam machen wollen, Browsergames in denen der aktuelle Auftrag abgearbeitet wird, Releases von Spielen, und die Zeit vor Silvester gemeinsam?

Einen Countdown!

Wie man so einen implementiert wollen wir uns nun ansehen.

Plain JavaScript Countdown

Als Beispiel wollen wir einen Countdown realisieren, der die Sekunden bis Neujahr 2012 anzeigt. Javascript ist dabei unser Freund. Diesmal!

Als aller erstes erzeugen wir uns zwei Objekte, eines für den jetztigen Zeitpunkt und eines für Neujahr 2012, dann berechnen wir uns die Differenz aus den beiden Zeiten:

var jetzt  = new Date();
var neujahr = new Date(2012,01,01,0,0,0);

var secsLeft = Math.floor((neujahr.getTime() - jetzt.getTime()) / 1000);

Diesen Teil des Codes stecken wir zwischen den <head> Tag der Webseite. Die Differenz müssen wir durch 1000 rechnen, da wir uns auf Millisekundenebene befinden und für unsere Anzeige lediglich die Sekunden relevant sein sollen. Math.floor verwenden wir, um ganze Zahlen anzuzeigen.

Dies müssen wir nun noch im HTML anzeigen, dazu benötigen wir zuerst ein Element mit der id counter im body des html codes, dieses befüllen wir dann regelmäßig mittels:

document.getElementById("counter").innerHTML = text;

All das fassen wir in einer Funktion zusammen z.B.

function countdown(){

Damit das Script nun regelmäßig ausgeführt wird rufen wir es rekursiv erneut auf mit einem Versatz von 1000 Millisekunden, also einer Sekunde.

window.setTimeout("countdown()", 1000);

Dies fügen wir als letzte Zeile in der vorher erstellten Funktion ein.

Die Funktion ist nun fertig, doch muss sie noch gestartet werden. Hier sind wir schon zu den ersten kleinen Kunstgriffen gezwungen, damit der Counter nicht nur im Firefox sondern auch im Internetexplorer seine Arbeit tut.

if (window.addEventListener) {
  window.addEventListener('load', countdown, false);
} else if (window.attachEvent) {
  window.attachEvent('onload', countdown);
}

Der Internetexplorer kennt die Methode addEventListener nicht sondern verwendet stattdessen attachEvent.

Hier nochmal das Gesamtwerk:

<html>
<head>
<script language="javascript" type="text/javascript">
function countdown(){
 var jetzt  = new Date();
 var neujahr = new Date(2012,01,01,0,0,0); 

 var secsLeft = Math.floor((neujahr.getTime() - jetzt.getTime()) / 1000);

 if(secsLeft > 0){
 var text = "Noch " + secsLeft + " Sekunden bis Neujahr 2012!";
 } else {
 var text = "Es ist bereits 2012!";
 }
 document.getElementById("counter").innerHTML = text;

 window.setTimeout("countdown()", 1000);
}

if (window.addEventListener) {
 window.addEventListener('load', countdown, false);
} else if (window.attachEvent) {
 window.attachEvent('onload', countdown);
}
</script>

<p id="counter">Noch 26665457 Sekunden bis Neujahr 2012!<p>

</p>
</body>
</html>

Eigentlich recht simpel und gradlinig. Doch wie wir gesehen haben gab es schon kleinere Probleme durch die Browserdiversifikation . Um den Code der eigentlichen Seite übersichtlich und schlank zu halten greife ich daher gerne auf entsprechende JS Libraries zurück.

JQuery Javascript Countdown

Dafür bauen wir zuerst JQuery in unserer Beispiel-Webseite im Head einbinden:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>

Die JQuery Standardlibrary bietet viele Funktionen, auf die man aufbauen kann. Damit sie selbst nicht zu aufgebläht und überladen wird, sind Zusatzfunktionen in Plugins ausgelagert. Deshalb besorgen wir uns noch von der Jquery-Homepage http://plugins.jquery.com/project/countdown2 das zugehörige Plugin und binden es ebenfalls ein im Head ein:

<script type="text/javascript" src="jquery.countdown.min.js"></script>

Dann mussen wir noch den Javascript Codeteil abändern:

<script type="text/javascript">
$(document).ready(function(){
   var newYear = new Date(2012,1,1,0,0,0);
   $('#counter').countdown({unitl: newYear, format: 'S'});
});
</script>

Auch hierbei darf der Javascript Code erst loslegen, nachdem das Dokument fertig ist. $(document).ready() ist dafür der Standardaufruf mittels JQuery. Darin erzeugen wir uns wieder ein Date Objekt, dermal das Zieldatum. Dann müssen wir noch das Plugin damit füttern. Per JQuery wird das Element mit der ID #Counter selektiert und dort ein .counter eingefügt, dem wir das Datum als until Parameter übergeben, außerdem legen wir noch ein Format fest, denn wir wollen nur Sekunden anzeigen lassen. Fertig!

Das ganze lässt sich noch weiter feinabstimmen: http://keith-wood.name/countdownRef.html Auf der Dokumentationsseite sind die Möglichkeiten das Plugin zu konfigurieren beschrieben. Sie enthalten unter anderem die Möglichkeit eigene Labels zu vergeben, so spart man sich für jede Sprache die die Website unterstützt die lokalisierte Fassung einzubinden.

Freilich hat der Homepagebesucher mit der JQuery Bibliothek und dem zugehörigen Plugin deutlich mehr Netzwerktraffic zu verbuchen, doch hat man damit für die Übersichtlichkeit und Wartbarkeit einiges gewonnen. Als weiteren Pluspunkt hat man das Gemurkse ob Internetexplorer oder ein anderer Browser an die Bibliothek delegiert. Und ob des Traffics bleibt zu sagen, dass sich das recht bald durch Synergieeffekte durch den Einsatz von JQuery bei anderen Teilen der Homepage bezahlt macht.

Fröhliches Runterzählen!

Ähnliche Artikel:

.htaccess mod_rewrite und RewriteRule testen

Wiedermal die .htaccess modifiziert, und leider ein Fehler drin. Martin Merlin bietet ein Online-Test Tool an zum einfachen debuggen.

Wiedermal die .htaccess modifiziert, und leider ein Fehler drin. Anpassen, hochladen, testen, anpassen, hochladen, testen, anpa…

Schnell wird man dieses Prozesses überdrüssig, und für ein kleines Hobbyprojekt hat man normalerweise keine Testumgebung zur Verfügung. Macht man die Änderungen direkt, resultiert das für einen signifikanten Zeitraum in Fehlverhalten auf der eigenen Homepage. Zusätzlich pfuscht einem das Caching-Verhalten des Browsers noch dazwischen, da müssen Alternativen her!

Ein eigener Apache Server zwecks Testen einer .htaccess Anpassung der RewriteCondition oder RewriteRule ist natürlich möglich, aber auch schon ein gehöriger Zusatzaufwand. Auch ein Unterverzeichnis am Webserver wäre eine Möglichkeit, bedeutet aber immernoch einen Uploadaufwand. Martin Melin hat sich wohl ähnliches gedacht bevor er sein Online Debug Tool für die mod_rewrite Regeln im .htaccess File geschrieben hat.

http://martinmelin.se/rewrite-rule-tester/

Beispiel rewrite-rule-testerIn diesem Tool kann man bequem seine Url Konvertierungen debuggen. Dazu gibt man seine Regeln bekannt und trägt eine Test URL ein. Führt man den Test durch spielt das Tool die Überprüfung der Regeln anhand dieser TestURL durch und präsentiert dannach ansehlich das Resultat. Farblich hervorgehoben wird welche Regeln geprüft wurden. Rot für Regeln die für die URL als nicht gültig erkannt wurden, Grün für die Treffer.

Im Beispiel Screenshot sind 5 Regeln definiert, da diese nach einem gültigen Treffer alle weitern Regeln überspringen [L] sind im Ergebnisbereich nur 4 Treffer angeführt.

Hat mir einigen Ärger und Zeit erspart.
Danke Martin für dieses Tool!

Ähnliche Artikel:

PHP und $_SERVER[‚SCRIPT_URI‘] – Beispiel Soap WSDL

Die Variablen im PHP Array $_SERVER sind sehr praktisch für die automatische Zusammenstellung von absoluten URLs. Eine Variable davon heißt SCRIPT_URI. Diese beinhaltet die komplette URL (aus der Adresszeile des Browsers) inlusive Protokoll, Port, …

Die Variablen im PHP Array $_SERVER sind sehr praktisch für die automatische Zusammenstellung von absoluten URLs.

Eine Variable davon heißt SCRIPT_URI (also anzusprechen unter $_SERVER[‚SCRIPT_URI‘])

Diese beinhaltet die komplette URL (aus der Adresszeile des Browsers) inlusive Protokoll, Port, …

Leider ist sie jedoch nicht immer verfügbar.

Nachprüfen lässt sich das mit einer php Datei mit Inhalt

<?php phpinfo();

Beim Aufruf der Seite bekommt man im Abschnitt Apache Environment alle $_SERVER Variablen zu sehen.

Falls sich hierbei $_SERVER[‚SCRIPT_URI‘] nicht findet fehlt das Apache Modul mod_rewrite bzw. es ist nicht aktiviert.

Dies lässt sich nachholen durch das Laden des entsprechenden Apache Moduls:

LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so

Wenn das Modul geladen ist, muss es noch mit:

RewriteEngine on

aktiviert werden. Dies kann sowohl in der Konfigurationsdatei des Apache Webservers erfolgen als auch in einer .htaccess Datei im jeweiligen Script Verzeichnis.

Ebenfalls Voraussetzung für das Vorhandensein der Variable ist, dass die Webseite in einem Virtuellen Host liegt:

<VirtualHost *:80>
	ServerAdmin webmaster@domain.tld
	DocumentRoot /....
	ServerName www.domain.tld
	...
</VirtualHost>

Dies sollte jedoch bei den meisten Hostern bereits der Fall sein (auch viele Linux Distributionen liefern Ihre Apache Konfiguration bereits standardmäßig mit Virtuellen Hosts aus).

Tipp:

Abgesehen vom Erstellen absoluter Links für beispielsweise Menüeinträge einer Webseite lässt sich $_SERVER[‚SCRIPT_URI‘] auch hervorragend dazu benutzen im Zusammenhang mit SOAP Webservices die SOAP Location der ausgelieferten WSDL Dateien anzupassen.

Auszug aus einer WSDL Datei:

...
<binding name="testBinding" type="tns:testPortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="login">
<soap:operation soapAction="%SOAP_LOCATION%?method=login" style="rpc" />
<wsdl:input>
...

Der dazugehörige PHP Code:

...
if( isset($_GET['wsdl']) ) {
    $wsdl = @file_get_contents( $wsdlFile );
    if( !$wsdl ) {
        die( 'invalid wsdl file' );
    }
    $wsdl = str_replace( '%SOAP_LOCATION%', $_SERVER['SCRIPT_URI'], $wsdl );
    header( 'Content-Type: text/xml' );
    echo $wsdl;
}
....

Ähnliche Artikel: