JavaScript-Lokalisierung – der ultimative Leitfaden

Starte deine Browser JavaScript-Lokalisierung mit diesem umfassenden Leitfaden und mach deine Anwendung bereit für internationale User.

Seltsam, gereift, relativ ausdrucksstark und erstaunlich – JavaScript ist heute die meistgenutzte Programmiersprache der Welt. Als Sprache der Browser – und mit Node auch auf dem Server im Einsatz – ist JavaScript fester Bestandteil der heutigen Web-Stacks.
Und mit vielen plattformübergreifenden mobilen Frameworks, Desktop-Wrappern, Game Engines und sogar Internet of Things (IoT)-Frameworks ist es im Grunde „die Welt von JavaScript“ – wir leben nur darin.
Und du bist hier, weil du all das 🔥 nutzen und lernen willst, wie du JavaScript-Anwendungen lokalisierst, damit sie für ein globales Publikum bereit sind. Keine Angst: Dieser Leitfaden behandelt alles, was du wissen musst, um mit der JavaScript-Lokalisierung für Browser zu starten.
Also: Zeit für ein bisschen Rock && Roll.

🔗 Ressource » Hol dir den gesamten Code, der diesen Artikel begleitet, aus unserem GitHub-Repository.

🗒 Hinweis » Internet Explorer (IE), mit einem globalen Marktanteil von 2,15 %, kann als veralteter Browser betrachtet werden. Der Kürze halber lassen wir IE-spezifische Lösungen in diesem Leitfaden also weg. Wenn du immer noch IE unterstützt, überprüfe, ob die integrierten JavaScript-Funktionen, die wir in diesem Artikel behandeln, Forks oder Polyfills benötigen.

Overview

Wie lokalisiere ich eine Webseite mit JavaScript?

Auch wenn es verlockend ist, einfach eine fertige Internationalisierungsbibliothek (i18n) für deine Lokalisierungsbedürfnisse zu nehmen – und das kann tatsächlich die richtige Wahl für dein Projekt sein – wirst du feststellen, dass dir für kleinere Projekte auch einfaches JavaScript völlig ausreicht. Wenn du dein eigenes erstellst, bekommst du außerdem ein schönes Kochbuch mit i18n-Techniken, die du mit jeder Bibliothek verwenden kannst, die du auswählst.

🤿 Mehr Info » Unser Artikel, Was ist I18n: Eine einfache Definition von Internationalisierung geht näher darauf ein, was Internationalisierung (i18n) und Lokalisierung (l10n) sind.

✋🏽 Hinweis » Wenn du eine traditionelle MPA (Multi-Page-Anwendung) erstellst, findet ein Großteil der Lokalisierung direkt auf dem Server statt. Wir arbeiten hier nur mit der Browser-Lokalisierung. Wir haben dich serverseitig abgedeckt – mit einem Node i18n-Tutorial und einem Full-Stack-JavaScript-i18n-Leitfaden.

Okay, nehmen wir an, du hast eine Seite, die du lokalisieren willst.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <!-- ... -->
  <title>My Appy Apperson</title>
</head>
<body>
  <div class="container">
    <h1>My Appy Apperson</h1>
    <p>Welcome to my little spot on the interwebs!</p>
  </div>
  <script src="js/scripts.js"></script>
</body>
</html>

Englische Version einer JavaScript-Demo-App | Phrase

🔗 Ressource » Du kannst den gesamten Code für die App, die wir in diesem Abschnitt bauen, aus dem vanilla-Ordner in unserem GitHub-Repository bekommen.
🔗 Ressource » Ich verwende die grundlegende Skeleton CSS-Bibliothek, falls du dich wunderst.

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.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <!-- ... -->
  <title>My Appy Apperson</title>
</head>
<body>
  <div class="container">
    <h1 data-i18n-key="app-title">My Appy Apperson</h1>
    <p data-i18n-key="lead">Welcome to my little spot on the interwebs!</p>
  </div>
  <script src="js/scripts.js"></script>
</body>
</html>

Beachte die data-i18n-key Attribute, die wir oben unseren Textcontainern hinzugefügt haben. Wir können darauf zugreifen, wenn das Dokument geladen wird, und deren Text durch Übersetzungen ersetzen. Weißt du was? Lass uns genau das tun.

// Das aktive Gebietsschema
const locale = "en";
// Wir können hier so viele Sprachen haben, wie wir wollen,
// und beliebige Sprachen verwenden, die wir möchten. Wir haben Englisch
// und Arabisch als Locales hier als Beispiele.
const translations = {
  // Englische Übersetzungen
  "en": {
    "app-title": "My Appy Apperson",
    "lead": "Welcome to my little spot on the interwebs!",
  },
  // Arabische Übersetzungen
  "ar": {
    "app-title": "تطبيقي المطبق",
    "lead": "أهلاً بك في مكاني الصغير على النت.",
  },
};
// Wenn der Seiteninhalt bereit ist...
document.addEventListener("DOMContentLoaded", () => {
  document
    // Finde alle Elemente mit dem Key-Attribut
    .querySelectorAll("[data-i18n-key]")
    .forEach(translateElement);
});
// Ersetze den inneren Text des gegebenen HTML-Elements
// mit der Übersetzung in der aktiven Sprache,
// entsprechend dem data-i18n-key des Elements
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const translation = translations[locale][key];
  element.innerText = translation;
}

Ändere die zweite Zeile oben zu const locale = "ar"; und lade die Seite neu. Wenn das DOMContentLoaded-Ereignis ausgelöst wird, werden die arabischen Übersetzungen auf unserer Seite übernommen.
Arabische Version einer JavaScript-Demo-App | Phrase

🗒 Hinweis » "en" und "ar" oben sind die ISO 639-1-Codes für Englisch und Arabisch. Normalerweise benutzt du ISO-Codes für Sprachen und Länder, wenn du lokalisierst.

Übersetzungen asynchron laden

Wir sind mit unserer i18n-Lösung gut gestartet. Allerdings skaliert das Hinzufügen von Sprachen und Übersetzungen im Moment nicht gut. Wenn unsere App wächst, würden wir wahrscheinlich unsere Übersetzungen in separate Dateien pro Sprache aufteilen. Die Übersetzungsdatei, die der aktiven Sprache entspricht, könnte dann ohne die Kosten für das Laden der anderen Sprachen geladen werden. Wir können dies ohne zu viel Aufwand umsetzen.
Zuerst verschieben wir unsere Übersetzungen aus dem Hauptskript in JSON-Dateien, jeweils eine pro unterstützter Sprache.

{
  "app-title": "My Appy Apperson",
  "lead": "Welcome to my little spot on the interwebs!"
}
{
  "app-title": "تطبيقي المطبق",
  "lead": "أهلاً بك في مكاني الصغير على النت."
}

Lass uns unser Skript so umarbeiten, dass wir die JSON-Dateien bei Bedarf asynchron laden.

// Die Sprache, die unsere App zuerst anzeigt
const defaultLocale = "en";
// Die aktive Sprache
let locale;
// Wird mit Übersetzungen der aktiven Sprache gefüllt
let translations = {};
// Wenn der Seiteninhalt bereit ist ...
document.addEventListener("DOMContentLoaded", () => {
  // Übersetze die Seite in die Standardsprache
  setLocale(defaultLocale);
});
// Lade Übersetzungen für die gegebene Sprache und übersetze
// die Seite für diese Sprache
async function setLocale(newLocale) {
  if (newLocale === locale) return;
  const newTranslations =
    await fetchTranslationsFor(newLocale);
  locale = newLocale;
  translations = newTranslations;
  translatePage();
}
// Hole das Übersetzungs-JSON-Objekt für die gegebene
// Sprache über das Netzwerk
async function fetchTranslationsFor(newLocale) {
  const response = await fetch(`/lang/${newLocale}.json`);
  return await response.json();
}
// Ersetze den inneren Text jedes Elements, das ein
// data-i18n-key Attribut mit der entsprechenden Übersetzung hat
// zu seinem data-i18n-key
function translatePage() {
  document
    .querySelectorAll("[data-i18n-key]")
    .forEach(translateElement);
}
// Ersetze den inneren Text des gegebenen HTML-Elements
// mit der Übersetzung in der aktiven Sprache,
// entsprechend dem data-i18n-key des Elements
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const translation = translations[key];
  element.innerText = translation;
}

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.

🗒 Hinweis » Wir verwenden die praktische Fetch API, die in modernen Browsern integriert ist, um unsere JSON-Dateien über das Netzwerk abzurufen.

Sprachwechsler erstellen

Unsere User können unsere großartigen asynchronen Fähigkeiten bisher noch nicht nutzen. Wollen wir ein Dropdown-Menü zum Sprachwechsel für sie bauen?
Wir fügen eine Navigationsleiste hinzu und platzieren unseren Umschalter in dieser Navigationsleiste.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
  <title>My Appy Apperson</title>
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <ul class="navbar-list navbar-left">
          <!-- Navigationslinks -->
        </ul>
        <div class="navbar-right">
          <!-- ... -->
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">Englisch</option>
            <option value="ar">Arabisch (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">Meine Appy Apperson</h1>
    <p data-i18n-key="lead">Welcome to my little spot on the interwebs!</p>
  </div>
  <script src="js/scripts.js"></script>
</body>
</html>

Ein einfaches <select> reicht hier aus. Wir können ein data-i18n-switcher-Attribut verwenden, damit unser JavaScript darauf zugreifen und die von dir ausgewählte Sprache laden kann.

const defaultLocale = "en";
let locale;
// ...
// Wenn der Seiteninhalt bereit ist ...
document.addEventListener("DOMContentLoaded", () => {
  setLocale(defaultLocale);
  bindLocaleSwitcher(defaultLocale);
});
// ...
// Immer wenn der User eine neue Sprache auswählt,
// laden und aktualisieren wir die Übersetzungen dieser Sprache
// die Seite
function bindLocaleSwitcher(initialValue) {
  const switcher =
    document.querySelector("[data-i18n-switcher]");
  switcher.value = initialValue;
  switcher.onchange = (e) => {
    // Setzt die Sprache auf die ausgewählte Option[value]
    setLocale(e.target.value);
  };
}

Mit dem onchange-Event-Handler kannst du die Übersetzungen deiner Seite entsprechend dem Wert der ausgewählten <option> aktualisieren. Et voila. Unsere Besucher können jetzt ihre eigene Sprache auswählen.
Demo-App mit onchange-Event-Handler | Phrase

📣 Ein großes Dankeschön an Hary Murdiono JS vom Noun Project für sein translate-Icon.

Die bevorzugten Spracheinstellungen deines Browsers erkennen

Manchmal ist es eine gute Idee, die bevorzugte Sprache des Users zu erraten, bevor du die Möglichkeit hast, deine eigene manuell auszuwählen. Die meisten Menschen haben ihre Browser-Benutzeroberfläche in ihrer bevorzugten Sprache eingestellt, oft in der Sprache des Betriebssystems.
Die Sprache der Browser-Oberfläche findest du im navigator-Objekt, genauer gesagt im Standardwert navigator.language (String).
Das ebenfalls standardisierte – wenn auch experimentell, während ich dies schreibe – navigator.languages-Array sollte die UI-Sprache als ersten Eintrag enthalten, zusätzlich zu allen Sprachen, die du in deinen bevorzugten Spracheinstellungen deines Browsers explizit festgelegt hast.
Eine kleine Funktion, die navigator.languages abfragt, kann uns beim Erkennen der Browsersprache helfen.

/**
 * Retrieve user-preferred locales from the browser
 *
 * @param {boolean} languageCodeOnly - when true, returns
 * ["en", "fr"] instead of ["en-US", "fr-FR"]
 * @returns array | undefined
 */
function browserLocales(languageCodeOnly = false) {
  return navigator.languages.map((locale) =>
    languageCodeOnly ? locale.split("-")[0] : locale,
  );
}

Stell dir vor, du hast Französisch (Kanada) und Chinesisch (Vereinfacht) in deinen Browsereinstellungen.
Browser-Spracheinstellungen | Phrase
In diesem Fall gibt browserLocales() ["fr-CA", "zh-CN"] zurück. Wenn wir browserLocales(true) aufrufen, erhalten wir ["fr", "zh"].
Wir können jetzt diese neue Funktion verwenden, um die bevorzugten Spracheinstellungen des Users zu erkennen, wenn wir unsere Seite zum ersten Mal laden.

// Die Sprache, die unsere App zuerst anzeigt
const defaultLocale = "en";
const supportedLocales = ["en", "ar"];
// ...
document.addEventListener("DOMContentLoaded", () => {
  const initialLocale =
    supportedOrDefault(browserLocales(true));
  setLocale(initialLocale);
  bindLocaleSwitcher(initialLocale);
});
// ...
function isSupported(locale) {
  return supportedLocales.indexOf(locale) > -1;
}
// Hol dir die erste unterstützte Sprache aus dem gegebenen
// Array oder gib unsere Standard-Sprache zurück
function supportedOrDefault(locales) {
  return locales.find(isSupported) || defaultLocale;
}
// ...
function browserLocales(languageCodeOnly = false) {
  return navigator.languages.map((locale) =>
    languageCodeOnly ? locale.split("-")[0] : locale,
  );
}

Beachte, dass wir das Konzept von supportedLocales eingeführt haben; das sind die einzigen Locales, für die wir Übersetzungen haben. Mit ihnen können wir auf unsere Standardsprache zurückgreifen, wenn keine deiner bevorzugten Sprachen in unserer unterstützten Liste enthalten ist.
Unsere App wird jetzt in die erste Sprache deiner bevorzugten Liste übersetzt, mit einem eleganten Fallback.

🤿 Mehr Info » Wir behandeln die Spracherkennung sowohl im Browser als auch auf dem Server ausführlich in Erkennung der Browsersprache mit JavaScript.

Handhabung der Schreibrichtung

Arabisch, Hebräisch, Persisch, Urdu und andere Sprachen verwenden Schriften, die von rechts nach links geschrieben werden. 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ützen kannst. Glücklicherweise wird ein Großteil der Arbeit hier vom Browser erledigt; wir müssen nur das <html dir> Attribut auf unseren Seiten setzen.

// ...
// Lade Übersetzungen für das gegebene Gebietsschema und übersetze
// die Seite für diese Sprache
async function setLocale(newLocale) {
  if (newLocale === locale) return;
  const newTranslations = await fetchTranslationsFor(
    newLocale,
  );
  locale = newLocale;
  translations = newTranslations;
  // Setze das <html dir> Attribut
  document.documentElement.dir = dir(newLocale);
  // Nicht notwendig für den Richtungsfluss, aber zur Sicherheit ...
  document.documentElement.lang = newLocale;
  translatePage();
}
// ...
function dir(locale) {
  return locale === "ar" ? "rtl" : "ltr";
}
// ...

Das <html dir> Attribut kann die Werte "ltr" oder "rtl" annehmen. Wir liefern diesen Wert über eine sehr einfache dir()-Funktion und setzen das Attribut jedes Mal, wenn du die Sprache wechselst.

HTML-Attribute der Browser-Entwicklertools | Phrase
Wenn wir unsere Browser-Entwicklertools öffnen, können wir sehen, wie sich die <html>-Attribute aktualisieren, während wir die Sprachen wechseln.

Der Browser stellt das Dokument automatisch von rechts nach links dar, wenn wir <html dir="rtl"> setzen. Allerdings kann jeder unserer benutzerdefinierten Richtungsstile betroffen sein. So erfordert margin-left: 20px einige angepasste, RTL-spezifische CSS-Regeln. Das ist im Allgemeinen nicht zu kompliziert; es liegt nur ein wenig außerhalb des Rahmens dieses Artikels.

🤿 Mehr Info » Lies mehr über lokalisierte CSS in Wie verwende ich eine CSS-Datei für die Website-Lokalisierung?

Mit unserem neuen Code bekommen wir eine arabische Seite, die Avicenna gefallen würde!
Demo-App mit rechtsbündigem arabischen Text | Phrase

Grundlegende Übersetzungsnachrichten

Bevor wir zu komplexeren Nachrichten übergehen, wie denen mit interpolierten Werten und Pluralformen, lass uns kurz anschauen, wie wir unsere Übersetzungsnachrichten implementiert haben.

// In unserer HTML-Seite
<h1 data-i18n-key="app-title">Meine Appy Apperson</h1>
// In unserem JavaScript
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const translation = translations[key];
  element.innerText = translation;
}
// Angesichts der Tatsache, dass wir arabische Übersetzungen aus ar.json geladen haben:
translations = {
  "app-title": "تطبيقي المطبق",
};
translateElement(document.querySelector("[data-i18n-key='lead']"));
// wird gerendert als
<h1 data-i18n-key="app-title">تطبيقي المطبق</h1>

