Wie man Übersetzungen aus Dateien in React Router v7 lädt

Übersetzbaren Inhalt vom Code trennen

Problem

Die direkte Hardcodierung von benutzerorientierten Zeichenketten in Komponenten erzeugt eine enge Kopplung zwischen Inhalt und Code. Jede neue Sprache erfordert, dass Entwickler Implementierungsdateien modifizieren, bedingte Logik erweitern und die Komplexität erhöhen. Wenn sich Texte ändern, erfordern selbst geringfügige Anpassungen der Formulierung Code-Deployments. Dieser Ansatz macht Übersetzungsworkflows von Engineering-Zyklen abhängig und verhindert, dass nicht-technische Teammitglieder Inhalte unabhängig verwalten können.

Mit dem Wachstum von Anwendungen werden verstreute String-Literale schwer zu verfolgen und zu warten. Jedes Vorkommen einer Phrase in einer Codebasis zu finden ist fehleranfällig, und die Konsistenz ähnlicher Nachrichten wird ohne eine zentralisierte Quelle der Wahrheit nahezu unmöglich sicherzustellen.

Lösung

Extrahieren Sie alle übersetzbaren Zeichenketten in externe JSON-Dateien, die nach Sprachen organisiert sind, mit einer Datei pro Locale. Ersetzen Sie hartcodierte Zeichenketten in Komponenten durch Nachrichten-Identifikatoren, die auf Einträge in diesen Dateien verweisen. Zur Laufzeit lädt die Anwendung die entsprechende Übersetzungsdatei basierend auf dem Locale des Benutzers und stellt diese Nachrichten der Internationalisierungsbibliothek zur Verfügung, die Identifikatoren in ihre übersetzten Werte auflöst.

Diese Trennung ermöglicht es Übersetzern, direkt mit JSON-Dateien zu arbeiten, ohne Code anzufassen, ermöglicht Inhaltsaktualisierungen durch einfache Dateiänderungen und bietet eine einzige Quelle der Wahrheit für die Zeichenketten jeder Sprache.

Schritte

1. Erstellen Sie Übersetzungsdateien für jedes Locale

Organisieren Sie Übersetzungsdateien in einem dedizierten Verzeichnis mit einer JSON-Datei pro Sprache. Strukturieren Sie jede Datei als ein flaches Objekt, das Nachrichten-Identifikatoren auf übersetzte Zeichenketten abbildet.

{
"welcome.title": "Welcome back",
"welcome.subtitle": "Continue where you left off",
"nav.home": "Home",
"nav.about": "About",
"nav.contact": "Contact"
}

Speichern Sie dies als app/translations/en.json für Englisch, und erstellen Sie dann parallele Dateien wie app/translations/es.json und app/translations/fr.json mit Übersetzungen für andere Sprachen. Verwenden Sie konsistente Schlüssel in allen Dateien, damit derselbe Identifikator in jedem Locale zur entsprechenden Übersetzung aufgelöst wird.

2. Übersetzungen in einem Route-Loader laden

Verwenden Sie einen Route-Loader, um die Übersetzungsdatei für die aktuelle Locale vor dem Rendering abzurufen. Dies stellt sicher, dass Nachrichten verfügbar sind, wenn Komponenten gemountet werden.

import type { Route } from "./+types/root";
import enMessages from "./translations/en.json";
import esMessages from "./translations/es.json";
import frMessages from "./translations/fr.json";

const messages: Record<string, Record<string, string>> = {
  en: enMessages,
  es: esMessages,
  fr: frMessages,
};

export async function loader({ request }: Route.LoaderArgs) {
  const url = new URL(request.url);
  const locale = url.searchParams.get("locale") || "en";

  return {
    locale,
    messages: messages[locale] || messages.en,
  };
}

Der Loader liest die Locale aus dem URL-Abfrageparameter und gibt sowohl die Locale als auch die entsprechenden Nachrichten zurück. Komponenten können auf diese Daten über loaderData zugreifen, um den Internationalisierungsanbieter zu konfigurieren.

3. Konfigurieren des IntlProvider mit geladenen Nachrichten

Umschließen Sie Ihre Anwendung mit dem IntlProvider von react-intl und übergeben Sie die Locale und Nachrichten aus den Loader-Daten.

import { IntlProvider } from "react-intl";
import { Outlet } from "react-router";
import type { Route } from "./+types/root";

export default function Root({ loaderData }: Route.ComponentProps) {
  return (
    <IntlProvider locale={loaderData.locale} messages={loaderData.messages}>
      <html lang={loaderData.locale}>
        <head>
          <meta charSet="utf-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1" />
        </head>
        <body>
          <Outlet />
        </body>
      </html>
    </IntlProvider>
  );
}

Der IntlProvider macht die Locale und Nachrichten für alle untergeordneten Komponenten über den React-Kontext verfügbar. Untergeordnete Routen werden durch den Outlet gerendert und erben den Zugriff auf die Übersetzungsdaten.

4. Referenzieren von Nachrichten durch Identifikatoren in Komponenten

Ersetzen Sie hartcodierte Zeichenketten durch FormattedMessage-Komponenten, die auf Nachrichtenidentifikatoren aus Ihren Übersetzungsdateien verweisen.

import { FormattedMessage } from "react-intl";

export default function Welcome() {
  return (
    <div>
      <h1>
        <FormattedMessage id="welcome.title" />
      </h1>
      <p>
        <FormattedMessage id="welcome.subtitle" />
      </p>
      <nav>
        <a href="/">
          <FormattedMessage id="nav.home" />
        </a>
        <a href="/about">
          <FormattedMessage id="nav.about" />
        </a>
        <a href="/contact">
          <FormattedMessage id="nav.contact" />
        </a>
      </nav>
    </div>
  );
}

Jede FormattedMessage-Komponente sucht ihre id im Nachrichtenobjekt, das vom IntlProvider bereitgestellt wird, und rendert die entsprechende übersetzte Zeichenkette. Wenn sich die Locale ändert und der Loader erneut mit anderen Nachrichten ausgeführt wird, zeigen alle Komponenten automatisch die neuen Übersetzungen ohne Codeänderungen an.