Intl.ListFormat und Intl.DisplayNames: Lokalisierte Listen und Namen ohne Uebersetzungstabellen
AI generated
JS
() =>
JavaScript · Internationalisierung · Browser-APIs
Intl.ListFormat und Intl.DisplayNames
Lokalisierte Listen und Namen ohne eigene Uebersetzungstabellen

Zwei unterschaetzte Intl-APIs uebernehmen Aufgaben, fuer die Teams sonst Uebersetzungstabellen pflegen: sprachlich korrekte Aufzaehlungen und lokalisierte Namen von Sprachen, Regionen und Waehrungen, direkt in der Browser-Engine.

14 Min. Lesezeit Intl.ListFormat Intl.DisplayNames i18n ohne Bibliothek

1. Warum native Lokalisierung wichtig ist

Wer eine Liste von Namen zu einem Satz zusammenfuegt, greift meist reflexhaft zu array.join(', '). Das Ergebnis ist im Englischen brauchbar, im Deutschen aber falsch: Aus 'Anna, Ben, Clara' wird nie 'Anna, Ben und Clara', wenn das letzte Komma stur durch join ersetzt wird. Jede Sprache hat eigene Regeln fuer Trennzeichen vor dem letzten Element, fuer Kurz- und Langformen und fuer den Unterschied zwischen Und-Listen und Oder-Listen. Wer das von Hand nachbaut, pflegt am Ende eine eigene kleine Grammatiktabelle pro Locale, die garantiert veraltet, sobald ein neues Sprachpaar dazukommt.

Das Intl-Namespace deckt mit NumberFormat und DateTimeFormat seit Jahren Zahlen und Datumswerte ab. Mit Intl.ListFormat und Intl.DisplayNames schliessen sich zwei Luecken, die vorher fast immer ueber Bibliotheken oder handgeschriebene Switch-Statements geloest wurden: sprachlich korrekte Aufzaehlungen und die Uebersetzung von Sprach-, Regions- und Waehrungscodes in lesbare Namen. Beide APIs nutzen dieselbe Locale-Datenbank wie der Rest von Intl, laufen komplett im Browser oder in Node und benoetigen kein zusaetzliches Bundle.

2. Intl.ListFormat: Grundlagen und Syntax

Der Konstruktor new Intl.ListFormat(locale, options) nimmt wie die anderen Intl-Klassen einen Locale-String oder ein Array von Locales sowie ein Optionsobjekt entgegen. Wichtig ist die format()-Methode: Sie erwartet ein Array von Strings und liefert einen fertig formatierten Satz zurueck, inklusive korrekter Trennzeichen und Konjunktion. Die Option style steuert die Ausfuehrlichkeit (long, short, narrow), die Option type steuert die Art der Verknuepfung.

Fuer Deutsch liefert format(['Rot', 'Gruen', 'Blau']) den String 'Rot, Gruen und Blau', fuer Englisch mit derselben Eingabe aber anderer Locale 'Red, Green, and Blue', inklusive Oxford-Komma nach den Regeln der jeweiligen Sprache. Der Entwickler muss dafuer keine einzige Sprachregel selbst kennen, er wechselt lediglich den Locale-String und bekommt automatisch korrekt formatierten Text.


const colorsDe = new Intl.ListFormat('de', { style: 'long', type: 'conjunction' });
console.log(colorsDe.format(['Rot', 'Gruen', 'Blau']));
// "Rot, Gruen und Blau"

const colorsEn = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' });
console.log(colorsEn.format(['Red', 'Green', 'Blue']));
// "Red, Green, and Blue"

3. Verschiedene Typen: Konjunktion, Disjunktion, Unit

Der type-Parameter unterscheidet drei Szenarien. conjunction erzeugt 'A, B und C' fuer Aufzaehlungen, bei denen alle Elemente gemeinsam gelten. disjunction erzeugt 'A, B oder C' fuer Auswahlmoeglichkeiten, etwa verfuegbare Zahlungsmethoden. unit schliesslich verbindet Werte ohne inhaltliche Konjunktion, etwa bei zusammengesetzten Massangaben wie '3 Stunden, 15 Minuten', wo weder 'und' noch 'oder' semantisch passt.