Das ist unser Übersetzungssystem in aller Kürze.

Interpolation

Was passiert, wenn wir Werte haben, die sich während der Laufzeit ändern und in unsere Nachrichten eingefügt werden müssen? Ein häufiges Beispiel ist der Name des aktuell angemeldeten Users. Wir müssen unser Übersetzungssystem aktualisieren, damit solche Fälle funktionieren.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
    <h1 data-i18n-key="app-title">Meine Appy Apperson</h1>
    <p
      data-i18n-key="lead"
      data-i18n-opt='{"username": "Swoodesh"}'
    >
      Welcome to my little spot on the interwebs, {username}!
    </p>
  </div>
  <script src="js/scripts.js"></script>
</body>
</html>

Wir kennzeichnen Platzhalter für Werte, die wir in unseren Nachrichten interpolieren möchten, mit der {variable} Syntax. Ein neues data-i18n-opt-Attribut speichert Key/Wert-Paare für die Interpolation in einem gültigen JSON-Objekt.
Natürlich benötigen wir die Platzhalter in unseren Sprachdateien.

{
  "lead": "Welcome to my little spot on the interwebs, {username}!",
}
{
  "lead": "أهلاً بك في مكاني الصغير على النت يا {username}.",
}

Jetzt können wir unsere translateElement-Funktion modifizieren, um Interpolationen zu verarbeiten.

// ...
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const translation = translations[key];
  const options = JSON.parse(
    element.getAttribute("data-i18n-opt")
  );
  element.innerText = options
    ? Interpolate(translation, options)
    : Übersetzung;
}
// Konvertier eine Nachricht wie "Hallo, {name}" in "Hallo, Chad"
// im Falle des Interpolationsobjekts {name: "Chad"}
function interpolate(message, interpolations) {
  return Object.keys(interpolations).reduce(
    (interpolated, key) =>
      interpolated.replace(
        new RegExp(`{\s*${key}\s*}`, "g"),
        interpolations[key],
      ),
    message,
  );
}
// ...

Wenn wir ein data-i18n-opt Attribut am Element erkennen, das an translateElement() übergeben wird, verarbeiten wir die übersetzte Nachricht mit einer neuen interpolate() Funktion, bevor wir das Element aktualisieren. Wenn du jetzt die Seite lädst, siehst du die Nachricht mit dem eingefügten Wert.
Demo-App mit Interpolation auf Englisch | Phrase
Demo-App mit Interpolation in arabischer sprache | Phrase
Natürlich sind statische Werte im HTML für uns von begrenztem Nutzen. Idealerweise willst du in der Lage sein, dynamisch mit JavaScript zu interpolieren. Das ist nicht so schwer zu coden.

Dynamisches Übersetzen nach dem Laden der Seite

Lass uns eine allgemeine Übersetzungsfunktion aus translateElement() extrahieren.

// ...
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const options =
    JSON.parse(element.getAttribute("data-i18n-opt")) || {};
  element.innerText = translate(key, options);
}
function translate(key, interpolations = {}) {
  return interpolate(translations[key], interpolations);
}
// ...

Wir haben einfach den Code, der das Abrufen einer Nachricht in der aktiven Sprache mit Interpolationen übernimmt, in eine neue translate()-Funktion ausgelagert. Wir können diese Funktion jetzt verwenden, um die Übersetzung eines Elements nach dem Laden der Seite zu aktualisieren. Angenommen, wir möchten unseren Text aktualisieren, nachdem du dich eingeloggt hast. Kein Problem.

const element =
  document.querySelector("[data-i18n-key='lead']");
// Unsere neue Funktion dient uns hier gut
element.innerText =
  translate("lead", { username: "Maggie" });
// Speichere die aktualisierten Interpolationen im Dokument
// für den Fall, dass das Element in Zukunft neu gerendert wird
element.setAttribute(
  "data-i18n-opt",
  JSON.stringify({ username: "Maggie" }),
);

Demo-App mit dynamischer Englisch-Übersetzung | Phrase
Jetzt kannst du die Übersetzungen der Elemente jederzeit aus deinem JavaScript aktualisieren.

Pluralformen

Wir müssen oft unterschiedliche Nachrichten basierend auf einem Zähler präsentieren – wie „1 Follower“ oder „20.000 Followers“ – verschiedene Sprachen haben unterschiedliche Pluralregeln. Während Englisch zwei Pluralformen hat: one und other, hat Arabisch zum Beispiel sechs Pluralformen. Historisch gesehen bedeutete dies, dass die Implementierung der Pluralformen-Unterstützung für Frontend-Apps nicht sehr einfach war. Glücklicherweise erleichtert das jetzt standardmäßige Intl.PluralRules-Objekt die Handhabung von Pluralformen.
Stell dir vor, wir sind fleißige Schreiberlinge und wollen der Welt zeigen, wie viele Artikel wir tatsächlich schon verfasst haben.

<p
  data-i18n-key="article-plural"
  data-i18n-opt='{"count": 122}'
>
  {count} articles written and counting.
</p>

Beachte, dass wir die Konvention verwenden, unseren Plural-Nachrichten-Key mit -plural zu beenden. Und natürlich benötigen wir eine erforderliche count Ganzzahl, um die richtige Pluralform auszuwählen. Wenn wir schon von Pluralformen sprechen, lass sie uns gleich hier hinzufügen.

{
  // Englisch hat zwei Pluralformen
  "article-plural": {
    "one": "{count} article and counting",
    "other": "{count} articles and counting"
  }
}
{
  // Arabisch hat sechs Pluralformen
  "article-plural": {
    "zero": "لا توجد مقالات",
    "one": "مقال {count}",
    "two": "مقالان",
    "few": "{count} مقالات",
    "many": "{count} مقال",
    "other": "{count} مقال"
  }
}

Lass uns jetzt unsere translate-Funktion aktualisieren, damit sie Pluralnachrichten verarbeiten kann.

// ...
function translate(key, interpolations = {}) {
  const message = translations[key];
  if (key.endsWith("-plural")) {
    return interpolate(
      pluralFormFor(message, interpolations.count),
      interpolations,
    );
  }
  return interpolate(message, interpolations);
}
// ...
/*
  Angenommen, es gibt ein forms-Objekt wie
  {
    "zero": "Keine Artikel"
    "one": "Ein Artikel",
    "other": "{count} Artikel"
  } und eine Anzahl von 3 ergibt "3 Artikel"
*/
function pluralFormFor(forms, count) {
  const matchingForm = new Intl.PluralRules(locale).select(count);
  return forms[matchingForm];
}
// ...

Der magische Trick hier ist der Teil, der new Intl.PluralRules(locale).select(...) ausführt. Das eingebaute Intl.PluralRules-Objekt kennt die Pluralregeln des jeweiligen Gebietsschemas, wenn ein Gebietsschema angegeben wird. Zum Beispiel, wenn "ar" an den Konstruktor übergeben wird und dann select(5) auf dem zurückgegebenen Objekt aufgerufen wird, gibt es "few" zurück – die korrekte Form an dieser Stelle.
Mit nur wenigen Zeilen Code hast du nun eine vollständig globalisierte Pluralunterstützung 🙌

Pluralisierung in Arabisch und Englisch | PhraseUnsere Pluralnachricht in Englisch und Arabisch, bei unterschiedlichen Zählungen

Zahlenformatierung

Dreihunderttausend Euro sind „€300.000,00“ auf Englisch (Vereinigte Staaten), „300.000,00 €“ auf Deutsch (Deutschland), „€3,00,000.00“ auf Hindi (Indien) – man beachte die Kommas in letzterem – und „٣٠٠٬٠٠٠٫٠٠ €“ auf Arabisch (Ägypten). Wie gehen wir mit all diesen Formaten um? Keine Sorge; ein weiteres Intl-Objekt, das Teil des modernen JavaScript-Standards ist, kommt hier zu Hilfe. Der edle Retter: Intl.NumberFormat!
Stell dir vor, wir sind angehende Unternehmer und wollen eine NFT-Tracking-Website mit Zahlen starten – klar, oder?

<p
  data-i18n-key="nyan-cat-price"
  data-i18n-opt='{"price": {"number" : 5300}}'
>
  Nyan Cat (Official) NFT: {price}
</p>

Unser data-i18n-opt identifiziert den Zahlenwert des {price} in unseren Locale-Dateien. Wir suchen gleich nach dem number-Key, wenn wir unseren Interpolationscode aktualisieren. Zuerst lass uns unsere Nachrichtenübersetzungen bereitstellen.

{
  "nyan-cat-price": "Nyan Cat (Official) NFT: {price}"
}
{
  "nyan-cat-price": "نيان كات NFT: {price}"
}

Okay – Lass uns unser JavaScript aktualisieren, damit es funktioniert.

