Rustango docs
← Anleitungen

Internationalisierung (i18n)

Internationalisierung bedeutet, Ihre App in der Sprache des Nutzers auszuliefern. Rustango teilt sie in zwei Hälften: das Auflösen, welche Locale eine Anfrage möchte (Cookie → Accept-Language → Standard — behandelt in Middleware), und das Übersetzen von Strings in diese Locale, worum es in diesem Leitfaden geht. Der Translator lädt Nachrichtenkataloge pro Locale, ersetzt {placeholders}, behandelt Pluralformen und fällt elegant zurück — Djangos gettext / {% trans %}, in Rust.

i18n in Rustango: Ein Translator hält Kataloge pro Locale; gettext schlägt einen Schlüssel mit Fallback nach (Locale → Basissprache → Standard → der Schlüssel selbst), ersetzt {name}-Platzhalter, und ngettext wählt Singular vs. Plural

Ein Begriff hier neu? Locale, Katalog, Accept-Language, Pluralisierung, RTL — siehe das Glossar.

Quelle: rustango::i18n (Translator, Locale, negotiate_language, plural_category, is_rtl_language, language_native_name und die tera_tags-Template-Bindings) — immer kompiliert. Die Middleware für Locale/Zeitzone pro Anfrage liegt in rustango::i18n::middleware / ::timezone (siehe Middleware).

Lauffähige Version: Jeder Ausschnitt ist aus i18n_doc.rs kopiert (cargo test -p rustango --test i18n_doc); DB-gestützte Übersetzungs-Overrides werden im Realbetrieb von i18n_db_overrides_sqlite_live.rs erprobt.

Inhaltsverzeichnis


Die zwei Hälften von i18n

AnliegenWoWas es tut
Die Locale auflöseni18n::middleware::LocaleMiddlewarewählt eine Locale pro Anfrage (Cookie → Accept-Language → Standard) und stellt den ActiveLocale-Extractor bereit
Strings übersetzeni18n::Translator (dieser Leitfaden)schlägt einen Nachrichtenschlüssel im Katalog der aktiven Locale nach
Daten in der TZ des Nutzers renderni18n::timezoneder {{ ts | localtime }}-Filter

Sie verdrahten die Middleware einmal und übersetzen dann mit der von ihr aufgelösten Locale.


Schritt 1 — Einen Translator bauen

Ein Translator hält einen Katalog (eine key → message-Map) pro Locale. Bauen Sie ihn mit einer Standard-Locale und fügen Sie dann Kataloge hinzu:

use rustango::i18n::{Locale, Translator};
use std::collections::HashMap;

let mut en = HashMap::new();
en.insert("greeting".to_owned(), "Hello".to_owned());
en.insert("welcome".to_owned(), "Welcome, {name}!".to_owned());

let mut fr = HashMap::new();
fr.insert("greeting".to_owned(), "Bonjour".to_owned());

let translator = Translator::new(Locale::new("en"))   // default locale
    .add_locale(Locale::new("en"), en)
    .add_locale(Locale::new("fr"), fr);

Halten Sie den Translator in Ihrem App-State (er ist günstig zum Clone-Teilen) und schlagen Sie Strings mit der von der Middleware aufgelösten ActiveLocale nach.


Schritt 2 — Strings übersetzen (gettext)

gettext(locale, key) gibt die Nachricht für diese Locale zurück — und degradiert sicher, wenn etwas fehlt:

translator.gettext("en", "greeting");      // "Hello"
translator.gettext("fr", "greeting");      // "Bonjour"

translator.gettext("en", "missing.key");   // "missing.key"  — returns the key, never panics
translator.gettext("de", "greeting");      // "Hello"        — unknown locale → default

Ein fehlender Schlüssel gibt den Schlüssel selbst zurück, sodass ein nicht übersetzter String sichtbar auftaucht, statt die Seite zum Absturz zu bringen.


Platzhalter

Nachrichten können {name}-Platzhalter tragen; translate ersetzt sie aus Schlüssel/Wert-Paaren:

// catalog: "welcome" = "Welcome, {name}!"
translator.translate("en", "welcome", &[("name", "Ada")]);   // "Welcome, Ada!"

gettext ist die Kurzform ohne Platzhalter; translate (und gettext_fmt) nehmen Parameter.


Pluralisierung

Pluralformen unterscheiden sich nach Sprache und Anzahl. ngettext ist die Zwei-Formen-Kurzform: Sie wählt den Singular-Schlüssel für die CLDR-Kategorie one und ansonsten den Plural-Schlüssel und bindet {count} automatisch:

// catalog: "cart.one" = "1 item",  "cart.other" = "{count} items"
translator.ngettext("en", "cart.one", "cart.other", 1);   // "1 item"
translator.ngettext("en", "cart.one", "cart.other", 5);   // "5 items"

Verwenden Sie ngettext_fmt, um zusätzliche Platzhalter neben {count} zu übergeben.

Sprachen mit mehr als zwei Formen

Polnisch, Ukrainisch, Russisch, Arabisch und andere haben few / many-Formen, die ein Singular/Plural-Paar nicht ausdrücken kann. plural_category(locale, n) gibt die CLDR-Kategorie zurück (one / few / many / other), und translate_plural wählt die passende Form aus einem Katalog pro Kategorie — ein Eintrag pro Schlüssel, der alle seine Formen hält:

