<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>martin-grandrath.de &#187; Wordpress</title>
	<atom:link href="http://martin-grandrath.de/themen/wordpress/feed/" rel="self" type="application/rss+xml" />
	<link>http://martin-grandrath.de</link>
	<description>Ein neues WordPress-Weblog</description>
	<lastBuildDate>Fri, 23 Oct 2009 22:35:41 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.5</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>WordPress Theme internationalization (Teil 2)</title>
		<link>http://martin-grandrath.de/2009/04/wordpress-theme-internationalization-teil-2/</link>
		<comments>http://martin-grandrath.de/2009/04/wordpress-theme-internationalization-teil-2/#comments</comments>
		<pubDate>Fri, 17 Apr 2009 20:14:52 +0000</pubDate>
		<dc:creator>Martin Grandrath</dc:creator>
				<category><![CDATA[Wordpress]]></category>
		<category><![CDATA[gettext]]></category>
		<category><![CDATA[Internationalization]]></category>
		<category><![CDATA[Make]]></category>
		<category><![CDATA[Themes]]></category>

		<guid isPermaLink="false">http://martin-grandrath.de/?p=117</guid>
		<description><![CDATA[Vor etwa einem Jahr habe ich bereits einen Artikel über WordPress Theme internationalization geschrieben und das grundlegende Vorgehen zum Erzeugen von Sprachdateien skizziert.  Ich möchte diesen im Folgenden durch meinen persönlichen Workflow anhand eines einfachen Beispiels ergänzen.
Vorbereitung
Wie bereits im ersten Teil dargestellt, muss zunächst in der Datei functions.php die &#187;Textdomain&#171; eingebunden werden.  Seit [...]]]></description>
			<content:encoded><![CDATA[<p>Vor etwa einem Jahr habe ich bereits einen Artikel über <a href="/2008/05/wordpress-theme-internationalization/">WordPress Theme internationalization</a> geschrieben und das grundlegende Vorgehen zum Erzeugen von Sprachdateien skizziert.  Ich möchte diesen im Folgenden durch meinen persönlichen Workflow anhand eines einfachen Beispiels ergänzen.</p>
<h3>Vorbereitung</h3>
<p>Wie bereits im ersten Teil dargestellt, muss zunächst in der Datei <strong><code>functions.php</code></strong> die &raquo;Textdomain&laquo; eingebunden werden.  Seit WordPress&nbsp;2.7 ist es möglich, zusätzlich zum Namen der Domain ein Verzeichnis anzugeben, in dem nach den Übersetzungen gesucht wird (in diesem Fall das Verzeichnis <code>messages/</code> unterhalb des Template-Verzeichnisses):</p>
<pre class="code code-php">
load_theme_textdomain('mydomain', get_template_directory().'/messages');
</pre>
<p>Anschließend müssen in den Templatedateien sämtliche Strings ausgezeichnet werden, die übersetzt werden sollen.  Wenn man ein neues Theme von Grund auf gestaltet, tut man gut daran, das von Anfang an zu tun.  Andernfalls ist das eine recht mühselige Arbeit.</p>
<h4>Ein paar Beispiele</h4>
<p>In der Datei <strong><code>index.php</code></strong> befindet sich innerhalb &raquo;Der Schleife&laquo; (The Loop) diese Zeile zur Ausgabe des Inhalts eines Artikels:</p>
<pre class="code code-php">
the_content('Read the rest of this entry');
</pre>
<p>Dies wird zu</p>
<pre class="code code-php">
the_content( __('Read the rest of this entry', 'mydomain') );
</pre>
<p>Die Funktion <code>__($message, $domain)</code> sucht innerhalb der Domain <code>$domain</code> (hier: &#8216;mydomain&#8217;) nach einer Übersetzung für den String <code>$message</code> (hier: &#8216;Read the rest of this entry&#8217;) und gibt diese zurück.  Oder, falls keine Übersetzung gefunden wurde, den ursprünglichen String.</p>
<p>In der Datei <strong><code>comments.php</code></strong> findet sich das Label für das Formularfeld &raquo;author&laquo;:</p>
<pre class="code code-php">
&lt;label for="author"&gt;Your name&lt;/label&gt;
</pre>
<p>Dies wird zu</p>
<pre class="code code-php">
&lt;label for="author"&gt;&lt;?php _e('Your name', 'mydomain'); ?&gt;&lt;/label&gt;
</pre>
<p>Die Funktion <code>_e(...)</code> ist äquivalent zu <code>echo __(...)</code></p>
<h3>Automatisierung mit GNU make</h3>
<p>Nun geht es darum, diese Strings zu extrahieren und in separaten Dateien zu speichern, die dann jeweils die Übersetzung in eine weitere Sprache ermöglichen.  Um das Hantieren mit den .po- und .mo-Dateien (siehe <a href="/2008/05/wordpress-theme-internationalization/">Teil&nbsp;1</a>) etwas einfacher zu gestalten, habe ich folgendes Makefile geschrieben:</p>
<pre class="code code-gnumake">
# List of locales to generate (space separated)
LOCALES = de_DE

# List of source files
SRC_FILES = ../*.php

# .pot file to create
POT_FILE = messages.pot

$(POT_FILE): $(SRC_FILES)
        xgettext --keyword=__ --keyword=_e --keyword=_c --keyword=__ngettext:1,2 --keyword=__ngettext_noop:1,2 $(SRC_FILES) &#038;&#038; \
        mv messages.po $(POT_FILE)

po_files: $(patsubst %,%.po,$(LOCALES))

mo_files: $(patsubst %,%.mo,$(LOCALES))

%.po: $(POT_FILE)
        [ -f $@ ] &#038;&#038; \
        msgmerge -U $@ $&lt; &#038;&#038; touch $@ || \
        msginit -l $* -o $@

%.mo: %.po
        msgfmt -o $@ $&lt;
</pre>
<p><strong>Wichtig:</strong> die eingerückten Zeilen müssen mit einem Tab und <em>nicht</em> mit Leerzeichen eingerückt werden!</p>
<p>Diese Datei wird unter dem Namen <code>Makefile</code> (Groß-/Kleinschreibung beachten) im Verzeichnis <code>messages/</code> abgelegt, das unterhalb des Template-Verzeichnisses angelegt wird:</p>
<pre>
mytheme/
|-- footer.php
|-- functions.php
|-- header.php
|-- index.php
|-- messages/
|   `-- Makefile
|-- screenshot.png
|-- searchform.php
|-- sidebar.php
|-- single.php
`-- style.css
</pre>
<p>Damit das Folgende funktioniert, müssen die GNU-Tools make und gettext installiert sein, was unter Debian der Befehl <kbd>aptitude install gettext make</kbd> erledigt.</p>
<h4>.po-Files generieren</h4>
<p>Zunächst wechseln wir in das neu angelegte Verzeichnis:</p>
<pre class="code">
$ cd messages/
$ ls
Makefile
</pre>
<p>Jetzt legen wir die .po-Datei an, in der die deutschen Übersetzungen gespeichert werden:</p>
<pre class="code">
$ make de_DE.po
</pre>
<h4>Übersetzen</h4>
<p>Nun wird die eigentliche Übersetzungsarbeit fällig.  Es bietet sich an, für das Bearbeiten der .po-Dateien eines der <a href="http://codex.wordpress.org/Translating_WordPress#Translation_Tools" rel="external">zahlreichen Tools</a> zu verwenden, die es einem erleichtern, sich ganz auf die Texte zu konzentrieren, ohne dass man Gefahr läuft die Syntax der Datei kaputt zu machen.</p>
<p>Vorab sollte man in der .po-Datei noch die Zeichenkodierung auf UTF-8 ändern, um sich Probleme mit Umlauten zu ersparen:</p>
<pre class="code">
"Content-Type: text/plain; charset=UTF-8\n"
</pre>
<h4>.mo-Files generieren</h4>
<p>Ist die Übersetzung abgeschlossen, kann die .po-Datei compiliert, d.h. die .mo-Datei erzeugt werden.  Das passiert durch den Aufruf von</p>
<pre class="code">
$ make de_DE.mo
</pre>
<p>Jetzt sollten die übersetzten Texte im Theme erscheinen.  Falls das nicht klappt, sollte als erstes geprüft werden, ob in der <code>wp-config.php</code> die Konstante <code>WPLANG</code> auf den Wert &#8216;de_DE&#8217; gesetzt ist.</p>
<h4>.po-Files aktualisieren</h4>
<p>Wird nun weiter am Template gearbeitet, kann es natürlich passieren, dass neue Strings definiert werden, die übersetzt werden wollen.  In diesem Fall aktualisiert der Aufruf von <kbd>make de_DE.po</kbd> die .po-Datei &ndash; alle bisher gemachten Übersetzungen bleiben dabei erhalten.  Die Erzeugung der .mo-Datei erfolgt wie gehabt.</p>
<h4>Übersetzungen in weitere Sprachen</h4>
<p>Wer sich mit Makefiles auskennt, hat vermutlich schon bemerkt, dass sich mit meinem Makefile eine ganze Liste von Sprachdateien in einem Rutsch aktualisieren lassen.  Dazu werden in der 2.&nbsp;Zeile alle Locales, die erzeugt werden sollen, durch Leerzeichen getrennt hintereinander aufgelistet (hier z.B. für Deutsch, Französisch, Spanisch und Italienisch):</p>
<pre class="code code-gnumake">
LOCALES = de_DE fr_FR es_ES it_IT
</pre>
<p>Nun können alle .po-Dateien mit <code>make po_files</code> bzw. alle .mo-Dateien mit <code>make mo_files</code> auf den neuesten Stand gebracht werden.  Es ist übrigens auch möglich, weitere Sprachen im Nachhinein zu ergänzen.  Make erzeugt bzw. aktualisiert immer nur die Dateien, die notwendig sind.</p>
]]></content:encoded>
			<wfw:commentRss>http://martin-grandrath.de/2009/04/wordpress-theme-internationalization-teil-2/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>WordPress Theme internationalization</title>
		<link>http://martin-grandrath.de/2008/05/wordpress-theme-internationalization/</link>
		<comments>http://martin-grandrath.de/2008/05/wordpress-theme-internationalization/#comments</comments>
		<pubDate>Wed, 14 May 2008 09:55:27 +0000</pubDate>
		<dc:creator>Martin Grandrath</dc:creator>
				<category><![CDATA[Wordpress]]></category>
		<category><![CDATA[Internationalization]]></category>
		<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Themes]]></category>

		<guid isPermaLink="false">http://www.martin-grandrath.de/?p=47</guid>
		<description><![CDATA[Gestern habe ich mich damit beschäftigt ein WordPress Theme zu internationalisieren, d.h. für Übersetzungen in verschiedene Sprachen vorzubereiten.  Da die WordPress Dokumentation leider nicht sonderlich übersichtlich ist und es eine Weile gedauert hat, bis ich fündig geworden bin, hier eine kurze Zusammenfassung.
Als Internationalisierung (i18n) wird die Vorbereitung eines Programms o.ä. für die Übersetzung bezeichnet, [...]]]></description>
			<content:encoded><![CDATA[<p>Gestern habe ich mich damit beschäftigt ein WordPress Theme zu internationalisieren, d.h. für Übersetzungen in verschiedene Sprachen vorzubereiten.  Da die <a href="http://codex.wordpress.org/" rel="external">WordPress Dokumentation</a> leider nicht sonderlich übersichtlich ist und es eine Weile gedauert hat, bis ich fündig geworden bin, hier eine kurze Zusammenfassung.</p>
<p>Als <em>Internationalisierung</em> (i18n) wird die Vorbereitung eines Programms o.ä. für die Übersetzung bezeichnet, d.h. es wird eine &raquo;Infrastruktur&laquo; geschaffen, so dass die eigentliche Übersetzung auch von Nicht-Programmierern übernommen werden kann.  Das Übersetzen in andere Sprachen ist die <em>Lokalisierung</em> (l10n).</p>
<p>Grundlage für die Internationalisierung von WordPress (inkl. Themes und Plugins) ist das GNU-Tool <a href="http://www.gnu.org/software/gettext/" rel="external">Gettext</a>, das nach folgendem Prinzip funktioniert:</p>
<ol>
<li>
<p>Alle Strings, die übersetzt werden sollen, werden im Quellcode mit speziellen Funktionen <em>ausgezeichnet</em></p>
</li>
<li>
<p>Dem Programm (bzw. Plugin oder Theme) wird einmalig ein Stück <em>Code hinzugefügt</em>, das die zur eingestellten Sprache passende Übersetzung einbindet &#8212; sofern vorhanden</p>
</li>
<li>
<p>Die markierten Strings werden mit xgettext extrahiert und in einer <em>Template-Datei</em> gespeichert (Endung .pot)</p>
</li>
<li>
<p>Aus diesem Template wird für jede Sprache eine <em>Datei für die Übersetzungen</em> abgeleitet (Endung .po)</p>
</li>
<li>
<p>Die fertigen Übersetzungsdateien werden <em>kompiliert</em>, d.h. maschinenlesbar gemacht (Endung .mo)</p>
</li>
</ol>
<p><span id="more-47"></span></p>
<h3>1. Vorbereitung des Codes</h3>
<p>Die eigentliche Vorbereitung des Quelltextes wird unter <a href="http://codex.wordpress.org/Translating_WordPress" rel="external">Translating WordPress</a> ganz gut beschrieben.  Im Wesentlichen müssen sämtliche übersetzbaren Strings durch eine der folgenden beiden PHP-Funktionen ersetzt werden:</p>
<ul>
<li><strong><code>__($message, $domain)</code></strong> Für die Zeichenkette <code>$message</code> wird innerhalb von <code>$domain</code> eine passende Übersetzung gesucht und zurückgegeben.  Ist keine Übersetzung vorhanden, wird <code>$message</code> zurückgegeben.
<p>Der Parameter <code>$domain</code> ist ebenfalls ein String und stellt einen Namensraum (Namespace) dar, der verhindert, dass identische Texte mit unterschiedlichen Übersetzungen in mehreren Kontexten Konflikte auslösen.  Voraussetzung dabei ist selbstverständlich, dass die Domain einen eindeutigen Bezeichner besitzt.  Bei Plugins wählt man dafür sinnvollerweise den Namen des Plugins, der für sich ja auch bereits eindeutig sein muss.  Bei Themes ist die Wahl dieses Bezeichners insofern weniger kritisch, da nicht mehrere Themes gleichzeitig angewendet werden können.  Der Name darf sich nur nicht mit dem eines Plugins überschneiden.</p>
</li>
<li><strong><code>_e($message, $domain)</code></strong> ist eine verkürzte Schreibweise für <code>echo __($message, $domain)</code>
</li>
</ul>
<p>Zum Einbinden der Übersetzung ergänzt man noch folgenden Code:</p>
<ul>
<li><strong>Für Themes</strong> (in functions.php):
<pre class="code
php">load_theme_textdomain('your_domain');</pre>
</li>
<li><strong>Für Plugins</strong> (wenn das Plugin &raquo;foobar&laquo; heißt):
<pre class="code php">$foobar_domain = 'foobar';
$foobar_is_setup = 0;

function foobar_setup()
{
    global $foobar_domain, $foobar_is_setup;

    if ($foobar_is_setup) {
        return;
    }

    load_plugin_textdomain($foobar_domain, 'wp-content/plugins/foobar');
}</pre>
</li>
</ul>
<h3>2. Erstellung der Template-Datei (.pot)</h3>
<p>Insbesondere bei diesem Punkt habe ich lange suchen müssen.  In der WordPress-Dokumentation steht nur die lapidare Aussage</p>
<blockquote cite="http://codex.wordpress.org/Translating_WordPress#gettext_files"><p> Generally, you can download a POT file for WordPress, so you shouldn&#8217;t have to generate your own.</p></blockquote>
<p>Wirklich sehr hilfreich!  Nach einigem googlen bin ich dann noch auf diesen Hinweis gestoßen:</p>
<blockquote cite="http://codex.wordpress.org/User:Nbachiyski/I18n_for_WordPress_Developers#Generating_a_POT_file"><p> Get the <a href="http://svn.automattic.com/wordpress-i18n/tools/trunk/makepot.php">makepot.php</a> script and execute it like this:</p>
<pre class="code">php makepot.php wp-plugin your-plugin-directory</pre>
</blockquote>
<p>Hier ist der xgettext-Aufruf, den dieses Script im Wesentlichen fabriziert (auszuführen im Verzeichnis des Themes):</p>
<pre class="code">xgettext --keyword=__ --keyword=_e --keyword=_c --keyword=__ngettext:1,2 --keyword=__ngettext_noop:1,2 *.php</pre>
<p>Ruft man xgettext per Hand auf, muss man noch beachten, dass xgettext (aus <a href="http://www.gnu.org/software/gettext/manual/gettext.html#Template">historischen Gründen</a>) eine Datei mit der Endung .po erstellt, die noch umbenannt werden muss!</p>
<p>Alle notwendigen Befehle (xgettext, msginit, msgfmt und msgmerge) installiert man übrigens unter Debian bzw. Ubuntu mit</p>
<pre class="code">aptitude install gettext</pre>
<h3>3. Anlegen und Einbinden einer neuen Übersetzung (.po und .mo)</h3>
<p>Ab hier ist der Rest wieder einfach.  Der Befehl</p>
<pre class="code">msginit -l de_DE -o de_DE.po</pre>
<p>initialisiert eine neue .po-Datei (hier z.B. für die locale de_DE).  Diese kann der Übersetzer nun mit dem <a href="http://codex.wordpress.org/Translating_WordPress#Translation_Tools" rel="external">Werkzeug seiner Wahl</a> bearbeiten.</p>
<p>Das Kompilieren in ein maschinenlesbares Format erledigt anschließend die Zeile</p>
<pre class="code">msgfmt -o de_DE.mo de_DE.po</pre>
<p>Die fertige .mo-Datei muss im Theme- bzw. Plugin-Verzeichnis abgelegt werden.  Für das Plugin &raquo;foobar&laquo; muss die fertige Datei allerdings &raquo;foobar-de_DE.mo&laquo; heißen!</p>
<h3>4. Update einer vorhandenen .po-Datei</h3>
<p>Kommen nun (z.B. aufgrund einer neuen Version) weitere zu übersetzende Strings hinzu, muss man nicht wieder von vorne anfangen.  Es genügt, die Template-Datei mit dem o.g. Aufruf von xgettext neu zu erzeugen. Anschließend werden die neuen Zeilen mit</p>
<pre class="code">msgmerge -U de_DE.po messages.pot</pre>
<p>in die vorhandene .po-Datei eingefügt.  Die Übersetzung und Kompilierung erfolgen dann wieder wie gehabt.</p>
<h3>Links zum Thema</h3>
<ul>
<li><a href="http://codex.wordpress.org/Translating_WordPress" rel="external">Translating WordPress</a></li>
<li><a href="http://codex.wordpress.org/Writing_a_Plugin#Internationalizing_Your_Plugin" rel="external">Internationalizing your plugin</a> (gilt auch für Themes)</li>
<li><a href="http://codex.wordpress.org/User:Nbachiyski/I18n_for_WordPress_Developers" rel="external">I18n for WordPress developers</a></li>
<li><a href="http://www.gnu.org/software/gettext/manual/gettext.html" rel="external">Gettext Handbuch</a></li>
</ul>
<p><strong>Update:</strong> Im <a href="/2009/04/wordpress-theme-internationalization-teil-2/">2.&nbsp;Teil</a> stelle ich eine Möglichkeit vor, die Generierung und Aktualisierung der .po- und .mo-Dateien mit make zu automatisieren.</p>
]]></content:encoded>
			<wfw:commentRss>http://martin-grandrath.de/2008/05/wordpress-theme-internationalization/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