// ...
const fullyQualifiedLocaleDefaults = {
  en: "en-US",
  ar: "ar-EG",
};
// ...
function interpolate(message, interpolations) {
  return Object.keys(interpolations).reduce(
    (interpolated, key) => {
      const value = formatNumber(interpolations[key]);
      return interpolated.replace(
        new RegExp(`{\s*${key}\s*}`, "g"),
        value,
      );
    },
    message,
  );
}
/*
  Angenommen, es gibt ein Wertobjekt wie
  {
    "number" : 300000,
    "style": "currency",
    "currency": "EUR"
  } und dass das aktive Gebietsschema "en" ist, gibt es "€300,000.00" zurück.
*/
function formatNumber(value) {
  if (typeof value === "object" && value.number) {
    const { number, ...options } = value;
    return new Intl.NumberFormat(
      fullyQualifiedLocaleDefaults[locale],
      options,
    ).format(number);
  } else {
    return value;
  }
}
// ...

Wenn wir unsere übersetzten Nachrichten interpolieren, geben wir den Wert, den wir einfügen, zunächst durch einen Zahlenformatierer, der wiederum das eingebaute Intl.NumberFormat Objekt verwendet. Anders gesagt: Mit sehr wenigen Codezeilen haben wir die Zahlenformatierung lokalisiert.
Demo-App mit US-Zahlenformat | Phrase
Demo-App mit arabischer Zahlenformatierung | Phrase

✋🏽 Hinweis » Am besten gibst du ein vollständig qualifiziertes Gebietsschema wie "en-US" an den Intl.NumberFormat()-Konstruktor weiter. Wenn wir nur einen Sprachcode übergeben, wie "en", entscheidet jeder Browser, welche Region verwendet wird, um die Zahlen zu formatieren: Ein Browser könnte standardmäßig "en-US" verwenden, während ein anderer "en-UK" wählt. Deshalb verwenden wir eine fullyQualifiedLocaleDefaults-Map in unserer formatNumber() Funktion, um eine einheitliche plattformübergreifende Formatierung zu erhalten.

Da wir alle Optionen, die in unserem Interpolationsobjekt definiert sind, an den Intl.NumberFormat() Konstruktor übergeben, können wir jederzeit seine zahlreichen Formatierungsoptionen nutzen.

<p
  data-i18n-key="nyan-cat-price"
  data-i18n-opt='{"price": {
    "number" : 5300,
    "style": "currency",
    "currency": "EUR"
  }}'
 >
  Nyan Cat (Official) NFT: {price}
</p>

Beispieltext US-Zahlenformat | PhraseBeispieltext Arabisches Zahlenformat | Phrase

🔗 Ressource » Unser Kurzleitfaden zur Zahlenlokalisierung konnte dir gefallen.

Datumsformatierung

Ähnlich wie bei Zahlen ist die Datumsformatierung regional unterschiedlich. Ein Beispiel: Der 5. Dezember 2021 wird in seiner Kurzform als „12/5/2021“ auf Englisch (US) und als „5.12.2021“ auf Deutsch (Deutschland) formatiert. Und wieder kann ein praktisches integriertes Intl.DateTimeFormat-Objekt die schwere Arbeit bei der Datumsformatierung übernehmen.
Angenommen, wir möchten das Veröffentlichungsdatum und die Uhrzeit eines unserer Artikel anzeigen.

<p
  data-i18n-key="publish-date"
  data-i18n-opt='{"publishDate": {
    "date": "2021-12-05 15:29:00"
  }}'
>
  Published on {publishDate}
</p>

Der spezielle date-key in unserem data-i18n-opt Objekt enthält den Datums- und Uhrzeitwert, den wir formatieren möchten. Fügen wir wie gewohnt unsere lokalisierten Nachrichten hinzu.

{
  // ...
  "publish-date": "Published {publishDate}"
}
{
  //...
  "publish-date": "نشر {publishDate}"
}

Jetzt aktualisieren wir unser Übersetzungssystem, um nach date-Keys zu suchen und deren Werte als lokalisierte Daten zu formatieren.

// ...
function interpolate(message, interpolations) {
  return Object.keys(interpolations).reduce(
    (interpolated, key) => {
      const value = formatDate(
        formatNumber(interpolations[key]),
      );
      return interpolated.replace(
        new RegExp(`{\s*${key}\s*}`, "g"),
        value,
      );
    },
    message,
  );
}
// ...
/*
  Angenommen, es gibt ein Wertobjekt wie
  {
    "date": "2021-12-05 15:29:00",
    "dateStyle": "long",
    "timeStyle": "short"
  } und dass das aktuelle Gebietsschema 'en' ist,
  gibt es  "December 5, 2021 at 3:29 PM" zurück.
*/
function formatDate(value) {
  if (typeof value === "object" && value.date) {
    const { date, ...options } = value;
    const parsedDate =
      typeof date === "string" ? Date.parse(date) : date;
    return new Intl.DateTimeFormat(
      fullyQualifiedLocaleDefaults[locale],
      options,
    ).format(parsedDate);
  } else {
    return value;
  }
}
// ...

Nachdem wir unser Wertobjekt an unseren Zahlen-Formatierer übergeben haben, verwenden wir erneut unseren neuen Datums-Formatierer. Die Datumsformatierungsoptionen werden an den Intl.DateTimeFormat-Konstruktor übergeben, was eine ziemlich große Flexibilität beim Formatieren von Datumswerten ermöglicht.

✋🏽 Hinweis » Wir verwenden oben Date.parse(), um sicherzustellen, dass unsere Zeichenfolge date in ein Date-Objekt umgewandelt wird, sonst gibt Intl.DateTimeFormat einen Fehler aus.

Damit haben wir die lokalisierte Datumsformatierung 👍

<p
  data-i18n-key="publish-date"
  data-i18n-opt='{"publishDate": {
    "date": "2021-12-05 15:29:00",
    "dateStyle": "long",
    "timeStyle": "short"
  }}'
>
  Published on {publishDate}
</p>

Demo-App mit US-Datumsformatierung | Phrase
Demo-App mit arabischer Datumsformatierung | Phrase

🤿 Mehr Info » Wenn du nach robusteren Datumsformatierungsfunktionen suchst, schau dir unsere Übersicht an: Was ist die beste JavaScript-Datums- und Zeitbibliothek? Und unser Menschenfreundlicher Weg zu lesbaren Datumsanzeigen in TypeScript/JavaScript bietet dir Formatierungen wie „vor 1 Stunde“.

🔗 Ressource » Wenn du ein deklaratives Framework wie React verwendest, kannst du das, was wir in diesem Abschnitt erstellt haben, weiterführen und Eigene JavaScript-i18n-Bibliothek mit TypeScript entwickeln.

Welche guten JavaScript-i18n-Bibliotheken kannst du verwenden?

Wir haben oben gezeigt, wie du deine eigene JavaScript-i18n-Bibliothek erstellst. Es könnte jedoch sinnvoller sein, für dein Projekt eine fertige i18n-Bibliothek zu übernehmen. Hier gibt es keinen Mangel an Optionen, und in diesem Artikel schauen wir uns die Bibliotheken Polyglot, i18next und Globalize genauer an.
Für eine noch größere Auswahl helfen dir unsere beliebten Artikel, den richtigen Einstieg zu finden:

🗒 Hinweis » Wenn du mit legacy gettext arbeitest, schau dir die Jed library an.

Wie lokalisiere ich eine Webseite mit Polyglot?

Polyglot ist eine kleine i18n-Bibliothek, die von Airbnb gepflegt wird und einige Lokalisierungsprobleme löst, die zuvor von JavaScripts Standardbibliotheken nicht unterstützt wurden. Am bemerkenswertesten unter den Funktionen von Polyglot ist die hervorragende Handhabung der Mehrzahl. Wie wir schon gesagt haben, wird der jetzt eingebaute Intl.PluralRules-Konstruktor von allen modernen Browsern unterstützt und löst das Pluralisierungsproblem ganz einfach. Dennoch wurde in der letzten Version dieses Artikels Polyglot stark behandelt, also wollten wir diesen Abschnitt einfügen, falls einige von euch immer noch einen Polyglot-Leitfaden wollen.

🗒 Hinweis » Es sei denn, dein Anwendungsfall erfordert Polyglot, schau dir die alternative i18next-Library im nächsten Abschnitt an, bevor du eine Lokalisierungslösung wählst.

Also denn: Lass uns unsere kleine Demo-App mit Airbnbs Lokalisierungsbibliothek lokalisieren.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <ul class="navbar-list navbar-start">
          <li class="navbar-item">
            <a href="#" data-i18n-key="home" class="navbar-link">
              Home
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n-key="about" class="navbar-link">
              About
            </a>
          </li>
        </ul>
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">English</option>
            <option value="ar">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">With Polyglot</h1>
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Cadence"}'>
      Welcome to my little spot on the interwebs, %{username}!
    </p>
    <p
      data-i18n-key="article-plural"
      data-i18n-opt='{"smart_count": 2}'
    >
      %{smart_count} articles written and counting.
    </p>
  </div>
</body>
</html>

JavaScript-Demo-App mit Polyglot | Phrase
Alles klar, lass uns dieses Ding mit Polyglot lokalisieren.

Installation

Polyglot hat einige NPM-Abhängigkeiten, also musst du Node lokal installieren. Mit Node kannst du eine package.json-Datei für deine Demo-App erstellen, indem du Folgendes in der Kommandozeile ausführst.

npm init -y

Um unsere NPM-Abhängigkeiten in eine Datei zu bündeln, die Browser lesen können, installieren wir das Webpack Module Bundler als Entwicklungsabhängigkeit. Der Webpack Dev Server hilft dir beim Hot-Reload deines Bundles im Browser, während du entwickelst.

npm install --save-dev webpack webpack-cli webpack-dev-server

Alles klar, jetzt installiere den Star der Show: Polyglot.

npm install node-polyglot

Eine index.js wird als Einstiegspunkt für unsere App dienen, und wir können sie verwenden, um einen Praxistest der Bibliotheksinstallationen durchzuführen.

import Polyglot from "node-polyglot";
console.log({ Polyglot });

Ein start-Skript in unserer package.json wird unsere Entwicklung erleichtern, indem es den Entwicklungsserver mit unserer individuellen Konfiguration startet.

{
  "name": "polyglot-demo",
  // ...
  "scripts": {
    "start": "webpack-dev-server --config webpack.config.js"
  },
  // ...
  "devDependencies": {
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1",
    "webpack-dev-server": "^4.6.0"
  },
  "dependencies": {
    "node-polyglot": "^2.4.2"
  }
}

🗒 Hinweis » Wir verwenden eine relativ einfache webpack.config.js, um unsere App zu bündeln und den Entwicklungsserver zu konfigurieren. Schau dir unser Git-Repository auf GitHub an.

Jetzt bringen wir unser gebündeltes JS direkt vor dem </body>-Tag in unserer public/index.html-Datei ein.

<script src="./bundle.js"></script>

Damit kannst du jetzt das start-Skript im Terminal ausführen, um das Webpack Dev Server zu starten.

npm start

Wenn alles gut läuft, öffnet sich unsere App automatisch im Browser. Wenn wir unsere Browser-Entwicklertools öffnen, sollten wir Konsolenprotokolle sehen, die in etwa wie die folgenden aussehen.
Konsole der Browser-Entwicklertools | Phrase

Grundlegende Übersetzungen

Schauen wir uns die grundlegende Nutzung von Polyglot an. Hier ist das Rezept:

// 1. Importiere die Bibliothek
import Polyglot from "node-polyglot";
// 2. Erstelle eine Instanz
const polyglot = new Polyglot();
// 3. Füge Übersetzungsnachrichten für das aktive Gebietsschema hinzu
polyglot.extend({
  "app-title": "With Polyglot",
});
// 4. Verwende die Texte, um Seitenelemente zu übersetzen
const element = document.querySelector(
  "[data-i18n-key='app-title']",
);
// polyglot.t() gibt eine Übersetzungsnachricht zurück, die angegeben wurde
// ein Key
element.innerHTML = polyglot.t("app-title");

Um die Sprache zu wechseln, kannst du die Seite mit den neuen Spracheinstellungen neu laden.

✋🏽 Hinweis » Wir verwenden innerHTML 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 innerHTML einfügst, bereinigst, um XSS-(Cross-Site-Scripting-)Angriffe zu vermeiden.

import Polyglot from "node-polyglot";
const polyglot = new Polyglot();
polyglot.extend({
  "app-title": "مع بوليجلوت",
});
const element = document.querySelector(
  "[data-i18n-key='app-title']",
);
element.innerHTML = polyglot.t("app-title");

Asynchrones Laden von Übersetzungsdateien

Während der Code oben für die kleinsten Apps gut funktioniert, könnten wir es noch besser machen, indem wir unsere Übersetzungsdateien in separate JSON-Dateien pro Locale aufteilen.

{
  "app-title": "With Polyglot",
  "home": "Home",
  "about": "About"
}
{
  "app-title": "مع بوليجلوت",
  "home": "الرئيسية",
  "about": "نبذة عنا"
}

Jetzt kannst du eine Standardsprache für deine App konfigurieren und ihre Übersetzungen aus dem Netzwerk laden, wenn deine Seite geladen wird.