Fuer Faelle, in denen einzelne Listenelemente optisch hervorgehoben werden sollen, etwa als Links oder mit fetter Schrift, liefert formatToParts() statt eines fertigen Strings ein Array von Teilobjekten mit type ('element' oder 'literal') und value. Damit laesst sich die Liste in JSX oder Template-Strings gezielt rendern, ohne die Formatierungslogik selbst nachzubauen.


const paymentOptions = new Intl.ListFormat('de', { type: 'disjunction' });
console.log(paymentOptions.format(['PayPal', 'Kreditkarte', 'Vorkasse']));
// "PayPal, Kreditkarte oder Vorkasse"

const parts = new Intl.ListFormat('de', { type: 'conjunction' })
  .formatToParts(['Anna', 'Ben', 'Clara']);
// [{type:'element',value:'Anna'}, {type:'literal',value:', '}, ...]

4. Intl.DisplayNames: Sprachen, Regionen, Waehrungen uebersetzen

Viele Anwendungen speichern Sprach-, Laender- oder Waehrungscodes (en, DE, EUR) und muessen daraus lesbare Namen in der aktuellen UI-Sprache erzeugen, etwa fuer einen Sprachwaehler oder eine Preisuebersicht. Vor Intl.DisplayNames bedeutete das entweder eine eigene Uebersetzungstabelle mit hunderten Eintraegen pro unterstuetzter UI-Sprache oder den Import einer schweren Locale-Datenbibliothek.

new Intl.DisplayNames(locales, { type }).of(code) uebernimmt genau diese Aufgabe. Der type-Parameter akzeptiert language, region, currency, script, calendar und dateTimeField. Damit laesst sich aus dem Code 'fr' im Deutschen 'Franzoesisch' erzeugen, aus 'DE' im Englischen 'Germany' oder aus 'EUR' im Deutschen 'Euro', alles ohne eine einzige selbst gepflegte Zeile Uebersetzungsdaten.


const regionNamesDe = new Intl.DisplayNames(['de'], { type: 'region' });
console.log(regionNamesDe.of('FR')); // "Frankreich"

const languageNamesEn = new Intl.DisplayNames(['en'], { type: 'language' });
console.log(languageNamesEn.of('de')); // "German"

const currencyNamesDe = new Intl.DisplayNames(['de'], { type: 'currency' });
console.log(currencyNamesDe.of('USD')); // "US-Dollar"

5. Fallback-Verhalten und Fehlerbehandlung

Nicht jeder String ist ein gueltiger Code. Uebergibt man of() einen ungueltigen Sprach- oder Regionscode, wirft die Methode einen RangeError, waehrend echte, aber unbekannte Codes je nach fallback-Option unterschiedlich behandelt werden. Die Option fallback kann 'code' (Standard, gibt den Rohcode zurueck, wenn kein Name bekannt ist) oder 'none' (gibt undefined zurueck) sein.

In der Praxis empfiehlt sich, Nutzereingaben oder externe Daten vor dem Aufruf zu validieren und den Aufruf zusaetzlich in try/catch zu kapseln, insbesondere wenn Codes aus einer API oder von Drittanbietern stammen. So verhindert man, dass eine einzelne fehlerhafte Codezeile die komplette Sprachauswahl-UI zum Absturz bringt, statt nur einen Eintrag auszulassen.


function safeRegionName(locale, code) {
  const dn = new Intl.DisplayNames([locale], { type: 'region', fallback: 'code' });
  try {
    return dn.of(code);
  } catch (error) {
    return code; // ungueltiges Format, Rohcode als Fallback
  }
}

6. Praxisbeispiel: Sprachauswahl-UI kombinieren

Beide APIs entfalten ihren Nutzen erst richtig in Kombination. Eine typische Aufgabe: Aus einem Array verfuegbarer Locale-Codes soll ein Satz wie 'Diese Seite ist verfuegbar in Deutsch, Englisch und Franzoesisch' entstehen, wobei die Namen selbst wiederum in der aktuell aktiven UI-Sprache angezeigt werden sollen, nicht in der jeweiligen Zielsprache.

Dafuer wird zuerst jeder Code ueber Intl.DisplayNames in einen lesbaren Namen uebersetzt, anschliessend wird das entstehende Array ueber Intl.ListFormat zu einem grammatikalisch korrekten Satz zusammengefuegt. Beide Schritte respektieren die aktuelle UI-Locale konsistent, sodass sich die gesamte Logik beim Sprachwechsel lediglich durch Austausch eines einzigen Locale-Strings anpasst.