use rustango::i18n::plural_category;

plural_category("en", 1);   // "one"
plural_category("fr", 0);   // "one"   — French treats 0 as singular
plural_category("pl", 2);   // "few"   — Polish 2–4
plural_category("pl", 5);   // "many"

// pl plural catalog: "deleted_pages" → { one, few, many }
let n = 5;
translator.translate_plural("pl", "deleted_pages", n, &[("count", &n.to_string())]);
// → "Usunięto 5 stron."   (the `many` form)

Eine fehlende Form fällt auf other zurück, dann auf den skalaren Lookup (und schließlich den Schlüssel), sodass eine nicht übersetzte Sprache dennoch rendert. Ostasiatische Sprachen (zh, ja, …) haben eine einzige other-Form.


Fallback-Reihenfolge

Lookups laufen eine Kette entlang, sodass eine partielle Übersetzung nie eine Lücke lässt: die angefragte Locale → ihre Basissprache → die Fallback-Kette → die Standard-Locale → der Schlüssel selbst. So löst eine frankokanadische Anfrage gegen den fr-Katalog auf:

// only "fr" is registered, not "fr-CA"
translator.gettext("fr-CA", "greeting");   // "Bonjour"  — base-language fallback

Setzen Sie zusätzliche Fallbacks mit Translator::new(default).with_fallback_chain(&["en"]).


Die Sprache aushandeln

Wenn Sie die Locale selbst auflösen (außerhalb der Middleware), parst negotiate_language einen Browser-Accept-Language-Header und wählt die beste Übereinstimmung aus den von Ihnen unterstützten Locales:

use rustango::i18n::negotiate_language;

negotiate_language("fr-FR,fr;q=0.9,en;q=0.8", &["en", "fr"]);   // Some("fr")
negotiate_language("de,ja;q=0.5", &["en", "fr"]);               // None — fall back to default

Genau das nutzt LocaleMiddleware unter der Haube.


Rechts-nach-links-Sprachen

is_rtl_language (und Locale::direction()) sagen Ihnen, ob Sie dir="rtl" auf der Seite setzen sollten — für Arabisch, Hebräisch, Persisch usw.:

use rustango::i18n::is_rtl_language;

is_rtl_language("ar");   // true
is_rtl_language("en");   // false

In einem Template: <html dir="{{ direction }}">, gespeist aus der ActiveLocale. Siehe die RTL-Notiz in Middleware.


In Templates übersetzen (Tera)

Alles Obige ist die Rust-API; in Tera-Templates — HTML-Views und die Admin-UI — wird derselbe Translator als Filter/Funktionen bereitgestellt. Registrieren Sie sie einmal gegen den Translator, und die aktive Locale (die von der Middleware gesetzte Kontextvariable LANG) treibt jeden Lookup:

rustango::i18n::tera_tags::register(&mut tera, translator.clone());
{{ "Save" | translate(locale=LANG) }}
<button title="{{ "Delete" | translate(locale=LANG) }}">…</button>

{# count-aware: pass the count both as the selector `n` and as a {count} arg #}
{{ translate_plural(key="deleted_pages", n=num, locale=LANG, count=num) }}

<html lang="{{ LANG }}" dir="{{ get_text_direction(locale=LANG) }}">

Der Schlüssel ist der englische Ausgangs-String (gettext-Stil), sodass ein nicht übersetzter String auf Englisch rendert statt leer — Sie umschließen einen UI-String zuerst und übersetzen ihn später, ohne eine Schlüssel-Registry zu pflegen.

Genau so lokalisiert rustango-cms seinen Admin: Jeder Chrome-String läuft durch translate / translate_plural, Kataloge werden pro Locale unter src/admin/locales/ ausgeliefert, die aktive Locale wird über die Kette Cookie → Nutzerpräferenz → Accept-Language → Tenant-Standard aufgelöst, und ein Seitenleisten-Umschalter plus eine Nutzer-Einstellung Preferences → Language lassen Redakteure wählen. Betreiber-Änderungen über die DB-Override-Schicht (unten) wirken ohne Redeploy.


Kataloge laden + Laufzeit-Overrides

In der Produktion bauen Sie selten Maps von Hand. Zwei Loader:

  • Translator::from_directory(dir, default) — lädt beim Start eine Katalogdatei pro Locale aus einem Verzeichnis.
  • Translator::from_settings(&settings.i18n) — verdrahtet ihn aus Ihrer Konfiguration.

Und Betreiber können Übersetzungen zur Laufzeit ohne Redeploy bearbeiten: set_override(locale, key, value) (oder load_overrides(rows) aus einer Tabelle) legen sich über die Datei-Kataloge und gewinnen für diesen Schlüssel. Dieser DB-Override-Fluss treibt den Admin-Übersetzungseditor und wird in i18n_db_overrides_sqlite_live.rs im Realbetrieb erprobt.


Siehe auch

  • Middleware — das Auflösen der Locale pro Anfrage (LocaleMiddleware, ActiveLocale) und der Zeitzonenfilter {{ ts | localtime }}.
  • HTML-Views · Der Admin — wo übersetzte Strings und der Übersetzungseditor auftauchen.
  • Glossar — Locale, Katalog, RTL und Verwandte in klarer Sprache.