import Polyglot from "node-polyglot";
const defaultLocale = "en";
const polyglot = new Polyglot();
// Lade Übersetzungen aus dem Netzwerk
async function loadTranslations(locale) {
  return await fetch(`/lang/${locale}.json`).then(
    (response) => response.json(),
  );
}
// Übersetze alle Elemente auf der Seite, die unser benutzerdefiniertes Attribut haben
// data-i18n-key-Attribut
function translatePage() {
  const translatableElements = document.querySelectorAll(
    "[data-i18n-key]",
  );
  translatableElements.forEach((el) => {
    const key = el.getAttribute("data-i18n-key");
    el.innerHTML = polyglot.t(key);
  });
}
// Init
(async function () {
  const translations = await loadTranslations(
    defaultLocale,
  );
  polyglot.extend(translations);
  translatePage();
})();

Damit erhalten wir die folgende Darstellung im Browser.
Englische Demo-App mit Polyglot sowie fehlenden Menüpunkten und Haupttitel | Phrase
Unsere Navigationsmenü-Punkte und der Haupttitel sind in unsere Standardsprache Englisch übersetzt. Beachte jedoch die Polyglot-Fehler in der Konsole und wie die fehlenden Keys (lead und article-plural) den Wert der Keys selbst anzeigen. Wir fügen gleich Übersetzungen für diese Keys hinzu, um das zu beheben.
Wenn wir defaultLocale in "ar" ändern, erhalten wir folgende Darstellung.
Arabische Demo-App mit Polyglot sowie fehlenden Menüpunkten und Haupttitel | Phrase

Sprachumschalter

Unsere App ist jetzt skalierbarer, da nur die Übersetzungen der aktiven Sprache geladen werden. Lass uns das nutzen, um einen Sprachumschalter zu bauen. Wir haben bereits das HTML für den Umschalter in unserer App:

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
		<!-- ... -->
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">English</option>
            <option value="ar">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <!-- ... -->
  <script src="./bundle.js"></script>
</body>
</html>

Das Einbinden dieses <select>-Elements aus unserem JavaScript ermöglicht es uns, unsere Sprachwechsel-Funktion hinzuzufügen.

import Polyglot from "node-polyglot";
const defaultLocale = "en";
const polyglot = new Polyglot();
// ...
// Lade Übersetzungen für das angegebene Gebietsschema und übersetze
// die Seitenelemente für diese Sprache
async function loadAndTranslate(locale) {
  const translations = await loadTranslations(locale);
  polyglot.replace(translations);
  translatePage();
}
// Jedes Mal, wenn der User die aktive Sprache wechselt, lade
// die Nachrichten dieses Gebietsschemas in die Seite
function bindLocaleSwitcher(initialValue) {
  const switcher = document.querySelector(
    "[data-i18n-switcher]",
  );
  switcher.value = initialValue;
  switcher.onchange = (e) => {
    loadAndTranslate(e.target.value);
  };
}
// Init
loadAndTranslate(defaultLocale);
bindLocaleSwitcher(defaultLocale);

Wir haben den Code, der unsere Übersetzungsnachrichten lädt und unsere Seiteninhalte übersetzt, in eine wiederverwendbare loadAndTranslate()<1>-Funktion umstrukturiert. Eine neue Funktion bindLocaleSwitcher() verbindet sich mit dem Sprachwechsler <select>; sie nutzt loadAndTranslate(), um die Übersetzungen basierend auf der von dir gewählten Sprache zu aktualisieren.

✋🏽 Hinweis » polyglot.extend() wird Übersetzungsnachrichten zu den bereits geladenen hinzufügen. Daher verwenden wir polyglot.replace(), um sicherzustellen, dass wir nur die Übersetzungen der aktiven Sprache laden.

Das sollte unseren schicken Sprachumschalter zum Laufen bringen.
Demo-App mit Polyglot und bearbeitetem Sprachumschalter | Phrase

Interpolation

Unser Haupttext enthält den Namen des aktuell angemeldeten Users (natürlich gemockt). Diese Art von interpoliertem Wert wird von Polyglot standardmäßig mit einer speziellen %{variable} Syntax behandelt.

🗒 Hinweis » Du kannst die Zeichen, die interpolierte Werte kennzeichnen, mit der interpolation -Option, die an den Polyglot-Konstruktor übergeben wird, ändern.

<!-- ... -->
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Cadence"}'>
      Welcome to my little spot on the interwebs, %{username}!
    </p>
<!-- ... -->
{
  // ...
  "lead": "Welcome to my little spot on the interwebs, %{username}!"
}
{
  // ...
  "lead": "أهلاً بك في مكاني الصغير على النت يا %{username}."
}

Ein paar Zeilen Code können zu unserer Funktion translatePage() hinzugefügt werden, um Interpolationen zu berücksichtigen.

// ...
// Übersetze alle Elemente auf der Seite, die ein
// data-i18n-key-Attribut haben
function translatePage() {
  const translatableElements = document.querySelectorAll(
    "[data-i18n-key]",
  );
  translatableElements.forEach((el) => {
    const key = el.getAttribute("data-i18n-key");
    // Extrahiere Interpolations-Keys/Werte aus dem HTML und
    // in JSON umwandeln
    const interpolations = el.getAttribute("data-i18n-opt");
    const parsedInterpolations = interpolations
      ? JSON.parse(interpolations)
      : {};
    // Übergebe die analysierten Interpolationen an polyglot.t(),
    // die automatisch Ersetzungen behandelt
    el.innerHTML = polyglot.t(key, parsedInterpolations);
  });
}
// ...

Mit dem obigen Code haben wir nun unseren interpolierten Einleitungsabsatz in der aktiven Sprache gerendert.
Englischsprachige Demo-App mit Polyglot und festem Einleitungsabsatz | Phrase
Arabische Demo-App mit Polyglot und festem Einleitungsabsatz | Phrase

Pluralformen

Angenommen, wir möchten dir als aktuell angemeldetem User zeigen, wie viele Nachrichten du erhalten hast: „Du hast 1 neue Nachricht“ oder „Du hast 12 neue Nachrichten“, zum Beispiel. Polyglot setzt Pluralformen auf diese Weise problemlos um. Wir müssen nur unsere Übersetzungen mit dem speziellen interpolierten Zahlenwert, smart_count, hinzufügen.

<!-- ... -->
    <p
      data-i18n-key="new-messages"
      data-i18n-opt='{"smart_count": 12}'
    >
      You have %{smart_count} new messages"
    </p>
<!-- ... -->

Polyglot verwendet smart_count, um je nach aktueller Sprache die korrekte Pluralform aus einer Übersetzungsnachricht auszuwählen. Pluralformen werden in unseren Nachrichten mit vier senkrechten Strichen |||| getrennt. Englisch hat die Pluralformen one und other, und wir müssen sie in der Reihenfolge bereitstellen:

{
  // ...
  "new-messages": "You have %{smart_count} new message |||| You have %{smart_count} new messages"
}

Arabisch hat sechs Pluralformen und wir fügen sie unseren Nachrichten auf die gleiche Weise hinzu.

{
  // ...
  "new-messages": "لا توجد لديك رسائل جديدة |||| لديك رسالة جديدة |||| لديك رسالتان جداد |||| لديك %{smart_count} رسائل جديدة |||| لديك %{smart_count} رسالة جديدة |||| لديك %{smart_count} رسالة جديدة"
}

🗒 Hinweis » Schau dir den Abschnitt Wie lokalisiere ich eine Webseite mit JavaScript? ➞ Pluralformen an, um mehr über Pluralformen zu erfahren.

Eine weitere Sache: Standardmäßig ist Polyglot völlig 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ücklich an.

// ...
// Lade Übersetzungen für das angegebene Gebietsschema und übersetze
// die Seitenelemente für diese Sprache
async function loadAndTranslate(locale) {
  const translations = await loadTranslations(locale);
  polyglot.locale(locale);
  polyglot.replace(translations);
  translatePage();
}
// ...

Das war's! Wir unterstützen jetzt fortschrittliche Pluralformen in unserer App.
Englische Locale-Demo-App mit Polyglot Smart Count | Phrase
Arabische Locale-Demo-App mit Polyglot Smart Count | Phrase

🔗 Ressource » Hol dir den gesamten Code für unsere Polyglot-App von unserem GitHub-Repo.

🔗 Ressource » Die offizielle Polyglot-Dokumentation ist genauso kompakt wie die Bibliothek selbst.

Wie lokalisiere ich eine Webseite mit i18next?

Während ich dies schreibe, ist i18next eine der beliebtesten JavaScript-i18n-Bibliotheken. Die „einmal lernen, überall [verwenden]“ Bibliothek funktioniert eigenständig und mit einer Vielzahl von JavaScript-Frameworks. Ein reichhaltiges Plugin-Ökosystem bedeutet, dass du oft nur eine NPM-Installation von der Lösung eines häufigen i18n-Problems entfernt bist. Das alles macht i18next zu einer einfachen Empfehlung.
Alles klar, genug Geschwafel. Lass uns unser kleines Demo noch einmal anschauen und es mit i18next lokalisieren.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <ul class="navbar-list navbar-start">
          <li class="navbar-item">
            <a href="#" data-i18n-key="home" class="navbar-link">
              Home
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n-key="about" class="navbar-link">
              About
            </a>
          </li>
        </ul>
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">English</option>
            <option value="ar">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">With i18next</h1>
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Zelda"}'>
      Welcome to my little spot on the interwebs, {{username}}!
    </p>
    <p data-i18n-key="new-messages" data-i18n-opt='{"count": 12}'>
      You have {{count}} new messages
    </p>
  </div>
  <script src="./bundle.js"></script>
</body>
</html>

Hier gibt es nicht viel Neues. Lass uns mit der Lokalisierung beginnen.

Installation

Wir verwenden Node und seinen NPM Paket-Manager, um i18next zu installieren. Zuerst erstellen wir die Datei package.json, um unsere Projektabhängigkeiten und NPM-Skripte zu verfolgen, indem wir Folgendes in der Kommandozeile ausführen.

npm init -y

Der Webpack-Bundler ermöglicht es uns, i18next, seine Plugins und unser eigenes JavaScript zu bündeln und in einer Datei im Browser bereitzustellen. Lass uns Webpack samt Entwicklungsserver installieren, der eine praktische Hot-Reload-Funktion hat, die die Entwicklung erleichtert:

npm install --save-dev webpack webpack-cli webpack-dev-server

Und vergiss unsere i18n-Bibliothek nicht:

npm install i18next

Ein praktisches npm start-Skript kann das Starten unseres Entwicklungsservers abkürzen.

{
  "name": "i18next-demo",
  //...
  "scripts": {
    "start": "webpack-dev-server --config webpack.config.js"
  },
  // ...
  "devDependencies": {
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1",
    "webpack-dev-server": "^4.6.0"
  },
  "dependencies": {
    "i18next": "^21.6.3"
  }
}

🗒 Hinweis » Wir verwenden eine relativ einfache webpack.config.js-Datei, um unsere App zu bündeln und den Dev-Server zu konfigurieren. Schau sie dir in unserem Git-Repo auf GitHub an.

Lass uns einen index.js-Einstiegspunkt für unsere App erstellen und i18next einem kurzen Test unterziehen, um zu prüfen, ob es richtig installiert ist.

import i18next from "i18next";
console.log({ i18next });

Wenn du npm start in der Befehlszeile ausführst, startet der Webpack Dev Server und lädt deine App automatisch im Browser.
Demo-App mit Webpack-Entwicklungsserver geladen | Phrase

Grundlegende Übersetzungsnachrichten

i18next ist flexibel darin, wie Übersetzungsnachrichten angenommen werden. Das meiste konfigurieren wir, während wir die Bibliothek mit i18next.init(...) initialisieren. Wir fügen unsere Übersetzungsnachrichten direkt unter der Option resources ein.

import i18next from "i18next";
i18next.init({
  // Das aktive Gebietsschema
  lng: "en",
  // aktiviert nützliche Konsolenausgaben während der Entwicklung
  debug: true,
  // Übersetzungsnachrichten, nach Locale-Code zugeordnet
  Ressourcen: {
    en: {
      // Standardmäßig erwartet i18next Nachrichten unter dem
      // "translation" namespace
      translation: {
        "app-title": "With Polyglot",
        home: "Home"
        about: "About",
      },
    },
    ar: {
      translation: {
        "app-title": "مع بوليجلوت",
        "home": "الرئيسية",
        "about": "نبذة عنا",
      },
    },
  },
});
// Seitenelemente übersetzen
const translatableElements = document.querySelectorAll(
  "[data-i18n-key]",
);
translatableElements.forEach((el) => {
  const key = el.getAttribute("data-i18n-key");
  el.innerHTML = i18next.t(key);
});

Damit sollten wir beim Neuladen unserer App im Browser keine Änderungen sehen. Wenn du jedoch lng in "ar" änderst, siehst du die folgenden arabischen Übersetzungen.
Arabische Locale-Demo-App mit fehlendem Key | Phrase

