{"id":133291,"date":"2022-09-14T07:00:43","date_gmt":"2022-09-14T05:00:43","guid":{"rendered":"https:\/\/phrase.com\/blog\/posts\/der-ultimative-leitfaden-zur-javascript-lokalisierung\/"},"modified":"2026-03-06T17:22:45","modified_gmt":"2026-03-06T16:22:45","slug":"step-step-guide-javascript-localization","status":"publish","type":"post","link":"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/","title":{"rendered":"JavaScript-Lokalisierung \u2013 der ultimative Leitfaden"},"content":{"rendered":"<p>Seltsam, gereift, relativ ausdrucksstark und erstaunlich \u2013 JavaScript ist heute die <a href=\"https:\/\/insights.stackoverflow.com\/survey\/2020#technology-programming-scripting-and-markup-languages-professional-developers\">meistgenutzte Programmiersprache<\/a> der Welt. Als Sprache der Browser \u2013 und mit <a href=\"https:\/\/nodejs.org\/en\/\">Node<\/a> auch auf dem Server im Einsatz \u2013 ist JavaScript fester Bestandteil der heutigen Web-Stacks.<br \/>\nUnd mit vielen plattform\u00fcbergreifenden mobilen Frameworks, Desktop-Wrappern, Game Engines und sogar Internet of Things (IoT)-Frameworks ist es im Grunde \u201edie Welt von JavaScript\u201c\u00a0\u2013 wir leben nur darin.<br \/>\nUnd du bist hier, weil du all das \ud83d\udd25 nutzen und lernen willst, wie du JavaScript-Anwendungen lokalisierst, damit sie f\u00fcr ein globales Publikum bereit sind. Keine Angst: Dieser Leitfaden behandelt alles, was du wissen musst, um mit der JavaScript-Lokalisierung f\u00fcr Browser zu starten.<br \/>\nAlso: Zeit f\u00fcr ein bisschen Rock <code>&amp;&amp;<\/code> Roll.<\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Hol dir den gesamten Code, der diesen Artikel begleitet, aus unserem <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\">GitHub-Repository<\/a>.<\/p>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Internet Explorer (IE), mit einem <a href=\"https:\/\/kinsta.com\/browser-market-share\/\">globalen Marktanteil von 2,15\u00a0%<\/a>, kann als veralteter Browser betrachtet werden. Der K\u00fcrze halber lassen wir IE-spezifische L\u00f6sungen in diesem Leitfaden also weg. Wenn du <em>immer noch<\/em> IE unterst\u00fctzt, \u00fcberpr\u00fcfe, ob die integrierten JavaScript-Funktionen, die wir in diesem Artikel behandeln, Forks oder Polyfills ben\u00f6tigen.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_81 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Overview<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#wie-lokalisiere-ich-eine-webseite-mit-javascript\" >Wie lokalisiere ich eine Webseite mit JavaScript?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#uebersetzungen-asynchron-laden\" >\u00dcbersetzungen asynchron laden<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#sprachwechsler-erstellen\" >Sprachwechsler erstellen<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#die-bevorzugten-spracheinstellungen-deines-browsers-erkennen\" >Die bevorzugten Spracheinstellungen deines Browsers erkennen<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#handhabung-der-schreibrichtung\" >Handhabung der Schreibrichtung<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#grundlegende-uebersetzungsnachrichten\" >Grundlegende \u00dcbersetzungsnachrichten<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#interpolation\" >Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#dynamisches-uebersetzen-nach-dem-laden-der-seite\" >Dynamisches \u00dcbersetzen nach dem Laden der Seite<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#pluralformen\" >Pluralformen<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#zahlenformatierung\" >Zahlenformatierung<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#datumsformatierung\" >Datumsformatierung<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#welche-guten-javascript-i18n-bibliotheken-kannst-du-verwenden\" >Welche guten JavaScript-i18n-Bibliotheken kannst du verwenden?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#wie-lokalisiere-ich-eine-webseite-mit-polyglot\" >Wie lokalisiere ich eine Webseite mit Polyglot?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#installation\" >Installation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#grundlegende-uebersetzungen\" >Grundlegende \u00dcbersetzungen<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#asynchrones-laden-von-uebersetzungsdateien\" >Asynchrones Laden von \u00dcbersetzungsdateien<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#sprachumschalter\" >Sprachumschalter<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#interpolation-2\" >Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#pluralformen-2\" >Pluralformen<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#wie-lokalisiere-ich-eine-webseite-mit-i18next\" >Wie lokalisiere ich eine Webseite mit i18next?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-21\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#installation-2\" >Installation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-22\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#grundlegende-uebersetzungsnachrichten-2\" >Grundlegende \u00dcbersetzungsnachrichten<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-23\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#asynchrone-uebersetzungsladefunktion\" >Asynchrone \u00dcbersetzungs\u00adladefunktion<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-24\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#unterstuetzte-sprachversionen-und-fallback-option\" >Unterst\u00fctzte Sprachversionen und Fallback-Option<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-25\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#automatische-erkennung-deiner-sprache-und-region\" >Automatische Erkennung deiner Sprache und Region<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-26\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#sprachumschalter-2\" >Sprachumschalter<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-27\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#interpolation-3\" >Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-28\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#pluralformen-3\" >Pluralformen<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-29\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#wie-lokalisiere-ich-eine-react-angular-oder-vue-app\" >Wie lokalisiere ich eine React-, Angular- oder Vue-App?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-30\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#angular-lokalisierungsartikel\" >Angular-Lokalisierungsartikel<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-31\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#lokalisierungsartikel-fuer-vuejs\" >Lokalisierungsartikel f\u00fcr Vue.js<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-32\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#lokalisierungsartikel-fuer-andere-frameworks\" >Lokalisierungsartikel f\u00fcr andere Frameworks<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-33\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#wie-lokalisiere-ich-eine-react-app-mit-i18next\" >Wie lokalisiere ich eine React-App mit i18next?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-34\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#installation-der-bibliothek\" >Installation der Bibliothek<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-35\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#grundlegende-uebersetzungen-2\" >Grundlegende \u00dcbersetzungen<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-36\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#asynchrones-laden-von-uebersetzungsdateien-2\" >Asynchrones Laden von \u00dcbersetzungsdateien<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-37\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#sprachumschalter-3\" >Sprachumschalter<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-38\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#artikel-zur-lokalisierung-mit-react\" >Artikel zur Lokalisierung mit React<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-39\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#wie-lokalisiere-ich-eine-webseite-mit-jquery-und-i18next\" >Wie lokalisiere ich eine Webseite mit jQuery und i18next?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-40\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#installation-3\" >Installation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-41\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#grundlegende-uebersetzungen-3\" >Grundlegende \u00dcbersetzungen<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-42\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#asynchrones-laden-von-uebersetzungsdateien-3\" >Asynchrones Laden von \u00dcbersetzungsdateien<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-43\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#sprachumschalter-4\" >Sprachumschalter<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-44\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#interpolation-4\" >Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-45\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#pluralformen-4\" >Pluralformen<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-46\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#wie-lokalisiere-ich-eine-webseite-mit-dem-icu-format-unter-verwendung-von-globalize\" >Wie lokalisiere ich eine Webseite mit dem ICU-Format unter Verwendung von Globalize?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-47\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#installation-4\" >Installation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-48\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#grundlegende-uebersetzungen-4\" >Grundlegende \u00dcbersetzungen<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-49\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#umgang-mit-fehlenden-nachrichten\" >Umgang mit fehlenden Nachrichten<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-50\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#asynchrones-laden-von-uebersetzungsdateien-4\" >Asynchrones Laden von \u00dcbersetzungsdateien<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-51\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#sprachumschalter-5\" >Sprachumschalter<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-52\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#interpolation-5\" >Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-53\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#pluralformen-5\" >Pluralformen<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-54\" href=\"https:\/\/phrase.com\/de\/blog\/posts\/step-step-guide-javascript-localization\/#damit-endet-unser-leitfaden-zur-javascript-lokalisierung\" >Damit endet unser Leitfaden zur JavaScript-Lokalisierung.<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"wie-lokalisiere-ich-eine-webseite-mit-javascript\"><\/span>Wie lokalisiere ich eine Webseite mit JavaScript?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Auch wenn es verlockend ist, einfach eine fertige Internationalisierungsbibliothek (i18n) f\u00fcr deine Lokalisierungsbed\u00fcrfnisse zu nehmen \u2013 und das kann tats\u00e4chlich die richtige Wahl f\u00fcr dein Projekt sein \u2013 wirst du feststellen, dass dir f\u00fcr kleinere Projekte auch einfaches JavaScript v\u00f6llig ausreicht. Wenn du dein eigenes erstellst, bekommst du au\u00dferdem ein sch\u00f6nes Kochbuch mit i18n-Techniken, die du mit <em>jeder<\/em> Bibliothek verwenden kannst, die du ausw\u00e4hlst.<\/p>\n<p>\ud83e\udd3f <em>Mehr Info \u00bb<\/em><i> <\/i>Unser Artikel, <a href=\"https:\/\/phrase.com\/de\/blog\/posts\/i18n-a-simple-definition\/\">Was ist I18n: Eine einfache Definition von Internationalisierung<\/a> geht n\u00e4her darauf ein, was Internationalisierung (i18n) und Lokalisierung (l10n) sind.<\/p>\n<p>\u270b\ud83c\udffd <em>Hinweis \u00bb<\/em> Wenn du eine traditionelle MPA (Multi-Page-Anwendung) erstellst, findet ein Gro\u00dfteil der Lokalisierung direkt auf dem Server statt. Wir arbeiten hier nur mit der Browser-Lokalisierung. Wir haben dich serverseitig abgedeckt \u2013 mit einem <a href=\"https:\/\/phrase.com\/blog\/posts\/nodejs-tutorial-on-creating-multilingual-web-app\/\">Node i18n-Tutorial<\/a> und einem <a href=\"https:\/\/phrase.com\/blog\/posts\/full-stack-javascript-i18n\/\">Full-Stack-JavaScript-i18n-Leitfaden<\/a>.<\/p>\n<p>Okay, nehmen wir an, du hast eine Seite, die du lokalisieren willst.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\" data-enlighter-group=\"e17c768d-5eb9-41e9-a82c-c8758e6dff71\" data-enlighter-title=\"index.html\" data-enlighter-linenumbers=\"false\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;meta charset=\"UTF-8\"&gt;\n  &lt;!-- ... --&gt;\n  &lt;title&gt;My Appy Apperson&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;h1&gt;My Appy Apperson&lt;\/h1&gt;\n    &lt;p&gt;Welcome to my little spot on the interwebs!&lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;script src=\"js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15564 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-en.png\" alt=\"Englische Version einer JavaScript-Demo-App | Phrase\" width=\"998\" height=\"316\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-en.png 998w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-en-300x95.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-en-768x243.png 768w\" sizes=\"auto, (max-width: 998px) 100vw, 998px\" \/><\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Du kannst den gesamten Code f\u00fcr die App, die wir in diesem Abschnitt bauen, aus dem <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/vanilla\">vanilla-Ordner in unserem GitHub-Repository<\/a> bekommen.<br \/>\n\ud83d\udd17 <em>Ressource \u00bb<\/em> Ich verwende die grundlegende <a href=\"http:\/\/getskeleton.com\/\">Skeleton CSS<\/a>-Bibliothek, falls du dich wunderst.<\/p>\n<p>Das sieht gut aus, aber es ist nicht wirklich globaltauglich, oder? Der gesamte Inhalt ist komplett auf Englisch festgelegt. Lass uns hier ein bisschen grundlegende i18n machen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-highlight=\"10,11\" data-enlighter-group=\"9ae201e0-a675-4f5e-8ebb-1c941d5f6f4b\" data-enlighter-title=\"index.html\" data-enlighter-linenumbers=\"false\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;meta charset=\"UTF-8\"&gt;\n  &lt;!-- ... --&gt;\n  &lt;title&gt;My Appy Apperson&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;My Appy Apperson&lt;\/h1&gt;\n    &lt;p data-i18n-key=\"lead\"&gt;Welcome to my little spot on the interwebs!&lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;script src=\"js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Beachte die <code>data-i18n-key<\/code> Attribute, die wir oben unseren Textcontainern hinzugef\u00fcgt haben. Wir k\u00f6nnen darauf zugreifen, wenn das Dokument geladen wird, und deren Text durch \u00dcbersetzungen ersetzen. Wei\u00dft du was? Lass uns genau das tun.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"eb3e63cb-cd04-47cf-a52d-41474533e624\" data-enlighter-title=\"\/js\/scripts.js\">\/\/ Das aktive Gebietsschema\nconst locale = \"en\";\n\/\/ Wir k\u00f6nnen hier so viele Sprachen haben, wie wir wollen,\n\/\/ und beliebige Sprachen verwenden, die wir m\u00f6chten. Wir haben Englisch\n\/\/ und Arabisch als Locales hier als Beispiele.\nconst translations = {\n  \/\/ Englische \u00dcbersetzungen\n  \"en\": {\n    \"app-title\": \"My Appy Apperson\",\n    \"lead\": \"Welcome to my little spot on the interwebs!\",\n  },\n  \/\/ Arabische \u00dcbersetzungen\n  \"ar\": {\n    \"app-title\": \"\u062a\u0637\u0628\u064a\u0642\u064a \u0627\u0644\u0645\u0637\u0628\u0642\",\n    \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a.\",\n  },\n};\n\/\/ Wenn der Seiteninhalt bereit ist...\ndocument.addEventListener(\"DOMContentLoaded\", () =&gt; {\n  document\n    \/\/ Finde alle Elemente mit dem Key-Attribut\n    .querySelectorAll(\"[data-i18n-key]\")\n    .forEach(translateElement);\n});\n\/\/ Ersetze den inneren Text des gegebenen HTML-Elements\n\/\/ mit der \u00dcbersetzung in der aktiven Sprache,\n\/\/ entsprechend dem data-i18n-key des Elements\nfunction translateElement(element) {\n  const key = element.getAttribute(\"data-i18n-key\");\n  const translation = translations[locale][key];\n  element.innerText = translation;\n}\n<\/pre>\n<p>\u00c4ndere die zweite Zeile oben zu <code>const locale = \"ar\";<\/code> und lade die Seite neu. Wenn das <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Window\/DOMContentLoaded_event\">DOMContentLoaded-Ereignis<\/a> ausgel\u00f6st wird, werden die arabischen \u00dcbersetzungen auf unserer Seite \u00fcbernommen.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15566 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-ar.png\" alt=\"Arabische Version einer JavaScript-Demo-App | Phrase\" width=\"572\" height=\"272\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-ar.png 572w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-ar-300x143.png 300w\" sizes=\"auto, (max-width: 572px) 100vw, 572px\" \/><\/p>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> <code>\"en\"<\/code> und <code>\"ar\"<\/code> oben sind die <a href=\"https:\/\/www.loc.gov\/standards\/iso639-2\/php\/code_list.php\">ISO 639-1-Codes<\/a> f\u00fcr Englisch und Arabisch. Normalerweise benutzt du ISO-Codes f\u00fcr <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/locale\/#language-code\">Sprachen<\/a> und <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/locale\/#country-code\">L\u00e4nder<\/a>, wenn du lokalisierst.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"uebersetzungen-asynchron-laden\"><\/span>\u00dcbersetzungen asynchron laden<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Wir sind mit unserer i18n-L\u00f6sung gut gestartet. Allerdings skaliert das Hinzuf\u00fcgen von Sprachen und \u00dcbersetzungen im Moment nicht gut. Wenn unsere App w\u00e4chst, w\u00fcrden wir wahrscheinlich unsere \u00dcbersetzungen in separate Dateien pro Sprache aufteilen. Die \u00dcbersetzungsdatei, die der aktiven Sprache entspricht, k\u00f6nnte dann ohne die Kosten f\u00fcr das Laden der anderen Sprachen geladen werden. Wir k\u00f6nnen dies ohne zu viel Aufwand umsetzen.<br \/>\nZuerst verschieben wir unsere \u00dcbersetzungen aus dem Hauptskript in JSON-Dateien, jeweils eine pro unterst\u00fctzter Sprache.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"6063d19b-b975-4367-8a9f-2be85e5d7770\" data-enlighter-title=\"\/lang\/en.json\">{\n  \"app-title\": \"My Appy Apperson\",\n  \"lead\": \"Welcome to my little spot on the interwebs!\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"c6570b0b-a294-414a-b00a-5ab4e1103c72\" data-enlighter-title=\"\/lang\/ar.json\">{\n  \"app-title\": \"\u062a\u0637\u0628\u064a\u0642\u064a \u0627\u0644\u0645\u0637\u0628\u0642\",\n  \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a.\"\n}\n<\/pre>\n<p>Lass uns unser Skript so umarbeiten, dass wir die JSON-Dateien bei Bedarf asynchron laden.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"eecf242f-182d-40ed-bc04-f77f3246859a\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"2,5,8,13,18,19,20,21,22,23,24,25,26,27,28,32,33,34,35,40-44,51\">\/\/ Die Sprache, die unsere App zuerst anzeigt\nconst defaultLocale = \"en\";\n\/\/ Die aktive Sprache\nlet locale;\n\/\/ Wird mit \u00dcbersetzungen der aktiven Sprache gef\u00fcllt\nlet translations = {};\n\/\/ Wenn der Seiteninhalt bereit ist\u00a0...\ndocument.addEventListener(\"DOMContentLoaded\", () =&gt; {\n  \/\/ \u00dcbersetze die Seite in die Standardsprache\n  setLocale(defaultLocale);\n});\n\/\/ Lade \u00dcbersetzungen f\u00fcr die gegebene Sprache und \u00fcbersetze\n\/\/ die Seite f\u00fcr diese Sprache\nasync function setLocale(newLocale) {\n  if (newLocale === locale) return;\n  const newTranslations =\n    await fetchTranslationsFor(newLocale);\n  locale = newLocale;\n  translations = newTranslations;\n  translatePage();\n}\n\/\/ Hole das \u00dcbersetzungs-JSON-Objekt f\u00fcr die gegebene\n\/\/ Sprache \u00fcber das Netzwerk\nasync function fetchTranslationsFor(newLocale) {\n  const response = await fetch(`\/lang\/${newLocale}.json`);\n  return await response.json();\n}\n\/\/ Ersetze den inneren Text jedes Elements, das ein\n\/\/ data-i18n-key Attribut mit der entsprechenden \u00dcbersetzung hat\n\/\/ zu seinem data-i18n-key\nfunction translatePage() {\n  document\n    .querySelectorAll(\"[data-i18n-key]\")\n    .forEach(translateElement);\n}\n\/\/ Ersetze den inneren Text des gegebenen HTML-Elements\n\/\/ mit der \u00dcbersetzung in der aktiven Sprache,\n\/\/ entsprechend dem data-i18n-key des Elements\nfunction translateElement(element) {\n  const key = element.getAttribute(\"data-i18n-key\");\n  const translation = translations[key];\n  element.innerText = translation;\n}\n<\/pre>\n<p>Wenn wir unsere Seite jetzt neu laden, sieht sie genau so aus wie zuvor. Allerdings haben wir unsere App unter der Haube deutlich skalierbarer und wartbarer gemacht.<\/p>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Wir verwenden die praktische <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Fetch_API\">Fetch API<\/a>, die in modernen Browsern integriert ist, um unsere JSON-Dateien \u00fcber das Netzwerk abzurufen.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"sprachwechsler-erstellen\"><\/span>Sprachwechsler erstellen<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Unsere User k\u00f6nnen unsere gro\u00dfartigen asynchronen F\u00e4higkeiten bisher noch nicht nutzen. Wollen wir ein Dropdown-Men\u00fc zum Sprachwechsel f\u00fcr sie bauen?<br \/>\nWir f\u00fcgen eine Navigationsleiste hinzu und platzieren unseren Umschalter in dieser Navigationsleiste.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-highlight=\"9,18,19,20,21,24\" data-enlighter-group=\"ffdcbc91-e87f-487d-b549-2a88f94eb91a\" data-enlighter-title=\"index.html\" data-enlighter-linenumbers=\"false\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n  &lt;title&gt;My Appy Apperson&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;ul class=\"navbar-list navbar-left\"&gt;\n          &lt;!-- Navigationslinks --&gt;\n        &lt;\/ul&gt;\n        &lt;div class=\"navbar-right\"&gt;\n          &lt;!-- ... --&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;Englisch&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabisch (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;Meine Appy Apperson&lt;\/h1&gt;\n    &lt;p data-i18n-key=\"lead\"&gt;Welcome to my little spot on the interwebs!&lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;script src=\"js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Ein einfaches <code>&lt;select&gt;<\/code> reicht hier aus. Wir k\u00f6nnen ein <code>data-i18n-switcher<\/code>-Attribut verwenden, damit unser JavaScript darauf zugreifen und die von dir ausgew\u00e4hlte Sprache laden kann.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"f468dc96-a9e9-47c3-84a4-e59ac95a6d68\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"10,18,19,20,21,22,23,24,25,26,27,28\">const defaultLocale = \"en\";\nlet locale;\n\/\/ ...\n\/\/ Wenn der Seiteninhalt bereit ist\u00a0...\ndocument.addEventListener(\"DOMContentLoaded\", () =&gt; {\n  setLocale(defaultLocale);\n  bindLocaleSwitcher(defaultLocale);\n});\n\/\/ ...\n\/\/ Immer wenn der User eine neue Sprache ausw\u00e4hlt,\n\/\/ laden und aktualisieren wir die \u00dcbersetzungen dieser Sprache\n\/\/ die Seite\nfunction bindLocaleSwitcher(initialValue) {\n  const switcher =\n    document.querySelector(\"[data-i18n-switcher]\");\n  switcher.value = initialValue;\n  switcher.onchange = (e) =&gt; {\n    \/\/ Setzt die Sprache auf die ausgew\u00e4hlte Option[value]\n    setLocale(e.target.value);\n  };\n}\n<\/pre>\n<p>Mit dem <code>onchange<\/code>-Event-Handler kannst du die \u00dcbersetzungen deiner Seite entsprechend dem Wert der ausgew\u00e4hlten <code>&lt;option&gt;<\/code> aktualisieren. Et voila. Unsere Besucher k\u00f6nnen jetzt ihre eigene Sprache ausw\u00e4hlen.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15567 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/locale-switcher.gif\" alt=\"Demo-App mit onchange-Event-Handler | Phrase\" width=\"600\" height=\"204\" \/><\/p>\n<p>\ud83d\udce3 <em>Ein gro\u00dfes Dankesch\u00f6n an<\/em> <a href=\"https:\/\/thenounproject.com\/search\/?q=translation&amp;i=4380109%0A\">Hary Murdiono JS vom Noun Project<\/a> f\u00fcr sein <em>translate<\/em>-Icon.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"die-bevorzugten-spracheinstellungen-deines-browsers-erkennen\"><\/span>Die bevorzugten Spracheinstellungen deines Browsers erkennen<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Manchmal ist es eine gute Idee, die bevorzugte Sprache des Users zu erraten, bevor du die M\u00f6glichkeit hast, deine eigene manuell auszuw\u00e4hlen. Die meisten Menschen haben ihre Browser-Benutzeroberfl\u00e4che in ihrer bevorzugten Sprache eingestellt, oft in der Sprache des Betriebssystems.<br \/>\nDie Sprache der Browser-Oberfl\u00e4che findest du im <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Navigator\/language\">navigator<\/a>-Objekt, genauer gesagt im Standardwert <code>navigator.language<\/code> (String).<br \/>\nDas ebenfalls standardisierte \u2013 wenn auch experimentell, w\u00e4hrend ich dies schreibe \u2013 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Navigator\/languages\">navigator.languages<\/a>-Array sollte die UI-Sprache als ersten Eintrag enthalten, zus\u00e4tzlich zu allen Sprachen, die du in deinen bevorzugten Spracheinstellungen deines Browsers explizit festgelegt hast.<br \/>\nEine kleine Funktion, die <code>navigator.languages<\/code> abfragt, kann uns beim Erkennen der Browsersprache helfen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"905d80b4-e5a7-4a8e-ba58-54f9ba5f8ee9\" data-enlighter-title=\"\/js\/scripts.js\">\/**\n * Retrieve user-preferred locales from the browser\n *\n * @param {boolean} languageCodeOnly - when true, returns\n * [\"en\", \"fr\"] instead of [\"en-US\", \"fr-FR\"]\n * @returns array | undefined\n *\/\nfunction browserLocales(languageCodeOnly = false) {\n  return navigator.languages.map((locale) =&gt;\n    languageCodeOnly ? locale.split(\"-\")[0] : locale,\n  );\n}\n<\/pre>\n<p>Stell dir vor, du hast Franz\u00f6sisch (Kanada) und Chinesisch (Vereinfacht) in deinen Browsereinstellungen.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15568 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/browser-locale-prefs-1024x605.png\" alt=\"Browser-Spracheinstellungen | Phrase\" width=\"1024\" height=\"605\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/browser-locale-prefs-1024x605.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/browser-locale-prefs-300x177.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/browser-locale-prefs-768x454.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/browser-locale-prefs.png 1438w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\nIn diesem Fall gibt <code>browserLocales()<\/code> <code>[\"fr-CA\", \"zh-CN\"]<\/code> zur\u00fcck. Wenn wir <code>browserLocales(true)<\/code> aufrufen, erhalten wir <code>[\"fr\", \"zh\"]<\/code>.<br \/>\nWir k\u00f6nnen jetzt diese neue Funktion verwenden, um die bevorzugten Spracheinstellungen des Users zu erkennen, wenn wir unsere Seite zum ersten Mal laden.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"72b255d6-6feb-43d4-8437-db90b0dbc34e\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"4,9,10,12,14,19,20,21,25,26,27\">\/\/ Die Sprache, die unsere App zuerst anzeigt\nconst defaultLocale = \"en\";\nconst supportedLocales = [\"en\", \"ar\"];\n\/\/ ...\ndocument.addEventListener(\"DOMContentLoaded\", () =&gt; {\n  const initialLocale =\n    supportedOrDefault(browserLocales(true));\n  setLocale(initialLocale);\n  bindLocaleSwitcher(initialLocale);\n});\n\/\/ ...\nfunction isSupported(locale) {\n  return supportedLocales.indexOf(locale) &gt; -1;\n}\n\/\/ Hol dir die erste unterst\u00fctzte Sprache aus dem gegebenen\n\/\/ Array oder gib unsere Standard-Sprache zur\u00fcck\nfunction supportedOrDefault(locales) {\n  return locales.find(isSupported) || defaultLocale;\n}\n\/\/ ...\nfunction browserLocales(languageCodeOnly = false) {\n  return navigator.languages.map((locale) =&gt;\n    languageCodeOnly ? locale.split(\"-\")[0] : locale,\n  );\n}\n<\/pre>\n<p>Beachte, dass wir das Konzept von <code>supportedLocales<\/code> eingef\u00fchrt haben; das sind die einzigen Locales, f\u00fcr die wir \u00dcbersetzungen haben. Mit ihnen k\u00f6nnen wir auf unsere Standardsprache zur\u00fcckgreifen, wenn keine deiner bevorzugten Sprachen in unserer unterst\u00fctzten Liste enthalten ist.<br \/>\nUnsere App wird jetzt in die erste Sprache deiner bevorzugten Liste \u00fcbersetzt, mit einem eleganten Fallback.<\/p>\n<p>\ud83e\udd3f <em>Mehr Info \u00bb<\/em> Wir behandeln die Spracherkennung sowohl im Browser als auch auf dem Server ausf\u00fchrlich in <a href=\"https:\/\/phrase.com\/de\/blog\/posts\/detecting-a-users-locale\/\">Erkennung der Browsersprache mit JavaScript<\/a>.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"handhabung-der-schreibrichtung\"><\/span>Handhabung der Schreibrichtung<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Arabisch, Hebr\u00e4isch, Persisch, Urdu und andere Sprachen <a href=\"https:\/\/www.worldatlas.com\/articles\/which-languages-are-written-from-right-to-left.html\">verwenden Schriften, die von rechts nach links geschrieben werden<\/a>. Auch wenn es viel mehr Sprachen gibt, die von links nach rechts (LTR) geschrieben werden, ist es gut zu wissen, wie du von rechts nach links geschriebene Sprachen (RTL) unterst\u00fctzen kannst. Gl\u00fccklicherweise wird ein Gro\u00dfteil der Arbeit hier vom Browser erledigt; wir m\u00fcssen nur das <code>&lt;html dir&gt;<\/code> Attribut auf unseren Seiten setzen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"6a86b7f1-65ce-4b15-891b-0425324892e3\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"16,26,27,28\">\/\/ ...\n\/\/ Lade \u00dcbersetzungen f\u00fcr das gegebene Gebietsschema und \u00fcbersetze\n\/\/ die Seite f\u00fcr diese Sprache\nasync function setLocale(newLocale) {\n  if (newLocale === locale) return;\n  const newTranslations = await fetchTranslationsFor(\n    newLocale,\n  );\n  locale = newLocale;\n  translations = newTranslations;\n  \/\/ Setze das &lt;html dir&gt; Attribut\n  document.documentElement.dir = dir(newLocale);\n  \/\/ Nicht notwendig f\u00fcr den Richtungsfluss, aber zur Sicherheit\u00a0...\n  document.documentElement.lang = newLocale;\n  translatePage();\n}\n\/\/ ...\nfunction dir(locale) {\n  return locale === \"ar\" ? \"rtl\" : \"ltr\";\n}\n\/\/ ...\n<\/pre>\n<p>Das <code>&lt;html dir&gt;<\/code> Attribut kann die Werte <code>\"ltr\"<\/code> oder <code>\"rtl\"<\/code> annehmen. Wir liefern diesen Wert \u00fcber eine sehr einfache <code>dir()<\/code>-Funktion und setzen das Attribut jedes Mal, wenn du die Sprache wechselst.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15569 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dom-lang-dir.png\" alt=\"HTML-Attribute der Browser-Entwicklertools | Phrase\" width=\"322\" height=\"208\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dom-lang-dir.png 322w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dom-lang-dir-300x194.png 300w\" sizes=\"auto, (max-width: 322px) 100vw, 322px\" \/><br \/>\n<em>Wenn wir unsere Browser-Entwicklertools \u00f6ffnen, k\u00f6nnen wir sehen, wie sich die <code>&lt;html&gt;<\/code>-Attribute aktualisieren, w\u00e4hrend wir die Sprachen wechseln<\/em>.<\/p>\n<p>Der Browser stellt das Dokument automatisch von rechts nach links dar, wenn wir <code>&lt;html dir=\"rtl\"&gt;<\/code> setzen. Allerdings kann jeder unserer benutzerdefinierten Richtungsstile betroffen sein. So erfordert <code>margin-left: 20px<\/code> einige angepasste, RTL-spezifische CSS-Regeln. Das ist im Allgemeinen nicht zu kompliziert; es liegt nur ein wenig au\u00dferhalb des Rahmens dieses Artikels.<\/p>\n<p>\ud83e\udd3f <em>Mehr Info \u00bb<\/em> Lies mehr \u00fcber lokalisierte CSS in <a href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/\">Wie verwende ich eine CSS-Datei f\u00fcr die Website-Lokalisierung?<\/a><\/p>\n<p>Mit unserem neuen Code bekommen wir eine arabische Seite, die Avicenna gefallen w\u00fcrde!<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15570 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/ar-rtl-1024x348.png\" alt=\"Demo-App mit rechtsb\u00fcndigem arabischen Text | Phrase\" width=\"1024\" height=\"348\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/ar-rtl-1024x348.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/ar-rtl-300x102.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/ar-rtl-768x261.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/ar-rtl.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"grundlegende-uebersetzungsnachrichten\"><\/span>Grundlegende \u00dcbersetzungsnachrichten<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Bevor wir zu komplexeren Nachrichten \u00fcbergehen, wie denen mit interpolierten Werten und Pluralformen, lass uns kurz anschauen, wie wir unsere \u00dcbersetzungsnachrichten implementiert haben.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">\/\/ In unserer HTML-Seite\n&lt;h1 data-i18n-key=\"app-title\"&gt;Meine Appy Apperson&lt;\/h1&gt;\n\/\/ In unserem JavaScript\nfunction translateElement(element) {\n  const key = element.getAttribute(\"data-i18n-key\");\n  const translation = translations[key];\n  element.innerText = translation;\n}\n\/\/ Angesichts der Tatsache, dass wir arabische \u00dcbersetzungen aus ar.json geladen haben:\ntranslations = {\n  \"app-title\": \"\u062a\u0637\u0628\u064a\u0642\u064a \u0627\u0644\u0645\u0637\u0628\u0642\",\n};\ntranslateElement(document.querySelector(\"[data-i18n-key='lead']\"));\n\/\/ wird gerendert als\n&lt;h1 data-i18n-key=\"app-title\"&gt;\u062a\u0637\u0628\u064a\u0642\u064a \u0627\u0644\u0645\u0637\u0628\u0642&lt;\/h1&gt;\n<\/pre>\n<p>Das ist unser \u00dcbersetzungssystem in aller K\u00fcrze.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"interpolation\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Was passiert, wenn wir Werte haben, die sich w\u00e4hrend der Laufzeit \u00e4ndern und in unsere Nachrichten eingef\u00fcgt werden m\u00fcssen? Ein h\u00e4ufiges Beispiel ist der Name des aktuell angemeldeten Users. Wir m\u00fcssen unser \u00dcbersetzungssystem aktualisieren, damit solche F\u00e4lle funktionieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-highlight=\"12,13,14,15,16,17\" data-enlighter-group=\"269a8ae8-8a14-4a95-9863-a481f6ae89f3\" data-enlighter-title=\"index.html\" data-enlighter-linenumbers=\"false\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;!-- ... --&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;Meine Appy Apperson&lt;\/h1&gt;\n    &lt;p\n      data-i18n-key=\"lead\"\n      data-i18n-opt='{\"username\": \"Swoodesh\"}'\n    &gt;\n      Welcome to my little spot on the interwebs, {username}!\n    &lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;script src=\"js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Wir kennzeichnen Platzhalter f\u00fcr Werte, die wir in unseren Nachrichten interpolieren m\u00f6chten, mit der <code>{variable}<\/code> Syntax. Ein neues <code>data-i18n-opt<\/code>-Attribut speichert Key\/Wert-Paare f\u00fcr die Interpolation in einem g\u00fcltigen JSON-Objekt.<br \/>\nNat\u00fcrlich ben\u00f6tigen wir die Platzhalter in unseren Sprachdateien.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"f78af90a-df90-420c-a3c7-7a289dcede90\" data-enlighter-title=\"\/lang\/en.json\">{\n  \"lead\": \"Welcome to my little spot on the interwebs, {username}!\",\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"9082479b-aff1-4962-b14e-d522963e7eb8\" data-enlighter-title=\"\/lang\/ar.json\">{\n  \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a \u064a\u0627 {username}.\",\n}\n<\/pre>\n<p>Jetzt k\u00f6nnen wir unsere <code>translateElement<\/code>-Funktion modifizieren, um Interpolationen zu verarbeiten.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"13c0b59f-dfdf-45cf-8d56-911e8a61422e\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"7,8,9,11,12,13,18,19,20,21,22,23,24,25,26,27\">\/\/ ...\nfunction translateElement(element) {\n  const key = element.getAttribute(\"data-i18n-key\");\n  const translation = translations[key];\n  const options = JSON.parse(\n    element.getAttribute(\"data-i18n-opt\")\n  );\n  element.innerText = options\n    ? Interpolate(translation, options)\n    : \u00dcbersetzung;\n}\n\/\/ Konvertier eine Nachricht wie \"Hallo, {name}\" in \"Hallo, Chad\"\n\/\/ im Falle des Interpolationsobjekts {name: \"Chad\"}\nfunction interpolate(message, interpolations) {\n  return Object.keys(interpolations).reduce(\n    (interpolated, key) =&gt;\n      interpolated.replace(\n        new RegExp(`{\\s*${key}\\s*}`, \"g\"),\n        interpolations[key],\n      ),\n    message,\n  );\n}\n\/\/ ...\n<\/pre>\n<p>Wenn wir ein <code>data-i18n-opt<\/code> Attribut am Element erkennen, das an <code>translateElement()<\/code> \u00fcbergeben wird, verarbeiten wir die \u00fcbersetzte Nachricht mit einer neuen <code>interpolate()<\/code> Funktion, bevor wir das Element aktualisieren. Wenn du jetzt die Seite l\u00e4dst, siehst du die Nachricht mit dem eingef\u00fcgten Wert.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15571 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-en-1024x348.png\" alt=\"Demo-App mit Interpolation auf Englisch | Phrase\" width=\"1024\" height=\"348\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-en-1024x348.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-en-300x102.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-en-768x261.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-en.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15572 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-ar-1024x348.png\" alt=\"Demo-App mit Interpolation in arabischer sprache | Phrase\" width=\"1024\" height=\"348\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-ar-1024x348.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-ar-300x102.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-ar-768x261.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-ar.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\nNat\u00fcrlich sind statische Werte im HTML f\u00fcr uns von begrenztem Nutzen. Idealerweise willst du in der Lage sein, dynamisch mit JavaScript zu interpolieren. Das ist nicht so schwer zu coden.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"dynamisches-uebersetzen-nach-dem-laden-der-seite\"><\/span>Dynamisches \u00dcbersetzen nach dem Laden der Seite<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Lass uns eine allgemeine \u00dcbersetzungsfunktion aus <code>translateElement()<\/code> extrahieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"6597c244-ae4d-4095-a22e-3f67cadd8248\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"6,7,9,12,13,14\">\/\/ ...\nfunction translateElement(element) {\n  const key = element.getAttribute(\"data-i18n-key\");\n  const options =\n    JSON.parse(element.getAttribute(\"data-i18n-opt\")) || {};\n  element.innerText = translate(key, options);\n}\nfunction translate(key, interpolations = {}) {\n  return interpolate(translations[key], interpolations);\n}\n\/\/ ...\n<\/pre>\n<p>Wir haben einfach den Code, der das Abrufen einer Nachricht in der aktiven Sprache mit Interpolationen \u00fcbernimmt, in eine neue <code>translate()<\/code>-Funktion ausgelagert. Wir k\u00f6nnen diese Funktion jetzt verwenden, um die \u00dcbersetzung eines Elements nach dem Laden der Seite zu aktualisieren. Angenommen, wir m\u00f6chten unseren Text aktualisieren, nachdem du dich eingeloggt hast. Kein Problem.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">const element =\n  document.querySelector(\"[data-i18n-key='lead']\");\n\/\/ Unsere neue Funktion dient uns hier gut\nelement.innerText =\n  translate(\"lead\", { username: \"Maggie\" });\n\/\/ Speichere die aktualisierten Interpolationen im Dokument\n\/\/ f\u00fcr den Fall, dass das Element in Zukunft neu gerendert wird\nelement.setAttribute(\n  \"data-i18n-opt\",\n  JSON.stringify({ username: \"Maggie\" }),\n);\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15573 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dynamic-interpolation-1024x348.png\" alt=\"Demo-App mit dynamischer Englisch-\u00dcbersetzung | Phrase\" width=\"1024\" height=\"348\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dynamic-interpolation-1024x348.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dynamic-interpolation-300x102.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dynamic-interpolation-768x261.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dynamic-interpolation.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\nJetzt kannst du die \u00dcbersetzungen der Elemente jederzeit aus deinem JavaScript aktualisieren.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"pluralformen\"><\/span>Pluralformen<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Wir m\u00fcssen oft unterschiedliche Nachrichten basierend auf einem Z\u00e4hler pr\u00e4sentieren\u00a0\u2013 wie \u201e1 Follower\u201c oder \u201e20.000 Followers<em><\/em>\u201c\u00a0\u2013 <a href=\"https:\/\/unicode-org.github.io\/cldr-staging\/charts\/latest\/supplemental\/language_plural_rules.html\">verschiedene Sprachen haben unterschiedliche Pluralregeln<\/a>. W\u00e4hrend Englisch zwei Pluralformen hat: <em>one<\/em> und <em>other<\/em>, hat Arabisch zum Beispiel sechs Pluralformen. Historisch gesehen bedeutete dies, dass die Implementierung der Pluralformen-Unterst\u00fctzung f\u00fcr Frontend-Apps nicht sehr einfach war. Gl\u00fccklicherweise erleichtert das jetzt <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/PluralRules\">standardm\u00e4\u00dfige Intl.PluralRules-Objekt<\/a> die Handhabung von Pluralformen.<br \/>\nStell dir vor, wir sind flei\u00dfige Schreiberlinge und wollen der Welt zeigen, wie viele Artikel wir tats\u00e4chlich schon verfasst haben.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;p\n  data-i18n-key=\"article-plural\"\n  data-i18n-opt='{\"count\": 122}'\n&gt;\n  {count} articles written and counting.\n&lt;\/p&gt;\n<\/pre>\n<p>Beachte, dass wir die Konvention verwenden, unseren Plural-Nachrichten-Key mit <code>-plural<\/code> zu beenden. Und nat\u00fcrlich ben\u00f6tigen wir eine erforderliche <code>count<\/code> Ganzzahl, um die richtige Pluralform auszuw\u00e4hlen. Wenn wir schon von Pluralformen sprechen, lass sie uns gleich hier hinzuf\u00fcgen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"75fdda27-47a8-4750-9436-e1a6d3120c1f\" data-enlighter-title=\"\/lang\/en.json\">{\n  \/\/ Englisch hat zwei Pluralformen\n  \"article-plural\": {\n    \"one\": \"{count} article and counting\",\n    \"other\": \"{count} articles and counting\"\n  }\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"f45a1ae7-53e1-4816-85d4-c79c3bd740d8\" data-enlighter-title=\"\/lang\/ar.json\">{\n  \/\/ Arabisch hat sechs Pluralformen\n  \"article-plural\": {\n    \"zero\": \"\u0644\u0627 \u062a\u0648\u062c\u062f \u0645\u0642\u0627\u0644\u0627\u062a\",\n    \"one\": \"\u0645\u0642\u0627\u0644 {count}\",\n    \"two\": \"\u0645\u0642\u0627\u0644\u0627\u0646\",\n    \"few\": \"{count} \u0645\u0642\u0627\u0644\u0627\u062a\",\n    \"many\": \"{count} \u0645\u0642\u0627\u0644\",\n    \"other\": \"{count} \u0645\u0642\u0627\u0644\"\n  }\n}\n<\/pre>\n<p>Lass uns jetzt unsere <code>translate<\/code>-Funktion aktualisieren, damit sie Pluralnachrichten verarbeiten kann.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"62d15884-1e5b-4035-b937-4091ed4ac4b9\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"6,7,8,9,10,11,26,27,28,29,30\">\/\/ ...\nfunction translate(key, interpolations = {}) {\n  const message = translations[key];\n  if (key.endsWith(\"-plural\")) {\n    return interpolate(\n      pluralFormFor(message, interpolations.count),\n      interpolations,\n    );\n  }\n  return interpolate(message, interpolations);\n}\n\/\/ ...\n\/*\n  Angenommen, es gibt ein forms-Objekt wie\n  {\n    \"zero\": \"Keine Artikel\"\n    \"one\": \"Ein Artikel\",\n    \"other\": \"{count} Artikel\"\n  } und eine Anzahl von 3 ergibt \"3 Artikel\"\n*\/\nfunction pluralFormFor(forms, count) {\n  const matchingForm = new Intl.PluralRules(locale).select(count);\n  return forms[matchingForm];\n}\n\/\/ ...\n<\/pre>\n<p>Der magische Trick hier ist der Teil, der <code>new Intl.PluralRules(locale).select(...)<\/code> ausf\u00fchrt. Das eingebaute <code>Intl.PluralRules<\/code>-Objekt kennt die Pluralregeln des jeweiligen Gebietsschemas, wenn ein Gebietsschema angegeben wird. Zum Beispiel, wenn <code>\"ar\"<\/code> an den Konstruktor \u00fcbergeben wird und dann <code>select(5)<\/code> auf dem zur\u00fcckgegebenen Objekt aufgerufen wird, gibt es <code>\"few\"<\/code> zur\u00fcck &#8211; die korrekte Form an dieser Stelle.<br \/>\nMit nur wenigen Zeilen Code hast du nun eine vollst\u00e4ndig globalisierte Pluralunterst\u00fctzung \ud83d\ude4c<\/p>\n<p style=\"text-align: center\" data-wp-editing=\"1\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15574 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/plural-forms-1024x372.png\" alt=\"Pluralisierung in Arabisch und Englisch | Phrase\" width=\"1024\" height=\"372\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/plural-forms-1024x372.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/plural-forms-300x109.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/plural-forms-768x279.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/plural-forms.png 1229w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><em>Unsere Pluralnachricht in Englisch und Arabisch, bei unterschiedlichen Z\u00e4hlungen<\/em><\/p>\n<h3><span class=\"ez-toc-section\" id=\"zahlenformatierung\"><\/span>Zahlenformatierung<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Dreihunderttausend Euro sind \u201e\u20ac300.000,00\u201c auf Englisch (Vereinigte Staaten), \u201e300.000,00 \u20ac\u201c auf Deutsch (Deutschland), \u201e\u20ac3,00,000.00\u201c auf Hindi (Indien) \u2013 man beachte die Kommas in letzterem \u2013 und \u201e\u0663\u0660\u0660\u066c\u0660\u0660\u0660\u066b\u0660\u0660 \u20ac\u201c auf Arabisch (\u00c4gypten). Wie gehen wir mit all diesen Formaten um? Keine Sorge; ein weiteres <code>Intl<\/code>-Objekt, das Teil des modernen JavaScript-Standards ist, kommt hier zu Hilfe. Der edle Retter: <code>Intl.NumberFormat<\/code>!<br \/>\nStell dir vor, wir sind angehende Unternehmer und wollen eine NFT-Tracking-Website mit Zahlen starten \u2013 klar, oder?<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;p\n  data-i18n-key=\"nyan-cat-price\"\n  data-i18n-opt='{\"price\": {\"number\" : 5300}}'\n&gt;\n  Nyan Cat (Official) NFT: {price}\n&lt;\/p&gt;\n<\/pre>\n<p>Unser <code>data-i18n-opt<\/code> identifiziert den Zahlenwert des <code>{price}<\/code> in unseren Locale-Dateien. Wir suchen gleich nach dem <code>number<\/code>-Key, wenn wir unseren Interpolationscode aktualisieren. Zuerst lass uns unsere Nachrichten\u00fcbersetzungen bereitstellen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"15136bc5-7049-41a1-a400-c01c52f8df32\" data-enlighter-title=\"\/lang\/en.json\">{\n  \"nyan-cat-price\": \"Nyan Cat (Official) NFT: {price}\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"8be65390-6187-4d54-bf60-d3d7fb8bcf39\" data-enlighter-title=\"\/lang\/ar.json\">{\n  \"nyan-cat-price\": \"\u0646\u064a\u0627\u0646 \u0643\u0627\u062a NFT: {price}\"\n}\n<\/pre>\n<p>Okay &#8211; Lass uns unser JavaScript aktualisieren, damit es funktioniert.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"eebd5a57-2ac6-40b8-9138-1b9ffa458059\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"3,4,5,6,13,32-43\">\/\/ ...\nconst fullyQualifiedLocaleDefaults = {\n  en: \"en-US\",\n  ar: \"ar-EG\",\n};\n\/\/ ...\nfunction interpolate(message, interpolations) {\n  return Object.keys(interpolations).reduce(\n    (interpolated, key) =&gt; {\n      const value = formatNumber(interpolations[key]);\n      return interpolated.replace(\n        new RegExp(`{\\s*${key}\\s*}`, \"g\"),\n        value,\n      );\n    },\n    message,\n  );\n}\n\/*\n  Angenommen, es gibt ein Wertobjekt wie\n  {\n    \"number\" : 300000,\n    \"style\": \"currency\",\n    \"currency\": \"EUR\"\n  } und dass das aktive Gebietsschema \"en\" ist, gibt es \"\u20ac300,000.00\" zur\u00fcck.\n*\/\nfunction formatNumber(value) {\n  if (typeof value === \"object\" &amp;&amp; value.number) {\n    const { number, ...options } = value;\n    return new Intl.NumberFormat(\n      fullyQualifiedLocaleDefaults[locale],\n      options,\n    ).format(number);\n  } else {\n    return value;\n  }\n}\n\/\/ ...\n<\/pre>\n<p>Wenn wir unsere \u00fcbersetzten Nachrichten interpolieren, geben wir den Wert, den wir einf\u00fcgen, zun\u00e4chst durch einen Zahlenformatierer, der wiederum das eingebaute <code>Intl.NumberFormat<\/code> Objekt verwendet. Anders gesagt: Mit sehr wenigen Codezeilen haben wir die Zahlenformatierung lokalisiert.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15576 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-en-1024x450.png\" alt=\"Demo-App mit US-Zahlenformat | Phrase\" width=\"1024\" height=\"450\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-en-1024x450.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-en-300x132.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-en-768x337.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-en.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15575 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-ar-1024x450.png\" alt=\"Demo-App mit arabischer Zahlenformatierung | Phrase\" width=\"1024\" height=\"450\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-ar-1024x450.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-ar-300x132.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-ar-768x337.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-ar.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p>\u270b\ud83c\udffd <em>Hinweis \u00bb<\/em> Am besten gibst du ein vollst\u00e4ndig qualifiziertes Gebietsschema wie <code>\"en-US\"<\/code> an den <code>Intl.NumberFormat()<\/code>-Konstruktor weiter. Wenn wir nur einen Sprachcode \u00fcbergeben, wie <code>\"en\"<\/code>, entscheidet jeder Browser, welche <em>Region<\/em> verwendet wird, um die Zahlen zu formatieren: Ein Browser k\u00f6nnte standardm\u00e4\u00dfig <code>\"en-US\"<\/code> verwenden, w\u00e4hrend ein anderer <code>\"en-UK\"<\/code> w\u00e4hlt. Deshalb verwenden wir eine <code>fullyQualifiedLocaleDefaults<\/code>-Map in unserer <code>formatNumber()<\/code> Funktion, um eine einheitliche plattform\u00fcbergreifende Formatierung zu erhalten.<\/p>\n<p>Da wir alle Optionen, die in unserem Interpolationsobjekt definiert sind, an den <code>Intl.NumberFormat()<\/code> Konstruktor \u00fcbergeben, k\u00f6nnen wir jederzeit seine <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/NumberFormat\/NumberFormat#parameters\">zahlreichen Formatierungsoptionen<\/a> nutzen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;p\n  data-i18n-key=\"nyan-cat-price\"\n  data-i18n-opt='{\"price\": {\n    \"number\" : 5300,\n    \"style\": \"currency\",\n    \"currency\": \"EUR\"\n  }}'\n &gt;\n  Nyan Cat (Official) NFT: {price}\n&lt;\/p&gt;\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15578 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-en.png\" alt=\"Beispieltext US-Zahlenformat\u00a0| Phrase\" width=\"632\" height=\"94\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-en.png 632w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-en-300x45.png 300w\" sizes=\"auto, (max-width: 632px) 100vw, 632px\" \/><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15577 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-ar.png\" alt=\"Beispieltext Arabisches Zahlenformat\u00a0| Phrase\" width=\"420\" height=\"104\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-ar.png 420w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-ar-300x74.png 300w\" sizes=\"auto, (max-width: 420px) 100vw, 420px\" \/><\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Unser <a href=\"https:\/\/phrase.com\/blog\/posts\/number-localization\/\">Kurzleitfaden zur Zahlenlokalisierung<\/a> konnte dir gefallen.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"datumsformatierung\"><\/span>Datumsformatierung<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>\u00c4hnlich wie bei Zahlen ist die Datumsformatierung regional unterschiedlich. Ein Beispiel: Der 5. Dezember 2021 wird in seiner Kurzform als &#8222;12\/5\/2021&#8220; auf Englisch (US) und als &#8222;5.12.2021&#8220; auf Deutsch (Deutschland) formatiert. Und wieder kann ein praktisches integriertes <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/DateTimeFormat\/DateTimeFormat\">Intl.DateTimeFormat<\/a>-Objekt die schwere Arbeit bei der Datumsformatierung \u00fcbernehmen.<br \/>\nAngenommen, wir m\u00f6chten das Ver\u00f6ffentlichungsdatum und die Uhrzeit eines unserer Artikel anzeigen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;p\n  data-i18n-key=\"publish-date\"\n  data-i18n-opt='{\"publishDate\": {\n    \"date\": \"2021-12-05 15:29:00\"\n  }}'\n&gt;\n  Published on {publishDate}\n&lt;\/p&gt;\n<\/pre>\n<p>Der spezielle <code>date<\/code>-key in unserem <code>data-i18n-opt<\/code> Objekt enth\u00e4lt den Datums- und Uhrzeitwert, den wir formatieren m\u00f6chten. F\u00fcgen wir wie gewohnt unsere lokalisierten Nachrichten hinzu.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"2a53594b-f0b3-42fa-8a05-45366d8f704a\" data-enlighter-title=\"\/lang\/en.json\">{\n  \/\/ ...\n  \"publish-date\": \"Published {publishDate}\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"b7482e03-350d-4750-a944-9eeb661556a1\" data-enlighter-title=\"\/lang\/ar.json\">{\n  \/\/...\n  \"publish-date\": \"\u0646\u0634\u0631 {publishDate}\"\n}\n<\/pre>\n<p>Jetzt aktualisieren wir unser \u00dcbersetzungssystem, um nach <code>date<\/code>-Keys zu suchen und deren Werte als lokalisierte Daten zu formatieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"9f97809e-bb75-48e8-85ab-d99b7aee1937\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"6,7,8,30,31,32,34,35,37,38,39,40,41,42,43,44\">\/\/ ...\nfunction interpolate(message, interpolations) {\n  return Object.keys(interpolations).reduce(\n    (interpolated, key) =&gt; {\n      const value = formatDate(\n        formatNumber(interpolations[key]),\n      );\n      return interpolated.replace(\n        new RegExp(`{\\s*${key}\\s*}`, \"g\"),\n        value,\n      );\n    },\n    message,\n  );\n}\n\/\/ ...\n\/*\n  Angenommen, es gibt ein Wertobjekt wie\n  {\n    \"date\": \"2021-12-05 15:29:00\",\n    \"dateStyle\": \"long\",\n    \"timeStyle\": \"short\"\n  } und dass das aktuelle Gebietsschema 'en' ist,\n  gibt es  \"December 5, 2021 at 3:29 PM\" zur\u00fcck.\n*\/\nfunction formatDate(value) {\n  if (typeof value === \"object\" &amp;&amp; value.date) {\n    const { date, ...options } = value;\n    const parsedDate =\n      typeof date === \"string\" ? Date.parse(date) : date;\n    return new Intl.DateTimeFormat(\n      fullyQualifiedLocaleDefaults[locale],\n      options,\n    ).format(parsedDate);\n  } else {\n    return value;\n  }\n}\n\/\/ ...\n<\/pre>\n<p>Nachdem wir unser Wertobjekt an unseren Zahlen-Formatierer \u00fcbergeben haben, verwenden wir erneut unseren neuen Datums-Formatierer. Die Datumsformatierungsoptionen werden an den <code>Intl.DateTimeFormat<\/code>-Konstruktor \u00fcbergeben, was eine<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/DateTimeFormat\/DateTimeFormat#parameters\"> ziemlich gro\u00dfe Flexibilit\u00e4t beim Formatieren von Datumswerten<\/a> erm\u00f6glicht.<\/p>\n<p>\u270b\ud83c\udffd <em>Hinweis \u00bb<\/em> Wir verwenden oben <code>Date.parse()<\/code>, um sicherzustellen, dass unsere Zeichenfolge <code>date<\/code> in ein <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Date\">Date-Objekt<\/a> umgewandelt wird, sonst gibt <code>Intl.DateTimeFormat<\/code> einen Fehler aus.<\/p>\n<p>Damit haben wir die lokalisierte Datumsformatierung \ud83d\udc4d<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;p\n  data-i18n-key=\"publish-date\"\n  data-i18n-opt='{\"publishDate\": {\n    \"date\": \"2021-12-05 15:29:00\",\n    \"dateStyle\": \"long\",\n    \"timeStyle\": \"short\"\n  }}'\n&gt;\n  Published on {publishDate}\n&lt;\/p&gt;\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15816 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-en.png\" alt=\"Demo-App mit US-Datumsformatierung | Phrase\" width=\"1330\" height=\"696\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-en-300x157.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-en-1024x536.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-en-768x402.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15817 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-ar.png\" alt=\"Demo-App mit arabischer Datumsformatierung | Phrase\" width=\"1330\" height=\"696\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-ar-300x157.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-ar-1024x536.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-ar-768x402.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<p>\ud83e\udd3f <em>Mehr Info \u00bb<\/em> Wenn du nach robusteren Datumsformatierungsfunktionen suchst, schau dir unsere \u00dcbersicht an: <a href=\"https:\/\/phrase.com\/blog\/posts\/best-javascript-date-time-libraries\/\">Was ist die beste JavaScript-Datums- und Zeitbibliothek?<\/a> Und unser <a href=\"https:\/\/phrase.com\/blog\/posts\/a-human-friendly-way-to-display-dates-in-typescript-javascript\/\">Menschenfreundlicher Weg zu lesbaren Datumsanzeigen in TypeScript\/JavaScript<\/a> bietet dir Formatierungen wie \u201evor 1 Stunde\u201c.<\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Wenn du ein deklaratives Framework wie React verwendest, kannst du das, was wir in diesem Abschnitt erstellt haben, weiterf\u00fchren und <a href=\"https:\/\/phrase.com\/blog\/posts\/roll-your-own-javascript-i18n-library-with-typescript-part-1\/\">Eigene JavaScript-i18n-Bibliothek mit TypeScript entwickeln<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"welche-guten-javascript-i18n-bibliotheken-kannst-du-verwenden\"><\/span>Welche guten JavaScript-i18n-Bibliotheken kannst du verwenden?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Wir haben oben gezeigt, wie du deine eigene JavaScript-i18n-Bibliothek erstellst. Es k\u00f6nnte jedoch sinnvoller sein, f\u00fcr dein Projekt eine fertige i18n-Bibliothek zu \u00fcbernehmen. Hier gibt es keinen Mangel an Optionen, und in diesem Artikel schauen wir uns die Bibliotheken Polyglot, i18next und Globalize genauer an.<br \/>\nF\u00fcr eine noch gr\u00f6\u00dfere Auswahl helfen dir unsere beliebten Artikel, den richtigen Einstieg zu finden:<\/p>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/the-best-javascript-i18n-libraries\/\">Die besten JavaScript-I18n-Bibliotheken<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/best-javascript-date-time-libraries\/\">Was ist die beste JavaScript-Datum- und Zeitbibliothek?<\/a><\/li>\n<\/ul>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Wenn du mit legacy gettext arbeitest, schau dir die <a href=\"https:\/\/github.com\/messageformat\/Jed\">Jed library<\/a> an.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"wie-lokalisiere-ich-eine-webseite-mit-polyglot\"><\/span>Wie lokalisiere ich eine Webseite mit Polyglot?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p><a href=\"https:\/\/airbnb.io\/polyglot.js\/\">Polyglot<\/a> ist eine kleine i18n-Bibliothek, die von Airbnb gepflegt wird und einige Lokalisierungsprobleme l\u00f6st, die zuvor von JavaScripts Standardbibliotheken nicht unterst\u00fctzt wurden. Am bemerkenswertesten unter den Funktionen von Polyglot ist die hervorragende Handhabung der Mehrzahl. Wie wir schon gesagt haben, wird der jetzt eingebaute <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/PluralRules\/PluralRules\">Intl.PluralRules-Konstruktor<\/a> von allen modernen Browsern unterst\u00fctzt und l\u00f6st das Pluralisierungsproblem ganz einfach. Dennoch wurde in der letzten Version dieses Artikels Polyglot stark behandelt, also wollten wir diesen Abschnitt einf\u00fcgen, falls einige von euch immer noch einen Polyglot-Leitfaden wollen.<\/p>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Es sei denn, dein Anwendungsfall erfordert Polyglot, schau dir die alternative <a href=\"#How_do_I_localize_a_web_page_with_i18next\">i18next-Library im n\u00e4chsten Abschnitt<\/a> an, bevor du eine Lokalisierungsl\u00f6sung w\u00e4hlst.<\/p>\n<p>Also denn: Lass uns unsere kleine Demo-App mit Airbnbs Lokalisierungsbibliothek lokalisieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"125ae0d3-e7f7-4d16-b5c2-2b8f1ebc5198\" data-enlighter-title=\"public\/index.html\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;ul class=\"navbar-list navbar-start\"&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"home\" class=\"navbar-link\"&gt;\n              Home\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"about\" class=\"navbar-link\"&gt;\n              About\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n        &lt;\/ul&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;With Polyglot&lt;\/h1&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Cadence\"}'&gt;\n      Welcome to my little spot on the interwebs, %{username}!\n    &lt;\/p&gt;\n    &lt;p\n      data-i18n-key=\"article-plural\"\n      data-i18n-opt='{\"smart_count\": 2}'\n    &gt;\n      %{smart_count} articles written and counting.\n    &lt;\/p&gt;\n  &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15818 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-before-i18n.png\" alt=\"JavaScript-Demo-App mit Polyglot | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-before-i18n.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-before-i18n-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-before-i18n-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-before-i18n-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\nAlles klar, lass uns dieses Ding mit Polyglot lokalisieren.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"installation\"><\/span>Installation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Polyglot hat einige <a href=\"https:\/\/www.npmjs.com\/\">NPM<\/a>-Abh\u00e4ngigkeiten, also musst du <a href=\"https:\/\/nodejs.org\/en\/\">Node<\/a> lokal installieren. Mit Node kannst du eine <code>package.json<\/code>-Datei f\u00fcr deine Demo-App erstellen, indem du Folgendes in der Kommandozeile ausf\u00fchrst.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm init -y\n<\/pre>\n<p>Um unsere NPM-Abh\u00e4ngigkeiten in eine Datei zu b\u00fcndeln, die Browser lesen k\u00f6nnen, installieren wir das <a href=\"https:\/\/webpack.js.org\/\">Webpack Module Bundler<\/a> als Entwicklungsabh\u00e4ngigkeit. Der <a href=\"https:\/\/github.com\/webpack\/webpack-dev-server\">Webpack Dev Server<\/a> hilft dir beim Hot-Reload deines Bundles im Browser, w\u00e4hrend du entwickelst.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install --save-dev webpack webpack-cli webpack-dev-server\n<\/pre>\n<p>Alles klar, jetzt installiere den Star der Show: Polyglot.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install node-polyglot\n<\/pre>\n<p>Eine <code>index.js<\/code> wird als Einstiegspunkt f\u00fcr unsere App dienen, und wir k\u00f6nnen sie verwenden, um einen Praxistest der Bibliotheksinstallationen durchzuf\u00fchren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"13c36abe-a6bd-4f2f-a7d5-0661798eb598\" data-enlighter-title=\"src\/index.js\">import Polyglot from \"node-polyglot\";\nconsole.log({ Polyglot });\n<\/pre>\n<p>Ein <code>start<\/code>-Skript in unserer <code>package.json<\/code> wird unsere Entwicklung erleichtern, indem es den Entwicklungsserver mit unserer individuellen Konfiguration startet.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"d15750f8-71f2-491d-bf0e-ad4c16fc24bb\" data-enlighter-title=\"package.json\" data-enlighter-highlight=\"7\">{\n  \"name\": \"polyglot-demo\",\n  \/\/ ...\n  \"scripts\": {\n    \"start\": \"webpack-dev-server --config webpack.config.js\"\n  },\n  \/\/ ...\n  \"devDependencies\": {\n    \"webpack\": \"^5.65.0\",\n    \"webpack-cli\": \"^4.9.1\",\n    \"webpack-dev-server\": \"^4.6.0\"\n  },\n  \"dependencies\": {\n    \"node-polyglot\": \"^2.4.2\"\n  }\n}\n<\/pre>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Wir verwenden eine relativ einfache <code>webpack.config.js<\/code>, um unsere App zu b\u00fcndeln und den Entwicklungsserver zu konfigurieren. <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/blob\/main\/polyglot\/webpack.config.js\">Schau dir unser Git-Repository auf GitHub an<\/a>.<\/p>\n<p>Jetzt bringen wir unser geb\u00fcndeltes JS direkt vor dem <code>&lt;\/body&gt;<\/code>-Tag in unserer <code>public\/index.html<\/code>-Datei ein.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;script src=\".\/bundle.js\"&gt;&lt;\/script&gt;\n<\/pre>\n<p>Damit kannst du jetzt das <code>start<\/code>-Skript im Terminal ausf\u00fchren, um das Webpack Dev Server zu starten.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm start\n<\/pre>\n<p>Wenn alles gut l\u00e4uft, \u00f6ffnet sich unsere App automatisch im Browser. Wenn wir unsere Browser-Entwicklertools \u00f6ffnen, sollten wir Konsolenprotokolle sehen, die in etwa wie die folgenden aussehen.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15819 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-webpack-server-browser-output.png\" alt=\"Konsole der Browser-Entwicklertools | Phrase\" width=\"1430\" height=\"342\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-webpack-server-browser-output.png 1430w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-webpack-server-browser-output-300x72.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-webpack-server-browser-output-1024x245.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-webpack-server-browser-output-768x184.png 768w\" sizes=\"auto, (max-width: 1430px) 100vw, 1430px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"grundlegende-uebersetzungen\"><\/span>Grundlegende \u00dcbersetzungen<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Schauen wir uns die grundlegende Nutzung von Polyglot an. Hier ist das Rezept:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">\/\/ 1. Importiere die Bibliothek\nimport Polyglot from \"node-polyglot\";\n\/\/ 2. Erstelle eine Instanz\nconst polyglot = new Polyglot();\n\/\/ 3. F\u00fcge \u00dcbersetzungsnachrichten f\u00fcr das aktive Gebietsschema hinzu\npolyglot.extend({\n  \"app-title\": \"With Polyglot\",\n});\n\/\/ 4. Verwende die Texte, um Seitenelemente zu \u00fcbersetzen\nconst element = document.querySelector(\n  \"[data-i18n-key='app-title']\",\n);\n\/\/ polyglot.t() gibt eine \u00dcbersetzungsnachricht zur\u00fcck, die angegeben wurde\n\/\/ ein Key\nelement.innerHTML = polyglot.t(\"app-title\");\n<\/pre>\n<p>Um die Sprache zu wechseln, kannst du die Seite mit den neuen Spracheinstellungen neu laden.<\/p>\n<p>\u270b\ud83c\udffd <em>Hinweis \u00bb<\/em> Wir verwenden <code>innerHTML<\/code> in diesem Artikel, um den Inhalt eines Elements festzulegen. Sei vorsichtig mit diesem Attribut in der Produktion; stelle sicher, dass du jegliches HTML, das du mit <code>innerHTML<\/code> einf\u00fcgst, bereinigst, um XSS-(Cross-Site-Scripting-)Angriffe zu vermeiden.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-highlight=\"6\">import Polyglot from \"node-polyglot\";\nconst polyglot = new Polyglot();\npolyglot.extend({\n  \"app-title\": \"\u0645\u0639 \u0628\u0648\u0644\u064a\u062c\u0644\u0648\u062a\",\n});\nconst element = document.querySelector(\n  \"[data-i18n-key='app-title']\",\n);\nelement.innerHTML = polyglot.t(\"app-title\");\n<\/pre>\n<h3><span class=\"ez-toc-section\" id=\"asynchrones-laden-von-uebersetzungsdateien\"><\/span>Asynchrones Laden von \u00dcbersetzungsdateien<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>W\u00e4hrend der Code oben f\u00fcr die kleinsten Apps gut funktioniert, k\u00f6nnten wir es noch besser machen, indem wir unsere \u00dcbersetzungsdateien in separate JSON-Dateien pro Locale aufteilen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"d3cbfa7d-9584-422b-ae6b-da4ae0f6239e\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \"app-title\": \"With Polyglot\",\n  \"home\": \"Home\",\n  \"about\": \"About\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"33d548fc-48fe-4e4f-a852-51da5f540ca6\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \"app-title\": \"\u0645\u0639 \u0628\u0648\u0644\u064a\u062c\u0644\u0648\u062a\",\n  \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n  \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\"\n}\n<\/pre>\n<p>Jetzt kannst du eine Standardsprache f\u00fcr deine App konfigurieren und ihre \u00dcbersetzungen aus dem Netzwerk laden, wenn deine Seite geladen wird.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"72b66df6-d597-40ec-bbda-ef066a489055\" data-enlighter-title=\"src\/index.js\">import Polyglot from \"node-polyglot\";\nconst defaultLocale = \"en\";\nconst polyglot = new Polyglot();\n\/\/ Lade \u00dcbersetzungen aus dem Netzwerk\nasync function loadTranslations(locale) {\n  return await fetch(`\/lang\/${locale}.json`).then(\n    (response) =&gt; response.json(),\n  );\n}\n\/\/ \u00dcbersetze alle Elemente auf der Seite, die unser benutzerdefiniertes Attribut haben\n\/\/ data-i18n-key-Attribut\nfunction translatePage() {\n  const translatableElements = document.querySelectorAll(\n    \"[data-i18n-key]\",\n  );\n  translatableElements.forEach((el) =&gt; {\n    const key = el.getAttribute(\"data-i18n-key\");\n    el.innerHTML = polyglot.t(key);\n  });\n}\n\/\/ Init\n(async function () {\n  const translations = await loadTranslations(\n    defaultLocale,\n  );\n  polyglot.extend(translations);\n  translatePage();\n})();\n<\/pre>\n<p>Damit erhalten wir die folgende Darstellung im Browser.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15820 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-en.png\" alt=\"Englische Demo-App mit Polyglot sowie fehlenden Men\u00fcpunkten und Haupttitel | Phrase\" width=\"1430\" height=\"978\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-en.png 1430w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-en-300x205.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-en-1024x700.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-en-768x525.png 768w\" sizes=\"auto, (max-width: 1430px) 100vw, 1430px\" \/><br \/>\nUnsere Navigationsmen\u00fc-Punkte und der Haupttitel sind in unsere Standardsprache Englisch \u00fcbersetzt. Beachte jedoch die Polyglot-Fehler in der Konsole und wie die fehlenden Keys (<code>lead<\/code> und <code>article-plural<\/code>) den Wert der Keys selbst anzeigen. Wir f\u00fcgen gleich \u00dcbersetzungen f\u00fcr diese Keys hinzu, um das zu beheben.<br \/>\nWenn wir <code>defaultLocale<\/code> in <code>\"ar\"<\/code> \u00e4ndern, erhalten wir folgende Darstellung.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15821 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-ar.png\" alt=\"Arabische Demo-App mit Polyglot sowie fehlenden Men\u00fcpunkten und Haupttitel | Phrase\" width=\"1436\" height=\"978\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-ar.png 1436w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-ar-300x204.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-ar-1024x697.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-ar-768x523.png 768w\" sizes=\"auto, (max-width: 1436px) 100vw, 1436px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"sprachumschalter\"><\/span>Sprachumschalter<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Unsere App ist jetzt skalierbarer, da nur die \u00dcbersetzungen der aktiven Sprache geladen werden. Lass uns das nutzen, um einen Sprachumschalter zu bauen. Wir haben bereits das HTML f\u00fcr den Umschalter in unserer App:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"dc20fe15-b6c0-48a7-bca9-d5e1be114c97\" data-enlighter-title=\"public\/index.html\" data-enlighter-highlight=\"18,19,20,21\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n\t\t&lt;!-- ... --&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;!-- ... --&gt;\n  &lt;script src=\".\/bundle.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Das Einbinden dieses <code>&lt;select&gt;<\/code>-Elements aus unserem JavaScript erm\u00f6glicht es uns, unsere Sprachwechsel-Funktion hinzuzuf\u00fcgen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"07b2796c-fc57-4ec8-aa9a-398d7edf394f\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"11,12,14,16,17,21,22,23,24,26,28,29,30,31,34,35\">import Polyglot from \"node-polyglot\";\nconst defaultLocale = \"en\";\nconst polyglot = new Polyglot();\n\/\/ ...\n\/\/ Lade \u00dcbersetzungen f\u00fcr das angegebene Gebietsschema und \u00fcbersetze\n\/\/ die Seitenelemente f\u00fcr diese Sprache\nasync function loadAndTranslate(locale) {\n  const translations = await loadTranslations(locale);\n  polyglot.replace(translations);\n  translatePage();\n}\n\/\/ Jedes Mal, wenn der User die aktive Sprache wechselt, lade\n\/\/ die Nachrichten dieses Gebietsschemas in die Seite\nfunction bindLocaleSwitcher(initialValue) {\n  const switcher = document.querySelector(\n    \"[data-i18n-switcher]\",\n  );\n  switcher.value = initialValue;\n  switcher.onchange = (e) =&gt; {\n    loadAndTranslate(e.target.value);\n  };\n}\n\/\/ Init\nloadAndTranslate(defaultLocale);\nbindLocaleSwitcher(defaultLocale);\n<\/pre>\n<p>Wir haben den Code, der unsere \u00dcbersetzungsnachrichten l\u00e4dt und unsere Seiteninhalte \u00fcbersetzt, in eine wiederverwendbare <code>loadAndTranslate()&lt;1&gt;-Funktion umstrukturiert. Eine neue Funktion <code>bindLocaleSwitcher()<\/code> verbindet sich mit dem Sprachwechsler <code>&lt;select&gt;<\/code>; sie nutzt <code>loadAndTranslate()<\/code>, um die \u00dcbersetzungen basierend auf der von dir gew\u00e4hlten Sprache zu aktualisieren.<\/p>\n<p>\u270b\ud83c\udffd <em>Hinweis \u00bb<\/em> <code>polyglot.extend()<\/code> wird \u00dcbersetzungsnachrichten zu den bereits geladenen hinzuf\u00fcgen. Daher verwenden wir <code>polyglot.replace()<\/code>, um sicherzustellen, dass wir nur die \u00dcbersetzungen der aktiven Sprache laden.<\/p>\n<p>Das sollte unseren schicken Sprachumschalter zum Laufen bringen.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15822 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-lang-switcher.gif\" alt=\"Demo-App mit Polyglot und bearbeitetem Sprachumschalter | Phrase\" width=\"600\" height=\"217\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"interpolation-2\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Unser Haupttext enth\u00e4lt den Namen des aktuell angemeldeten Users (nat\u00fcrlich gemockt). Diese Art von interpoliertem Wert wird von Polyglot standardm\u00e4\u00dfig mit einer speziellen <code>%{variable}<\/code> Syntax behandelt.<\/p>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Du kannst die Zeichen, die interpolierte Werte kennzeichnen, mit der <code>interpolation<\/code> <a href=\"https:\/\/airbnb.io\/polyglot.js\/#options-overview\">-Option, die an den Polyglot-Konstruktor \u00fcbergeben wird<\/a>, \u00e4ndern.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"daf6cafc-7812-46e9-bcd7-963798dbd65f\" data-enlighter-title=\"public\/index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Cadence\"}'&gt;\n      Welcome to my little spot on the interwebs, %{username}!\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"a78a94fe-dc3d-48c0-bf90-185531c0411d\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \/\/ ...\n  \"lead\": \"Welcome to my little spot on the interwebs, %{username}!\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"7868539a-4ab0-4bdc-823e-614ea96bbd06\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \/\/ ...\n  \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a \u064a\u0627 %{username}.\"\n}\n<\/pre>\n<p>Ein paar Zeilen Code k\u00f6nnen zu unserer Funktion <code>translatePage()<\/code> hinzugef\u00fcgt werden, um Interpolationen zu ber\u00fccksichtigen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"5bd2109a-45be-407b-9277-b08354101d9c\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"13,14,15,16,17,18,20,21,22\">\/\/ ...\n\/\/ \u00dcbersetze alle Elemente auf der Seite, die ein\n\/\/ data-i18n-key-Attribut haben\nfunction translatePage() {\n  const translatableElements = document.querySelectorAll(\n    \"[data-i18n-key]\",\n  );\n  translatableElements.forEach((el) =&gt; {\n    const key = el.getAttribute(\"data-i18n-key\");\n    \/\/ Extrahiere Interpolations-Keys\/Werte aus dem HTML und\n    \/\/ in JSON umwandeln\n    const interpolations = el.getAttribute(\"data-i18n-opt\");\n    const parsedInterpolations = interpolations\n      ? JSON.parse(interpolations)\n      : {};\n    \/\/ \u00dcbergebe die analysierten Interpolationen an polyglot.t(),\n    \/\/ die automatisch Ersetzungen behandelt\n    el.innerHTML = polyglot.t(key, parsedInterpolations);\n  });\n}\n\/\/ ...\n<\/pre>\n<p>Mit dem obigen Code haben wir nun unseren interpolierten Einleitungsabsatz in der aktiven Sprache gerendert.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15824 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-en.png\" alt=\"Englischsprachige Demo-App mit Polyglot und festem Einleitungsabsatz | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15825 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-ar.png\" alt=\"Arabische Demo-App mit Polyglot und festem Einleitungsabsatz | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"pluralformen-2\"><\/span>Pluralformen<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Angenommen, wir m\u00f6chten dir als aktuell angemeldetem User zeigen, wie viele Nachrichten du erhalten hast: \u201eDu hast 1 neue Nachricht\u201c oder \u201eDu hast 12 neue Nachrichten\u201c, zum Beispiel. Polyglot setzt Pluralformen auf diese Weise problemlos um. Wir m\u00fcssen nur unsere \u00dcbersetzungen mit dem speziellen interpolierten Zahlenwert, <code>smart_count<\/code>, hinzuf\u00fcgen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"dbe32626-2aef-4da8-9c76-db1941115eff\" data-enlighter-title=\"public\/index.html\">&lt;!-- ... --&gt;\n    &lt;p\n      data-i18n-key=\"new-messages\"\n      data-i18n-opt='{\"smart_count\": 12}'\n    &gt;\n      You have %{smart_count} new messages\"\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>Polyglot verwendet <code>smart_count<\/code>, um je nach aktueller Sprache die korrekte Pluralform aus einer \u00dcbersetzungsnachricht auszuw\u00e4hlen. Pluralformen werden in unseren Nachrichten mit vier senkrechten Strichen <code>||||<\/code> getrennt. Englisch hat die Pluralformen <code>one<\/code> und <code>other<\/code>, und wir m\u00fcssen sie in der Reihenfolge bereitstellen:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"effada7e-fa5e-4980-bade-8326330e9360\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \/\/ ...\n  \"new-messages\": \"You have %{smart_count} new message |||| You have %{smart_count} new messages\"\n}\n<\/pre>\n<p>Arabisch hat sechs Pluralformen und wir f\u00fcgen sie unseren Nachrichten auf die gleiche Weise hinzu.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"e551b929-010f-4505-857f-566dad4d8275\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \/\/ ...\n  \"new-messages\": \"\u0644\u0627 \u062a\u0648\u062c\u062f \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629 |||| \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629 |||| \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u062a\u0627\u0646 \u062c\u062f\u0627\u062f |||| \u0644\u062f\u064a\u0643 %{smart_count} \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629 |||| \u0644\u062f\u064a\u0643 %{smart_count} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629 |||| \u0644\u062f\u064a\u0643 %{smart_count} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\"\n}\n<\/pre>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Schau dir den Abschnitt <a href=\"#Plurals\">Wie lokalisiere ich eine Webseite mit JavaScript? \u279e Pluralformen<\/a> an, um mehr \u00fcber Pluralformen zu erfahren.<\/p>\n<p>Eine weitere Sache: Standardm\u00e4\u00dfig ist Polyglot v\u00f6llig neutral im Bezug auf das aktive Gebietsschema. Daher kennt es die Pluralregeln des aktiven Gebietsschemas nicht, es sei denn, wir geben das Gebietsschema beim Laden ausdr\u00fccklich an.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"4ffe4b2d-4324-4b53-a227-3d0fa25322c6\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"8\">\/\/ ...\n\/\/ Lade \u00dcbersetzungen f\u00fcr das angegebene Gebietsschema und \u00fcbersetze\n\/\/ die Seitenelemente f\u00fcr diese Sprache\nasync function loadAndTranslate(locale) {\n  const translations = await loadTranslations(locale);\n  polyglot.locale(locale);\n  polyglot.replace(translations);\n  translatePage();\n}\n\/\/ ...\n<\/pre>\n<p>Das war's! Wir unterst\u00fctzen jetzt fortschrittliche Pluralformen in unserer App.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15826 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-en.png\" alt=\"Englische Locale-Demo-App mit Polyglot Smart Count | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15827 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-ar.png\" alt=\"Arabische Locale-Demo-App mit Polyglot Smart Count | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Hol dir den gesamten Code f\u00fcr unsere Polyglot-App von <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/polyglot\">unserem GitHub-Repo<\/a>.<\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Die <a href=\"https:\/\/airbnb.io\/polyglot.js\/\">offizielle Polyglot-Dokumentation<\/a> ist genauso kompakt wie die Bibliothek selbst.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"wie-lokalisiere-ich-eine-webseite-mit-i18next\"><\/span>Wie lokalisiere ich eine Webseite mit i18next?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>W\u00e4hrend ich dies schreibe, ist <a href=\"https:\/\/www.i18next.com\/\">i18next<\/a> eine der <a href=\"https:\/\/www.npmtrends.com\/globalize-vs-i18next-vs-node-polyglot-vs-react-intl\">beliebtesten JavaScript-i18n-Bibliotheken<\/a>. Die \u201e<a href=\"https:\/\/www.i18next.com\/#learn-once-translate-everywhere\">einmal lernen, \u00fcberall [verwenden]<\/a>\u201c Bibliothek funktioniert eigenst\u00e4ndig und mit einer Vielzahl von JavaScript-Frameworks. Ein reichhaltiges Plugin-\u00d6kosystem bedeutet, dass du oft nur eine NPM-Installation von der L\u00f6sung eines h\u00e4ufigen i18n-Problems entfernt bist. Das alles macht i18next zu einer einfachen Empfehlung.<br \/>\nAlles klar, genug Geschwafel. Lass uns unser kleines Demo noch einmal anschauen und es mit i18next lokalisieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"ebba1c2b-e62b-4423-8548-a8ccb4bcccdf\" data-enlighter-title=\"public\/index.html\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;ul class=\"navbar-list navbar-start\"&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"home\" class=\"navbar-link\"&gt;\n              Home\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"about\" class=\"navbar-link\"&gt;\n              About\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n        &lt;\/ul&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;With i18next&lt;\/h1&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Zelda\"}'&gt;\n      Welcome to my little spot on the interwebs, {{username}}!\n    &lt;\/p&gt;\n    &lt;p data-i18n-key=\"new-messages\" data-i18n-opt='{\"count\": 12}'&gt;\n      You have {{count}} new messages\n    &lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;script src=\".\/bundle.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Hier gibt es nicht viel Neues. Lass uns mit der Lokalisierung beginnen.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"installation-2\"><\/span>Installation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Wir verwenden <a href=\"https:\/\/nodejs.org\/en\/\">Node<\/a> und seinen <a href=\"https:\/\/www.npmjs.com\/\">NPM<\/a> Paket-Manager, um i18next zu installieren. Zuerst  erstellen wir die Datei <code>package.json<\/code>, um unsere Projektabh\u00e4ngigkeiten und NPM-Skripte zu verfolgen, indem wir Folgendes in der Kommandozeile ausf\u00fchren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm init -y\n<\/pre>\n<p>Der <a href=\"https:\/\/webpack.js.org\/\">Webpack<\/a>-Bundler erm\u00f6glicht es uns, i18next, seine Plugins und unser eigenes JavaScript zu b\u00fcndeln und in einer Datei im Browser bereitzustellen. Lass uns Webpack samt Entwicklungsserver installieren, der eine praktische Hot-Reload-Funktion hat, die die Entwicklung erleichtert:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install --save-dev webpack webpack-cli webpack-dev-server\n<\/pre>\n<p>Und vergiss unsere i18n-Bibliothek nicht:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install i18next\n<\/pre>\n<p>Ein praktisches <code>npm start<\/code>-Skript kann das Starten unseres Entwicklungsservers abk\u00fcrzen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"ff750f62-93c9-495b-8b54-ffae0aa84a85\" data-enlighter-title=\"package.json\" data-enlighter-highlight=\"7\">{\n  \"name\": \"i18next-demo\",\n  \/\/...\n  \"scripts\": {\n    \"start\": \"webpack-dev-server --config webpack.config.js\"\n  },\n  \/\/ ...\n  \"devDependencies\": {\n    \"webpack\": \"^5.65.0\",\n    \"webpack-cli\": \"^4.9.1\",\n    \"webpack-dev-server\": \"^4.6.0\"\n  },\n  \"dependencies\": {\n    \"i18next\": \"^21.6.3\"\n  }\n}\n<\/pre>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Wir verwenden eine relativ einfache <code>webpack.config.js<\/code>-Datei, um unsere App zu b\u00fcndeln und den Dev-Server zu konfigurieren. <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/blob\/main\/i18next\/webpack.config.js\">Schau sie dir in unserem Git-Repo auf GitHub an<\/a>.<\/p>\n<p>Lass uns einen <code>index.js<\/code>-Einstiegspunkt f\u00fcr unsere App erstellen und i18next einem kurzen Test unterziehen, um zu pr\u00fcfen, ob es richtig installiert ist.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"c346dcb9-2358-4eb7-b8a7-cbd6553cc9f1\" data-enlighter-title=\"src\/index.js\">import i18next from \"i18next\";\nconsole.log({ i18next });\n<\/pre>\n<p>Wenn du <code>npm start<\/code> in der Befehlszeile ausf\u00fchrst, startet der Webpack Dev Server und l\u00e4dt deine App automatisch im Browser.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15828 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-post-install.png\" alt=\"Demo-App mit Webpack-Entwicklungsserver geladen | Phrase\" width=\"1434\" height=\"1024\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-post-install.png 1434w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-post-install-300x214.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-post-install-1024x731.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-post-install-768x548.png 768w\" sizes=\"auto, (max-width: 1434px) 100vw, 1434px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"grundlegende-uebersetzungsnachrichten-2\"><\/span>Grundlegende \u00dcbersetzungsnachrichten<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>i18next ist flexibel darin, wie \u00dcbersetzungsnachrichten angenommen werden. Das meiste konfigurieren wir, w\u00e4hrend wir die Bibliothek mit <code>i18next.init(...)<\/code> initialisieren. Wir f\u00fcgen unsere \u00dcbersetzungsnachrichten direkt unter der Option <code>resources<\/code> ein.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"6b18cd08-103b-40a0-965c-4d0635e3b780\" data-enlighter-title=\"src\/index.js\">import i18next from \"i18next\";\ni18next.init({\n  \/\/ Das aktive Gebietsschema\n  lng: \"en\",\n  \/\/ aktiviert n\u00fctzliche Konsolenausgaben w\u00e4hrend der Entwicklung\n  debug: true,\n  \/\/ \u00dcbersetzungsnachrichten, nach Locale-Code zugeordnet\n  Ressourcen: {\n    en: {\n      \/\/ Standardm\u00e4\u00dfig erwartet i18next Nachrichten unter dem\n      \/\/ \"translation\" namespace\n      translation: {\n        \"app-title\": \"With Polyglot\",\n        home: \"Home\"\n        about: \"About\",\n      },\n    },\n    ar: {\n      translation: {\n        \"app-title\": \"\u0645\u0639 \u0628\u0648\u0644\u064a\u062c\u0644\u0648\u062a\",\n        \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n        \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\",\n      },\n    },\n  },\n});\n\/\/ Seitenelemente \u00fcbersetzen\nconst translatableElements = document.querySelectorAll(\n  \"[data-i18n-key]\",\n);\ntranslatableElements.forEach((el) =&gt; {\n  const key = el.getAttribute(\"data-i18n-key\");\n  el.innerHTML = i18next.t(key);\n});\n<\/pre>\n<p>Damit sollten wir beim Neuladen unserer App im Browser keine \u00c4nderungen sehen. Wenn du jedoch <code>lng<\/code> in <code>\"ar\"<\/code> \u00e4nderst, siehst du die folgenden arabischen \u00dcbersetzungen.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15880 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-basic-translations-1.png\" alt=\"Arabische Locale-Demo-App mit fehlendem Key | Phrase\" width=\"1434\" height=\"1270\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-basic-translations-1.png 1434w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-basic-translations-1-300x266.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-basic-translations-1-1024x907.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-basic-translations-1-768x680.png 768w\" sizes=\"auto, (max-width: 1434px) 100vw, 1434px\" \/><\/p>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Die <code>debug: true<\/code>-Option aktiviert sehr n\u00fctzliche Konsolenprotokolle im Browser. Beachte oben die Informationen zu fehlenden Keys, zum Beispiel.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"asynchrone-uebersetzungsladefunktion\"><\/span>Asynchrone \u00dcbersetzungs\u00adladefunktion<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Zukunftsorientierte Entwickler wie wir sollten unsere App skalierbarer machen, indem wir unsere \u00dcbersetzungen in separate Dateien aufteilen \u2013 eine pro Locale.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"c49398ad-5069-4bad-a35c-b2c6c85dd670\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \"app-title\": \"Mit i18next\"\n  \"home\": \"Home\",\n  \"about\": \"About\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"23ed2f14-52e9-41d3-979d-79e3fbd1c07d\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \"app-title\": \"\u0645\u0639 \u0622\u064a \u0623\u064a\u062a\u064a\u0646 \u0646\u064a\u0643\u0633\u062a\",\n  \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n  \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\"\n}\n<\/pre>\n<p>i18next ist ausgereift und hat viele Grundlagen abgedeckt. Deshalb m\u00fcssen wir keinen eigenen Code schreiben, um \u00dcbersetzungsdateien aus dem Netzwerk zu laden. Das <a href=\"https:\/\/github.com\/i18next\/i18next-http-backend\">offizielle HTTP-Backend<\/a> wird in die Bibliothek integriert und erledigt die gesamte Arbeit f\u00fcr uns. Lass es uns installieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install i18next-http-backend\n<\/pre>\n<p>Wir k\u00f6nnen jetzt das Backend in unsere <code>index.js<\/code> einbinden und <code>nutzen()<\/code>, w\u00e4hrend wir i18next initialisieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"f7604a6a-683e-43b1-b53b-e043f26d4a97\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"2,4,5,6,7,8,9,10,14,16,17,19,20,21,22,23,24,26,27,40,41,42,43\">import i18next from \"i18next\";\nimport HttpApi from \"i18next-http-backend\";\n\/\/ Wir machen die Funktion asynchron, damit wir warten k\u00f6nnen,\n\/\/ bis die \u00dcbersetzungsdatei aus dem Netzwerk\n\/\/ \u00fcbertragen wird.\nasync function initI18next() {\n  \/\/ Wir verwenden() das Backend und warten, bis es die\n  \/\/ \u00dcbersetzungen aus dem Netzwerk l\u00e4dt.\n  await i18next.use(HttpApi).init({\n    lng: \"en\",\n    debug: true,\n    \/\/ Entferne die eingebetteten `Ressourcen`\n    \/\/ Laden der Entwicklungssprache deaktivieren\n    fallbackLng: false,\n    \/\/ Http-Backend konfigurieren\n    backend: {\n      loadPath: \"\/lang\/{{lng}}.json\",\n    },\n  });\n}\n\/\/ Schnelle Umstrukturierung des Seiten\u00fcbersetzungscodes\n\/\/ zu einer Funktion\nfunction translatePageElements() {\n  const translatableElements = document.querySelectorAll(\n    \"[data-i18n-key]\",\n  );\n  translatableElements.forEach((el) =&gt; {\n    const key = el.getAttribute(\"data-i18n-key\");\n    el.innerHTML = i18next.t(key);\n  });\n}\n\/\/ Initialisierung\n(async function () {\n  await initI18next();\n  translatePageElements();\n})();\n<\/pre>\n<p>Die Option <code>backend.loadPath<\/code> \u00fcberschreibt den Standardpfad der \u00dcbersetzungsdatei des Backends. Ein spezieller <code>{{lng}}<\/code> Platzhalter wird durch das aktive Gebietsschema ersetzt. Ein Beispiel: Wenn unsere App zum ersten Mal geladen wird, sucht das Backend nach <code>\/lang\/en.json<\/code>, da wir die Standardsprache in unserer Konfiguration zuvor als <code>en<\/code> festgelegt haben.<br \/>\nDas war\u2019s auch schon. Unsere \u00dcbersetzungen werden jetzt aus dem Netzwerk geladen, anstatt in unseren Code eingebettet zu werden.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"unterstuetzte-sprachversionen-und-fallback-option\"><\/span>Unterst\u00fctzte Sprachversionen und Fallback-Option<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Oft m\u00f6chten wir eine Liste von Sprachen (Locales) angeben, die unsere App unterst\u00fctzt, sowie eine Sprache (Locale), auf die zur\u00fcckgegriffen wird, wenn eine \u00dcbersetzung fehlt. Du kannst die Konfigurationsoptionen <code>supportLngs<\/code> und <code>fallbackLng<\/code> von i18next verwenden, um das zu erreichen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"80f41c54-7e75-43b6-902e-a1e086c14cf1\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"7,8\">\/\/ ...\nasync function initI18next() {\n  await i18next.use(HttpApi).init({\n    lng: \"en\",\n    debug: true,\n    supportedLngs: [\"en\", \"ar\"],\n    fallbackLng: \"en\",\n    backend: {\n      loadPath: \"\/lang\/{{lng}}.json\",\n    },\n  });\n}\n\/\/ ...\n<\/pre>\n<p>\u270b\ud83c\udffd <em>Hinweis \u00bb<\/em> Die Fallback-Sprache wird <em>immer<\/em> geladen, unabh\u00e4ngig vom aktiven Gebietsschema.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"automatische-erkennung-deiner-sprache-und-region\"><\/span>Automatische Erkennung deiner Sprache und Region<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Es ist \u00fcblich, die Browsereinstellungen der User zu erkennen und deren Spracheinstellung zu verwenden, wenn wir sie unterst\u00fctzen. Das ist normalerweise etwas knifflig, aber i18next hat ein <a href=\"https:\/\/github.com\/i18next\/i18next-browser-languageDetector\">offizielles Plugin<\/a>, das uns hier schnell weiterhilft. Lass uns damit anfangen, es zu installieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install i18next-browser-languagedetector\n<\/pre>\n<p>\u00c4hnlich wie beim HTTP-Backend <code>importierst<\/code> du das Detektor-Plugin und <code>verwendest()<\/code> es bei der Initialisierung.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"8ffa7f23-6818-4876-8036-17aa252c47cf\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"3,8,14-16\" data-enlighter-linenumbers=\"false\">import i18next from \"i18next\";\nimport HttpApi from \"i18next-http-backend\";\nimport LanguageDetector from \"i18next-browser-languagedetector\";\nasync function initI18next() {\n  await i18next\n    .use(HttpApi)\n    .use(LanguageDetector)\n    .init({\n      debug: true,\n      supportedLngs: [\"en\", \"ar\"],\n      fallbackLng: \"en\",\n      \/\/ Erlaube, \"en\" zu verwenden f\u00fcr\n      \/\/ \"en-US\", \"en-CA\" usw.\n      nonExplicitSupportedLngs: true,\n      backend: {\n        loadPath: \"\/lang\/{{lng}}.json\",\n      },\n    });\n}\n\/\/ ...\n<\/pre>\n<p>Und das ist alles, was du brauchst, um mithilfe von i18next eine solide automatische Spracherkennung zu erstellen.<\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Du fragst dich vielleicht, welche Kriterien die Spracherkennung verwendet, um die Locale des Users zu bestimmen. Wir behandeln dies ausf\u00fchrlich in unserem <a href=\"https:\/\/phrase.com\/de\/blog\/posts\/localizing-react-apps-with-i18next\/#Automatically_Detecting_the_Users_Language\">Leitfaden zur React-Lokalisierung mit i18next<\/a>.<\/p>\n<p>\u270b\ud83c\udffd <em>Hinweis \u00bb<\/em> Die Sprach- und Regionserkennung speichert die erkannte Spracheinstellung standardm\u00e4\u00dfig im lokalen Speicher deines Browsers und verwendet <em>diesen<\/em> Wert, wenn du unsere Website erneut besuchst.<\/p>\n<p>\ud83e\udd3f <em>Mehr Info \u00bb<\/em> Wir haben einen speziellen Leitfaden zur <a href=\"https:\/\/phrase.com\/de\/blog\/posts\/detecting-a-users-locale\/\">Erkennung der Browsersprache mit JavaScript<\/a>, der dein Interesse wecken k\u00f6nnte.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"sprachumschalter-2\"><\/span>Sprachumschalter<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Die automatische Spracherkennung ist zwar praktisch, aber oft brauchst du eine Benutzeroberfl\u00e4che, um deine bevorzugte Sprache explizit festzulegen. Wir haben bereits das Markup f\u00fcr einen Sprachumschalter eingerichtet.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"0ae136e3-6aab-4d56-8a59-449d5efe8768\" data-enlighter-title=\"public\/index.html\" data-enlighter-highlight=\"18,19,20,21\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n      &lt;!-- ... --&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;script src=\".\/bundle.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Lass uns diesen HTML-Code nutzen und die Funktion <code>i18next.changeLanguage()<\/code> verwenden, um deine aktive Sprache auf die von dir gew\u00e4hlte Sprache einzustellen. Nachdem die lokalen Nachrichten geladen sind, kannst du <code>translatePageElements<\/code> anh\u00e4ngen, um die Seite mit den aktualisierten \u00dcbersetzungen neu zu rendern.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"799f4601-6390-44a3-ba97-66e0fd7f1412\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"3,4,5,6,8,10,11,12,13,14,15,21\">\/\/ ...\nfunction bindLocaleSwitcher(initialValue) {\n  const switcher = document.querySelector(\n    \"[data-i18n-switcher]\",\n  );\n  switcher.value = initialValue;\n  switcher.onchange = (e) =&gt; {\n    i18next\n      .changeLanguage(e.target.value)\n      .then(translatePageElements);\n  };\n}\n\/\/ Init\n(async function () {\n  await initI18next();\n  translatePageElements();\n  bindLocaleSwitcher(i18next.resolvedLanguage);\n})();\n<\/pre>\n<p>\u270b\ud83c\udffd <em>Hinweis \u00bb<\/em> Der Lokalisierungsdetektor k\u00f6nnte eine Sprache erkannt haben, die wir nicht unterst\u00fctzen, und <em>dieser<\/em> Wert wird in <code>i18next.language<\/code> vorhanden sein. Wir verwenden <code>i18next.resolvedLanguage<\/code>, um sicherzustellen, dass wir die aktive, <em>unterst\u00fctzte<\/em> Sprache beim Initialisieren unseres Sprachumschalters verwenden.<\/p>\n<p>Et voil\u00e0! Die Sprachwechsel-Benutzeroberfl\u00e4che ist fertig:<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15830 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-locale-switcher.gif\" alt=\"Polyglot Demo-App mit einer Sprachwechsel-Benutzeroberfl\u00e4che | Phrase\" width=\"600\" height=\"217\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"interpolation-3\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Standardm\u00e4\u00dfig verwendet i18next die <code>{{variable}}<\/code>-Syntax, um interpolierte Werte in \u00dcbersetzungen zu kennzeichnen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"5a3233fd-74a1-4c21-aec9-5a5dad9750a3\" data-enlighter-title=\"public\/index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Zelda\"}'&gt;\n      Welcome to my little spot on the interwebs, {{username}}!\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"9fedd78c-4683-4235-ba4e-4e91a3117bd9\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \/\/ ...\n  \"lead\": \"Welcome to my little spot on the interwebs, {{username}}!\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"7d7ce46a-079e-4239-91c3-3cf21ccf0310\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \/\/ ...\n  \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a \u064a\u0627 {{username}}.\"\n}\n<\/pre>\n<p>Wir k\u00f6nnen diese dynamischen Werte aus unseren HTML-Attributen abrufen und sie an <code>i18next.t()<\/code> \u00fcbergeben, das die Interpolation f\u00fcr uns \u00fcbernimmt.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"85eb09f1-0bcf-49d3-b44a-e803b326513f\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"11,12,13,14,16\">\/\/ ...\nfunction translatePageElements() {\n  const translatableElements = document.querySelectorAll(\n    \"[data-i18n-key]\",\n  );\n  translatableElements.forEach((el) =&gt; {\n    const key = el.getAttribute(\"data-i18n-key\");\n    const interpolations = el.getAttribute(\"data-i18n-opt\");\n    const parsedInterpolations = interpolations\n      ? JSON.parse(interpolations)\n      : {};\n    el.innerHTML = i18next.t(key, parsedInterpolations);\n  });\n}\n\/\/ ...\n<\/pre>\n<p>Damit werden unsere dynamischen Werte in unseren Nachrichten ersetzt.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15831 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-en.png\" alt=\"Demo-App mit Polyglot im englischen Sprachraum und interpoliertem Einleitungstext | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15832 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-ar.png\" alt=\"Demo-App mit Polyglot im arabischen Locale und interpoliertem Einleitungsabsatz | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"pluralformen-3\"><\/span>Pluralformen<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Unter der Haube versucht i18next, die Standard-<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/PluralRules\/PluralRules\">Intl.PluralRules<\/a> zu verwenden, um Pluralformen zu verwalten. Eine spezielle interpolierte <code>count<\/code>-Variable steuert die Wahl der Pluralform, abh\u00e4ngig von der aktiven Locale.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"6f1ac8ea-b04b-4ffa-97ff-0d5d961018fb\" data-enlighter-title=\"public\/index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n-key=\"new-messages\" data-i18n-opt='{\"count\": 12}'&gt;\n      You have {{count}} new messages\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>i18next verwendet eine <code>message_form<\/code> Konvention f\u00fcr plurale Nachrichten-Keys. Ein Beispiel: Um die <code>one<\/code> und <code>other<\/code> Formen in einer englischen <code>new-messages<\/code> \u00dcbersetzung zu behandeln, k\u00f6nnen wir Folgendes angeben.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"f5f7aa95-6e4d-4517-8245-ba0595110e31\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \/\/ ...\n  \"new-messages_one\": \"Du hast {{count}} neue Nachricht\"\n  \"new-messages_other\": \"Du hast {{count}} neue Nachrichten\"\n}\n<\/pre>\n<p>Arabisch hat sechs Pluralformen, und wir k\u00f6nnen sie auf \u00e4hnliche Weise angeben.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"a4211fe0-4e56-4b3c-a0de-83e9fb259bc9\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \/\/ ...\n  \"new-messages_zero\": \"\u0644\u0627 \u062a\u0648\u062c\u062f \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_one\": \"\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_two\": \"\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u062a\u0627\u0646 \u062c\u062f\u0627\u062f\",\n  \"new-messages_few\": \"\u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_many\": \"\u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_other\": \"\u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\"\n}\n<\/pre>\n<p>Mit wenig Aufwand kann unsere App Nachrichten im Plural anzeigen.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15833 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-en.png\" alt=\"Demo-App mit Polyglot in englischer Locale und Pluralisierung | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15834 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-ar.png\" alt=\"Demo-App mit Polyglot in arabischer Locale und Pluralisierung | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Hol dir den gesamten Code, den wir oben behandelt haben, aus unserem <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/i18next\">GitHub-Repo<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"wie-lokalisiere-ich-eine-react-angular-oder-vue-app\"><\/span>Wie lokalisiere ich eine React-, Angular- oder Vue-App?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>In den letzten Jahren haben deklarative Frameworks wie <a href=\"https:\/\/reactjs.org\/\">React<\/a>, <a href=\"https:\/\/angular.io\/\">Angular<\/a>, <a href=\"https:\/\/vuejs.org\/\">Vue.js<\/a> und andere die Frontend-Web-Welt im Sturm erobert. Wir behandeln diese Frameworks ausf\u00fchrlich in unserem Blog. Da React das beliebteste unter den gro\u00dfen Frameworks ist, geben wir dir hier gleich einen kurzen Leitfaden zur Lokalisierung von React-Apps. Und f\u00fcr andere deklarative Frameworks schau dir unsere folgenden ausf\u00fchrlichen Artikel an.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"angular-lokalisierungsartikel\"><\/span>Angular-Lokalisierungsartikel<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/angular-localization-i18n\/\">\u00dcbersetzen von Angular-Anwendungen mit dem integrierten I18n-Modul<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/best-libraries-for-angular-i18n\/\">Was ist die beste Angular-Bibliothek f\u00fcr Internationalisierung?<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/angular-l10n-with-i18next\/\">Angular L10n mit I18next<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/angular-10-tutorial-localization-transloco\/\">Angular 10 Tutorial zur Lokalisierung mit Transloco<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/full-stack-i18n-angular-net-core\/\">Full-Stack I18n mit Angular und .NET Core<\/a><\/li>\n<\/ul>\n<h3><span class=\"ez-toc-section\" id=\"lokalisierungsartikel-fuer-vuejs\"><\/span>Lokalisierungsartikel f\u00fcr Vue.js<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/de\/blog\/posts\/ultimate-guide-to-vue-localization-with-vue-i18n\/\">Der ultimative Vue-3-Lokalisierungsleitfaden<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/vue-translation-with-vue-i18next\/\">Tiefgehender Einblick: Vue-\u00dcbersetzung mit vue-i18next\u00a0<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/nuxt-js-tutorial-i18n\/\">Das einzige Nuxt.js-Tutorial zu I18n, das du jemals brauchen wirst<\/a> (Vue-basiert)<\/li>\n<\/ul>\n<h3><span class=\"ez-toc-section\" id=\"lokalisierungsartikel-fuer-andere-frameworks\"><\/span>Lokalisierungsartikel f\u00fcr andere Frameworks<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Uns ist bewusst, dass einige unserer Leser vielleicht Svelte, Next oder ein anderes Framework verwenden. Deshalb schreiben wir immer \u00fcber das Neueste und Beste. Hier ist eine Auswahl an Leitf\u00e4den, wie du die App, die du gerade in deinem Lieblings-Framework baust, lokalisieren kannst:<\/p>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/how-to-localize-a-svelte-app-with-svelte-i18n\/\">So lokalisierst du eine Svelte-App mit svelte-i18n<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/a-step-by-step-guide-to-svelte-localization-with-svelte-i18n-v3\/\">Ein Schritt-f\u00fcr-Schritt-Leitfaden zur Svelte-Lokalisierung mit svelte-i18n v3<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/\">So lokalisierst du SolidJS-Apps mit I18next<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-mithril-applications\/\">So lokalisierst du Mithril-Anwendungen<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-aureliajs-applications\/\">So lokalisierst du Apps mit dem Aurelia-Framework<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-stimulusjs-i18next\/\">Lokalisierung von StimulusJS-Anwendungen mit I18next<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/full-stack-javascript-i18n\/\">Full-Stack JavaScript I18n Schritt f\u00fcr Schritt<\/a> (mit Next.js und Sails.js)<\/li>\n<\/ul>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Genauer gesagt: Wenn du mit internationalen Telefonnummern arbeitest, schau dir die <a href=\"https:\/\/phrase.com\/blog\/posts\/libphonenumber-international-phone-numbers\/\">libphonenumber-Bibliothek<\/a> an. Sie ist framework-unabh\u00e4ngig!<\/p>\n<h2><span class=\"ez-toc-section\" id=\"wie-lokalisiere-ich-eine-react-app-mit-i18next\"><\/span>Wie lokalisiere ich eine React-App mit i18next?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Wie versprochen, werfen wir einen schnellen Blick darauf, wie du deine React-Apps mit der i18next-Bibliothek lokalisierst. Wir haben i18next bereits fr\u00fcher in diesem Artikel behandelt, daher konzentrieren wir uns hier auf die React-Integration.<\/p>\n<p>\ud83e\udd3f <em>Mehr Info \u00bb<\/em> <a href=\"https:\/\/phrase.com\/de\/blog\/posts\/localizing-react-apps-with-i18next\/\">Ein Leitfaden zur React-Lokalisierung mit i18next<\/a> deckt mehr ab und geht mehr in die Tiefe als unser kurzer \u00dcberblick hier.<\/p>\n<p>Zuerst nehmen wir unsere vertraute Demo-App und zerlegen sie in React-Komponenten.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"08d116ad-cce1-45d7-b2aa-3e30e3c99bf5\" data-enlighter-title=\"src\/App.js\">import \".\/App.css\";\nimport Navbar from \".\/layout\/Navbar\";\nfunction App() {\n  return (\n    &lt;div className=\"container\"&gt;\n      &lt;Navbar \/&gt;\n      &lt;h1&gt;React i18n&lt;\/h1&gt;\n      &lt;p&gt;\n        Welcome to my little spot on the interwebs, user\n      &lt;\/p&gt;\n      &lt;p&gt;You have count new messages&lt;\/p&gt;\n    &lt;\/div&gt;\n  );\n}\nexport default App;\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"08acb43f-245a-4d89-b514-85c2056fd238\" data-enlighter-title=\"src\/layout\/Navbar.js\">import LocaleSwitcher from \"..\/features\/LocaleSwitcher\";\nfunction Navbar() {\n  return (\n    &lt;nav className=\"navbar\"&gt;\n      &lt;div className=\"container\"&gt;\n        &lt;ul className=\"navbar-list navbar-start\"&gt;\n          &lt;li className=\"navbar-item\"&gt;\n            &lt;a href=\"#\" className=\"navbar-link\"&gt;\n              Home\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n          &lt;li className=\"navbar-item\"&gt;\n            &lt;a href=\"#\" className=\"navbar-link\"&gt;\n              About\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n        &lt;\/ul&gt;\n        &lt;div className=\"navbar-end\"&gt;\n          &lt;LocaleSwitcher \/&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n  );\n}\nexport default Navbar;\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"ebef0bfc-8f59-4073-ba13-411c09225f66\" data-enlighter-title=\"src\/features\/LocaleSwitcher.js\">function LocaleSwitcher() {\n  return (\n    &lt;&gt;\n      &lt;img\n        alt=\"Translation icon\"\n        src=\"img\/translation-icon@2x.png\"\n        className=\"translation-icon\"\n      \/&gt;\n      &lt;select className=\"locale-switcher\"&gt;\n        &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n        &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n      &lt;\/select&gt;\n    &lt;\/&gt;\n  );\n}\nexport default LocaleSwitcher;\n<\/pre>\n<p>Der Sprachumschalter tut momentan nicht viel, aber das \u00e4ndern wir bald. F\u00fcrs Erste haben wir einen guten Ausgangspunkt f\u00fcr die Lokalisierung.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15836 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-starter.png\" alt=\"React-Demo-App auf Englisch mit i18n-Bibliothek | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-starter.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-starter-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-starter-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-starter-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"installation-der-bibliothek\"><\/span>Installation der Bibliothek<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Zus\u00e4tzlich zu i18next nutzen wir das offizielle <a href=\"https:\/\/react.i18next.com\/\">react-i18next <\/a>-Integrationsframework, das die Verwendung von i18next mit React zum Kinderspiel macht. Wechsle ins Projektverzeichnis und f\u00fchre folgenden Befehl in der Kommandozeile aus, um die Bibliotheken zu installieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install i18next react-i18next\n<\/pre>\n<p>Als N\u00e4chstes initialisieren wir i18next und <code>verwende()<\/code> die React-Integration, wie wir es machen. Der grundlegendste Weg, i18next \u00dcbersetzungen bereitzustellen, besteht darin, die <code>resources<\/code> Option bei der Initialisierung zu verwenden.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"89a353c7-4c36-417b-a10f-68d97405c398\" data-enlighter-title=\"src\/services\/i18n.js\">import i18next from \"i18next\";\nimport { initReactI18next } from \"react-i18next\";\ni18next.use(initReactI18next).init({\n  Ressourcen: {\n    en: {\n      translation: {\n        \"app-title\": \"With React\",\n      },\n    },\n    ar: {\n      translation: {\n        \"app-title\": \"\u0645\u0639 \u0631\u064a\u0623\u0643\u062a\",\n      },\n    },\n  },\n  lng: \"en\",\n  debug: true,\n  interpolation: {\n    escapeValue: false,\n  },\n});\nexport default i18next;\n<\/pre>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Wir setzen die <code>interpolation.escapeValue<\/code> auf <code>false<\/code>, um <a href=\"https:\/\/www.i18next.com\/translation-function\/interpolation#unescape\">die standardm\u00e4\u00dfige Escapierung, die i18next zum Schutz vor XSS-Angriffen durchf\u00fchrt, zu deaktivieren<\/a>, da React das ohnehin f\u00fcr uns erledigt.<\/p>\n<p>Ziehen wir unser Modul in unser Stammverzeichnis <code>index.js<\/code>, damit wir i18next initialisieren k\u00f6nnen, wenn unsere App geladen wird.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"e0298a27-1532-4aa8-a85d-17ce5bf38526\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"5\">import React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport \".\/lib\/skeleton\/normalize.css\";\nimport \".\/lib\/skeleton\/skeleton.css\";\nimport \".\/services\/i18n\";\nimport App from \".\/App\";\nimport reportWebVitals from \".\/reportWebVitals\";\nReactDOM.render(\n  &lt;React.StrictMode&gt;\n    &lt;App \/&gt;\n  &lt;\/React.StrictMode&gt;,\n  document.getElementById(\"root\"),\n);\n\/\/ ...\n<\/pre>\n<h3><span class=\"ez-toc-section\" id=\"grundlegende-uebersetzungen-2\"><\/span>Grundlegende \u00dcbersetzungen<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Die vertraute <code>i18next.t()<\/code>-\u00dcbersetzungsfunktion kannst du auch in unseren React-Komponenten verwenden. Wir m\u00fcssen nur den <code>useTranslation<\/code> React Hook importieren, damit <code>t<\/code> verf\u00fcgbar ist.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-highlight=\"1,6,12\">import { useTranslation } from \"react-i18next\";\nimport Navbar from \".\/layout\/Navbar\";\nimport \".\/App.css\";\nfunction App() {\n  const { t } = useTranslation();\n  return (\n    &lt;div className=\"container\"&gt;\n      &lt;Navbar \/&gt;\n      &lt;h1&gt;{t(\"app-title\")}&lt;\/h1&gt;\n      \/\/ ...\n    &lt;\/div&gt;\n  );\n}\nexport default App;\n<\/pre>\n<p>Wenn du die App neu l\u00e4dst, sieht alles genauso aus wie vorher. Wenn du jedoch den Wert <code>lng<\/code> in unserem Initialisierer <code>src\/services\/i18n.js<\/code> auf <code>\"ar\"<\/code> \u00e4nderst, solltest du sehen, dass der App-Titel auf Arabisch lokalisiert wird.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15837 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-basic-ar.png\" alt=\"React-Demo-App mit i18n-Bibliothek und arabischem Titel | Phrase \" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-basic-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-basic-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-basic-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-basic-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"asynchrones-laden-von-uebersetzungsdateien-2\"><\/span>Asynchrones Laden von \u00dcbersetzungsdateien<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Mach unsere App skalierbarer, indem du unsere \u00dcbersetzungen in sprachspezifische Dateien aufteilst. Das praktische, offizielle <a href=\"https:\/\/github.com\/i18next\/i18next-http-backend\">i18next-http-backend<\/a> Plugin macht das f\u00fcr uns zu einer schnellen Angelegenheit. Lass es uns installieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install i18next-http-backend\n<\/pre>\n<p>Das Backend wird standardm\u00e4\u00dfig nach Dateien unter <code>\/locales\/{{lng}}\/{{ns}}.json<\/code> suchen, wobei <code>{{lng}}<\/code> die aktive Sprache aufl\u00f6st und <code>{{ns}}<\/code> den aktiven Namensraum aufl\u00f6st. Da der Standardnamensraum <code>translation<\/code> ist, k\u00f6nnen wir unsere englischen \u00dcbersetzungen in <code>public\/locales\/en\/translation.json<\/code> ablegen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"22ff6381-59c4-4834-85c2-b2485183bb89\" data-enlighter-title=\"public\/locales\/en\/translation.json\">{\n  \"app-title\": \"With React\",\n  \"home\": \"Home\",\n  \"about\": \"About\"\n}\n<\/pre>\n<p>Unsere arabische Datei folgt derselben Konvention:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"081af770-67c2-44e7-a080-d8082020f2ec\" data-enlighter-title=\"public\/locales\/ar\/translation.json\">{\n  \"app-title\": \"\u0645\u0639 \u0631\u064a\u0623\u0643\u062a\",\n  \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n  \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\"\n}\n<\/pre>\n<p>Wir m\u00fcssen jetzt nur noch das Backend importieren und <code>verwenden()<\/code>, wenn wir i18next initialisieren. Wir sollten auch unsere Inline-\u00dcbersetzungen unter dem <code>resources<\/code>-Key entfernen, da das Plugin jetzt unsere \u00dcbersetzungsnachrichten aus dem Netzwerk l\u00e4dt.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"a8be33ed-ee58-4094-849b-8c996a1750fa\" data-enlighter-title=\"src\/services\/i18n.js\" data-enlighter-highlight=\"3,7,10\">import i18next from \"i18next\";\nimport { initReactI18next } from \"react-i18next\";\nimport HttpApi from \"i18next-http-backend\";\ni18next\n  .use(initReactI18next)\n  .use(HttpApi)\n  .init({\n    \/\/ Entferne `resources`\n    lng: \"en\",\n    debug: true,\n    interpolation: {\n      escapeValue: false,\n    },\n  });\nexport default i18next;\n<\/pre>\n<p>Wenn du die App neu l\u00e4dst, solltest du exakt dieselbe lokalisierte Darstellung sehen. Nat\u00fcrlich werden die \u00dcbersetzungen der aktiven Sprache jetzt \u00fcber das Netzwerk geladen, sodass deine App schneller l\u00e4dt und leichter zu skalieren und zu warten ist.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15838 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-async.png\" alt=\"React-Demo-App mit asynchronem Laden von \u00dcbersetzungsdateien | Phrase\" width=\"1434\" height=\"1234\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-async.png 1434w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-async-300x258.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-async-1024x881.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-async-768x661.png 768w\" sizes=\"auto, (max-width: 1434px) 100vw, 1434px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"sprachumschalter-3\"><\/span>Sprachumschalter<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Eine Sprachwechsel-UI baust du mit React und i18next ganz einfach. Wir k\u00f6nnen unsere <code>LocaleSwitcher<\/code>-Komponente aktualisieren und das <code>&lt;select&gt;<\/code> darin so steuern, dass die aktive Sprache auf die von dir gew\u00e4hlte ge\u00e4ndert wird.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"686af5ef-bb97-4677-b31f-3e6b0710d4f7\" data-enlighter-title=\"src\/features\/LocaleSwitcher.js\" data-enlighter-highlight=\"1,4,16,17,18,19\">import { useTranslation } from \"react-i18next\";\nfunction LocaleSwitcher() {\n  const { i18n } = useTranslation();\n  return (\n    &lt;&gt;\n      &lt;img\n        src=\"img\/translation-icon@2x.png\"\n        alt=\"Translation icon\"\n        className=\"translation-icon\"\n      \/&gt;\n      &lt;select\n        className=\"locale-switcher\"\n        value={i18n.language}\n        onChange={(e) =&gt;\n          i18n.changeLanguage(e.target.value)\n        }\n      &gt;\n        &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n        &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n      &lt;\/select&gt;\n    &lt;\/&gt;\n  );\n}\nexport default LocaleSwitcher;\n<\/pre>\n<p>Die i18next React-Integration stellt sicher, dass die \u00dcbersetzungen neu gerendert werden, wenn die aktive Sprache ge\u00e4ndert wird.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15839 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-lang-switcher.gif\" alt=\"React-Demo-App mit funktionierendem Sprachumschalter | Phrase\" width=\"600\" height=\"217\" \/><\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Hol dir den Code f\u00fcr alles, was wir oben gebaut haben, aus <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/react\">unserem GitHub-Repository<\/a>.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"artikel-zur-lokalisierung-mit-react\"><\/span>Artikel zur Lokalisierung mit React<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Wir lieben es, in unserem Blog \u00fcber React zu schreiben, deshalb freuen wir uns, dir eine Auswahl unserer tiefgehenden Artikel und React-basierten Framework-Tutorials rund um das Thema Lokalisierung zu zeigen:<\/p>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/de\/blog\/posts\/localizing-react-apps-with-i18next\/\">Ein Leitfaden f\u00fcr die React-Lokalisierung mit i18next<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/de\/blog\/posts\/localizing-react-apps-with-i18next\/\">Einf\u00fchrung in JavaScript I18n mit i18next und Moment.js<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/react-redux-tutorial-internationalization-with-react-i18n-redux\/\">React Redux Tutorial: Internationalisierung mit react-i18n-redux<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/roll-your-own-i18n-solution-react-redux\/\">Erstelle deine eigene i18n-L\u00f6sung mit React und Redux<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-javascript-react-apps-with-linguijs\/\">Lokalisierung von JavaScript- und React-Apps mit LinguiJS<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localized-server-side-rendering-with-react\/\">Lokalisierte serverseitige Darstellung mit React<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-meteor-applications-react\/\">Meteor-Anwendungen mit React lokalisieren<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/react-native-i18n-with-expo-and-i18next-part-1\/\">Ein umfassender Leitfaden zur Lokalisierung von React Native<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/i18n-with-gatsby\/\">Alles, was du \u00fcber i18n mit Gatsby wissen musst<\/a> (React-basiert)<\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/full-stack-javascript-i18n\/\">Full-Stack JavaScript I18n Schritt f\u00fcr Schritt<\/a> (mit dem React-basierten Next.js)<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"wie-lokalisiere-ich-eine-webseite-mit-jquery-und-i18next\"><\/span>Wie lokalisiere ich eine Webseite mit jQuery und i18next?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Auch wenn <a href=\"https:\/\/jquery.com\/\">jQuery<\/a> heute nicht mehr so angesagt ist wie vor ein paar Jahren, geh\u00f6rt es immer noch zu den beliebtesten JavaScript-Bibliotheken. Du wirst feststellen, dass das Lokalisieren von jQuery-Apps mit der <a href=\"https:\/\/www.i18next.com\/\">i18next<\/a> Bibliothek ziemlich einfach ist. Ein <a href=\"https:\/\/github.com\/i18next\/jquery-i18next\">offizielles i18next jQuery-Plugin<\/a> ist ganz einfach einzurichten, also lass uns damit unsere bew\u00e4hrte Demo-App lokalisieren.<\/p>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Wir haben <a href=\"#How_do_I_localize_a_web_page_with_i18next\">i18next ausf\u00fchrlicher<\/a> weiter oben in diesem Artikel behandelt, daher konzentrieren wir uns hier auf die jQuery-Integration.<\/p>\n<p>Wenn du mitgelesen hast, wird dir die folgende Starter-App bekannt vorkommen. Sie dient als gute Grundlage f\u00fcr unsere Lokalisierungsarbeit.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"93953e8f-9e89-4fa3-a120-0357ddc3d91b\" data-enlighter-title=\"index.html\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;ul class=\"navbar-list navbar-start\"&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n=\"home\" class=\"navbar-link\"&gt;\n              Home\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n=\"about\" class=\"navbar-link\"&gt;\n              About\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n        &lt;\/ul&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;h1 data-i18n=\"app-title\"&gt;jQuery i18n&lt;\/h1&gt;\n    &lt;p data-i18n=\"lead\" data-i18n-options='{\"username\": \"Jackie\"}'&gt;\n      Welcome to my little spot on the interwebs, {{username}}!\n    &lt;\/p&gt;\n    &lt;p data-i18n=\"new-messages\" data-i18n-options='{\"count\": 3}'&gt;\n      You have {{count}} new messages\n    &lt;\/p&gt;\n  &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Standardm\u00e4\u00dfig verwendet das i18next jQuery-Plugin <code>data-i18n<\/code> (nicht wie zuvor <code>data-i18n-key<\/code>) als \u00dcbersetzungs-Key. Das <a href=\"https:\/\/github.com\/i18next\/jquery-i18next#initialize-the-plugin\">kannst du in den Plugin-Optionen \u00e4ndern<\/a>.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15883 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-start.png\" alt=\"jQuery-Demo-App | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-start.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-start-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-start-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-start-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<p>Zeit f\u00fcr's Lokalisieren? Los geht's.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"installation-3\"><\/span>Installation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Der einfachste Weg, i18next und das jQuery-Plugin zu installieren, ist, ihre minifizierten Distributionsdateien herunterzuladen und sie in dein HTML einzubinden. Du kannst die Dateien an den folgenden Orten abrufen.<\/p>\n<ul>\n<li><a href=\"https:\/\/code.jquery.com\/jquery-3.6.0.min.js\">jQuery komprimiert<\/a><\/li>\n<li><a href=\"https:\/\/unpkg.com\/i18next\/dist\/umd\/i18next.min.js\">i18next minified<\/a><\/li>\n<li><a href=\"https:\/\/raw.githubusercontent.com\/i18next\/jquery-i18next\/master\/jquery-i18next.min.js\">jQuery i18next Plugin minifiziert<\/a><\/li>\n<\/ul>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Schau dir die <a href=\"https:\/\/github.com\/i18next\/jquery-i18next#introduction\">offizielle Dokumentation des i18next jQuery Plugins<\/a> auf GitHub an.<\/p>\n<p>Nachdem du die oben genannten Dateien heruntergeladen hast, kannst du sie in ein <code>js\/lib<\/code> Verzeichnis in deinem Projekt ablegen und in deine Haupt-HTML-Seite einbinden.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"699ed787-7851-4f9d-a589-affb7b514112\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"13,14,15,17,18\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n   &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;script src=\".\/js\/lib\/jquery-3.6.0.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/lib\/i18next.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/lib\/jquery-i18next.min.js\"&gt;&lt;\/script&gt;\n  &lt;!-- Unser benutzerdefiniertes JavaScript kommt gleich... --&gt;\n  &lt;script src=\".\/js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<h3><span class=\"ez-toc-section\" id=\"grundlegende-uebersetzungen-3\"><\/span>Grundlegende \u00dcbersetzungen<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Mit unseren installierten Bibliotheken k\u00f6nnen wir jetzt etwas Setup-Code schreiben, um die grundlegende Lokalisierungsarbeit zum Laufen zu bringen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"e193e879-0e13-4ea2-916d-6237d777b668\" data-enlighter-title=\"js\/scripts.js\">\/\/ i18next initialisieren\ni18next.init({\n  lng: \"en\",     \/\/ Ausgangssprache\n  debug: true,   \/\/ Bietet hilfreiche Konsolenmeldungen\n  resources: {   \/\/ \u00dcbersetzungen\n    en: {\n      translation: {\n        \"app-title\": \"jQuery + i18next\",\n      },\n    },\n    ar: {\n      translation: {\n        \"app-title\": \"\u062c\u064a \u0643\u0648\u064a\u0631\u064a + \u0622\u064a \u0625\u064a\u062a\u064a\u0646 \u0646\u064a\u0643\u0633\u062a\",\n      },\n    },\n  },\n});\n\/\/ i18next jQuery Plugin initialisieren\njqueryI18next.init(i18next, $);\n\/\/ Seitenelemente \u00fcbersetzen\n$(\"body\").localize();\n<\/pre>\n<p>Wir haben <code>i18next.init(...)<\/code> bereits fr\u00fcher in diesem Artikel behandelt. Beachte, dass wir hier auch einen <code>jQueryI18next.init(...)<\/code> Aufruf haben. Im einfachsten Fall \u00fcbernimmt die jQuery i18next Plugin <code>init<\/code>-Funktion die aktive <code>i18next<\/code> Instanz sowie einen Verweis auf das jQuery-Objekt <code>$<\/code>.<br \/>\nWenn das Plugin initialisiert wird, f\u00fcgt es eine <code>localize()<\/code>-Funktion zu jQuery hinzu. Wenn du <code>$(selector).localize()<\/code> aufrufst, lokalisiert das Plugin alle Elemente unter der ausgew\u00e4hlten Hierarchie. F\u00fcr jedes Element wird, wenn ein <code>data-i18n<\/code> Attribut gefunden wird, die entsprechende \u00dcbersetzung f\u00fcr die aktive Sprache eingef\u00fcgt.<br \/>\nEin Beispiel:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">\/\/ In unserem JavaScript\ni18next.init({\n  lng: \"en\",\n  Ressourcen: {\n    en: {\n      translation: {\n        \"app-title\": \"jQuery + i18next\",\n      },\n    },\n    ar: {\n      translation: {\n        \"app-title\": \"\u062c\u064a \u0643\u0648\u064a\u0631\u064a + \u0622\u064a \u0625\u064a\u062a\u064a\u0646 \u0646\u064a\u0643\u0633\u062a\",\n      },\n    },\n  },\n});\njqueryI18next.init(i18next, $);\n$(\"#main-title\").localize();\n\/\/ In unserem HTML\n&lt;h1 id=\"main-title\" data-i18n=\"app-title\"&gt;&lt;\/h1&gt;\n\/\/ Wird gerendert als\n&lt;h1 id=\"main-title\" data-i18n=\"app-title\"&gt;jQuery + i18next&lt;\/h1&gt;\n<\/pre>\n<p>Einfach und sch\u00f6n \ud83d\ude0a<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15884 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-en.png\" alt=\"jQuery-Demo-App mit geladener i18next-Bibliothek in englischer Sprache | Phrase\" width=\"1430\" height=\"1284\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-en.png 1430w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-en-300x269.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-en-1024x919.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-en-768x690.png 768w\" sizes=\"auto, (max-width: 1430px) 100vw, 1430px\" \/><\/div>\n<p>Und wenn wir unsere Ausgangssprache auf Arabisch \u00e4ndern, indem wir <code>lng<\/code> auf <code>\"ar\"<\/code> \u00e4ndern, erhalten wir stattdessen einen arabischen Titel.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15885 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-ar.png\" alt=\"jQuery-Demo-App mit geladener i18next-Bibliothek, in arabischer Lokalisierung | Phrase\" width=\"1434\" height=\"1280\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-ar.png 1434w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-ar-300x268.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-ar-1024x914.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-ar-768x686.png 768w\" sizes=\"auto, (max-width: 1434px) 100vw, 1434px\" \/><\/div>\n<h3><span class=\"ez-toc-section\" id=\"asynchrones-laden-von-uebersetzungsdateien-3\"><\/span>Asynchrones Laden von \u00dcbersetzungsdateien<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><em>\u201eWie w\u00e4re es, wenn wir unsere \u00dcbersetzungsdateien in separate Dateien aufteilen, eine pro Sprachvariante?\u201c<\/em>, h\u00f6re ich dich fragen. Keine Sorge: Das <a href=\"https:\/\/github.com\/i18next\/i18next-http-backend\">offizielle i18next HTTP-Backend-Plugin<\/a> deckt das ab.<br \/>\nUm das Plugin zu installieren, schnapp dir <a href=\"https:\/\/raw.githubusercontent.com\/i18next\/i18next-http-backend\/master\/i18nextHttpBackend.min.js\">das minimierte Verteilungsskript von GitHub<\/a> und leg es in dein <code>js\/lib<\/code> Verzeichnis. Und nat\u00fcrlich sollten wir es auch in unser HTML einf\u00fcgen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"11a726ed-213c-4c67-a446-3420034397dd\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"15\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n   &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;script src=\".\/js\/lib\/jquery-3.6.0.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/lib\/i18next.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/lib\/i18nextHttpBackend.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/lib\/jquery-i18next.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Jetzt k\u00f6nnen wir unsere App skalierbar machen, indem wir die \u00dcbersetzungen in separate JSON-Dateien auslagern.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"39d7927a-c499-4d2a-b8db-34d65015996c\" data-enlighter-title=\"locales\/en\/translation.json\">{\n  \"app-title\": \"Mit jQuery + i18next\"\n  \"home\": \"Home\",\n  \"about\": \"About\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"37d46e00-a79c-4812-82e9-a89ba2ee6aa5\" data-enlighter-title=\"locales\/ar\/translation.json\">{\n  \"app-title\": \"\u0645\u0639 \u062c\u064a \u0643\u0648\u064a\u0631\u064a \u0648 \u0622\u064a \u0625\u064a\u062a\u064a\u0646 \u0646\u064a\u0643\u0633\u062a\"\n  \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n  \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\"\n}\n<\/pre>\n<p>Wir m\u00fcssen unseren Setup-Code \u00fcberarbeiten, um bei der Initialisierung von i18next das HTTP-Plugin <code>use()<\/code> zu verwenden. Warte auch, bis die \u00dcbersetzungsdatei deiner Ausgangssprache heruntergeladen ist, bevor du versuchst, die Seitenelemente zu \u00fcbersetzen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"19886823-97b7-4f73-8fe9-7f1d0af23104\" data-enlighter-title=\"js\/scripts.js\" data-enlighter-highlight=\"1,2,3,4,5,8,9,21,22,23,24\">\/\/ Warten auf \u00dcbersetzungen, die \u00fcber das Netzwerk \u00fcbertragen werden,\n\/\/ bevor das jQuery-Plugin initialisiert wird.\nasync function initI18n() {\n  \/\/ Verwende das Http-Backend-Plugin, um \u00dcbersetzungen herunterzuladen\n  await i18next.use(i18nextHttpBackend).init({\n    lng: \"en\",\n  \/\/ Entferne die `resources`-Option, da unsere \u00dcbersetzungen\n  \/\/ sich jetzt in JSON-Dateien befinden.\n  });\n  jqueryI18next.init(i18next, $);\n}\n\/\/ Refaktorisieren zu Funktion\nfunction translatePage() {\n  $(\"body\").localize();\n}\n\/\/ Init\n(async function () {\n  \/\/ Warten, bis i18next initialisiert ist\n  \/\/ Seitenelemente \u00fcbersetzen\n  await initI18n();\n  translatePage();\n})();\n<\/pre>\n<p>Wenn wir unsere App neu laden, bemerken wir keinen Unterschied in der Ausgabe. Ein n\u00e4herer Blick auf den Netzwerk-Tab der Entwicklertools zeigt eine wartungsfreundlichere \u201eLade es herunter, wenn du es brauchst\u201c-L\u00f6sung f\u00fcr \u00dcbersetzungsdateien.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15886 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-async.png\" alt=\"jQuery-Demo-App mit asynchronem Laden von \u00dcbersetzungsdateien | Phrase\" width=\"1432\" height=\"1454\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-async.png 1432w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-async-295x300.png 295w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-async-1009x1024.png 1009w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-async-768x780.png 768w\" sizes=\"auto, (max-width: 1432px) 100vw, 1432px\" \/><\/div>\n<h3><span class=\"ez-toc-section\" id=\"sprachumschalter-4\"><\/span>Sprachumschalter<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Vielleicht ist dir aufgefallen, dass wir etwas HTML-Code haben, der wie eine Sprachumschalter-UI in unserer Demo aussieht.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"49b71021-1b5e-49ca-ac02-f2af3f97ab97\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"18,19,20,21\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;!-- ... --&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;!-- ... --&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Lass uns das <code>&lt;select&gt;<\/code>-Element oben so verkn\u00fcpfen, dass unsere App ihre \u00dcbersetzungen auf die vom User ausgew\u00e4hlte Sprache umschaltet.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"5b4b7415-ce8e-4c83-b94f-a48864669b90\" data-enlighter-title=\"js\/scripts.js\" data-enlighter-highlight=\"3,4,6,7,9,11,12,13,14,15,16,17,18,23\">\/\/ ...\nfunction bindLocaleSwitcher() {\n  const $switcher = $(\"[data-i18n-switcher]\");\n  \/\/ Anfangswert\n  $switcher.val(i18next.language);\n  $switcher.on(\"change\", async function () {\n    \/\/ Das \u00c4ndern der aktiven Sprache f\u00fchrt dazu, dass ihre\n    \/\/ \u00dcbersetzungen aus dem Netzwerk laden. Also\n    \/\/ warten wir auf das Laden, bevor wir\n    \/\/ Seitenelemente aktualisieren.\n    await i18next.changeLanguage($switcher.val());\n    translatePage();\n  });\n}\n(async function () {\n  await initI18n();\n  translatePage();\n  bindLocaleSwitcher();\n})();\n<\/pre>\n<p>Und schon haben wir einen funktionierenden Sprachumschalter.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15887 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-locale-switcher.gif\" alt=\"jQuery-Demo-App mit Sprachumschalter | Phrase\" width=\"600\" height=\"217\" \/><\/div>\n<h3><span class=\"ez-toc-section\" id=\"interpolation-4\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Der Umgang mit dynamischen Werten in unseren \u00dcbersetzungen ist mit i18next sofort verf\u00fcgbar. Wir m\u00fcssen nur eine <code>data-i18n-options<\/code> JSON-Map im Element bereitstellen, f\u00fcr das wir Interpolationen haben.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"7d27ccfb-de5d-4985-86c1-a33c28264797\" data-enlighter-title=\"index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n=\"lead\" data-i18n-options='{\"username\": \"Jackie\"}'&gt;\n      Welcome to my little spot on the interwebs, {{username}}!\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>Das i18next jQuery-Plugin sucht standardm\u00e4\u00dfig nicht nach <code>data-i18n-options<\/code>; wir m\u00fcssen seine <code>useOptionsAttr<\/code>-<a href=\"https:\/\/github.com\/i18next\/jquery-i18next#initialize-the-plugin\">Konfigurationsoption<\/a> verwenden, um die automatische Interpolation zu aktivieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"1094953e-ed26-4d2c-b79f-afb14132c03c\" data-enlighter-title=\"js\/scripts.js\" data-enlighter-highlight=\"4\">async function initI18n() {\n  await i18next.use(i18nextHttpBackend).init({ lng: \"en\" });\n  jqueryI18next.init(i18next, $, { useOptionsAttr: true });\n}\n\/\/ ...\n<\/pre>\n<p>Lass uns sicherstellen, dass wir den Platzhalter <code>{{variable}}<\/code>haben, den i18next in unseren \u00dcbersetzungsnachrichten erwartet.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"2fda0df5-e0a2-4e37-942c-b20d43bacf1a\" data-enlighter-title=\"locales\/en\/translation.json\" data-enlighter-highlight=\"5\">{\n  \"app-title\": \"Mit jQuery + i18next\"\n  \"home\": \"Home\",\n  \"about\": \"About\",\n  \"lead\": \"Welcome to my little spot on the interwebs, {{username}}!\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"8804283f-f7e0-4a9b-82a3-2e3b9d26fb58\" data-enlighter-title=\"locales\/ar\/translation.json\" data-enlighter-highlight=\"5\">{\n  \"app-title\": \"\u0645\u0639 \u062c\u064a \u0643\u0648\u064a\u0631\u064a \u0648 \u0622\u064a \u0625\u064a\u062a\u064a\u0646 \u0646\u064a\u0643\u0633\u062a\"\n  \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n  \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\",\n  \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a \u064a\u0627 {{username}}.\"\n}\n<\/pre>\n<p>Na also: Die Interpolationen wurden interpoliert.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15888 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-en.png\" alt=\"Englischer Einleitungsabsatz mit Interpolation | Phrase\" width=\"824\" height=\"98\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-en.png 824w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-en-300x36.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-en-768x91.png 768w\" sizes=\"auto, (max-width: 824px) 100vw, 824px\" \/><\/div>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15889 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-ar.png\" alt=\"Arabischer Einleitungsabsatz mit Interpolation | Phrase\" width=\"542\" height=\"102\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-ar.png 542w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-ar-300x56.png 300w\" sizes=\"auto, (max-width: 542px) 100vw, 542px\" \/><\/div>\n<h3><span class=\"ez-toc-section\" id=\"pluralformen-4\"><\/span>Pluralformen<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Wenn wir Pluralformen haben, verwenden wir dasselbe <code>data-i18n-options<\/code>-Attribut, um eine spezielle <code>count<\/code>-Z\u00e4hlervariable bereitzustellen.<\/p>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Schau dir den vorherigen Abschnitt <a href=\"#Plurals-3\">i18next \u279e Pluralformen<\/a> an, wenn du wissen willst, wie die Bibliothek mit Pluralformen arbeitet.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"9ba58e5c-c7d1-49b9-8baf-4acd07bade77\" data-enlighter-title=\"index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n=\"new-messages\" data-i18n-options='{\"count\": 3}'&gt;\n      You have {{count}} new messages\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>Wir verwenden eine <code>key_form<\/code>-Konvention, wenn wir unsere Pluralnachrichten angeben. Englisch hat zwei Pluralformen, <code>one<\/code> und <code>other<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"77d50ab2-ce8e-44a9-b61e-40b6146e4c19\" data-enlighter-title=\"locales\/en\/translation.json\">{\n  \/\/ ...\n  \"new-messages_one\": Du hast {{count}} neue Nachricht\n  \"new-messages_other\": \"Du hast {{count}} neue Nachrichten\"\n}\n<\/pre>\n<p>Arabisch hat sechs Pluralformen, und sie werden automatisch von i18next behandelt.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"8cf54801-115c-44bd-b70d-4be380c413c6\" data-enlighter-title=\"locales\/ar\/translation.json\">{\n  \/\/ ...\n  \"new-messages_zero\": \"\u0644\u0627 \u062a\u0648\u062c\u062f \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_one\": \"\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_two\": \"\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u062a\u0627\u0646 \u062c\u062f\u0627\u062f\",\n  \"new-messages_few\": \"\u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_many\": \"\u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\",\n  \u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\n}\n<\/pre>\n<p>So bekommt unsere App ohne zus\u00e4tzlichen Code eine komplexe Pluralunterst\u00fctzung.<\/p>\n<div><strong style=\"color: #ff6600\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15891 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-ar.png\" alt=\"Englische Sprachversion der jQuery-Demo-App mit Pluralisierung | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/strong><\/div>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15890 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-en.png\" alt=\"Englische Sprachversion der jQuery-Demo-App mit Pluralisierung | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Hol dir den <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/jquery\">voll funktionsf\u00e4higen Code f\u00fcr die obige App aus unserem GitHub-Repository<\/a>.<\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Wenn du nach einer Alternative zu i18next suchst, schau dir <a href=\"https:\/\/phrase.com\/blog\/posts\/jquery-i18n-the-advanced-guide\/\">jQuery i18n f\u00fcr Fortgeschrittene<\/a> an, der die <a href=\"https:\/\/github.com\/wikimedia\/jquery.i18n\">jQuery.i18n<\/a>-Bibliothek verwendet.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"wie-lokalisiere-ich-eine-webseite-mit-dem-icu-format-unter-verwendung-von-globalize\"><\/span>Wie lokalisiere ich eine Webseite mit dem ICU-Format unter Verwendung von Globalize?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Wenn du die nahezu vollst\u00e4ndige Lokalisierungsabdeckung der <a href=\"https:\/\/icu.unicode.org\/home\">International Components for Unicode (ICU)<\/a> und des Unicode <a href=\"https:\/\/cldr.unicode.org\/\">Common Locale Data Repository (CLDR)<\/a> in deiner JavaScript-App haben m\u00f6chtest, erledigt die <a href=\"https:\/\/github.com\/globalizejs\/globalize\">Globalize<\/a>-Bibliothek das auf jeden Fall. Lass uns gemeinsam betrachten, wie du Globalize installierst und damit unsere bescheidene Demo-App lokalisierst.<\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> <a href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/\">Der fehlende Leitfaden zum ICU-Nachrichtenformat<\/a> behandelt ICU und CLDR ausf\u00fchrlicher.<\/p>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Im Gegensatz zu vielen anderen Bibliotheken, die wir in diesem Artikel behandeln, unterst\u00fctzt Globalize <em>tats\u00e4chlich<\/em> Internet Explorer 9+.<\/p>\n<p><em>Welche Demo?<\/em>, fragst du. Unser zuverl\u00e4ssiger One-Pager nat\u00fcrlich.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"3120f7b6-18a4-4377-a423-5d0f2490a644\" data-enlighter-title=\"index.html\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;ul class=\"navbar-list navbar-start\"&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"home\" class=\"navbar-link\"&gt;\n              Home\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"about\" class=\"navbar-link\"&gt;\n              About\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n        &lt;\/ul&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;With Globalize&lt;\/h1&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Stella\"}'&gt;\n      Welcome to my little spot on the interwebs, {username}!\n    &lt;\/p&gt;\n    &lt;p data-i18n-key=\"new-messages\" data-i18n-opt='{\"count\": 100}'&gt;\n      You have # new messages\n    &lt;\/p&gt;\n  &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15892 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-start.png\" alt=\"Globalize Demo-App | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-start.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-start-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-start-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-start-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<p>Fangen wir der Lokalisierung an.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"installation-4\"><\/span>Installation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Globalize ist sehr modular, daher kann die Installation mit reinem JavaScript etwas umst\u00e4ndlich sein. Dennoch ist es ein relativ einfaches Projekt.<\/p>\n<ul>\n<li>Lade die <a href=\"https:\/\/github.com\/globalizejs\/globalize\/releases\">neueste Globalize-Version<\/a> herunter<\/li>\n<li>Lade die <a href=\"https:\/\/github.com\/rxaviers\/cldrjs\/releases\">neueste CLDR-Traverser (cldr.js)-Version<\/a> herunter<\/li>\n<li>Lade die <a href=\"https:\/\/github.com\/unicode-org\/cldr-json\/releases\">neueste CLDR JSON-Datenversion<\/a> herunter. Achte darauf, dass du die <code>-full<\/code> Variante aus der Ver\u00f6ffentlichungs\u00fcbersicht herunterl\u00e4dst, um hier mitmachen zu k\u00f6nnen.<\/li>\n<\/ul>\n<p>Das sollte uns drei ZIP-Dateien geben. Lass sie uns entpacken, ihre entpackten obersten Verzeichnisse umbenennen und in unser Projektverzeichnis verschieben. Ich habe meins unter dem Verzeichnis <code>\/lib<\/code> in meinem Projekt abgelegt, sodass mein Projekt jetzt so aussieht:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-linenumbers=\"false\">.\n\u251c\u2500\u2500 css\/\n\u251c\u2500\u2500 img\/\n\u251c\u2500\u2500 lib\/\n\u2502   \u251c\u2500\u2500 cldr\/          &lt;&lt; umbenannt von cldr-x.x.x\n\u2502   \u251c\u2500\u2500 cldr-json\/     &lt;&lt; umbenannt von cldr-x.x.x-json-full\n\u2502   \u2514\u2500\u2500 globalize\/     &lt;&lt; umbenannt von globalize.x.x\n\u2514\u2500\u2500 index.html\n<\/pre>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Die offizielle Dokumentation beschreibt <a href=\"https:\/\/github.com\/globalizejs\/globalize#installation\">verschiedene M\u00f6glichkeiten der Installation von Globalize<\/a>.<\/p>\n<h4>Verwendung des Anforderungstools zur Bestimmung der ben\u00f6tigten Skripte<\/h4>\n<p>Wir ben\u00f6tigen immer einige Teile von Globalize und cldr.js; andere h\u00e4ngen von den Lokalisierungsfunktionen unserer App ab. Ein praktisches Tool, <a href=\"https:\/\/johnnyreilly.github.io\/globalize-so-what-cha-want\/#\/?currency=true&amp;date=true&amp;message=true&amp;number=true&amp;plural=true&amp;relativeTime=true&amp;unit=true\">So What\u2019cha Want<\/a>, kann dir sagen, welche Dateien du je nach deinen ausgew\u00e4hlten Funktionen in dein Projekt holen solltest. Wir k\u00f6nnen beginnen, indem wir alle Funktionen abw\u00e4hlen, au\u00dfer <em>message<\/em>.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15893 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool.png\" alt=\"So What\u2019cha Want Globalize Screenshot | Phrase\" width=\"2856\" height=\"1262\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool.png 2856w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool-300x133.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool-1024x452.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool-768x339.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool-1536x679.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool-2048x905.png 2048w\" sizes=\"auto, (max-width: 2856px) 100vw, 2856px\" \/><\/div>\n<p>Beachte die beiden Dateilisten am unteren Ende der Seite. Die Liste auf der linken Seite zeigt an, welche Dateien von Globalize und cldr.js importiert werden sollen; wir k\u00f6nnen <code>&lt;script&gt;<\/code>-Tags daf\u00fcr verwenden.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"1079e969-94cb-435c-8773-0ad911048d64\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"13,14,15,16,17,18,20,21\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;!-- Globalize-Anforderungen --&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr\/event.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr\/supplemental.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/globalize\/dist\/globalize.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/globalize\/dist\/globalize\/message.js\"&gt;&lt;\/script&gt;\n  &lt;!-- Unser App-Einstiegspunkt --&gt;\n  &lt;script src=\".\/index.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Die Liste unten rechts von <em>So What'cha Want<\/em> enth\u00e4lt die CLDR JSON-Daten, die wir ben\u00f6tigen. Hol dir dieses JSON in deinen Code und gib es an Globalize \u00fcber die <code>load()<\/code>-Funktion weiter.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"819ea5ad-0a88-42b0-9f9a-e3245b463959\" data-enlighter-title=\"index.js\">\/\/ Wir f\u00fcgen nach und nach mehr zu dieser Liste hinzu\nconst supplementals = [\"likelySubtags\"];\nasync function loadIntoGlobalize(featureUrls) {\n  await Promise.all(\n    featureUrls.map((url) =&gt; fetchJson(url))\n  ).then((downloaded) =&gt;\n    downloaded.forEach((feature) =&gt; Globalize.load(feature))\n  );\n}\nfunction supplementalUrlsFor(options) {\n  return options.map(\n    (feature) =&gt;\n      `\/lib\/cldr-json\/cldr-core\/supplemental\/${feature}.json`\n  );\n}\nasync function fetchJson(url) {\n  const response = await fetch(url);\n  return await response.json();\n}\n(async function () {\n  \/\/ Lade erg\u00e4nzende Anforderungen\n  await loadIntoGlobalize(\n    supplementalUrlsFor(supplementals)\n  );\n})();\n<\/pre>\n<p>Das war's mit der Einrichtung. Okay, es ist nicht die einfachste Bibliothek zu installieren, aber f\u00fcr ein Projekt, das eine zuverl\u00e4ssige Lokalisierung ben\u00f6tigt, ist es die M\u00fche wert.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"grundlegende-uebersetzungen-4\"><\/span>Grundlegende \u00dcbersetzungen<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Die Verwendung von Globalize zur \u00dcbersetzung von Seitenelementen ist ein einfacher dreistufiger Prozess.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"7f6d347f-e2aa-4e6a-b077-f960eea2e6fa\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"8,9,10,11,12,13,14,15,16,18,19,21,22,23,24\">\/\/ ...\n(async function () {\n  await loadIntoGlobalize(\n    supplementalUrlsFor(supplementals)\n  );\n  \/\/ 1. Lade \u00dcbersetzungsnachrichten\n  Globalize.loadMessages({\n    en: {\n      \"app-title\": \"Hello Globalize!\",\n    },\n    ar: {\n      \"app-title\": \"\u0623\u0647\u0644\u0627\u064b \u062c\u0644\u0648\u0628\u0627\u0644\u0627\u064a\u0632\",\n    },\n  });\n  \/\/ 2. Setze die Standardsprache\n  Globalize.locale(\"en\");\n  \/\/ 3. Verwende formatMessage(), um die \u00dcbersetzung anhand des Keys zu erhalten\n  document.querySelector(\n    \"[data-i18n-key='app-title']\"\n  ).textContent = Globalize.formatMessage(\"app-title\");\n})();\n<\/pre>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Die offizielle Globalize-Dokumentation hat einen <a href=\"https:\/\/github.com\/globalizejs\/globalize#api\">praktischen API-Abschnitt<\/a>, der verf\u00fcgbare Funktionen auflistet.<\/p>\n<p>Cooool. Unser Titel wird mit der \u00dcbersetzung unserer Standardsprache angezeigt.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15894 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-en.png\" alt=\"Globalize Demo-App Englische \u00dcbersetzung | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<p>Wenn wir den obigen Aufruf \u00e4ndern, so dass er <code>Globalize.locale(\"ar\")<\/code> lautet, erhalten wir den Titel unserer App auf Arabisch.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15895 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-ar.png\" alt=\"Globalize Demo-App Arabische \u00dcbersetzung | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<h3><span class=\"ez-toc-section\" id=\"umgang-mit-fehlenden-nachrichten\"><\/span>Umgang mit fehlenden Nachrichten<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Verallgemeinere den Code, der Seitenelemente \u00fcbersetzt, indem du eine <code>translatePageElements()<\/code>-Funktion schreibst. Im Gegensatz zu anderen i18n-Bibliotheken l\u00f6st Globalize einen Fehler aus und stoppt, wenn bei einem bestimmten Key f\u00fcr <code>formatMessage()<\/code> eine Nachricht fehlt. Nichts, was ein wenig <code>try\/catch<\/code> nicht abmildern k\u00f6nnte ...<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"5ea79d29-3db0-486a-896a-e57c7423c91a\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"3,4,5,6,8,9,11,12,13,14,15,16,17,19,20,21,22,23,24,25,26,35\">\/\/ ...\nfunction translatePageElements() {\n  const elements = document.querySelectorAll(\n    \"[data-i18n-key]\"\n  );\n  elements.forEach((element) =&gt; {\n    const key = element.getAttribute(\"data-i18n-key\");\n    try {\n      element.innerHTML = Globalize.formatMessage(key);\n    } catch (error) {\n      if (error.code === \"E_MISSING_MESSAGE\") {\n        \/\/ Zeig Konsolenwarnungen bei fehlenden Nachrichten an,\n        \/\/ anstatt zum Stillstand zu kommen.\n        console.warn(error.message);\n        \/\/ Zeige den Key-Wert auf der Seite f\u00fcr eine fehlende Nachricht an.\n        element.innerHTML = key;\n      } else {\n        console.error(error);\n      }\n    }\n  });\n}\n(async function () {\n  \/\/ ...\n  Globalize.loadMessages(\/* ... *\/);\n  Globalize.locale(\"en\");\n  translatePageElements();\n})();\n<\/pre>\n<p>Weniger \"Absturz bei fehlender Nachricht\", und mehr \"Key-Wert anzeigen und in der Konsole warnen\".<\/p>\n<h3><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15910 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-missing-messages.png\" alt=\"Globalize Demo-App mit Warnung im Code | Phrase\" width=\"1432\" height=\"888\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-missing-messages.png 1432w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-missing-messages-300x186.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-missing-messages-1024x635.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-missing-messages-768x476.png 768w\" sizes=\"auto, (max-width: 1432px) 100vw, 1432px\" \/><\/h3>\n<h3><span class=\"ez-toc-section\" id=\"asynchrones-laden-von-uebersetzungsdateien-4\"><\/span>Asynchrones Laden von \u00dcbersetzungsdateien<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Wie wir es mit anderen L\u00f6sungen in diesem Artikel gemacht haben, lass uns unsere \u00dcbersetzungsnachrichten in JSON-Dateien pro Locale aufteilen, um Skalierbarkeit und Wartbarkeit zu gew\u00e4hrleisten.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"fb73dd42-e042-4d6a-9c1c-e96886cf5603\" data-enlighter-title=\"lang\/en.json\">{\n  \/\/ Globalize erwartet, dass der Sprachcode der oberste Key ist\n  \"en\": {\n    \"app-title\": \"Mit Globalize\"\n    \"home\": \"Home\",\n    \"about\": \"About\"\n  }\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"a710d68f-9adb-4364-b4ef-437ec0c6ff35\" data-enlighter-title=\"lang\/ar.json\">{\n  \"ar\": {\n    \"app-title\": \"\u0645\u0639 \u062c\u0644\u0648\u0628\u0627\u0644\u0627\u064a\u0632\",\n    \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n    \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\"\n  }\n}\n<\/pre>\n<p>Eine wiederverwendbare <code>setLocale()<\/code>-Funktion kann unsere \u00dcbersetzungsdatei laden, Globalize konfigurieren und unsere Seitenelemente neu rendern.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"346671e3-919a-4566-a041-8086dea23f25\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"1,5,6,7,8,9,10,19\">const defaultLocale = \"en\";\n\/\/ ...\n async function setLocale(locale) {\n  const messages = await fetchJson(`\/lang\/${locale}.json`);\n  Globalize.loadMessages(messages);\n  Globalize.locale(locale);\n  translatePageElements();\n}\n\/\/ ...\n(async function () {\n  await loadIntoGlobalize(\n    supplementalUrlsFor(supplementals)\n  );\n  setLocale(defaultLocale);\n})();\n<\/pre>\n<p>Asynchrones Laden der \u00dcbersetzungsdateien: erledigt.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"sprachumschalter-5\"><\/span>Sprachumschalter<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Wir k\u00f6nnen die Funktion <code>setLocale()<\/code>, die wir gerade geschrieben haben, verwenden, um unsere Sprachumschaltung zum Laufen zu bringen. Vermutlich erinnerst du dich, dass unsere Demo-App bereits etwas HTML-Code f\u00fcr den Umschalter hat ...<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"15ce0f80-f807-4c90-9fbd-22d73a8287f6\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"18,19,20,21\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;!-- ... --&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;!-- ... --&gt;\n  &lt;script src=\".\/index.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Ein bisschen JavaScript bringt den Umschalter zum Umschalten.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"5a823850-e4e9-473e-9653-d9a5438b1a71\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"5,6,7,8,10,11,13,14,15,16,25\">const defaultLocale = \"en\";\n\/\/ ...\nfunction bindLocaleSwitcher() {\n  const switcher = document.querySelector(\n    \"[data-i18n-switcher]\"\n  );\n  \/\/ Globalize.locale() gibt das aktive Gebietsschema zur\u00fcck.\n  switcher.value = Globalize.locale().locale;\n  switcher.onchange = (e) =&gt; {\n    setLocale(e.target.value);\n  };\n}\n(async function () {\n  await loadIntoGlobalize(\n    supplementalUrlsFor(supplementals)\n  );\n  await setLocale(defaultLocale);\n  bindLocaleSwitcher(defaultLocale);\n})();\n<\/pre>\n<p>Und damit kannst du die Sprache ausw\u00e4hlen, die du bevorzugst.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15896 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-locale-switcher.gif\" alt=\"Globalize-Demo-App mit Sprachumschalter | Phrase\" width=\"600\" height=\"217\" \/><\/div>\n<h4>Sprachbezeichnungen in der aktiven Sprache anzeigen<\/h4>\n<p>Eines der m\u00e4chtigsten Features, wenn du eine ICU-Bibliothek wie Globalize nutzt, ist der Zugriff auf eine immens gro\u00dfe Vielfalt an CLDR-Lokalisierungsdaten. Zum Beispiel k\u00f6nntest du die Sprachen in deinem Sprachumschalter <em>im aktiven Gebietsschema<\/em> anzeigen \u2013 Englisch w\u00e4re zum Beispiel \u201e\u0627\u0644\u0625\u0646\u062c\u0644\u064a\u0632\u064a\u0629\u201c auf Arabisch. Dazu m\u00fcssten wir die Haupt-CLDR-Daten importieren und dann unseren Text <code>select &gt; option<\/code> aktualisieren.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"eda6ed92-1c6d-4d7e-8d19-cbdab81843d6\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"2,3,4,11,16,19,20,21,22,23,24,25,26,27,28,29,30,32,33,34,35,36,37,38,40,41,43,44,45,46,48,49,51,52,53,54,55,56\">const defaultLocale = \"ar\";\nconst mains = {\n    localenames: [\"languages\"],\n};\nconst supplementals = \"likelySubtags\"];\nasync function setLocale(locale) {\n  const messages = await fetchJson(`\/lang\/${locale}.json`);\n  Globalize.loadMessages(messages);\n  await loadIntoGlobalize(mainUrlsFor(mains, locale));\n  Globalize.locale(locale);\n  translatePageElements();\n  setLocaleSwitcherDisplayNames();\n}\n\/\/ Given options = {\n\/\/   localenames: [\"languages\"],\n\/\/   Daten: [\"ca-generic\", \"ca-gregorian\"],\n\/\/ }\n\/\/ und locale = \"en\" gibt ein Array zur\u00fcck wie folgt\n\/\/ [\n\/\/    \"\/lib\/cldr-json\/cldr-localenames-full\/main\/en\/languages.json\",\n\/\/    \"\/lib\/cldr-json\/cldr-dates-full\/main\/en\/ca-generic.json\",\n\/\/    \"\/lib\/cldr-json\/cldr-dates-full\/main\/en\/ca-gregorian.json\"\n\/\/ ]\nfunction mainUrlsFor(options, locale) {\n  const result = [];\n  Object.keys(options).forEach((key) =&gt; {\n    options[key].forEach((collection) =&gt; {\n      result.push(\n        `\/lib\/cldr-json\/cldr-${key}-full\/main\/${locale}\/${collection}.json`\n      );\n    });\n  });\n  return result;\n}\nfunction setLocaleSwitcherDisplayNames() {\n  const options = document.querySelectorAll(\n    \"[data-i18n-switcher] option\"\n  );\n  options.forEach((option) =&gt; {\n    const localeCode = option.value;\n    \/\/ Hol dir die CLDR-Hauptdaten \u00fcber den Pfad\n    option.textContent = Globalize.cldr.main(\n      `localeDisplayNames\/languages\/${localeCode}`\n    );\n  });\n}\n\/\/ ...\n<\/pre>\n<p>Du kannst unseren Haupt-JSON-Ladecode verwenden, wann immer du die CLDR-JSON-Hauptdaten abrufen willst. Andernfalls sorgt ein wenig DOM-Manipulation daf\u00fcr, dass wir beim Ausw\u00e4hlen eines neuen Gebietsschemas lokalisierte Locale-Namen erhalten (so meta!).<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15897 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-locale-display-names.gif\" alt=\"Globalize-Demo-App mit lokalisierten Gebietsschema-Namen | Phrase\" width=\"600\" height=\"217\" \/><\/div>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Die offiziellen cldr.js-Dokumente erkl\u00e4ren ausf\u00fchrlicher, <a href=\"https:\/\/github.com\/rxaviers\/cldrjs#get-item-given-its-path\">wie man CLDR-JSON-Daten abrufen kann<\/a>.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"interpolation-5\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Die Verarbeitung dynamischer Werte in unserer Nachricht erfolgt \u00fcber das ICU-Nachrichtenformat mithilfe der Syntax <code>{variable}<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"1c1af1b3-a6c4-4113-9def-94a483d7cc7a\" data-enlighter-title=\"lang\/en.json\">{\n  \"en\": {\n    \/\/ ...\n    \"lead\": \"Welcome to my little spot on the interwebs, {username}!\"\n  }\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"175d373e-1962-4588-80c4-f65b80eff5d2\" data-enlighter-title=\"lang\/ar\">{\n  \"ar\": {\n    \/\/ ...\n    \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a \u064a\u0627 {username}.\"\n  }\n}\n<\/pre>\n<p>Dazu k\u00f6nnen wir Key\/Wert-Ersatzpaare in unserem HTML bereitstellen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"677e0228-8329-44a2-bcb6-63c6ee81b5c7\" data-enlighter-title=\"index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Stella\"}'&gt;\n      Welcome to my little spot on the interwebs, {username}!\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>Und jetzt m\u00fcssen wir nur noch diese Key\/Wert-Paare lesen und sie an <code>Globalize.formatMessage()<\/code> \u00fcbergeben.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"61ef9e83-97f1-453f-8037-ebf3044facce\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"11,12,13,14,15,20\">\/\/ ...\nfunction translatePageElements() {\n  const elements = document.querySelectorAll(\n    \"[data-i18n-key]\"\n  );\n  elements.forEach((element) =&gt; {\n    const key = element.getAttribute(\"data-i18n-key\");\n    const interpolations =\n      element.getAttribute(\"data-i18n-opt\");\n    const parsedInterpolations = interpolations\n      ? JSON.parse(interpolations)\n      : {};\n    try {\n      element.innerHTML = Globalize.formatMessage(\n        key,\n        parsedInterpolations\n      );\n    } catch (error) {\n      if (error.code === \"E_MISSING_MESSAGE\") {\n        console.warn(error.message);\n      } else {\n        console.error(error);\n      }\n    }\n  });\n}\n\/\/ ...\n<\/pre>\n<p>Kein Problem.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15898 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-en.png\" alt=\"Globalize-Demo-App mit Interpolation auf Englisch | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15899 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-ar.png\" alt=\"Globalize-Demo-App mit Interpolation im arabischen Gebietsschema | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<div><\/div>\n<h3><span class=\"ez-toc-section\" id=\"pluralformen-5\"><\/span>Pluralformen<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Die Handhabung von Pluralformen im ICU-Format ist un\u00fcbertroffen und deckt komplexe Pluralformen wie die in Russisch oder Arabisch ab. Nat\u00fcrlich m\u00fcssen wir die Pluralfunktion erst einrichten, bevor wir sie verwenden k\u00f6nnen.<\/p>\n<h4>Pluralanforderungen hinzuf\u00fcgen<\/h4>\n<p>Lass uns zu <a href=\"https:\/\/johnnyreilly.github.io\/globalize-so-what-cha-want\/#\/?currency=true&amp;date=true&amp;message=true&amp;number=true&amp;plural=true&amp;relativeTime=true&amp;unit=true\">So What\u2019cha Want<\/a> gehen, um herauszufinden, was wir einbeziehen m\u00fcssen, wenn wir <em>plural<\/em> aktivieren.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15900 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs.png\" alt=\"Screenshot So What\u2019cha Want f\u00fcr Globalize | Phrase\" width=\"2876\" height=\"1270\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs.png 2876w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs-300x132.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs-1024x452.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs-768x339.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs-1536x678.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs-2048x904.png 2048w\" sizes=\"auto, (max-width: 2876px) 100vw, 2876px\" \/><\/div>\n<p>Gar nicht schlecht. Zuerst m\u00fcssen wir ein <code>&lt;script&gt;<\/code>-Tag f\u00fcr die Funktion hinzuf\u00fcgen.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"83186151-0cf9-432c-95cf-e4e7b1dbaf29\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"18\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr\/event.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr\/supplemental.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/globalize\/dist\/globalize.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/globalize\/dist\/globalize\/message.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/globalize\/dist\/globalize\/plural.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/index.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Wir ben\u00f6tigen au\u00dferdem die erg\u00e4nzende JSON f\u00fcr <code>plurals<\/code> und wahrscheinlich auch die f\u00fcr <code>ordinals<\/code>, wenn wir Formatierungen wie \u201e3.\u201c und \u201e4.\u201c in unseren Nachrichten abdecken wollen. Wir m\u00fcssen nur die Anforderungen zu unserem <code>supplementals<\/code>-Konfigurationsarray hinzuf\u00fcgen. Unsere App ist bereits so eingerichtet, dass sie das entsprechende JSON f\u00fcr uns beim Start l\u00e4dt.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"1b2d9cb4-79c6-4aa9-960f-8d34d4764fc3\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"4,5\">const defaultLocale = \"ar\";\nconst supplementals = [\n  \"likelySubtags\",\n  \"plurals\",\n  \"ordinals\",\n];\n\/\/ ...\n<\/pre>\n<p>Jetzt k\u00f6nnen wir unsere Pluralnachrichten hinzuf\u00fcgen. Die ICU-Plural-Syntax ist weitgehend intuitiv: Eine <code>count<\/code>-Variable bestimmt die gew\u00e4hlte Pluralform, und ein spezielles <code>#<\/code>-Symbol wird beim Anzeigen durch den <code>count<\/code>-Wert ersetzt.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"188a8655-10bf-427b-b559-8905194c51e5\" data-enlighter-title=\"lang\/en.json\">{\n  \"en\": {\n    \/\/ ...\n    \"neue-nachrichten\": [\n      \/\/ Wir k\u00f6nnen `count` so nennen, wie wir wollen - solange wir\n      \/\/ den Key in unserem Aufruf von formatMessage() abstimmen.\n      \"You have {count, plural,\",\n      \"    one {# new message}\",\n      \"  other {# new messages}\",\n      \"}\"\n    ]\n  }\n}\n<\/pre>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Globalize erm\u00f6glicht es uns, mehrzeilige Nachrichten durch die Verwendung von Arrays aufzuteilen.<\/p>\n<p>Die sechs Pluralformen im Arabischen werden durch das ICU-Format behandelt.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"8039927b-6183-45cb-822a-b52ccadd2653\" data-enlighter-title=\"lang\/ar.json\" data-enlighter-linenumbers=\"false\">{\n  \"ar\": {\n    \/\/ ...\n    \"new-messages\": [\n      \"{count, plural, \",\n      \"   zero {\u0644\u0627 \u062a\u0648\u062c\u062f \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629}\",\n          one {\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629}\n          two {\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u062a\u0627\u0646 \u062c\u062f\u0627\u062f}\n          few {\u0644\u062f\u064a\u0643 # \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629}\n         many {\u0644\u062f\u064a\u0643 # \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629}\n      \"  other {\u0644\u062f\u064a\u0643 # \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629}\",\n      \"}\"\n    ]\n  }\n}\n<\/pre>\n<p>\ud83e\udd3f <em>Mehr Info \u00bb<\/em> Wir behandeln ICU-Mehrzahlformen ausf\u00fchrlicher in \"<a href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#Plurals\">Der fehlende Leitfaden zum ICU-Nachrichtenformat<\/a>\".<\/p>\n<p>Nat\u00fcrlich m\u00fcssen wir sicherstellen, dass <code>count<\/code> an <code>Globalize.formatMessage()<\/code> \u00fcbergeben wird.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"e9ab1b69-a2cd-44a1-95cf-06d075283840\" data-enlighter-title=\"index.html\" data-enlighter-linenumbers=\"false\">&lt;!-- ... --&gt;\n    &lt;p data-i18n-key=\"new-messages\" data-i18n-opt='{\"count\": 110}'&gt;\n      You have # new messages\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>Und damit sind die Pluralformen im Grunde erledigt.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15901 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-en.png\" alt=\"Globalize Demo-App auf Englisch mit Pluralisierung | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15902 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-ar.png\" alt=\"Globalize-Demo-App auf Arabisch mit Pluralisierung | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<div><\/div>\n<div>\ud83d\udd17 <em>Ressource \u00bb<\/em> Hol dir den gesamten Globalize-Demo-Code aus unserem <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/globalize\">GitHub-Repository<\/a>.<\/div>\n<p>\ud83d\uddd2 <em>Hinweis \u00bb<\/em> Die ICU-Implementierung von Globalize bietet umfassende Datums- und Zahlenformatierung. Schau dir die <a href=\"https:\/\/github.com\/globalizejs\/globalize#api\">offizielle Dokumentation<\/a> an, um mehr zu erfahren.<\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Wir behandeln weitere Globalize-Installationsoptionen und Anwendungsf\u00e4lle in <a href=\"https:\/\/phrase.com\/blog\/posts\/js-i18n-with-globalizejs\/\">JS I18n mit Globalize.js<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"damit-endet-unser-leitfaden-zur-javascript-lokalisierung\"><\/span>Damit endet unser Leitfaden zur JavaScript-Lokalisierung.<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Und das war's f\u00fcr dieses Mal. Wir hoffen, dir hat unser Ausflug in einige der beliebtesten JavaScript-Lokalisierungsl\u00f6sungen gefallen.<\/p>\n<p>\ud83d\udd17 <em>Ressource \u00bb<\/em> Wenn du mit Ruby on Rails arbeitest, k\u00f6nnte dir <a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-javascript-in-rails-apps\/\">JavaScript in Rails-Apps lokalisieren<\/a> einiges bringen.<\/p>\n<p>Und wenn du deinen Lokalisierungsprozess auf das n\u00e4chste Level bringen willst, schau dir Phrase an. Phrase unterst\u00fctzt das ICU-Format, alle anderen hier behandelten \u00dcbersetzungsformate und viele mehr. Mit seiner CLI und der Synchronisierung mit Bitbucket, GitHub und GitLab l\u00e4uft deine i18n wie von selbst. Die voll ausgestattete Phrase-Webkonsole mit maschinellem Lernen und smarten Vorschl\u00e4gen macht richtig Spa\u00df beim \u00dcbersetzen. Und sobald die \u00dcbersetzungen bereit sind, k\u00f6nnen sie automatisch mit deinem Projekt synchronisiert werden. Einmal eingestellt, kannst du es vergessen und dich ganz auf deinen geliebten Code konzentrieren.<\/p>\n<p>Schau dir alle <a href=\"https:\/\/phrase.com\/de\/roles\/developers\/\">Phrase-Funktionen f\u00fcr Entwickler<\/a> an und \u00fcberzeuge dich selbst, wie es deine Softwarelokalisierungs-Workflows vereinfachen kann.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Starte deine Browser JavaScript-Lokalisierung mit diesem umfassenden Leitfaden und mach deine Anwendung bereit f\u00fcr internationale User.<\/p>\n","protected":false},"author":41,"featured_media":2612,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"post-refresh-updated","format":"standard","meta":{"_acf_changed":false,"_stopmodifiedupdate":false,"_modified_date":"","_searchwp_excluded":"","episode_type":"","audio_file":"","podmotor_file_id":"","podmotor_episode_id":"","cover_image":"","cover_image_id":"","duration":"","filesize":"","filesize_raw":"","date_recorded":"","explicit":"","block":"","itunes_episode_number":"","itunes_title":"","itunes_season_number":"","itunes_episode_type":"","footnotes":""},"categories":[46],"class_list":["post-133291","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-lokalisierung"],"acf":[],"_links":{"self":[{"href":"https:\/\/phrase.com\/de\/wp-json\/wp\/v2\/posts\/133291","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/phrase.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/phrase.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/phrase.com\/de\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/phrase.com\/de\/wp-json\/wp\/v2\/comments?post=133291"}],"version-history":[{"count":4,"href":"https:\/\/phrase.com\/de\/wp-json\/wp\/v2\/posts\/133291\/revisions"}],"predecessor-version":[{"id":136332,"href":"https:\/\/phrase.com\/de\/wp-json\/wp\/v2\/posts\/133291\/revisions\/136332"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/phrase.com\/de\/wp-json\/wp\/v2\/media\/2612"}],"wp:attachment":[{"href":"https:\/\/phrase.com\/de\/wp-json\/wp\/v2\/media?parent=133291"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/phrase.com\/de\/wp-json\/wp\/v2\/categories?post=133291"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}