{"id":20864,"date":"2026-10-18T14:41:00","date_gmt":"2026-10-18T12:41:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=20864"},"modified":"2026-10-09T22:29:15","modified_gmt":"2026-10-09T20:29:15","slug":"plugin-internationalisierung","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/","title":{"rendered":"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript"},"content":{"rendered":"<p>Ein Plugin, das nur Englisch kann, zeigt auf einer deutschen oder polnischen Website englische Schaltfl\u00e4chen, obwohl WordPress selbst l\u00e4ngst die Sprache der Besucher spricht. Dieser Blog erscheint auf Englisch, Deutsch und Polnisch; jede Zeichenkette eines hier eingesetzten Plugins muss also dreifach vorliegen, auch die Texte, die JavaScript erst nachtr\u00e4glich in die Seite schreibt. WordPress l\u00f6st das auf drei Ebenen: gettext-Aufrufe in PHP, eigene JSON-Dateien f\u00fcr Skripte und Sprachpakete, die translate.wordpress.org baut und ausliefert. Viele \u00e4ltere Anleitungen rufen <code>load_plugin_textdomain()<\/code> noch fr\u00fch in <code>plugins_loaded<\/code> auf, obwohl der Aufruf seit WordPress 6.7 nur noch den Pfad registriert; die mit 6.7 eingef\u00fchrte Meldung stammt von \u00dcbersetzungsaufrufen vor <code>after_setup_theme<\/code>.<\/p>\n<p>Dieser Beitrag baut ein kleines Plugin \u201eLW Reading Time\u201c mit einer PHP-Datei und einem Skript, das <code>@wordpress\/i18n<\/code> importiert. Er zeigt die \u00dcbersetzungsfunktionen, die Pluralregeln f\u00fcr Deutsch und Polnisch, was WordPress 7.1.2 (aktuelle Version am 29.09.2026) selbst l\u00e4dt, wie eine Zeichenkette \u00fcber GlotPress in ein Sprachpaket gelangt und wie sich \u00dcbersetzungsdateien lokal mit WP-CLI erzeugen lassen. <a href=\"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-check-lokal-und-ci\/\">Plugin Check<\/a> und <a href=\"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/readme-plugin-header-und-svn\/\">die Felder der <code>readme.txt<\/code><\/a> sind Thema eigener Beitr\u00e4ge dieser Reihe; hier tauchen sie nur dort auf, wo \u00dcbersetzungen sie ber\u00fchren.<\/p>\n<h2>Die Text Domain: eine Kennung, identisch mit dem Slug<\/h2>\n<p>Jeder gettext-Aufruf tr\u00e4gt als letztes Argument die Text Domain, die eine Zeichenkette der richtigen \u00dcbersetzungsdatei zuordnet. F\u00fcr Plugins im Verzeichnis gilt eine feste Regel: Die Text Domain entspricht dem Slug, ein Plugin mit dem Slug <code>akismet<\/code> nutzt also die Text Domain <code>akismet<\/code>, und mehr als eine Text Domain pro Plugin ist nicht vorgesehen. Laut Meta-Handbuch l\u00e4sst sich ein Plugin sonst nicht sauber in translate.wordpress.org importieren und bekommt keine Sprachpakete. Erlaubt sind Kleinbuchstaben und Bindestriche, keine Unterstriche, keine Leerzeichen.<\/p>\n<p>Au\u00dferdem muss die Text Domain als literale Zeichenkette dastehen. <code>wp i18n make-pot<\/code> und der Importer auf WordPress.org lesen den Quelltext, ohne ihn auszuf\u00fchren; eine Konstante oder Variable anstelle von <code>'lw-reading-time'<\/code> bleibt f\u00fcr sie unsichtbar, und das Plugin-Handbuch r\u00e4t ausdr\u00fccklich davon ab. Auch Zeichenketten, die der Core bereits \u00fcbersetzt, etwa \u201eSettings\u201c, bekommen die eigene Text Domain. Sonst verlieren sie ihre \u00dcbersetzung, sobald der Core den Wortlaut \u00e4ndert.<\/p>\n<p>Der Header <code>Text Domain<\/code> ist laut Handbuch seit WordPress 4.6 optional, <code>Domain Path<\/code> darf bei Plugins im Verzeichnis fehlen. Es lohnt sich trotzdem, beide zu setzen: <code>make-pot<\/code> liest die Text Domain standardm\u00e4\u00dfig aus dem Header, und seit WordPress 6.8 laden Plugins au\u00dferhalb des Verzeichnisses ihre \u00dcbersetzungen genau \u00fcber dieses Paar automatisch. Plugin Check 2.1.0 (erschienen am 16.08.2026) meldet ein ung\u00fcltiges Format der Text Domain als Fehler, einen vom Slug abweichenden Header als Warnung und ebenso einen <code>Domain Path<\/code> ohne f\u00fchrenden Schr\u00e4gstrich oder mit Verweis auf einen fehlenden Ordner. Das Beispiel-Plugin bringt deshalb einen vorhandenen Ordner <code>languages\/<\/code> mit der POT-Datei mit.<\/p>\n<h2>Die gettext-Funktionen in PHP<\/h2>\n<p>Die Hauptdatei des Beispiel-Plugins zeigt die Lesezeit \u00fcber einzelnen Beitr\u00e4gen an und erg\u00e4nzt eine Schaltfl\u00e4che, die das Skript sp\u00e4ter umbeschriftet. Jede sichtbare Zeichenkette l\u00e4uft durch eine \u00dcbersetzungsfunktion:<\/p>\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"php\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">PHP<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-php\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-meta\">&lt;?php<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/**<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Plugin Name:       LW Reading Time<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Description:       Shows the estimated reading time above single posts.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Version:           1.0.0<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Requires at least: 6.8<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Requires PHP:      7.4<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Author:            Lukas Wojcik<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * License:           GPL-2.0-or-later<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * License URI:       https:\/\/www.gnu.org\/licenses\/gpl-2.0.html<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Text Domain:       lw-reading-time<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Domain Path:       \/languages<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * @package LW_Reading_Time<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *\/<\/span>\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">defined<\/span>( <span class=\"cbb-string\">'ABSPATH'<\/span> ) || exit;\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">const<\/span> <span class=\"cbb-constant\">LW_READING_TIME_WORDS_PER_MINUTE<\/span> = <span class=\"cbb-number\">200<\/span>;\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/**<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Returns the estimated reading time of a post in whole minutes.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * @param WP_Post $post Post object.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * @return int Minutes, at least 1.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *\/<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">function<\/span> <span class=\"cbb-function\">lw_reading_time_minutes<\/span>( WP_Post <span class=\"cbb-variable\">$post<\/span> ) {\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-variable\">$text<\/span>  = <span class=\"cbb-function\">wp_strip_all_tags<\/span>( <span class=\"cbb-function\">strip_shortcodes<\/span>( <span class=\"cbb-variable\">$post<\/span><span class=\"cbb-operator\">-&gt;<\/span>post_content ) );\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-variable\">$words<\/span> = <span class=\"cbb-function\">preg_split<\/span>( <span class=\"cbb-string\">'\/\\s+\/u'<\/span>, <span class=\"cbb-function\">trim<\/span>( <span class=\"cbb-variable\">$text<\/span> ), <span class=\"cbb-number\">-1<\/span>, <span class=\"cbb-constant\">PREG_SPLIT_NO_EMPTY<\/span> );\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">return<\/span> <span class=\"cbb-function\">max<\/span>( <span class=\"cbb-number\">1<\/span>, <span class=\"cbb-type\">(int)<\/span> <span class=\"cbb-function\">ceil<\/span>( <span class=\"cbb-function\">count<\/span>( <span class=\"cbb-variable\">$words<\/span> ) \/ <span class=\"cbb-constant\">LW_READING_TIME_WORDS_PER_MINUTE<\/span> ) );\n<\/span><span class=\"cbb-line\">}\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/**<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Prepends the reading time and a \"mark as read\" button to single posts.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * @param string $content Post content.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * @return string<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *\/<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">function<\/span> <span class=\"cbb-function\">lw_reading_time_prepend<\/span>( <span class=\"cbb-variable\">$content<\/span> ) {\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">if<\/span> ( ! <span class=\"cbb-function\">is_singular<\/span>( <span class=\"cbb-string\">'post'<\/span> ) || ! <span class=\"cbb-function\">in_the_loop<\/span>() || ! <span class=\"cbb-function\">is_main_query<\/span>() ) {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-keyword\">return<\/span> <span class=\"cbb-variable\">$content<\/span>;\n<\/span><span class=\"cbb-line\">\t}\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-variable\">$post<\/span>    = <span class=\"cbb-function\">get_post<\/span>();\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-variable\">$minutes<\/span> = <span class=\"cbb-function\">lw_reading_time_minutes<\/span>( <span class=\"cbb-variable\">$post<\/span> );\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-variable\">$label<\/span> = <span class=\"cbb-function\">sprintf<\/span>(\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-comment\">\/* translators: %s: estimated reading time in minutes. *\/<\/span>\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-function\">_n<\/span>( <span class=\"cbb-string\">'Reading time: %s minute'<\/span>, <span class=\"cbb-string\">'Reading time: %s minutes'<\/span>, <span class=\"cbb-variable\">$minutes<\/span>, <span class=\"cbb-string\">'lw-reading-time'<\/span> ),\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-function\">number_format_i18n<\/span>( <span class=\"cbb-variable\">$minutes<\/span> )\n<\/span><span class=\"cbb-line\">\t);\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-variable\">$button<\/span> = <span class=\"cbb-function\">sprintf<\/span>(\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-string\">'&lt;button type=\"button\" class=\"lw-reading-time__mark\" data-post=\"%1$d\"&gt;%2$s&lt;\/button&gt;'<\/span>,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-type\">(int)<\/span> <span class=\"cbb-variable\">$post<\/span><span class=\"cbb-operator\">-&gt;<\/span>ID,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-function\">esc_html__<\/span>( <span class=\"cbb-string\">'Mark as read'<\/span>, <span class=\"cbb-string\">'lw-reading-time'<\/span> )\n<\/span><span class=\"cbb-line\">\t);\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">return<\/span> <span class=\"cbb-string\">'&lt;p class=\"lw-reading-time\"&gt;'<\/span> . <span class=\"cbb-function\">esc_html<\/span>( <span class=\"cbb-variable\">$label<\/span> ) . <span class=\"cbb-string\">' '<\/span> . <span class=\"cbb-variable\">$button<\/span> . <span class=\"cbb-string\">'&lt;\/p&gt;'<\/span> . <span class=\"cbb-variable\">$content<\/span>;\n<\/span><span class=\"cbb-line\">}\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">add_filter<\/span>( <span class=\"cbb-string\">'the_content'<\/span>, <span class=\"cbb-string\">'lw_reading_time_prepend'<\/span> );\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/**<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> * Enqueues the front-end script and connects it to its translation files.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"> *\/<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">function<\/span> <span class=\"cbb-function\">lw_reading_time_enqueue_script<\/span>() {\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">if<\/span> ( ! <span class=\"cbb-function\">is_singular<\/span>( <span class=\"cbb-string\">'post'<\/span> ) ) {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-keyword\">return<\/span>;\n<\/span><span class=\"cbb-line\">\t}\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-variable\">$asset_file<\/span> = <span class=\"cbb-function\">plugin_dir_path<\/span>( __FILE__ ) . <span class=\"cbb-string\">'build\/index.asset.php'<\/span>;\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">if<\/span> ( ! <span class=\"cbb-function\">file_exists<\/span>( <span class=\"cbb-variable\">$asset_file<\/span> ) ) {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-keyword\">return<\/span>;\n<\/span><span class=\"cbb-line\">\t}\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-variable\">$asset<\/span> = <span class=\"cbb-keyword\">require<\/span> <span class=\"cbb-variable\">$asset_file<\/span>;\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-function\">wp_enqueue_script<\/span>(\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-string\">'lw-reading-time'<\/span>,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-function\">plugins_url<\/span>( <span class=\"cbb-string\">'build\/index.js'<\/span>, __FILE__ ),\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-variable\">$asset<\/span>&#91;<span class=\"cbb-string\">'dependencies'<\/span>&#93;,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-variable\">$asset<\/span>&#91;<span class=\"cbb-string\">'version'<\/span>&#93;,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-keyword\">array<\/span>( <span class=\"cbb-string\">'in_footer'<\/span> <span class=\"cbb-operator\">=&gt;<\/span> <span class=\"cbb-constant\">true<\/span> )\n<\/span><span class=\"cbb-line\">\t);\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-comment\">\/\/ Looks for lw-reading-time-{locale}-{md5 of \"build\/index.js\"}.json in wp-content\/languages\/plugins, without a language pack there in languages\/ (Domain Path).<\/span>\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-function\">wp_set_script_translations<\/span>( <span class=\"cbb-string\">'lw-reading-time'<\/span>, <span class=\"cbb-string\">'lw-reading-time'<\/span> );\n<\/span><span class=\"cbb-line\">}\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">add_action<\/span>( <span class=\"cbb-string\">'wp_enqueue_scripts'<\/span>, <span class=\"cbb-string\">'lw_reading_time_enqueue_script'<\/span> );<\/span><\/code><\/pre><\/figure>\n\n\n<p><code>__()<\/code> gibt die \u00dcbersetzung zur\u00fcck, <code>_e()<\/code> gibt sie direkt aus. Genau diese Ausgabe ohne Escaping bem\u00e4ngeln die WordPress Coding Standards: Der Sniff <code>EscapeOutput<\/code> f\u00fchrt <code>_e()<\/code> und <code>_ex()<\/code> als unsichere Ausgabefunktionen und schl\u00e4gt <code>esc_html_e()<\/code> oder <code>esc_attr_e()<\/code> vor. Die kombinierten Funktionen <code>esc_html__()<\/code>, <code>esc_html_e()<\/code>, <code>esc_attr__()<\/code>, <code>esc_attr_e()<\/code>, <code>esc_html_x()<\/code> und <code>esc_attr_x()<\/code> \u00fcbersetzen und escapen in einem Schritt. Eine \u00dcbersetzung bleibt Text von au\u00dferhalb des Codes und wird wie jede andere Ausgabe behandelt. Im Beispiel umschlie\u00dft <code>esc_html()<\/code> das Ergebnis von <code>sprintf()<\/code>; ein Escaping nur des Musters lie\u00dfe den eingesetzten Wert ungepr\u00fcft.<\/p>\n<h3>Platzhalter und \u00dcbersetzerkommentare<\/h3>\n<p>Variablen geh\u00f6ren nie in die zu \u00fcbersetzende Zeichenkette. Ein Aufruf wie <code>_e( \"Your city is $city.\" )<\/code> scheitert, weil der Extraktor den Text mit dem w\u00f6rtlichen <code>$city<\/code> findet, w\u00e4hrend WordPress zur Laufzeit nach einer Zeichenkette mit dem tats\u00e4chlichen Ortsnamen sucht, die in keiner \u00dcbersetzungsdatei steht. Richtig ist ein Platzhalter mit <code>printf()<\/code> oder <code>sprintf()<\/code>. Bei mehreren Platzhaltern empfiehlt das Handbuch nummerierte Argumente wie <code>%1$s<\/code> und <code>%2$s<\/code> in einfachen Anf\u00fchrungszeichen, damit eine \u00dcbersetzung die Reihenfolge \u00e4ndern kann.<\/p>\n<p>Ein Kommentar, der mit <code>translators:<\/code> beginnt, erkl\u00e4rt den \u00dcbersetzenden, wof\u00fcr ein Platzhalter steht. Er muss der letzte PHP-Kommentar vor dem gettext-Aufruf sein; <code>make-pot<\/code> \u00fcbernimmt ihn als <code>#.<\/code>-Zeile in die POT-Datei, und GlotPress zeigt ihn neben der Zeichenkette an. Die Zahl selbst l\u00e4uft durch <code>number_format_i18n()<\/code>, weil Deutsch und Polnisch andere Tausendertrennzeichen verwenden als Englisch.<\/p>\n<p><code>_x()<\/code> erg\u00e4nzt einen Kontext f\u00fcr W\u00f6rter mit mehreren Bedeutungen; GlotPress f\u00fchrt Kontext und Text dann als eigenen Eintrag. Das Skript weiter unten nutzt <code>_x( 'Read', \u2026 )<\/code> f\u00fcr den Zustand nach dem Klick. Ohne Kontext k\u00f6nnte \u201eRead\u201c als Verb \u201eLesen\u201c oder \u201eCzytaj\u201c \u00fcbersetzt werden, die Schaltfl\u00e4che braucht aber \u201eGelesen\u201c bzw. \u201ePrzeczytany\u201c.<\/p>\n<h3>Pluralformen: zwei im Englischen und Deutschen, drei im Polnischen<\/h3>\n<p><code>_n()<\/code> erh\u00e4lt Singular, Plural, Anzahl und Text Domain. Englisch und Deutsch kommen mit zwei Formen aus, Polnisch braucht drei. Die Regel steht im Header <code>Plural-Forms<\/code> jeder Sprache. GlotPress, die Software hinter translate.wordpress.org, definiert Polnisch mit <code>nplurals=3<\/code> und dem Ausdruck <code>(n == 1) ? 0 : ((n % 10 &gt;= 2 &amp;&amp; n % 10 &lt;= 4 &amp;&amp; (n % 100 &lt; 12 || n % 100 &gt; 14)) ? 1 : 2)<\/code>; ein polnischer Export von translate.wordpress.org tr\u00e4gt denselben Header. F\u00fcr die Lesezeit ergibt das:<\/p>\n<table>\n<thead>\n<tr><th>Minuten<\/th><th>Englisch<\/th><th>Deutsch<\/th><th>Polnisch (Form)<\/th><\/tr>\n<\/thead>\n<tbody>\n<tr><td>1<\/td><td>Reading time: 1 minute<\/td><td>Lesezeit: 1 Minute<\/td><td>Czas czytania: 1 minuta (0)<\/td><\/tr>\n<tr><td>2<\/td><td>Reading time: 2 minutes<\/td><td>Lesezeit: 2 Minuten<\/td><td>Czas czytania: 2 minuty (1)<\/td><\/tr>\n<tr><td>5<\/td><td>Reading time: 5 minutes<\/td><td>Lesezeit: 5 Minuten<\/td><td>Czas czytania: 5 minut (2)<\/td><\/tr>\n<tr><td>12<\/td><td>Reading time: 12 minutes<\/td><td>Lesezeit: 12 Minuten<\/td><td>Czas czytania: 12 minut (2)<\/td><\/tr>\n<tr><td>21<\/td><td>Reading time: 21 minutes<\/td><td>Lesezeit: 21 Minuten<\/td><td>Czas czytania: 21 minut (2)<\/td><\/tr>\n<tr><td>22<\/td><td>Reading time: 22 minutes<\/td><td>Lesezeit: 22 Minuten<\/td><td>Czas czytania: 22 minuty (1)<\/td><\/tr>\n<\/tbody>\n<\/table>\n<p>Daraus folgt eine Regel aus dem Handbuch: Auch der Singular enth\u00e4lt den Platzhalter, also <code>'%s minute'<\/code> statt <code>'One minute'<\/code>, denn in manchen Sprachen deckt die \u201eSingular\u201c-Form auch andere Zahlen als eins ab. Der Code \u00fcbergibt nur die Zahl; welcher der drei polnischen S\u00e4tze erscheint, entscheidet die \u00dcbersetzungsdatei. F\u00fcr Pluraltexte, deren Anzahl beim Definieren noch nicht feststeht, gibt es <code>_n_noop()<\/code> und <code>translate_nooped_plural()<\/code>.<\/p>\n<h2>Wer die \u00dcbersetzungen l\u00e4dt: Just-in-time statt load_plugin_textdomain<\/h2>\n<p>Mit WordPress 4.6 \u00e4nderte sich das Laden grundlegend. Die Entwicklernotiz vom Juli 2016 f\u00fchrte das Just-in-time-Laden ein: Trifft WordPress zum ersten Mal auf eine Text Domain, sucht <code>_load_textdomain_just_in_time()<\/code> in <code>wp-content\/languages<\/code> nach der passenden Datei und l\u00e4dt sie. Plugins aus dem Verzeichnis brauchen seitdem kein <code>load_plugin_textdomain()<\/code> mehr. Dieselbe Version drehte die Suchreihenfolge um: Sprachpakete in <code>wp-content\/languages<\/code> haben Vorrang vor Dateien im Plugin-Ordner.<\/p>\n<p>WordPress 6.5 ersetzte das \u00dcbersetzungssystem. Sprachpakete enthalten seitdem neben <code>.po<\/code> und <code>.mo<\/code> auch eine <code>.l10n.php<\/code>-Datei. Ist sie vorhanden, l\u00e4dt WordPress sie anstelle der <code>.mo<\/code>-Datei. Das geht schneller, ben\u00f6tigt weniger Speicher und nutzt OPcache. Mit dem Filter <code>translation_file_format<\/code> l\u00e4sst sich wieder auf <code>mo<\/code> umstellen.<\/p>\n<p>Seit WordPress 6.7 registriert <code>load_plugin_textdomain()<\/code> nur noch den Pfad; das eigentliche Laden \u00fcbernimmt der Just-in-time-Mechanismus. Gleichzeitig kam eine <code>_doing_it_wrong()<\/code>-Meldung hinzu. Werden \u00dcbersetzungen einer Domain vor <code>after_setup_theme<\/code> angefordert, meldet WordPress, das Laden sei \u201etoo early\u201c ausgel\u00f6st worden, und nennt <code>init<\/code> als fr\u00fchesten Zeitpunkt. Laut Entwicklernotiz muss WordPress beim fr\u00fchen Laden den aktuellen Benutzer vorzeitig ermitteln. Andere Plugins k\u00f6nnen dann Sprache oder Dateipfad nicht mehr filtern.<\/p>\n<p>F\u00fcr Tests ist ein Detail aus der <code>l10n.php<\/code> des Core wichtig: Die Meldung erscheint, sobald der Core f\u00fcr die Domain ein Sprachverzeichnis kennt, auch wenn keine \u00dcbersetzungsdatei vorhanden ist. Seit 6.8 registriert der Core f\u00fcr jedes aktive Plugin den Ordner aus <code>Domain Path<\/code> oder, ohne diesen Header, den Plugin-Ordner. Deshalb l\u00f6st das folgende Beispiel unter WordPress 7.1.2 die Meldung auch auf einer englischen Website ohne jede \u00dcbersetzungsdatei aus.<\/p>\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"php\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">PHP<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-php\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-meta\">&lt;?php<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ Too early: runs while WordPress loads the plugin file, before after_setup_theme and init.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ WordPress 6.7+ reports (since 6.8 also without a translation file, even on en_US)<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ _load_textdomain_just_in_time() via _doing_it_wrong().<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">define<\/span>( <span class=\"cbb-string\">'LW_READING_TIME_TITLE'<\/span>, <span class=\"cbb-function\">__<\/span>( <span class=\"cbb-string\">'Reading time'<\/span>, <span class=\"cbb-string\">'lw-reading-time'<\/span> ) );\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ Better: translate at the moment the text is needed (init or later).<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">function<\/span> <span class=\"cbb-function\">lw_reading_time_title<\/span>() {\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">return<\/span> <span class=\"cbb-function\">__<\/span>( <span class=\"cbb-string\">'Reading time'<\/span>, <span class=\"cbb-string\">'lw-reading-time'<\/span> );\n<\/span><span class=\"cbb-line\">}\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ Plugin metadata without translating it: third parameter $translate = false.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">function<\/span> <span class=\"cbb-function\">lw_reading_time_version<\/span>() {\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">if<\/span> ( ! <span class=\"cbb-function\">function_exists<\/span>( <span class=\"cbb-string\">'get_plugin_data'<\/span> ) ) {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-keyword\">require_once<\/span> <span class=\"cbb-constant\">ABSPATH<\/span> . <span class=\"cbb-string\">'wp-admin\/includes\/plugin.php'<\/span>;\n<\/span><span class=\"cbb-line\">\t}\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-variable\">$data<\/span> = <span class=\"cbb-function\">get_plugin_data<\/span>( __FILE__, <span class=\"cbb-constant\">false<\/span>, <span class=\"cbb-constant\">false<\/span> );\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">return<\/span> <span class=\"cbb-variable\">$data<\/span>&#91;<span class=\"cbb-string\">'Version'<\/span>&#93;;\n<\/span><span class=\"cbb-line\">}<\/span><\/code><\/pre><\/figure>\n\n\n<p>Die Notiz nennt zwei typische Ursachen: <code>get_plugin_data()<\/code> ohne <code>$translate = false<\/code> und Klassen, die direkt in der Hauptdatei instanziiert werden und im Konstruktor <code>__()<\/code> aufrufen. Zum Aufsp\u00fcren schl\u00e4gt sie einen Callback auf <code>doing_it_wrong_run<\/code> vor, der f\u00fcr <code>_load_textdomain_just_in_time<\/code> einen Backtrace ausgibt, oder Query Monitor.<\/p>\n<p>WordPress 6.8 dehnte das Just-in-time-Laden auf Plugins au\u00dferhalb des Verzeichnisses aus und wertet daf\u00fcr die Header <code>Text Domain<\/code> und <code>Domain Path<\/code> aus. Die Entwicklernotiz fasst es in zwei F\u00e4llen zusammen:<\/p>\n<ul>\n<li>Plugin auf WordPress.org, Mindestversion WordPress 4.6 oder h\u00f6her: kein Aufruf von <code>load_plugin_textdomain()<\/code> n\u00f6tig.<\/li>\n<li>Plugin mit den Headern <code>Text Domain<\/code> und <code>Domain Path<\/code>, Mindestversion WordPress 6.8 oder h\u00f6her: ebenfalls kein Aufruf n\u00f6tig.<\/li>\n<\/ul>\n<p>In allen anderen F\u00e4llen, etwa bei einem anderswo vertriebenen Plugin mit Unterst\u00fctzung \u00e4lterer Versionen, bleibt der Aufruf, wandert aber nach <code>init<\/code>. Plugin Check setzt das f\u00fcr das Verzeichnis durch: Der Sniff <code>PluginCheck.CodeAnalysis.DiscouragedFunctions.load_plugin_textdomainFound<\/code> erzeugt eine Warnung, sobald die Mindestversion aus <code>Requires at least<\/code> (Header oder Readme) \u00fcber 4.6 liegt. Die Meldung erg\u00e4nzt, dass WordPress die \u00dcbersetzungen unter dem Plugin-Slug automatisch l\u00e4dt.<\/p>\n<h2>JavaScript: @wordpress\/i18n, Build-Schritt und wp_set_script_translations<\/h2>\n<p>Seit WordPress 5.0 stellt das Skript-Handle <code>wp-i18n<\/code> dieselben Funktionen im Browser bereit: <code>__()<\/code>, <code>_x()<\/code>, <code>_n()<\/code>, <code>_nx()<\/code> und <code>sprintf()<\/code>. Das Beispiel importiert sie aus dem npm-Paket <code>@wordpress\/i18n<\/code> und baut die Datei mit <code>@wordpress\/scripts<\/code>:<\/p>\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"json\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">JSON<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-json\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\">{\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"name\"<\/span>: <span class=\"cbb-string\">\"lw-reading-time\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"version\"<\/span>: <span class=\"cbb-string\">\"1.0.0\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"private\"<\/span>: <span class=\"cbb-constant\">true<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"license\"<\/span>: <span class=\"cbb-string\">\"GPL-2.0-or-later\"<\/span>,\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"scripts\"<\/span>: {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"build\"<\/span>: <span class=\"cbb-string\">\"wp-scripts build\"<\/span>,\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"start\"<\/span>: <span class=\"cbb-string\">\"wp-scripts start\"<\/span>\n<\/span><span class=\"cbb-line\">\t},\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-attr\">\"devDependencies\"<\/span>: {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-attr\">\"@wordpress\/scripts\"<\/span>: <span class=\"cbb-string\">\"^36.0.0\"<\/span>\n<\/span><span class=\"cbb-line\">\t}\n<\/span><span class=\"cbb-line\">}<\/span><\/code><\/pre><\/figure>\n\n\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"javascript\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">JavaScript<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-javascript\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ src\/index.js<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">import<\/span> { _n, _x, sprintf } <span class=\"cbb-keyword\">from<\/span> <span class=\"cbb-string\">'@wordpress\/i18n'<\/span>;\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">const<\/span> STORAGE_KEY = <span class=\"cbb-string\">'lwReadingTimeRead'<\/span>;\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">function<\/span> <span class=\"cbb-function\">getReadPosts<\/span>() {\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">try<\/span> {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-keyword\">const<\/span> stored = JSON.<span class=\"cbb-function\">parse<\/span>( window.localStorage.<span class=\"cbb-function\">getItem<\/span>( STORAGE_KEY ) );\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-keyword\">return<\/span> <span class=\"cbb-type\">Array<\/span>.<span class=\"cbb-function\">isArray<\/span>( stored ) ? stored : &#91;&#93;;\n<\/span><span class=\"cbb-line\">\t} <span class=\"cbb-keyword\">catch<\/span> ( error ) {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-keyword\">return<\/span> &#91;&#93;;\n<\/span><span class=\"cbb-line\">\t}\n<\/span><span class=\"cbb-line\">}\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">function<\/span> <span class=\"cbb-function\">saveReadPosts<\/span>( ids ) {\n<\/span><span class=\"cbb-line\">\t<span class=\"cbb-keyword\">try<\/span> {\n<\/span><span class=\"cbb-line\">\t\twindow.localStorage.<span class=\"cbb-function\">setItem<\/span>( STORAGE_KEY, JSON.<span class=\"cbb-function\">stringify<\/span>( ids ) );\n<\/span><span class=\"cbb-line\">\t} <span class=\"cbb-keyword\">catch<\/span> ( error ) {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-comment\">\/\/ Storage may be unavailable (e.g. private windows); the button still updates.<\/span>\n<\/span><span class=\"cbb-line\">\t}\n<\/span><span class=\"cbb-line\">}\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">document.<span class=\"cbb-function\">querySelectorAll<\/span>( <span class=\"cbb-string\">'.lw-reading-time__mark'<\/span> ).<span class=\"cbb-function\">forEach<\/span>( ( button ) <span class=\"cbb-operator\">=&gt;<\/span> {\n<\/span><span class=\"cbb-line\">\tbutton.<span class=\"cbb-function\">addEventListener<\/span>( <span class=\"cbb-string\">'click'<\/span>, () <span class=\"cbb-operator\">=&gt;<\/span> {\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-keyword\">const<\/span> postId = <span class=\"cbb-function\">Number<\/span>( button.dataset.post );\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-keyword\">const<\/span> readPosts = <span class=\"cbb-function\">getReadPosts<\/span>();\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t\t<span class=\"cbb-keyword\">if<\/span> ( ! readPosts.<span class=\"cbb-function\">includes<\/span>( postId ) ) {\n<\/span><span class=\"cbb-line\">\t\t\treadPosts.<span class=\"cbb-function\">push<\/span>( postId );\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-function\">saveReadPosts<\/span>( readPosts );\n<\/span><span class=\"cbb-line\">\t\t}\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\t\tbutton.disabled = <span class=\"cbb-constant\">true<\/span>;\n<\/span><span class=\"cbb-line\">\t\tbutton.textContent = <span class=\"cbb-function\">_x<\/span>(\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-string\">'Read'<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-string\">'status of a post after it was marked as read'<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-string\">'lw-reading-time'<\/span>\n<\/span><span class=\"cbb-line\">\t\t);\n<\/span><span class=\"cbb-line\">\t\tbutton.title = <span class=\"cbb-function\">sprintf<\/span>(\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-comment\">\/* translators: %d: number of posts marked as read in this browser. *\/<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t<span class=\"cbb-function\">_n<\/span>(\n<\/span><span class=\"cbb-line\">\t\t\t\t<span class=\"cbb-string\">'%d post marked as read in this browser'<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t\t<span class=\"cbb-string\">'%d posts marked as read in this browser'<\/span>,\n<\/span><span class=\"cbb-line\">\t\t\t\treadPosts.length,\n<\/span><span class=\"cbb-line\">\t\t\t\t<span class=\"cbb-string\">'lw-reading-time'<\/span>\n<\/span><span class=\"cbb-line\">\t\t\t),\n<\/span><span class=\"cbb-line\">\t\t\treadPosts.length\n<\/span><span class=\"cbb-line\">\t\t);\n<\/span><span class=\"cbb-line\">\t} );\n<\/span><span class=\"cbb-line\">} );<\/span><\/code><\/pre><\/figure>\n\n\n<p>Ohne <code>block.json<\/code> nimmt <code>wp-scripts build<\/code> die Datei <code>src\/index.js<\/code> als Einstiegspunkt und schreibt <code>build\/index.js<\/code>. Das Dependency-Extraction-Plugin ersetzt den Import durch das globale <code>wp.i18n<\/code> und erzeugt <code>build\/index.asset.php<\/code> mit <code>wp-i18n<\/code> als Abh\u00e4ngigkeit und einem Versions-Hash; die PHP-Datei reicht beides an <code>wp_enqueue_script()<\/code> weiter. Die Webpack-Konfiguration beh\u00e4lt Kommentare mit <code>translators:<\/code> in der minimierten Ausgabe, und <code>make-pot<\/code> erkennt die Aufrufformen, die Webpack erzeugt.<\/p>\n<p>Die Verbindung zwischen Skript und \u00dcbersetzung stellt <code>wp_set_script_translations()<\/code> her, verf\u00fcgbar seit WordPress 5.0, mit optionaler Text Domain seit 5.1. Die Funktion wirkt nur bei einem bereits registrierten Skript, erkennbar am Handle \u2013 demselben Bezeichner, mit dem <a href=\"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/performance-optimierung-via-functions-php-selektives-dequeuing\/\">der Beitrag zum selektiven Dequeuing von Plugin-Skripten<\/a> arbeitet. WordPress sucht dann eine JSON-Datei im Jed-Format und legt deren Inhalt in <code>wp.i18n<\/code> ab, bevor das Skript l\u00e4uft. Der Dateiname folgt dem Muster <code>{domain}-{locale}-{md5}.json<\/code>; der Hash wird aus dem Pfad des Skripts relativ zum Plugin-Ordner berechnet und lautet f\u00fcr <code>build\/index.js<\/code> <code>dfbff627e6c248bcb3b61d7d06da9ca9<\/code>. Der Core bildet <code>.min.js<\/code> vor dem Hashen auf <code>.js<\/code> ab, und <code>make-pot<\/code> \u00fcberspringt <code>*.min.js<\/code> standardm\u00e4\u00dfig. Ein optionaler dritter Parameter nennt ein Verzeichnis, das WordPress zuerst pr\u00fcft, sowohl auf <code>{domain}-{locale}-{handle}.json<\/code> als auch auf den Hash-Namen, bevor es auf <code>wp-content\/languages\/plugins<\/code> zur\u00fcckf\u00e4llt.<\/p>\n<p>F\u00fcr Skriptmodule, die \u00fcber <code>wp_register_script_module()<\/code> registriert werden, kam mit WordPress 7.0 das Gegenst\u00fcck <code>wp_set_script_module_translations()<\/code> hinzu. Das Beispiel nutzt ein klassisches Skript, daher bleibt es bei diesem Hinweis.<\/p>\n<h2>Vom Quelltext zum Sprachpaket<\/h2>\n<p>Bei Plugins im Verzeichnis liest WordPress.org nach dem n\u00e4chsten SVN-Commit alle PHP- und JavaScript-Dateien aus und importiert die Zeichenketten in translate.wordpress.org. Dort erscheint jedes Plugin mit vier Teilprojekten: Development, Development Readme, Stable (latest release) und Stable Readme (latest release). Vorhandene <code>.mo<\/code>-Dateien im Plugin werden einmalig \u00fcbernommen; danach findet die \u00dcbersetzungsarbeit auf der Plattform statt.<\/p>\n<p>\u00dcbersetzungen vorschlagen darf jede Person mit einem WordPress.org-Konto. Vorschl\u00e4ge starten im Status \u201eWaiting\u201c und z\u00e4hlen erst, wenn ein General Translation Editor der Sprache oder ein f\u00fcr das Plugin ernannter Project Translation Editor sie auf \u201eCurrent\u201c setzt. Ungepr\u00fcfte maschinelle \u00dcbersetzungen werden nicht angenommen. Das erste Sprachpaket einer Sprache entsteht, sobald mindestens 90 Prozent der Zeichenketten im Teilprojekt Stable freigegeben sind; Readme-Texte z\u00e4hlen dabei nicht mit. Danach l\u00f6st jede \u00c4nderung an Zeichenketten oder \u00dcbersetzungen ein neues Sprachpaket aus, auch wenn der Anteil wieder unter 90 Prozent f\u00e4llt.<\/p>\n<p>Ein Sprachpaket enth\u00e4lt die <code>.po<\/code>&#8211; und <code>.mo<\/code>-Datei, seit WordPress 6.5 die <code>.l10n.php<\/code>-Datei und je Skript mit \u00fcbersetzbaren Zeichenketten eine JSON-Datei. WordPress installiert es \u00fcber die regul\u00e4ren \u00dcbersetzungsaktualisierungen nach <code>wp-content\/languages\/plugins\/<\/code>. Dort finden das Just-in-time-Laden und <code>wp_set_script_translations()<\/code> die Dateien ohne weiteren Code im Plugin.<\/p>\n<figure class=\"lw-diagram\">\n<img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/wp-plugin-i18n-de.png?v=20260930\" width=\"1120\" height=\"580\" alt=\"Eine Zeichenkette vom Quelltext bis zur Ausgabe: __() und _n() in PHP und @wordpress\/i18n im Skript werden nach dem SVN-Commit importiert, in GlotPress vorgeschlagen und freigegeben, ab 90 % Freigabe zum Sprachpaket gebaut und f\u00fcr PHP und JavaScript just in time geladen; lokal baut WP-CLI die Dateien ohne GlotPress\">\n<figcaption>Wie translate.wordpress.org aus einem gettext-Aufruf ein Sprachpaket macht, das WordPress f\u00fcr PHP und JavaScript l\u00e4dt<\/figcaption>\n<\/figure>\n<h2>Eigene \u00dcbersetzungsdateien mit WP-CLI<\/h2>\n<p>Lokale Dateien sind n\u00f6tig f\u00fcr Plugins au\u00dferhalb des Verzeichnisses, f\u00fcr die Zeit vor dem ersten Sprachpaket und f\u00fcr Kundenprojekte mit eigenem Wortlaut. Die Befehle unter <code>wp i18n<\/code> decken die ganze Kette ab:<\/p>\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"bash\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">Shell<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-bash\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-comment\"># Run inside wp-content\/plugins\/lw-reading-time\/<\/span>\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"># 1. Build the script: creates build\/index.js and build\/index.asset.php (dependency \"wp-i18n\").<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">npm<\/span> install\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">npm<\/span> run build\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"># 2. Extract all strings from PHP and from the built JavaScript into a template.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">wp<\/span> i18n make-pot . languages\/lw-reading-time.pot <span class=\"cbb-attr\">--exclude<\/span>=src\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"># 3. Copy the template to languages\/lw-reading-time-pl_PL.po (and -de_DE.po),<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">#    translate it in Poedit or a text editor, then compile the runtime files.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">wp<\/span> i18n make-mo languages\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">wp<\/span> i18n make-php languages\n<\/span><span class=\"cbb-line\"><span class=\"cbb-function\">wp<\/span> i18n make-json languages <span class=\"cbb-attr\">--no-purge<\/span>\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"># Result for Polish:<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"># languages\/lw-reading-time-pl_PL.po<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"># languages\/lw-reading-time-pl_PL.mo<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"># languages\/lw-reading-time-pl_PL.l10n.php<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\"># languages\/lw-reading-time-pl_PL-dfbff627e6c248bcb3b61d7d06da9ca9.json   (md5 of \"build\/index.js\")<\/span><\/span><\/code><\/pre><\/figure>\n\n\n<p><code>make-pot<\/code> durchsucht PHP-, Blade- und JavaScript-Dateien sowie den Plugin-Header und schlie\u00dft <code>node_modules<\/code>, <code>vendor<\/code>, Versionsverwaltungsordner und <code>*.min.js<\/code> immer aus. Im Beispiel sorgt <code>--exclude=src<\/code> daf\u00fcr, dass nur die gebaute Datei als Quelle der JavaScript-Texte erscheint. <code>make-mo<\/code> kompiliert die Bin\u00e4rdateien, <code>make-php<\/code> erzeugt die <code>.l10n.php<\/code>-Dateien und kam mit WP-CLI 2.10.0 (i18n-command 2.6.0). <code>make-json<\/code> schreibt je JavaScript-Quelldatei eine JSON-Datei im Jed-Format und entfernt standardm\u00e4\u00dfig anschlie\u00dfend die JavaScript-Texte aus der PO-Datei; mit <code>--no-purge<\/code> bleibt sie unver\u00e4ndert. Die polnische PO-Datei enth\u00e4lt dann alle drei Pluralformen:<\/p>\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"plain\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">Text<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-plain\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"># languages\/lw-reading-time-pl_PL.po (excerpt)\n<\/span><span class=\"cbb-line\">msgid \"\"\n<\/span><span class=\"cbb-line\">msgstr \"\"\n<\/span><span class=\"cbb-line\">\"Language: pl_PL\\n\"\n<\/span><span class=\"cbb-line\">\"Content-Type: text\/plain; charset=UTF-8\\n\"\n<\/span><span class=\"cbb-line\">\"Plural-Forms: nplurals=3; plural=(n == 1) ? 0 : ((n % 10 &gt;= 2 &amp;&amp; n % 10 &lt;= 4 &amp;&amp; (n % 100 &lt; 12 || n % 100 &gt; 14)) ? 1 : 2);\\n\"\n<\/span><span class=\"cbb-line\">\"X-Domain: lw-reading-time\\n\"\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">#. translators: %s: estimated reading time in minutes.\n<\/span><span class=\"cbb-line\">#: lw-reading-time.php:50\n<\/span><span class=\"cbb-line\">msgid \"Reading time: %s minute\"\n<\/span><span class=\"cbb-line\">msgid_plural \"Reading time: %s minutes\"\n<\/span><span class=\"cbb-line\">msgstr&#91;0&#93; \"Czas czytania: %s minuta\"\n<\/span><span class=\"cbb-line\">msgstr&#91;1&#93; \"Czas czytania: %s minuty\"\n<\/span><span class=\"cbb-line\">msgstr&#91;2&#93; \"Czas czytania: %s minut\"\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">#: lw-reading-time.php:57\n<\/span><span class=\"cbb-line\">msgid \"Mark as read\"\n<\/span><span class=\"cbb-line\">msgstr \"Oznacz jako przeczytany\"\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">#: build\/index.js:1\n<\/span><span class=\"cbb-line\">msgctxt \"status of a post after it was marked as read\"\n<\/span><span class=\"cbb-line\">msgid \"Read\"\n<\/span><span class=\"cbb-line\">msgstr \"Przeczytany\"\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">#. translators: %d: number of posts marked as read in this browser.\n<\/span><span class=\"cbb-line\">#: build\/index.js:2\n<\/span><span class=\"cbb-line\">msgid \"%d post marked as read in this browser\"\n<\/span><span class=\"cbb-line\">msgid_plural \"%d posts marked as read in this browser\"\n<\/span><span class=\"cbb-line\">msgstr&#91;0&#93; \"%d wpis oznaczony jako przeczytany w tej przegl\u0105darce\"\n<\/span><span class=\"cbb-line\">msgstr&#91;1&#93; \"%d wpisy oznaczone jako przeczytane w tej przegl\u0105darce\"\n<\/span><span class=\"cbb-line\">msgstr&#91;2&#93; \"%d wpis\u00f3w oznaczonych jako przeczytane w tej przegl\u0105darce\"<\/span><\/code><\/pre><\/figure>\n\n\n<p>Seit 4.6 haben Sprachpakete in <code>wp-content\/languages<\/code> Vorrang vor Dateien im Plugin. Soll ein Plugin trotzdem eigene \u00dcbersetzungen laden, verweist das Handbuch auf den Filter <code>load_textdomain_mofile<\/code>; seit 6.5 bietet der Core zus\u00e4tzlich <code>load_translation_file<\/code>, der den Pfad der <code>.mo<\/code>&#8211; wie der <code>.l10n.php<\/code>-Datei erh\u00e4lt. F\u00fcr Skripte legt der dritte Parameter von <code>wp_set_script_translations()<\/code> das Verzeichnis fest, das WordPress zuerst pr\u00fcft.<\/p>\n<h2>Grenzen und offene Punkte<\/h2>\n<ul>\n<li>Das Beispiel-Plugin lief unter WordPress 7.1.2 mit WP-CLI 2.12.0 und <code>@wordpress\/scripts<\/code> 36.0.0; das Paket verlangt Node 22.22.2 oder neuer, unter Node 20 warnte npm nur. Die Zeilennummern im PO-Auszug stammen aus diesem Build und verschieben sich mit jeder Code\u00e4nderung.<\/li>\n<li>Ob der Importer auf WordPress.org Ordner wie <code>src\/<\/code> oder minimierte Dateien ebenso \u00fcberspringt wie <code>make-pot<\/code>, ist in den genutzten Quellen nicht dokumentiert. F\u00fcr das Sprachpaket z\u00e4hlt, dass die gebaute Datei im SVN liegt.<\/li>\n<li>Ohne dritten Parameter findet <code>wp_set_script_translations()<\/code> unter WordPress 7.1.2 die JSON-Dateien auch im Ordner aus <code>Domain Path<\/code>, weil der Core das f\u00fcr die Domain registrierte Verzeichnis verwendet. Liegt in <code>wp-content\/languages\/plugins<\/code> eine \u00dcbersetzungsdatei derselben Sprache, bleiben die JSON-Dateien des Plugins unbeachtet.<\/li>\n<li><code>_n()<\/code> erwartet eine ganze Zahl; Bruchwerte wie 1,5 Minuten folgen im Polnischen anderen grammatischen Regeln. Das Beispiel rundet auf ganze Minuten auf.<\/li>\n<\/ul>\n\n<div class=\"lw-faq\">\n<h2>Fragen und Antworten<\/h2>\n\n<h3>Muss ein Plugin im Verzeichnis eigene .mo-Dateien mitliefern?<\/h3>\n<p>Nein. Nach dem Import in translate.wordpress.org entstehen Sprachpakete, die WordPress selbst nach <code>wp-content\/languages\/plugins\/<\/code> l\u00e4dt. Vorhandene <code>.mo<\/code>-Dateien werden beim ersten Import einmalig \u00fcbernommen; laut Meta-Handbuch lassen sie sich danach aus Paket und SVN entfernen.<\/p>\n\n<h3>Erscheint die Meldung zum zu fr\u00fchen Laden nur auf einer Website mit \u00dcbersetzungsdatei?<\/h3>\n<p>Nein, unter WordPress 7.1.2 nicht. Die Pr\u00fcfung in <code>_load_textdomain_just_in_time()<\/code> greift, sobald der Core f\u00fcr die Domain ein Sprachverzeichnis kennt, und seit 6.8 registriert er f\u00fcr jedes aktive Plugin den Plugin-Ordner oder dessen <code>Domain Path<\/code>. Ein Test auf einer Website mit <code>en_US<\/code> ganz ohne \u00dcbersetzungsdatei zeigt die Meldung deshalb ebenfalls. Sprachpakete bleiben n\u00f6tig, um die \u00fcbersetzte Ausgabe selbst zu pr\u00fcfen.<\/p>\n\n<h3>PHP-Texte sind \u00fcbersetzt, die Skripttexte bleiben englisch. Woran liegt das meist?<\/h3>\n<ul>\n<li><code>wp_set_script_translations()<\/code> l\u00e4uft vor der Registrierung des Skripts oder mit falschem Handle.<\/li>\n<li>Der Hash im JSON-Dateinamen passt nicht zum relativen Pfad der ausgelieferten Datei, etwa weil die POT-Datei aus <code>src\/<\/code> statt aus <code>build\/<\/code> erzeugt wurde.<\/li>\n<li>Die gebaute Datei fehlt im SVN, sodass WordPress.org keine JavaScript-Texte findet.<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"lw-quellen\">\n<h2>Quellen<\/h2>\n<ul>\n<li><a href=\"https:\/\/developer.wordpress.org\/plugins\/internationalization\/how-to-internationalize-your-plugin\/\" target=\"_blank\" rel=\"noopener noreferrer\">Plugin Handbook: How to Internationalize Your Plugin<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/plugins\/internationalization\/localization\/\" target=\"_blank\" rel=\"noopener noreferrer\">Plugin Handbook: Localization<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/block-editor\/how-to-guides\/internationalization\/\" target=\"_blank\" rel=\"noopener noreferrer\">Block Editor Handbook: Internationalization<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/__\/\" target=\"_blank\" rel=\"noopener noreferrer\">Code Reference: __()<\/a>, <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/_e\/\" target=\"_blank\" rel=\"noopener noreferrer\">_e()<\/a>, <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/_x\/\" target=\"_blank\" rel=\"noopener noreferrer\">_x()<\/a>, <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/_n\/\" target=\"_blank\" rel=\"noopener noreferrer\">_n()<\/a>, <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/esc_html__\/\" target=\"_blank\" rel=\"noopener noreferrer\">esc_html__()<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/load_plugin_textdomain\/\" target=\"_blank\" rel=\"noopener noreferrer\">Code Reference: load_plugin_textdomain()<\/a> und <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/wp_set_script_translations\/\" target=\"_blank\" rel=\"noopener noreferrer\">wp_set_script_translations()<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/make.wordpress.org\/core\/2016\/07\/06\/i18n-improvements-in-4-6\/\" target=\"_blank\" rel=\"noopener noreferrer\">Make WordPress Core: I18N Improvements in 4.6<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/make.wordpress.org\/core\/2018\/11\/09\/new-javascript-i18n-support-in-wordpress\/\" target=\"_blank\" rel=\"noopener noreferrer\">Make WordPress Core: JavaScript i18n support in WordPress 5.0<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/make.wordpress.org\/core\/2024\/02\/27\/i18n-improvements-6-5-performant-translations\/\" target=\"_blank\" rel=\"noopener noreferrer\">Make WordPress Core: I18N Improvements in 6.5 (Performant Translations)<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/make.wordpress.org\/core\/2024\/10\/21\/i18n-improvements-6-7\/\" target=\"_blank\" rel=\"noopener noreferrer\">Make WordPress Core: Internationalization improvements in 6.7<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/make.wordpress.org\/core\/2025\/03\/12\/i18n-improvements-6-8\/\" target=\"_blank\" rel=\"noopener noreferrer\">Make WordPress Core: Internationalization improvements in 6.8<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/make.wordpress.org\/meta\/handbook\/tutorials-guides\/translations\/\" target=\"_blank\" rel=\"noopener noreferrer\">Meta Handbook: Translations<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/make.wordpress.org\/polyglots\/handbook\/plugin-theme-authors-guide\/\" target=\"_blank\" rel=\"noopener noreferrer\">Polyglots Handbook: Plugin\/Theme Authors Guide<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/developer.wordpress.org\/cli\/commands\/i18n\/make-pot\/\" target=\"_blank\" rel=\"noopener noreferrer\">WP-CLI: wp i18n make-pot<\/a>, <a href=\"https:\/\/developer.wordpress.org\/cli\/commands\/i18n\/make-json\/\" target=\"_blank\" rel=\"noopener noreferrer\">make-json<\/a>, <a href=\"https:\/\/developer.wordpress.org\/cli\/commands\/i18n\/make-php\/\" target=\"_blank\" rel=\"noopener noreferrer\">make-php<\/a>, <a href=\"https:\/\/developer.wordpress.org\/cli\/commands\/i18n\/make-mo\/\" target=\"_blank\" rel=\"noopener noreferrer\">make-mo<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/github.com\/WordPress\/wordpress-develop\/blob\/7.1\/src\/wp-includes\/l10n.php\" target=\"_blank\" rel=\"noopener noreferrer\">wordpress-develop, Branch 7.1: wp-includes\/l10n.php<\/a> und <a href=\"https:\/\/github.com\/WordPress\/wordpress-develop\/blob\/7.1\/src\/wp-includes\/script-modules.php\" target=\"_blank\" rel=\"noopener noreferrer\">script-modules.php<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/github.com\/GlotPress\/GlotPress\/blob\/develop\/locales\/locales.php\" target=\"_blank\" rel=\"noopener noreferrer\">GlotPress: locales.php (Pluralregeln)<\/a> und <a href=\"https:\/\/translate.wordpress.org\/projects\/wp-plugins\/akismet\/\" target=\"_blank\" rel=\"noopener noreferrer\">translate.wordpress.org: Projekt Akismet, polnischer Export<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/github.com\/WordPress\/plugin-check\/releases\/tag\/2.1.0\" target=\"_blank\" rel=\"noopener noreferrer\">Plugin Check 2.1.0<\/a>: DiscouragedFunctionsSniff und Plugin_Header_Fields_Check (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/github.com\/WordPress\/gutenberg\/tree\/trunk\/packages\/scripts\" target=\"_blank\" rel=\"noopener noreferrer\">@wordpress\/scripts<\/a> (README und Webpack-Konfiguration) und <a href=\"https:\/\/github.com\/WordPress\/gutenberg\/tree\/trunk\/packages\/i18n\" target=\"_blank\" rel=\"noopener noreferrer\">@wordpress\/i18n<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/github.com\/WordPress\/WordPress-Coding-Standards\/blob\/develop\/WordPress\/Sniffs\/Security\/EscapeOutputSniff.php\" target=\"_blank\" rel=\"noopener noreferrer\">WordPress Coding Standards: EscapeOutputSniff<\/a> (abgerufen am 29.09.2026)<\/li>\n<li><a href=\"https:\/\/wordpress.org\/download\/releases\/\" target=\"_blank\" rel=\"noopener noreferrer\">WordPress Releases<\/a> (abgerufen am 29.09.2026)<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Ein kleines Plugin in PHP und JavaScript zeigt, wie Zeichenketten \u00fcbersetzbar werden, wie polnische Pluralformen funktionieren und welche Dateien translate.wordpress.org liefert.<\/p>\n","protected":false},"author":1,"featured_media":20907,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[92644],"tags":[92268,92274,92235,92277],"class_list":["post-20864","post","type-post","status-publish","format-standard","hentry","category-tutorials-de-wordpress-plugins-tricks-de","tag-javascript-de","tag-php-de","tag-tutorial-de","tag-wordpress-de"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"Text Domain, __(), _n() und @wordpress\/i18n: wie WordPress-Plugins \u00fcbersetzt werden, wann load_plugin_textdomain entf\u00e4llt und was WP-CLI erzeugt.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"Text Domain, __(), _n() und @wordpress\/i18n: wie WordPress-Plugins \u00fcbersetzt werden, wann load_plugin_textdomain entf\u00e4llt und was WP-CLI erzeugt.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-18T12:41:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20864-plugin-internationalisierung.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Lukas Wojcik\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lukas Wojcik\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"18 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/\"},\"author\":{\"name\":\"Lukas Wojcik\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript\",\"datePublished\":\"2026-10-18T12:41:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/\"},\"wordCount\":2380,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/fi-20864-plugin-internationalisierung.png\",\"keywords\":[\"JavaScript\",\"PHP\",\"Tutorial\",\"WordPress\"],\"articleSection\":[\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/\",\"name\":\"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/fi-20864-plugin-internationalisierung.png\",\"datePublished\":\"2026-10-18T12:41:00+00:00\",\"description\":\"Text Domain, __(), _n() und @wordpress\\\/i18n: wie WordPress-Plugins \u00fcbersetzt werden, wann load_plugin_textdomain entf\u00e4llt und was WP-CLI erzeugt.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/fi-20864-plugin-internationalisierung.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/fi-20864-plugin-internationalisierung.png\",\"width\":1200,\"height\":630,\"caption\":\"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/wordpress-plugins-tricks-de\\\/tutorials-de-wordpress-plugins-tricks-de\\\/plugin-internationalisierung\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript | Lukas Wojcik","description":"Text Domain, __(), _n() und @wordpress\/i18n: wie WordPress-Plugins \u00fcbersetzt werden, wann load_plugin_textdomain entf\u00e4llt und was WP-CLI erzeugt.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/","og_locale":"en_US","og_type":"article","og_title":"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript | Lukas Wojcik","og_description":"Text Domain, __(), _n() und @wordpress\/i18n: wie WordPress-Plugins \u00fcbersetzt werden, wann load_plugin_textdomain entf\u00e4llt und was WP-CLI erzeugt.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-10-18T12:41:00+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20864-plugin-internationalisierung.png","type":"image\/png"}],"author":"Lukas Wojcik","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Lukas Wojcik","Est. reading time":"18 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/"},"author":{"name":"Lukas Wojcik","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript","datePublished":"2026-10-18T12:41:00+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/"},"wordCount":2380,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20864-plugin-internationalisierung.png","keywords":["JavaScript","PHP","Tutorial","WordPress"],"articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/","url":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/","name":"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20864-plugin-internationalisierung.png","datePublished":"2026-10-18T12:41:00+00:00","description":"Text Domain, __(), _n() und @wordpress\/i18n: wie WordPress-Plugins \u00fcbersetzt werden, wann load_plugin_textdomain entf\u00e4llt und was WP-CLI erzeugt.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20864-plugin-internationalisierung.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/fi-20864-plugin-internationalisierung.png","width":1200,"height":630,"caption":"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/wordpress-plugins-tricks-de\/tutorials-de-wordpress-plugins-tricks-de\/plugin-internationalisierung\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Plugins \u00fcbersetzen: Text Domain, Sprachpakete und JavaScript"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/20864","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=20864"}],"version-history":[{"count":2,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/20864\/revisions"}],"predecessor-version":[{"id":22724,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/20864\/revisions\/22724"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/20907"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=20864"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=20864"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=20864"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}