🗒 Hinweis » Die debug: true-Option aktiviert sehr nützliche Konsolenprotokolle im Browser. Beachte oben die Informationen zu fehlenden Keys, zum Beispiel.

Asynchrone Übersetzungs­ladefunktion

Zukunftsorientierte Entwickler wie wir sollten unsere App skalierbarer machen, indem wir unsere Übersetzungen in separate Dateien aufteilen – eine pro Locale.

{
  "app-title": "Mit i18next"
  "home": "Home",
  "about": "About"
}
{
  "app-title": "مع آي أيتين نيكست",
  "home": "الرئيسية",
  "about": "نبذة عنا"
}

i18next ist ausgereift und hat viele Grundlagen abgedeckt. Deshalb müssen wir keinen eigenen Code schreiben, um Übersetzungsdateien aus dem Netzwerk zu laden. Das offizielle HTTP-Backend wird in die Bibliothek integriert und erledigt die gesamte Arbeit für uns. Lass es uns installieren.

npm install i18next-http-backend

Wir können jetzt das Backend in unsere index.js einbinden und nutzen(), während wir i18next initialisieren.

import i18next from "i18next";
import HttpApi from "i18next-http-backend";
// Wir machen die Funktion asynchron, damit wir warten können,
// bis die Übersetzungsdatei aus dem Netzwerk
// übertragen wird.
async function initI18next() {
  // Wir verwenden() das Backend und warten, bis es die
  // Übersetzungen aus dem Netzwerk lädt.
  await i18next.use(HttpApi).init({
    lng: "en",
    debug: true,
    // Entferne die eingebetteten `Ressourcen`
    // Laden der Entwicklungssprache deaktivieren
    fallbackLng: false,
    // Http-Backend konfigurieren
    backend: {
      loadPath: "/lang/{{lng}}.json",
    },
  });
}
// Schnelle Umstrukturierung des Seitenübersetzungscodes
// zu einer Funktion
function translatePageElements() {
  const translatableElements = document.querySelectorAll(
    "[data-i18n-key]",
  );
  translatableElements.forEach((el) => {
    const key = el.getAttribute("data-i18n-key");
    el.innerHTML = i18next.t(key);
  });
}
// Initialisierung
(async function () {
  await initI18next();
  translatePageElements();
})();

Die Option backend.loadPath überschreibt den Standardpfad der Übersetzungsdatei des Backends. Ein spezieller {{lng}} Platzhalter wird durch das aktive Gebietsschema ersetzt. Ein Beispiel: Wenn unsere App zum ersten Mal geladen wird, sucht das Backend nach /lang/en.json, da wir die Standardsprache in unserer Konfiguration zuvor als en festgelegt haben.
Das war’s auch schon. Unsere Übersetzungen werden jetzt aus dem Netzwerk geladen, anstatt in unseren Code eingebettet zu werden.

Unterstützte Sprachversionen und Fallback-Option

Oft möchten wir eine Liste von Sprachen (Locales) angeben, die unsere App unterstützt, sowie eine Sprache (Locale), auf die zurückgegriffen wird, wenn eine Übersetzung fehlt. Du kannst die Konfigurationsoptionen supportLngs und fallbackLng von i18next verwenden, um das zu erreichen.

// ...
async function initI18next() {
  await i18next.use(HttpApi).init({
    lng: "en",
    debug: true,
    supportedLngs: ["en", "ar"],
    fallbackLng: "en",
    backend: {
      loadPath: "/lang/{{lng}}.json",
    },
  });
}
// ...

✋🏽 Hinweis » Die Fallback-Sprache wird immer geladen, unabhängig vom aktiven Gebietsschema.

Automatische Erkennung deiner Sprache und Region

Es ist üblich, die Browsereinstellungen der User zu erkennen und deren Spracheinstellung zu verwenden, wenn wir sie unterstützen. Das ist normalerweise etwas knifflig, aber i18next hat ein offizielles Plugin, das uns hier schnell weiterhilft. Lass uns damit anfangen, es zu installieren.

npm install i18next-browser-languagedetector

Ähnlich wie beim HTTP-Backend importierst du das Detektor-Plugin und verwendest() es bei der Initialisierung.

import i18next from "i18next";
import HttpApi from "i18next-http-backend";
import LanguageDetector from "i18next-browser-languagedetector";
async function initI18next() {
  await i18next
    .use(HttpApi)
    .use(LanguageDetector)
    .init({
      debug: true,
      supportedLngs: ["en", "ar"],
      fallbackLng: "en",
      // Erlaube, "en" zu verwenden für
      // "en-US", "en-CA" usw.
      nonExplicitSupportedLngs: true,
      backend: {
        loadPath: "/lang/{{lng}}.json",
      },
    });
}
// ...

Und das ist alles, was du brauchst, um mithilfe von i18next eine solide automatische Spracherkennung zu erstellen.

🔗 Ressource » Du fragst dich vielleicht, welche Kriterien die Spracherkennung verwendet, um die Locale des Users zu bestimmen. Wir behandeln dies ausführlich in unserem Leitfaden zur React-Lokalisierung mit i18next.

✋🏽 Hinweis » Die Sprach- und Regionserkennung speichert die erkannte Spracheinstellung standardmäßig im lokalen Speicher deines Browsers und verwendet diesen Wert, wenn du unsere Website erneut besuchst.

🤿 Mehr Info » Wir haben einen speziellen Leitfaden zur Erkennung der Browsersprache mit JavaScript, der dein Interesse wecken könnte.

Sprachumschalter

Die automatische Spracherkennung ist zwar praktisch, aber oft brauchst du eine Benutzeroberfläche, um deine bevorzugte Sprache explizit festzulegen. Wir haben bereits das Markup für einen Sprachumschalter eingerichtet.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
      <!-- ... -->
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">English</option>
            <option value="ar">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <!-- ... -->
  </div>
  <script src="./bundle.js"></script>
</body>
</html>

Lass uns diesen HTML-Code nutzen und die Funktion i18next.changeLanguage() verwenden, um deine aktive Sprache auf die von dir gewählte Sprache einzustellen. Nachdem die lokalen Nachrichten geladen sind, kannst du translatePageElements anhängen, um die Seite mit den aktualisierten Übersetzungen neu zu rendern.

// ...
function bindLocaleSwitcher(initialValue) {
  const switcher = document.querySelector(
    "[data-i18n-switcher]",
  );
  switcher.value = initialValue;
  switcher.onchange = (e) => {
    i18next
      .changeLanguage(e.target.value)
      .then(translatePageElements);
  };
}
// Init
(async function () {
  await initI18next();
  translatePageElements();
  bindLocaleSwitcher(i18next.resolvedLanguage);
})();

✋🏽 Hinweis » Der Lokalisierungsdetektor könnte eine Sprache erkannt haben, die wir nicht unterstützen, und dieser Wert wird in i18next.language vorhanden sein. Wir verwenden i18next.resolvedLanguage, um sicherzustellen, dass wir die aktive, unterstützte Sprache beim Initialisieren unseres Sprachumschalters verwenden.

Et voilà! Die Sprachwechsel-Benutzeroberfläche ist fertig:
Polyglot Demo-App mit einer Sprachwechsel-Benutzeroberfläche | Phrase

Interpolation

Standardmäßig verwendet i18next die {{variable}}-Syntax, um interpolierte Werte in Übersetzungen zu kennzeichnen.

<!-- ... -->
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Zelda"}'>
      Welcome to my little spot on the interwebs, {{username}}!
    </p>
<!-- ... -->
{
  // ...
  "lead": "Welcome to my little spot on the interwebs, {{username}}!"
}
{
  // ...
  "lead": "أهلاً بك في مكاني الصغير على النت يا {{username}}."
}

Wir können diese dynamischen Werte aus unseren HTML-Attributen abrufen und sie an i18next.t() übergeben, das die Interpolation für uns übernimmt.

// ...
function translatePageElements() {
  const translatableElements = document.querySelectorAll(
    "[data-i18n-key]",
  );
  translatableElements.forEach((el) => {
    const key = el.getAttribute("data-i18n-key");
    const interpolations = el.getAttribute("data-i18n-opt");
    const parsedInterpolations = interpolations
      ? JSON.parse(interpolations)
      : {};
    el.innerHTML = i18next.t(key, parsedInterpolations);
  });
}
// ...

Damit werden unsere dynamischen Werte in unseren Nachrichten ersetzt.
Demo-App mit Polyglot im englischen Sprachraum und interpoliertem Einleitungstext | Phrase
Demo-App mit Polyglot im arabischen Locale und interpoliertem Einleitungsabsatz | Phrase

Pluralformen

Unter der Haube versucht i18next, die Standard-Intl.PluralRules zu verwenden, um Pluralformen zu verwalten. Eine spezielle interpolierte count-Variable steuert die Wahl der Pluralform, abhängig von der aktiven Locale.

<!-- ... -->
    <p data-i18n-key="new-messages" data-i18n-opt='{"count": 12}'>
      You have {{count}} new messages
    </p>
<!-- ... -->

i18next verwendet eine message_form Konvention für plurale Nachrichten-Keys. Ein Beispiel: Um die one und other Formen in einer englischen new-messages Übersetzung zu behandeln, können wir Folgendes angeben.

{
  // ...
  "new-messages_one": "Du hast {{count}} neue Nachricht"
  "new-messages_other": "Du hast {{count}} neue Nachrichten"
}

Arabisch hat sechs Pluralformen, und wir können sie auf ähnliche Weise angeben.

{
  // ...
  "new-messages_zero": "لا توجد لديك رسائل جديدة",
  "new-messages_one": "لديك رسالة جديدة",
  "new-messages_two": "لديك رسالتان جداد",
  "new-messages_few": "لديك {{count}} رسائل جديدة",
  "new-messages_many": "لديك {{count}} رسالة جديدة",
  "new-messages_other": "لديك {{count}} رسالة جديدة"
}

Mit wenig Aufwand kann unsere App Nachrichten im Plural anzeigen.
Demo-App mit Polyglot in englischer Locale und Pluralisierung | Phrase
Demo-App mit Polyglot in arabischer Locale und Pluralisierung | Phrase

🔗 Ressource » Hol dir den gesamten Code, den wir oben behandelt haben, aus unserem GitHub-Repo.

Wie lokalisiere ich eine React-, Angular- oder Vue-App?

In den letzten Jahren haben deklarative Frameworks wie React, Angular, Vue.js und andere die Frontend-Web-Welt im Sturm erobert. Wir behandeln diese Frameworks ausführlich in unserem Blog. Da React das beliebteste unter den großen Frameworks ist, geben wir dir hier gleich einen kurzen Leitfaden zur Lokalisierung von React-Apps. Und für andere deklarative Frameworks schau dir unsere folgenden ausführlichen Artikel an.

Angular-Lokalisierungsartikel

Lokalisierungsartikel für Vue.js

Lokalisierungsartikel für andere Frameworks

Uns ist bewusst, dass einige unserer Leser vielleicht Svelte, Next oder ein anderes Framework verwenden. Deshalb schreiben wir immer über das Neueste und Beste. Hier ist eine Auswahl an Leitfäden, wie du die App, die du gerade in deinem Lieblings-Framework baust, lokalisieren kannst:

🗒 Hinweis » Genauer gesagt: Wenn du mit internationalen Telefonnummern arbeitest, schau dir die libphonenumber-Bibliothek an. Sie ist framework-unabhängig!

Wie lokalisiere ich eine React-App mit i18next?

Wie versprochen, werfen wir einen schnellen Blick darauf, wie du deine React-Apps mit der i18next-Bibliothek lokalisierst. Wir haben i18next bereits früher in diesem Artikel behandelt, daher konzentrieren wir uns hier auf die React-Integration.

🤿 Mehr Info » Ein Leitfaden zur React-Lokalisierung mit i18next deckt mehr ab und geht mehr in die Tiefe als unser kurzer Überblick hier.

Zuerst nehmen wir unsere vertraute Demo-App und zerlegen sie in React-Komponenten.

import "./App.css";
import Navbar from "./layout/Navbar";
function App() {
  return (
    <div className="container">
      <Navbar />
      <h1>React i18n</h1>
      <p>
        Welcome to my little spot on the interwebs, user
      </p>
      <p>You have count new messages</p>
    </div>
  );
}
export default App;
import LocaleSwitcher from "../features/LocaleSwitcher";
function Navbar() {
  return (
    <nav className="navbar">
      <div className="container">
        <ul className="navbar-list navbar-start">
          <li className="navbar-item">
            <a href="#" className="navbar-link">
              Home
            </a>
          </li>
          <li className="navbar-item">
            <a href="#" className="navbar-link">
              About
            </a>
          </li>
        </ul>
        <div className="navbar-end">
          <LocaleSwitcher />
        </div>
      </div>
    </nav>
  );
}
export default Navbar;
function LocaleSwitcher() {
  return (
    <>
      <img
        alt="Translation icon"
        src="img/translation-icon@2x.png"
        className="translation-icon"
      />
      <select className="locale-switcher">
        <option value="en">English</option>
        <option value="ar">Arabic (العربية)</option>
      </select>
    </>
  );
}
export default LocaleSwitcher;