function describeAvailableLanguages(uiLocale, availableCodes) {
  const languageNames = new Intl.DisplayNames([uiLocale], { type: 'language' });
  const names = availableCodes.map((code) => languageNames.of(code));
  const list = new Intl.ListFormat(uiLocale, { style: 'long', type: 'conjunction' });
  return list.format(names);
}

describeAvailableLanguages('de', ['de', 'en', 'fr']);
// "Deutsch, Englisch und Franzoesisch"

7. Performance: Instanzen wiederverwenden und cachen

Die Konstruktion eines Intl.ListFormat- oder Intl.DisplayNames-Objekts ist im Vergleich zu einem einfachen String-Join teuer, weil die Engine Locale-Daten laden und Regeln kompilieren muss. Wird eine Instanz nur fuer einen einzigen format()-Aufruf angelegt und danach verworfen, etwa innerhalb einer Render-Funktion, die bei jedem State-Update neu laeuft, entsteht unnoetiger Overhead.

Best Practice ist, Instanzen pro Locale und Optionskombination einmalig zu erzeugen und in einer Map zu cachen, statt sie bei jedem Aufruf neu zu instanziieren. Der Cache-Key setzt sich aus Locale und den relevanten Optionen zusammen. Bei einem Sprachwechsel wird lediglich ein neuer Cache-Eintrag angelegt, alte Eintraege koennen bei Bedarf verworfen werden.


const listFormatCache = new Map();

function getListFormat(locale, options) {
  const key = `${locale}:${options.type ?? 'conjunction'}:${options.style ?? 'long'}`;
  if (!listFormatCache.has(key)) {
    listFormatCache.set(key, new Intl.ListFormat(locale, options));
  }
  return listFormatCache.get(key);
}

8. Browser-Support und Feature-Detection

Intl.ListFormat und Intl.DisplayNames sind in allen aktuellen evergreen Browsern (Chrome, Edge, Firefox, Safari) sowie in Node.js ab Version 14 verfuegbar, gehoeren also inzwischen zur Grundausstattung moderner JavaScript-Umgebungen. Fuer Projekte, die noch aeltere Browser oder eingeschraenkte Node-Builds ohne volle ICU-Daten unterstuetzen muessen, bietet das formatjs-Projekt Polyfills mit identischer API.

Eine einfache Feature-Detection prueft vor dem Einsatz, ob die Klasse ueberhaupt existiert, und faellt andernfalls auf eine simple join-Implementierung zurueck, die zwar nicht perfekt lokalisiert ist, aber wenigstens keinen Laufzeitfehler produziert. So bleibt die Anwendung auch in seltenen Alt-Umgebungen funktionsfaehig, waehrend moderne Browser die volle lokalisierte Ausgabe erhalten.

9. Best Practices und Zusammenfassung

Intl.ListFormat und Intl.DisplayNames ersetzen zwei Klassen von Code, die Teams bisher oft selbst gepflegt haben: Grammatikregeln fuer Aufzaehlungen und Uebersetzungstabellen fuer Sprach-, Regions- und Waehrungscodes. Beide APIs sind kostenlos, laufen nativ in der Engine und liefern nach den offiziellen CLDR-Locale-Daten formatierte Ausgaben, die sich mit jedem Browser-Update automatisch aktualisieren.

In der Praxis lohnt sich die Kombination mit anderen Intl-APIs wie NumberFormat und DateTimeFormat zu einer einheitlichen Lokalisierungsschicht, ergaenzt um Instanz-Caching fuer Performance und Validierung beziehungsweise try/catch fuer robuste Fehlerbehandlung bei unbekannten Codes. Wer diese Bausteine konsequent nutzt, braucht fuer viele i18n-Aufgaben keine externe Bibliothek mehr.

API Zweck Wichtige Option Beispiel-Ausgabe (de)
Intl.ListFormat Aufzaehlungen grammatikalisch korrekt verbinden type: conjunction/disjunction/unit 'Rot, Gruen und Blau'
Intl.DisplayNames Codes in lesbare Namen uebersetzen type: language/region/currency 'Frankreich' fuer 'FR'
formatToParts() Listenteile einzeln rendern (Links, Fettschrift) - [{type:'element',...}]
Instanz-Cache Wiederholte Konstruktion vermeiden Map nach Locale+Optionen -

Mironsoft

Moderne Browser-APIs, Performance und wartbares JavaScript

JavaScript, das im echten Browser robust bleibt, nicht nur im Tutorial?