Der Sprachumschalter tut momentan nicht viel, aber das ändern wir bald. Fürs Erste haben wir einen guten Ausgangspunkt für die Lokalisierung.
React-Demo-App auf Englisch mit i18n-Bibliothek | Phrase

Installation der Bibliothek

Zusätzlich zu i18next nutzen wir das offizielle react-i18next -Integrationsframework, das die Verwendung von i18next mit React zum Kinderspiel macht. Wechsle ins Projektverzeichnis und führe folgenden Befehl in der Kommandozeile aus, um die Bibliotheken zu installieren.

npm install i18next react-i18next

Als Nächstes initialisieren wir i18next und verwende() die React-Integration, wie wir es machen. Der grundlegendste Weg, i18next Übersetzungen bereitzustellen, besteht darin, die resources Option bei der Initialisierung zu verwenden.

import i18next from "i18next";
import { initReactI18next } from "react-i18next";
i18next.use(initReactI18next).init({
  Ressourcen: {
    en: {
      translation: {
        "app-title": "With React",
      },
    },
    ar: {
      translation: {
        "app-title": "مع ريأكت",
      },
    },
  },
  lng: "en",
  debug: true,
  interpolation: {
    escapeValue: false,
  },
});
export default i18next;

🗒 Hinweis » Wir setzen die interpolation.escapeValue auf false, um die standardmäßige Escapierung, die i18next zum Schutz vor XSS-Angriffen durchführt, zu deaktivieren, da React das ohnehin für uns erledigt.

Ziehen wir unser Modul in unser Stammverzeichnis index.js, damit wir i18next initialisieren können, wenn unsere App geladen wird.

import React from "react";
import ReactDOM from "react-dom";
import "./lib/skeleton/normalize.css";
import "./lib/skeleton/skeleton.css";
import "./services/i18n";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root"),
);
// ...

Grundlegende Übersetzungen

Die vertraute i18next.t()-Übersetzungsfunktion kannst du auch in unseren React-Komponenten verwenden. Wir müssen nur den useTranslation React Hook importieren, damit t verfügbar ist.

import { useTranslation } from "react-i18next";
import Navbar from "./layout/Navbar";
import "./App.css";
function App() {
  const { t } = useTranslation();
  return (
    <div className="container">
      <Navbar />
      <h1>{t("app-title")}</h1>
      // ...
    </div>
  );
}
export default App;

Wenn du die App neu lädst, sieht alles genauso aus wie vorher. Wenn du jedoch den Wert lng in unserem Initialisierer src/services/i18n.js auf "ar" änderst, solltest du sehen, dass der App-Titel auf Arabisch lokalisiert wird.
React-Demo-App mit i18n-Bibliothek und arabischem Titel | Phrase

Asynchrones Laden von Übersetzungsdateien

Mach unsere App skalierbarer, indem du unsere Übersetzungen in sprachspezifische Dateien aufteilst. Das praktische, offizielle i18next-http-backend Plugin macht das für uns zu einer schnellen Angelegenheit. Lass es uns installieren.

npm install i18next-http-backend

Das Backend wird standardmäßig nach Dateien unter /locales/{{lng}}/{{ns}}.json suchen, wobei {{lng}} die aktive Sprache auflöst und {{ns}} den aktiven Namensraum auflöst. Da der Standardnamensraum translation ist, können wir unsere englischen Übersetzungen in public/locales/en/translation.json ablegen.

{
  "app-title": "With React",
  "home": "Home",
  "about": "About"
}

Unsere arabische Datei folgt derselben Konvention:

{
  "app-title": "مع ريأكت",
  "home": "الرئيسية",
  "about": "نبذة عنا"
}

Wir müssen jetzt nur noch das Backend importieren und verwenden(), wenn wir i18next initialisieren. Wir sollten auch unsere Inline-Übersetzungen unter dem resources-Key entfernen, da das Plugin jetzt unsere Übersetzungsnachrichten aus dem Netzwerk lädt.

import i18next from "i18next";
import { initReactI18next } from "react-i18next";
import HttpApi from "i18next-http-backend";
i18next
  .use(initReactI18next)
  .use(HttpApi)
  .init({
    // Entferne `resources`
    lng: "en",
    debug: true,
    interpolation: {
      escapeValue: false,
    },
  });
export default i18next;

Wenn du die App neu lädst, solltest du exakt dieselbe lokalisierte Darstellung sehen. Natürlich werden die Übersetzungen der aktiven Sprache jetzt über das Netzwerk geladen, sodass deine App schneller lädt und leichter zu skalieren und zu warten ist.
React-Demo-App mit asynchronem Laden von Übersetzungsdateien | Phrase

Sprachumschalter

Eine Sprachwechsel-UI baust du mit React und i18next ganz einfach. Wir können unsere LocaleSwitcher-Komponente aktualisieren und das <select> darin so steuern, dass die aktive Sprache auf die von dir gewählte geändert wird.

import { useTranslation } from "react-i18next";
function LocaleSwitcher() {
  const { i18n } = useTranslation();
  return (
    <>
      <img
        src="img/translation-icon@2x.png"
        alt="Translation icon"
        className="translation-icon"
      />
      <select
        className="locale-switcher"
        value={i18n.language}
        onChange={(e) =>
          i18n.changeLanguage(e.target.value)
        }
      >
        <option value="en">English</option>
        <option value="ar">Arabic (العربية)</option>
      </select>
    </>
  );
}
export default LocaleSwitcher;

Die i18next React-Integration stellt sicher, dass die Übersetzungen neu gerendert werden, wenn die aktive Sprache geändert wird.
React-Demo-App mit funktionierendem Sprachumschalter | Phrase

🔗 Ressource » Hol dir den Code für alles, was wir oben gebaut haben, aus unserem GitHub-Repository.

Artikel zur Lokalisierung mit React

Wir lieben es, in unserem Blog über 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:

Wie lokalisiere ich eine Webseite mit jQuery und i18next?

Auch wenn jQuery heute nicht mehr so angesagt ist wie vor ein paar Jahren, gehört es immer noch zu den beliebtesten JavaScript-Bibliotheken. Du wirst feststellen, dass das Lokalisieren von jQuery-Apps mit der i18next Bibliothek ziemlich einfach ist. Ein offizielles i18next jQuery-Plugin ist ganz einfach einzurichten, also lass uns damit unsere bewährte Demo-App lokalisieren.

🗒 Hinweis » Wir haben i18next ausführlicher weiter oben in diesem Artikel behandelt, daher konzentrieren wir uns hier auf die jQuery-Integration.

Wenn du mitgelesen hast, wird dir die folgende Starter-App bekannt vorkommen. Sie dient als gute Grundlage für unsere Lokalisierungsarbeit.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <ul class="navbar-list navbar-start">
          <li class="navbar-item">
            <a href="#" data-i18n="home" class="navbar-link">
              Home
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n="about" class="navbar-link">
              About
            </a>
          </li>
        </ul>
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">English</option>
            <option value="ar">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n="app-title">jQuery i18n</h1>
    <p data-i18n="lead" data-i18n-options='{"username": "Jackie"}'>
      Welcome to my little spot on the interwebs, {{username}}!
    </p>
    <p data-i18n="new-messages" data-i18n-options='{"count": 3}'>
      You have {{count}} new messages
    </p>
  </div>
</body>
</html>

🗒 Hinweis » Standardmäßig verwendet das i18next jQuery-Plugin data-i18n (nicht wie zuvor data-i18n-key) als Übersetzungs-Key. Das kannst du in den Plugin-Optionen ändern.

jQuery-Demo-App | Phrase

Zeit für's Lokalisieren? Los geht's.

Installation

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.

🔗 Ressource » Schau dir die offizielle Dokumentation des i18next jQuery Plugins auf GitHub an.

Nachdem du die oben genannten Dateien heruntergeladen hast, kannst du sie in ein js/lib Verzeichnis in deinem Projekt ablegen und in deine Haupt-HTML-Seite einbinden.

<!DOCTYPE html>
<html lang="en">
<head>
   <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
  </div>
  <script src="./js/lib/jquery-3.6.0.min.js"></script>
  <script src="./js/lib/i18next.min.js"></script>
  <script src="./js/lib/jquery-i18next.min.js"></script>
  <!-- Unser benutzerdefiniertes JavaScript kommt gleich... -->
  <script src="./js/scripts.js"></script>
</body>
</html>

Grundlegende Übersetzungen

Mit unseren installierten Bibliotheken können wir jetzt etwas Setup-Code schreiben, um die grundlegende Lokalisierungsarbeit zum Laufen zu bringen.

// i18next initialisieren
i18next.init({
  lng: "en",     // Ausgangssprache
  debug: true,   // Bietet hilfreiche Konsolenmeldungen
  resources: {   // Übersetzungen
    en: {
      translation: {
        "app-title": "jQuery + i18next",
      },
    },
    ar: {
      translation: {
        "app-title": "جي كويري + آي إيتين نيكست",
      },
    },
  },
});
// i18next jQuery Plugin initialisieren
jqueryI18next.init(i18next, $);
// Seitenelemente übersetzen
$("body").localize();

Wir haben i18next.init(...) bereits früher in diesem Artikel behandelt. Beachte, dass wir hier auch einen jQueryI18next.init(...) Aufruf haben. Im einfachsten Fall übernimmt die jQuery i18next Plugin init-Funktion die aktive i18next Instanz sowie einen Verweis auf das jQuery-Objekt $.
Wenn das Plugin initialisiert wird, fügt es eine localize()-Funktion zu jQuery hinzu. Wenn du $(selector).localize() aufrufst, lokalisiert das Plugin alle Elemente unter der ausgewählten Hierarchie. Für jedes Element wird, wenn ein data-i18n Attribut gefunden wird, die entsprechende Übersetzung für die aktive Sprache eingefügt.
Ein Beispiel:

// In unserem JavaScript
i18next.init({
  lng: "en",
  Ressourcen: {
    en: {
      translation: {
        "app-title": "jQuery + i18next",
      },
    },
    ar: {
      translation: {
        "app-title": "جي كويري + آي إيتين نيكست",
      },
    },
  },
});
jqueryI18next.init(i18next, $);
$("#main-title").localize();
// In unserem HTML
<h1 id="main-title" data-i18n="app-title"></h1>
// Wird gerendert als
<h1 id="main-title" data-i18n="app-title">jQuery + i18next</h1>

Einfach und schön 😊

jQuery-Demo-App mit geladener i18next-Bibliothek in englischer Sprache | Phrase

Und wenn wir unsere Ausgangssprache auf Arabisch ändern, indem wir lng auf "ar" ändern, erhalten wir stattdessen einen arabischen Titel.

jQuery-Demo-App mit geladener i18next-Bibliothek, in arabischer Lokalisierung | Phrase

Asynchrones Laden von Übersetzungsdateien

„Wie wäre es, wenn wir unsere Übersetzungsdateien in separate Dateien aufteilen, eine pro Sprachvariante?“, höre ich dich fragen. Keine Sorge: Das offizielle i18next HTTP-Backend-Plugin deckt das ab.
Um das Plugin zu installieren, schnapp dir das minimierte Verteilungsskript von GitHub und leg es in dein js/lib Verzeichnis. Und natürlich sollten wir es auch in unser HTML einfügen.

<!DOCTYPE html>
<html lang="en">
<head>
   <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
  </div>
  <script src="./js/lib/jquery-3.6.0.min.js"></script>
  <script src="./js/lib/i18next.min.js"></script>
  <script src="./js/lib/i18nextHttpBackend.min.js"></script>
  <script src="./js/lib/jquery-i18next.min.js"></script>
  <script src="./js/scripts.js"></script>
</body>
</html>

Jetzt können wir unsere App skalierbar machen, indem wir die Übersetzungen in separate JSON-Dateien auslagern.

{
  "app-title": "Mit jQuery + i18next"
  "home": "Home",
  "about": "About"
}
{
  "app-title": "مع جي كويري و آي إيتين نيكست"
  "home": "الرئيسية",
  "about": "نبذة عنا"
}

Wir müssen unseren Setup-Code überarbeiten, um bei der Initialisierung von i18next das HTTP-Plugin use() zu verwenden. Warte auch, bis die Übersetzungsdatei deiner Ausgangssprache heruntergeladen ist, bevor du versuchst, die Seitenelemente zu übersetzen.

// Warten auf Übersetzungen, die über das Netzwerk übertragen werden,
// bevor das jQuery-Plugin initialisiert wird.
async function initI18n() {
  // Verwende das Http-Backend-Plugin, um Übersetzungen herunterzuladen
  await i18next.use(i18nextHttpBackend).init({
    lng: "en",
  // Entferne die `resources`-Option, da unsere Übersetzungen
  // sich jetzt in JSON-Dateien befinden.
  });
  jqueryI18next.init(i18next, $);
}
// Refaktorisieren zu Funktion
function translatePage() {
  $("body").localize();
}
// Init
(async function () {
  // Warten, bis i18next initialisiert ist
  // Seitenelemente übersetzen
  await initI18n();
  translatePage();
})();

Wenn wir unsere App neu laden, bemerken wir keinen Unterschied in der Ausgabe. Ein näherer Blick auf den Netzwerk-Tab der Entwicklertools zeigt eine wartungsfreundlichere „Lade es herunter, wenn du es brauchst“-Lösung für Übersetzungsdateien.

jQuery-Demo-App mit asynchronem Laden von Übersetzungsdateien | Phrase

Sprachumschalter

Vielleicht ist dir aufgefallen, dass wir etwas HTML-Code haben, der wie eine Sprachumschalter-UI in unserer Demo aussieht.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <!-- ... -->
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">English</option>
            <option value="ar">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <!-- ... -->
  </div>
  <!-- ... -->
</body>
</html>

Lass uns das <select>-Element oben so verknüpfen, dass unsere App ihre Übersetzungen auf die vom User ausgewählte Sprache umschaltet.

// ...
function bindLocaleSwitcher() {
  const $switcher = $("[data-i18n-switcher]");
  // Anfangswert
  $switcher.val(i18next.language);
  $switcher.on("change", async function () {
    // Das Ändern der aktiven Sprache führt dazu, dass ihre
    // Übersetzungen aus dem Netzwerk laden. Also
    // warten wir auf das Laden, bevor wir
    // Seitenelemente aktualisieren.
    await i18next.changeLanguage($switcher.val());
    translatePage();
  });
}
(async function () {
  await initI18n();
  translatePage();
  bindLocaleSwitcher();
})();

Und schon haben wir einen funktionierenden Sprachumschalter.

jQuery-Demo-App mit Sprachumschalter | Phrase

Interpolation

Der Umgang mit dynamischen Werten in unseren Übersetzungen ist mit i18next sofort verfügbar. Wir müssen nur eine data-i18n-options JSON-Map im Element bereitstellen, für das wir Interpolationen haben.

<!-- ... -->
    <p data-i18n="lead" data-i18n-options='{"username": "Jackie"}'>
      Welcome to my little spot on the interwebs, {{username}}!
    </p>
<!-- ... -->

Das i18next jQuery-Plugin sucht standardmäßig nicht nach data-i18n-options; wir müssen seine useOptionsAttr-Konfigurationsoption verwenden, um die automatische Interpolation zu aktivieren.

async function initI18n() {
  await i18next.use(i18nextHttpBackend).init({ lng: "en" });
  jqueryI18next.init(i18next, $, { useOptionsAttr: true });
}
// ...

Lass uns sicherstellen, dass wir den Platzhalter {{variable}}haben, den i18next in unseren Übersetzungsnachrichten erwartet.

{
  "app-title": "Mit jQuery + i18next"
  "home": "Home",
  "about": "About",
  "lead": "Welcome to my little spot on the interwebs, {{username}}!"
}
{
  "app-title": "مع جي كويري و آي إيتين نيكست"
  "home": "الرئيسية",
  "about": "نبذة عنا",
  "lead": "أهلاً بك في مكاني الصغير على النت يا {{username}}."
}

Na also: Die Interpolationen wurden interpoliert.

Englischer Einleitungsabsatz mit Interpolation | Phrase
Arabischer Einleitungsabsatz mit Interpolation | Phrase

Pluralformen

Wenn wir Pluralformen haben, verwenden wir dasselbe data-i18n-options-Attribut, um eine spezielle count-Zählervariable bereitzustellen.

🗒 Hinweis » Schau dir den vorherigen Abschnitt i18next ➞ Pluralformen an, wenn du wissen willst, wie die Bibliothek mit Pluralformen arbeitet.

<!-- ... -->
    <p data-i18n="new-messages" data-i18n-options='{"count": 3}'>
      You have {{count}} new messages
    </p>
<!-- ... -->

Wir verwenden eine key_form-Konvention, wenn wir unsere Pluralnachrichten angeben. Englisch hat zwei Pluralformen, one und other.

{
  // ...
  "new-messages_one": Du hast {{count}} neue Nachricht
  "new-messages_other": "Du hast {{count}} neue Nachrichten"
}

Arabisch hat sechs Pluralformen, und sie werden automatisch von i18next behandelt.

{
  // ...
  "new-messages_zero": "لا توجد لديك رسائل جديدة",
  "new-messages_one": "لديك رسالة جديدة",
  "new-messages_two": "لديك رسالتان جداد",
  "new-messages_few": "لديك {{count}} رسائل جديدة",
  "new-messages_many": "لديك {{count}} رسالة جديدة",
  لديك {{count}} رسالة جديدة
}

So bekommt unsere App ohne zusätzlichen Code eine komplexe Pluralunterstützung.

Englische Sprachversion der jQuery-Demo-App mit Pluralisierung | Phrase
Englische Sprachversion der jQuery-Demo-App mit Pluralisierung | Phrase

🔗 Ressource » Hol dir den voll funktionsfähigen Code für die obige App aus unserem GitHub-Repository.

🔗 Ressource » Wenn du nach einer Alternative zu i18next suchst, schau dir jQuery i18n für Fortgeschrittene an, der die jQuery.i18n-Bibliothek verwendet.

Wie lokalisiere ich eine Webseite mit dem ICU-Format unter Verwendung von Globalize?

Wenn du die nahezu vollständige Lokalisierungsabdeckung der International Components for Unicode (ICU) und des Unicode Common Locale Data Repository (CLDR) in deiner JavaScript-App haben möchtest, erledigt die Globalize-Bibliothek das auf jeden Fall. Lass uns gemeinsam betrachten, wie du Globalize installierst und damit unsere bescheidene Demo-App lokalisierst.

🔗 Ressource » Der fehlende Leitfaden zum ICU-Nachrichtenformat behandelt ICU und CLDR ausführlicher.

🗒 Hinweis » Im Gegensatz zu vielen anderen Bibliotheken, die wir in diesem Artikel behandeln, unterstützt Globalize tatsächlich Internet Explorer 9+.

Welche Demo?, fragst du. Unser zuverlässiger One-Pager natürlich.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <ul class="navbar-list navbar-start">
          <li class="navbar-item">
            <a href="#" data-i18n-key="home" class="navbar-link">
              Home
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n-key="about" class="navbar-link">
              About
            </a>
          </li>
        </ul>
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">English</option>
            <option value="ar">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">With Globalize</h1>
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Stella"}'>
      Welcome to my little spot on the interwebs, {username}!
    </p>
    <p data-i18n-key="new-messages" data-i18n-opt='{"count": 100}'>
      You have # new messages
    </p>
  </div>
</body>
</html>
Globalize Demo-App | Phrase

Fangen wir der Lokalisierung an.

Installation

Globalize ist sehr modular, daher kann die Installation mit reinem JavaScript etwas umständlich sein. Dennoch ist es ein relativ einfaches Projekt.

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 /lib in meinem Projekt abgelegt, sodass mein Projekt jetzt so aussieht:

.
├── css/
├── img/
├── lib/
│   ├── cldr/          << umbenannt von cldr-x.x.x
│   ├── cldr-json/     << umbenannt von cldr-x.x.x-json-full
│   └── globalize/     << umbenannt von globalize.x.x
└── index.html

🔗 Ressource » Die offizielle Dokumentation beschreibt verschiedene Möglichkeiten der Installation von Globalize.

Verwendung des Anforderungstools zur Bestimmung der benötigten Skripte

Wir benötigen immer einige Teile von Globalize und cldr.js; andere hängen von den Lokalisierungsfunktionen unserer App ab. Ein praktisches Tool, So What’cha Want, kann dir sagen, welche Dateien du je nach deinen ausgewählten Funktionen in dein Projekt holen solltest. Wir können beginnen, indem wir alle Funktionen abwählen, außer message.

So What’cha Want Globalize Screenshot | Phrase

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önnen <script>-Tags dafür verwenden.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
  </div>
  <!-- Globalize-Anforderungen -->
  <script src="./lib/cldr/dist/cldr.js"></script>
  <script src="./lib/cldr/dist/cldr/event.js"></script>
  <script src="./lib/cldr/dist/cldr/supplemental.js"></script>
  <script src="./lib/globalize/dist/globalize.js"></script>
  <script src="./lib/globalize/dist/globalize/message.js"></script>
  <!-- Unser App-Einstiegspunkt -->
  <script src="./index.js"></script>
</body>
</html>

Die Liste unten rechts von So What'cha Want enthält die CLDR JSON-Daten, die wir benötigen. Hol dir dieses JSON in deinen Code und gib es an Globalize über die load()-Funktion weiter.

// Wir fügen nach und nach mehr zu dieser Liste hinzu
const supplementals = ["likelySubtags"];
async function loadIntoGlobalize(featureUrls) {
  await Promise.all(
    featureUrls.map((url) => fetchJson(url))
  ).then((downloaded) =>
    downloaded.forEach((feature) => Globalize.load(feature))
  );
}
function supplementalUrlsFor(options) {
  return options.map(
    (feature) =>
      `/lib/cldr-json/cldr-core/supplemental/${feature}.json`
  );
}
async function fetchJson(url) {
  const response = await fetch(url);
  return await response.json();
}
(async function () {
  // Lade ergänzende Anforderungen
  await loadIntoGlobalize(
    supplementalUrlsFor(supplementals)
  );
})();

Das war's mit der Einrichtung. Okay, es ist nicht die einfachste Bibliothek zu installieren, aber für ein Projekt, das eine zuverlässige Lokalisierung benötigt, ist es die Mühe wert.

Grundlegende Übersetzungen

Die Verwendung von Globalize zur Übersetzung von Seitenelementen ist ein einfacher dreistufiger Prozess.

// ...
(async function () {
  await loadIntoGlobalize(
    supplementalUrlsFor(supplementals)
  );
  // 1. Lade Übersetzungsnachrichten
  Globalize.loadMessages({
    en: {
      "app-title": "Hello Globalize!",
    },
    ar: {
      "app-title": "أهلاً جلوبالايز",
    },
  });
  // 2. Setze die Standardsprache
  Globalize.locale("en");
  // 3. Verwende formatMessage(), um die Übersetzung anhand des Keys zu erhalten
  document.querySelector(
    "[data-i18n-key='app-title']"
  ).textContent = Globalize.formatMessage("app-title");
})();

🔗 Ressource » Die offizielle Globalize-Dokumentation hat einen praktischen API-Abschnitt, der verfügbare Funktionen auflistet.

Cooool. Unser Titel wird mit der Übersetzung unserer Standardsprache angezeigt.

Globalize Demo-App Englische Übersetzung | Phrase

Wenn wir den obigen Aufruf ändern, so dass er Globalize.locale("ar") lautet, erhalten wir den Titel unserer App auf Arabisch.

Globalize Demo-App Arabische Übersetzung | Phrase

Umgang mit fehlenden Nachrichten

Verallgemeinere den Code, der Seitenelemente übersetzt, indem du eine translatePageElements()-Funktion schreibst. Im Gegensatz zu anderen i18n-Bibliotheken löst Globalize einen Fehler aus und stoppt, wenn bei einem bestimmten Key für formatMessage() eine Nachricht fehlt. Nichts, was ein wenig try/catch nicht abmildern könnte ...

// ...
function translatePageElements() {
  const elements = document.querySelectorAll(
    "[data-i18n-key]"
  );
  elements.forEach((element) => {
    const key = element.getAttribute("data-i18n-key");
    try {
      element.innerHTML = Globalize.formatMessage(key);
    } catch (error) {
      if (error.code === "E_MISSING_MESSAGE") {
        // Zeig Konsolenwarnungen bei fehlenden Nachrichten an,
        // anstatt zum Stillstand zu kommen.
        console.warn(error.message);
        // Zeige den Key-Wert auf der Seite für eine fehlende Nachricht an.
        element.innerHTML = key;
      } else {
        console.error(error);
      }
    }
  });
}
(async function () {
  // ...
  Globalize.loadMessages(/* ... */);
  Globalize.locale("en");
  translatePageElements();
})();

Weniger "Absturz bei fehlender Nachricht", und mehr "Key-Wert anzeigen und in der Konsole warnen".

Globalize Demo-App mit Warnung im Code | Phrase

Asynchrones Laden von Übersetzungsdateien

Wie wir es mit anderen Lösungen in diesem Artikel gemacht haben, lass uns unsere Übersetzungsnachrichten in JSON-Dateien pro Locale aufteilen, um Skalierbarkeit und Wartbarkeit zu gewährleisten.

{
  // Globalize erwartet, dass der Sprachcode der oberste Key ist
  "en": {
    "app-title": "Mit Globalize"
    "home": "Home",
    "about": "About"
  }
}
{
  "ar": {
    "app-title": "مع جلوبالايز",
    "home": "الرئيسية",
    "about": "نبذة عنا"
  }
}