Wir prüfen bestehenden Frontend-Code auf veraltete Patterns, unnötige Bibliotheken und Performance-Fallen und ersetzen sie durch moderne, native Browser-APIs, die weniger Bundle-Gewicht und weniger Wartungslast bedeuten.

Code-Review

Veraltete Patterns, unnötige Dependencies und Memory Leaks systematisch aufspüren.

Performance-Optimierung

Bundle-Größe, Ladezeit und Runtime-Performance mit modernen APIs verbessern.

Modernisierung

Native Browser-APIs statt schwerer Bibliotheken gezielt einführen.

10. Zusammenfassung

Intl.ListFormat & DisplayNames: Das Wichtigste auf einen Blick

ListFormat

Fuegt Arrays zu grammatikalisch korrekten Aufzaehlungen zusammen, inklusive korrekter Konjunktionen pro Sprache.

DisplayNames

Uebersetzt Sprach-, Regions- und Waehrungscodes in lesbare Namen ohne eigene Uebersetzungstabelle.

Performance

Intl-Instanzen sind teuer in der Erzeugung, deshalb pro Locale und Option cachen statt neu instanziieren.

Support

In allen evergreen Browsern und Node ab 14 verfuegbar, formatjs liefert Polyfills fuer Altfaelle.

11. FAQ: Intl.ListFormat & DisplayNames: Das Wichtigste auf einen Blick

1Wofuer wird Intl.ListFormat konkret gebraucht?
Fuer das lokalisierte Zusammenfuegen von Arrays zu Saetzen, etwa Tag-Listen, Zutatenlisten oder verfuegbare Zahlungsmethoden, mit sprachlich korrekten Trennzeichen und Konjunktionen statt eines einfachen Kommas.
2Was ist der Unterschied zwischen type conjunction und disjunction?
Conjunction erzeugt Und-Listen wie 'A, B und C', disjunction erzeugt Oder-Listen wie 'A, B oder C'. Unit verbindet Werte ohne inhaltliche Verknuepfung, etwa zusammengesetzte Massangaben.
3Kann Intl.DisplayNames auch Skripte und Kalender uebersetzen?
Ja, der type-Parameter akzeptiert neben language, region und currency auch script, calendar und dateTimeField, womit sich etwa 'gregory' als 'Gregorianischer Kalender' ausgeben laesst.
4Was passiert bei einem unbekannten, aber syntaktisch gueltigen Code?
Abhaengig von der fallback-Option gibt of() entweder den Rohcode zurueck (fallback: 'code', Standard) oder undefined (fallback: 'none'). Ein syntaktisch ungueltiger Code wirft dagegen einen RangeError.
5Sind diese APIs teuer in der Ausfuehrung?
Die Konstruktion einer Instanz ist vergleichsweise teuer, der eigentliche format()-Aufruf danach ist schnell. Deshalb Instanzen pro Locale und Optionskombination cachen statt bei jedem Render neu erzeugen.
6Funktioniert das auch in Node.js?
Ja, ab Node.js 14 mit vollstaendigen ICU-Daten (Standard bei den offiziellen Node-Builds seit Version 14). Bei minimalen ICU-Builds koennen einzelne Locales fehlen.
7Brauche ich noch eine i18n-Bibliothek daneben?
Fuer UI-Text-Uebersetzungen selbst ja, aber fuer Listenformatierung und Code-zu-Name-Uebersetzung ersetzen ListFormat und DisplayNames typische Bibliotheksfunktionen vollstaendig und ohne zusaetzliches Bundle-Gewicht.
8Wie verhaelt sich formatToParts im Vergleich zu format?
format() liefert einen fertigen String, formatToParts() liefert ein Array von Teilobjekten mit type und value, was gezielte Formatierung einzelner Listenelemente in JSX oder Templates erlaubt.
9Gibt es einen Polyfill fuer aeltere Browser?
Ja, das formatjs-Projekt bietet mit @formatjs/intl-listformat und @formatjs/intl-displaynames API-kompatible Polyfills fuer Umgebungen ohne native Unterstuetzung.
10Kann ich mit DisplayNames auch Emoji-Flaggen erzeugen?
Nein, DisplayNames liefert nur Textnamen, keine Flaggen-Emojis. Flaggen muessten separat ueber Unicode-Regional-Indicator-Symbole aus dem Regionscode zusammengesetzt werden.