Eine wiederverwendbare setLocale()-Funktion kann unsere Übersetzungsdatei laden, Globalize konfigurieren und unsere Seitenelemente neu rendern.

const defaultLocale = "en";
// ...
 async function setLocale(locale) {
  const messages = await fetchJson(`/lang/${locale}.json`);
  Globalize.loadMessages(messages);
  Globalize.locale(locale);
  translatePageElements();
}
// ...
(async function () {
  await loadIntoGlobalize(
    supplementalUrlsFor(supplementals)
  );
  setLocale(defaultLocale);
})();

Asynchrones Laden der Übersetzungsdateien: erledigt.

Sprachumschalter

Wir können die Funktion setLocale(), 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ür den Umschalter hat ...

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <!-- ... -->
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">English</option>
            <option value="ar">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <!-- ... -->
  </div>
  <!-- ... -->
  <script src="./index.js"></script>
</body>
</html>

Ein bisschen JavaScript bringt den Umschalter zum Umschalten.

const defaultLocale = "en";
// ...
function bindLocaleSwitcher() {
  const switcher = document.querySelector(
    "[data-i18n-switcher]"
  );
  // Globalize.locale() gibt das aktive Gebietsschema zurück.
  switcher.value = Globalize.locale().locale;
  switcher.onchange = (e) => {
    setLocale(e.target.value);
  };
}
(async function () {
  await loadIntoGlobalize(
    supplementalUrlsFor(supplementals)
  );
  await setLocale(defaultLocale);
  bindLocaleSwitcher(defaultLocale);
})();

Und damit kannst du die Sprache auswählen, die du bevorzugst.

Globalize-Demo-App mit Sprachumschalter | Phrase

Sprachbezeichnungen in der aktiven Sprache anzeigen

Eines der mächtigsten Features, wenn du eine ICU-Bibliothek wie Globalize nutzt, ist der Zugriff auf eine immens große Vielfalt an CLDR-Lokalisierungsdaten. Zum Beispiel könntest du die Sprachen in deinem Sprachumschalter im aktiven Gebietsschema anzeigen – Englisch wäre zum Beispiel „الإنجليزية“ auf Arabisch. Dazu müssten wir die Haupt-CLDR-Daten importieren und dann unseren Text select > option aktualisieren.

const defaultLocale = "ar";
const mains = {
    localenames: ["languages"],
};
const supplementals = "likelySubtags"];
async function setLocale(locale) {
  const messages = await fetchJson(`/lang/${locale}.json`);
  Globalize.loadMessages(messages);
  await loadIntoGlobalize(mainUrlsFor(mains, locale));
  Globalize.locale(locale);
  translatePageElements();
  setLocaleSwitcherDisplayNames();
}
// Given options = {
//   localenames: ["languages"],
//   Daten: ["ca-generic", "ca-gregorian"],
// }
// und locale = "en" gibt ein Array zurück wie folgt
// [
//    "/lib/cldr-json/cldr-localenames-full/main/en/languages.json",
//    "/lib/cldr-json/cldr-dates-full/main/en/ca-generic.json",
//    "/lib/cldr-json/cldr-dates-full/main/en/ca-gregorian.json"
// ]
function mainUrlsFor(options, locale) {
  const result = [];
  Object.keys(options).forEach((key) => {
    options[key].forEach((collection) => {
      result.push(
        `/lib/cldr-json/cldr-${key}-full/main/${locale}/${collection}.json`
      );
    });
  });
  return result;
}
function setLocaleSwitcherDisplayNames() {
  const options = document.querySelectorAll(
    "[data-i18n-switcher] option"
  );
  options.forEach((option) => {
    const localeCode = option.value;
    // Hol dir die CLDR-Hauptdaten über den Pfad
    option.textContent = Globalize.cldr.main(
      `localeDisplayNames/languages/${localeCode}`
    );
  });
}
// ...

Du kannst unseren Haupt-JSON-Ladecode verwenden, wann immer du die CLDR-JSON-Hauptdaten abrufen willst. Andernfalls sorgt ein wenig DOM-Manipulation dafür, dass wir beim Auswählen eines neuen Gebietsschemas lokalisierte Locale-Namen erhalten (so meta!).

Globalize-Demo-App mit lokalisierten Gebietsschema-Namen | Phrase

🔗 Ressource » Die offiziellen cldr.js-Dokumente erklären ausführlicher, wie man CLDR-JSON-Daten abrufen kann.

Interpolation

Die Verarbeitung dynamischer Werte in unserer Nachricht erfolgt über das ICU-Nachrichtenformat mithilfe der Syntax {variable}.

{
  "en": {
    // ...
    "lead": "Welcome to my little spot on the interwebs, {username}!"
  }
}
{
  "ar": {
    // ...
    "lead": "أهلاً بك في مكاني الصغير على النت يا {username}."
  }
}

Dazu können wir Key/Wert-Ersatzpaare in unserem HTML bereitstellen.

<!-- ... -->
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Stella"}'>
      Welcome to my little spot on the interwebs, {username}!
    </p>
<!-- ... -->

Und jetzt müssen wir nur noch diese Key/Wert-Paare lesen und sie an Globalize.formatMessage() übergeben.

// ...
function translatePageElements() {
  const elements = document.querySelectorAll(
    "[data-i18n-key]"
  );
  elements.forEach((element) => {
    const key = element.getAttribute("data-i18n-key");
    const interpolations =
      element.getAttribute("data-i18n-opt");
    const parsedInterpolations = interpolations
      ? JSON.parse(interpolations)
      : {};
    try {
      element.innerHTML = Globalize.formatMessage(
        key,
        parsedInterpolations
      );
    } catch (error) {
      if (error.code === "E_MISSING_MESSAGE") {
        console.warn(error.message);
      } else {
        console.error(error);
      }
    }
  });
}
// ...

Kein Problem.

Globalize-Demo-App mit Interpolation auf Englisch | Phrase
Globalize-Demo-App mit Interpolation im arabischen Gebietsschema | Phrase

Pluralformen

Die Handhabung von Pluralformen im ICU-Format ist unübertroffen und deckt komplexe Pluralformen wie die in Russisch oder Arabisch ab. Natürlich müssen wir die Pluralfunktion erst einrichten, bevor wir sie verwenden können.

Pluralanforderungen hinzufügen

Lass uns zu So What’cha Want gehen, um herauszufinden, was wir einbeziehen müssen, wenn wir plural aktivieren.

Screenshot So What’cha Want für Globalize | Phrase

Gar nicht schlecht. Zuerst müssen wir ein <script>-Tag für die Funktion hinzufügen.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
  </div>
  <script src="./lib/cldr/dist/cldr.js"></script>
  <script src="./lib/cldr/dist/cldr/event.js"></script>
  <script src="./lib/cldr/dist/cldr/supplemental.js"></script>
  <script src="./lib/globalize/dist/globalize.js"></script>
  <script src="./lib/globalize/dist/globalize/message.js"></script>
  <script src="./lib/globalize/dist/globalize/plural.js"></script>
  <script src="./index.js"></script>
</body>
</html>

Wir benötigen außerdem die ergänzende JSON für plurals und wahrscheinlich auch die für ordinals, wenn wir Formatierungen wie „3.“ und „4.“ in unseren Nachrichten abdecken wollen. Wir müssen nur die Anforderungen zu unserem supplementals-Konfigurationsarray hinzufügen. Unsere App ist bereits so eingerichtet, dass sie das entsprechende JSON für uns beim Start lädt.

const defaultLocale = "ar";
const supplementals = [
  "likelySubtags",
  "plurals",
  "ordinals",
];
// ...

Jetzt können wir unsere Pluralnachrichten hinzufügen. Die ICU-Plural-Syntax ist weitgehend intuitiv: Eine count-Variable bestimmt die gewählte Pluralform, und ein spezielles #-Symbol wird beim Anzeigen durch den count-Wert ersetzt.

{
  "en": {
    // ...
    "neue-nachrichten": [
      // Wir können `count` so nennen, wie wir wollen - solange wir
      // den Key in unserem Aufruf von formatMessage() abstimmen.
      "You have {count, plural,",
      "    one {# new message}",
      "  other {# new messages}",
      "}"
    ]
  }
}

🗒 Hinweis » Globalize ermöglicht es uns, mehrzeilige Nachrichten durch die Verwendung von Arrays aufzuteilen.

Die sechs Pluralformen im Arabischen werden durch das ICU-Format behandelt.

{
  "ar": {
    // ...
    "new-messages": [
      "{count, plural, ",
      "   zero {لا توجد لديك رسائل جديدة}",
          one {لديك رسالة جديدة}
          two {لديك رسالتان جداد}
          few {لديك # رسائل جديدة}
         many {لديك # رسالة جديدة}
      "  other {لديك # رسالة جديدة}",
      "}"
    ]
  }
}

🤿 Mehr Info » Wir behandeln ICU-Mehrzahlformen ausführlicher in "Der fehlende Leitfaden zum ICU-Nachrichtenformat".

Natürlich müssen wir sicherstellen, dass count an Globalize.formatMessage() übergeben wird.

<!-- ... -->
    <p data-i18n-key="new-messages" data-i18n-opt='{"count": 110}'>
      You have # new messages
    </p>
<!-- ... -->

Und damit sind die Pluralformen im Grunde erledigt.

Globalize Demo-App auf Englisch mit Pluralisierung | Phrase
Globalize-Demo-App auf Arabisch mit Pluralisierung | Phrase
🔗 Ressource » Hol dir den gesamten Globalize-Demo-Code aus unserem GitHub-Repository.

🗒 Hinweis » Die ICU-Implementierung von Globalize bietet umfassende Datums- und Zahlenformatierung. Schau dir die offizielle Dokumentation an, um mehr zu erfahren.

🔗 Ressource » Wir behandeln weitere Globalize-Installationsoptionen und Anwendungsfälle in JS I18n mit Globalize.js.

Damit endet unser Leitfaden zur JavaScript-Lokalisierung.

Und das war's für dieses Mal. Wir hoffen, dir hat unser Ausflug in einige der beliebtesten JavaScript-Lokalisierungslösungen gefallen.

🔗 Ressource » Wenn du mit Ruby on Rails arbeitest, könnte dir JavaScript in Rails-Apps lokalisieren einiges bringen.

Und wenn du deinen Lokalisierungsprozess auf das nächste Level bringen willst, schau dir Phrase an. Phrase unterstützt das ICU-Format, alle anderen hier behandelten Übersetzungsformate und viele mehr. Mit seiner CLI und der Synchronisierung mit Bitbucket, GitHub und GitLab läuft deine i18n wie von selbst. Die voll ausgestattete Phrase-Webkonsole mit maschinellem Lernen und smarten Vorschlägen macht richtig Spaß beim Übersetzen. Und sobald die Übersetzungen bereit sind, können sie automatisch mit deinem Projekt synchronisiert werden. Einmal eingestellt, kannst du es vergessen und dich ganz auf deinen geliebten Code konzentrieren.

Schau dir alle Phrase-Funktionen für Entwickler an und überzeuge dich selbst, wie es deine Softwarelokalisierungs-Workflows vereinfachen kann.

Verwandte Beiträge

Blog post

Die Lokalisierung von Unity-Spielen mit dem offiziellen Phrase Plug-in

Willst du dein Unity-Spiel ohne das CSV-Chaos lokalisieren? Entdecke, wie das offizielle Phrase Strings Unity-Plugin deinen Lokalisierungs-Workflow vereinfacht – vom Setup der Strings-Tabelle bis zum direkten Abrufen von Übersetzungen in dein Projekt. Egal, ob du für Deutsch, Serbisch oder andere Sprachen entwickelst – dieser Leitfaden zeigt dir, wie du schnell loslegst und wie ein Profi lokalisierst.

Python localization blog post featured image | Phrase

Blog post

Der ultimative Guide zur Python-Lokalisierung

Mit dem Schritt-für-Schritt-Leitfaden zur Vorbereitung von Python-Apps für die Lokalisierung und den gängigen Modulen kann mehrsprachige Unterstützung für eine globale Benutzerbasis implementiert werden.

Vue localization blog post featured image | Phrase

Blog post

Ein umfassender Leitfaden zur Lokalisierung mit Vue

In diesem Leitfaden zur Vue-Lokalisierung erläutern wir, wie die Vue I18n Bibliothek in die App integriert werden kann, um sie für Nutzer weltweit zugänglich zu machen.

Software localization blog category featured image | Phrase

Blog post

Flutter-Lokalisierung: der ultimative Leitfaden

Lass uns die Geheimnisse der Flutter-Lokalisierung entschlüsseln, damit du die Sprache deiner Nutzer sprichst und deinen Weg zur globalen Dominanz programmierst.

Software localization blog category featured image | Phrase

Blog post

So wird das Lokalisierungs-Plugin von Phrase Strings für WordPress verwendet

Im Folgenden zeigen wir, wie WordPress-Seiten, -Beiträge und mehr mit der Integration von Phrase Strings für WordPress in mehrere Sprachen übersetzt werden können.