Intl.ListFormat API
Arrays in lokal angepasste, lesbare Listen formatieren
Einführung
Wenn mehrere Elemente für Nutzer:innen angezeigt werden, verbinden Entwickler:innen Arrays oft mit Kommas und fügen vor dem letzten Element ein "und" hinzu:
const users = ["Alice", "Bob", "Charlie"];
const message = users.slice(0, -1).join(", ") + ", and " + users[users.length - 1];
// "Alice, Bob, and Charlie"
Dieser Ansatz kodiert die englischen Zeichensetzungsregeln fest und funktioniert in anderen Sprachen nicht korrekt. Im Japanischen werden andere Partikeln verwendet, im Deutschen gelten andere Abstandsregeln und im Chinesischen gibt es andere Trennzeichen. Die Intl.ListFormat API löst dieses Problem, indem sie Listen gemäß den Konventionen der jeweiligen Sprache formatiert.
Was macht Intl.ListFormat?
Intl.ListFormat wandelt Arrays in menschenlesbare Listen um, die die grammatikalischen und zeichensetzungsbezogenen Regeln jeder Sprache einhalten. Sie unterstützt drei Listentypen, die in allen Sprachen vorkommen:
- Konjunktionslisten verwenden "und" zur Verbindung von Elementen ("A, B und C")
- Disjunktionslisten nutzen "oder" für Alternativen ("A, B oder C")
- Einheitenlisten formatieren Maßeinheiten ohne Konjunktionen ("5 ft, 2 in")
Die API kennt die Formatierung dieser Listentypen in jeder Sprache – von der Zeichensetzung über die Wortwahl bis hin zu Abständen.
Grundlegende Verwendung
Erstellen Sie einen Formatter mit einer Locale und Optionen und rufen Sie dann format() mit einem Array auf:
const formatter = new Intl.ListFormat("en", {
type: "conjunction",
style: "long"
});
const items = ["bread", "milk", "eggs"];
console.log(formatter.format(items));
// "bread, milk, and eggs"
Der Formatter verarbeitet Arrays beliebiger Länge, auch Randfälle:
formatter.format([]); // ""
formatter.format(["bread"]); // "bread"
formatter.format(["bread", "milk"]); // "bread and milk"
Listentypen steuern Konjunktionen
Die Option type bestimmt, welche Konjunktion in der formatierten Liste verwendet wird.
Konjunktionslisten
Verwenden Sie type: "conjunction" für Listen, bei denen alle Elemente gemeinsam gelten. Dies ist der Standardtyp:
const formatter = new Intl.ListFormat("en", { type: "conjunction" });
console.log(formatter.format(["HTML", "CSS", "JavaScript"]));
// "HTML, CSS, and JavaScript"
Typische Anwendungsfälle sind das Anzeigen ausgewählter Elemente, das Auflisten von Features und das Darstellen mehrerer Werte, die alle zutreffen.
Disjunktionslisten
Verwenden Sie type: "disjunction" für Listen, die Alternativen oder Auswahlmöglichkeiten präsentieren:
const formatter = new Intl.ListFormat("en", { type: "disjunction" });
console.log(formatter.format(["credit card", "debit card", "PayPal"]));
// "credit card, debit card, or PayPal"
Dies erscheint in Optionslisten, Fehlermeldungen mit mehreren Lösungen und in jedem Kontext, in dem Benutzer ein Element auswählen.
Einheitenlisten
Verwenden Sie type: "unit" für Messungen und technische Werte, die ohne Konjunktionen erscheinen sollen:
const formatter = new Intl.ListFormat("en", { type: "unit" });
console.log(formatter.format(["5 feet", "2 inches"]));
// "5 feet, 2 inches"
Einheitenlisten funktionieren für Messungen, technische Spezifikationen und zusammengesetzte Werte.
Listenstile steuern die Ausführlichkeit
Die Option style passt an, wie ausführlich die Formatierung erscheint. Es gibt drei Stile: long, short und narrow.
const items = ["Monday", "Wednesday", "Friday"];
const long = new Intl.ListFormat("en", { style: "long" });
console.log(long.format(items));
// "Monday, Wednesday, and Friday"
const short = new Intl.ListFormat("en", { style: "short" });
console.log(short.format(items));
// "Monday, Wednesday, and Friday"
const narrow = new Intl.ListFormat("en", { style: "narrow" });
console.log(narrow.format(items));
// "Monday, Wednesday, Friday"
Im Englischen erzeugen long und short für die meisten Listen identische Ausgaben. Der Stil narrow lässt die Konjunktion weg. Andere Sprachen zeigen mehr Variation zwischen den Stilen, insbesondere bei Disjunktionslisten.
Wie verschiedene Sprachen Listen formatieren
Jede Sprache hat unterschiedliche Regeln für die Listenformatierung. Intl.ListFormat behandelt diese Unterschiede automatisch.
Englisch verwendet Kommas, Leerzeichen und Konjunktionen:
const en = new Intl.ListFormat("en");
console.log(en.format(["Tokyo", "Paris", "London"]));
// "Tokyo, Paris, and London"
Deutsch verwendet die gleiche Kommastruktur, aber andere Konjunktionen:
const de = new Intl.ListFormat("de");
console.log(de.format(["Tokyo", "Paris", "London"]));
// "Tokyo, Paris und London"
Japanisch verwendet andere Trennzeichen und Partikeln:
const ja = new Intl.ListFormat("ja");
console.log(ja.format(["東京", "パリ", "ロンドン"]));
// "東京、パリ、ロンドン"
Chinesisch verwendet völlig andere Interpunktion:
const zh = new Intl.ListFormat("zh");
console.log(zh.format(["东京", "巴黎", "伦敦"]));
// "东京、巴黎和伦敦"
Diese Unterschiede gehen über die Interpunktion hinaus und umfassen Abstandsregeln, Konjunktionsplatzierung und grammatikalische Partikeln. Das Hardcodieren eines einzelnen Ansatzes funktioniert für andere Sprachen nicht.
Verwendung von formatToParts für benutzerdefiniertes Rendering
Die Methode formatToParts() gibt ein Array von Objekten anstelle eines Strings zurück. Jedes Objekt repräsentiert ein Element der formatierten Liste:
const formatter = new Intl.ListFormat("en");
const parts = formatter.formatToParts(["red", "green", "blue"]);
console.log(parts);
// [
// { type: "element", value: "red" },
// { type: "literal", value: ", " },
// { type: "element", value: "green" },
// { type: "literal", value: ", and " },
// { type: "element", value: "blue" }
// ]
Jeder Teil hat einen type und einen value. Der type ist entweder "element" für Listenelemente oder "literal" für Formatierungsinterpunktion und Konjunktionen.
Diese Struktur ermöglicht benutzerdefiniertes Rendering, bei dem Elemente und Literale unterschiedliche Stile benötigen:
const formatter = new Intl.ListFormat("en");
const items = ["Alice", "Bob", "Charlie"];
const html = formatter.formatToParts(items)
.map(part => {
if (part.type === "element") {
return `<strong>${part.value}</strong>`;
}
return part.value;
})
.join("");
console.log(html);
// "<strong>Alice</strong>, <strong>Bob</strong>, and <strong>Charlie</strong>"
Dieser Ansatz bewahrt die lokal korrekte Interpunktion und wendet gleichzeitig eine benutzerdefinierte Darstellung auf die eigentlichen Listenelemente an.
Wiederverwendung von Formatierern für bessere Performance
Das Erstellen von Intl.ListFormat-Instanzen verursacht Overhead. Erstellen Sie Formatierer einmal und verwenden Sie sie wieder:
// Create once
const listFormatter = new Intl.ListFormat("en", { type: "conjunction" });
// Reuse many times
function displayUsers(users) {
return listFormatter.format(users.map(u => u.name));
}
function displayTags(tags) {
return listFormatter.format(tags);
}
Für Anwendungen mit mehreren Locales speichern Sie Formatierer in einer Map:
const formatters = new Map();
function getListFormatter(locale, options) {
const key = `${locale}-${options.type}-${options.style}`;
if (!formatters.has(key)) {
formatters.set(key, new Intl.ListFormat(locale, options));
}
return formatters.get(key);
}
const formatter = getListFormatter("en", { type: "conjunction", style: "long" });
console.log(formatter.format(["a", "b", "c"]));
Dieses Muster reduziert wiederholte Initialisierungskosten und unterstützt gleichzeitig mehrere Locales und Konfigurationen.
Formatierung von Fehlermeldungen
Formularvalidierung erzeugt oft mehrere Fehler. Formatieren Sie diese mit Disjunktionslisten, um Optionen darzustellen:
const formatter = new Intl.ListFormat("en", { type: "disjunction" });
function validatePassword(password) {
const errors = [];
if (password.length < 8) {
errors.push("at least 8 characters");
}
if (!/[A-Z]/.test(password)) {
errors.push("an uppercase letter");
}
if (!/[0-9]/.test(password)) {
errors.push("a number");
}
if (errors.length > 0) {
return `Password must contain ${formatter.format(errors)}.`;
}
return null;
}
console.log(validatePassword("weak"));
// "Password must contain at least 8 characters, an uppercase letter, or a number."
Die Disjunktionsliste verdeutlicht, dass Benutzer eines dieser Probleme beheben müssen, und die Formatierung passt sich den Konventionen jedes Locales an.
Anzeige ausgewählter Elemente
Wenn Benutzer mehrere Elemente auswählen, formatieren Sie die Auswahl mit Konjunktionslisten:
const formatter = new Intl.ListFormat("en", { type: "conjunction" });
function getSelectionMessage(selectedFiles) {
if (selectedFiles.length === 0) {
return "No files selected";
}
if (selectedFiles.length === 1) {
return `${selectedFiles[0]} selected`;
}
return `${formatter.format(selectedFiles)} selected`;
}
console.log(getSelectionMessage(["report.pdf", "data.csv", "notes.txt"]));
// "report.pdf, data.csv, and notes.txt selected"
Dieses Muster funktioniert für Dateiauswahlen, Filteroptionen, Kategorieauswahlen und jede Multi-Select-Oberfläche.
Umgang mit langen Listen
Bei Listen mit vielen Elementen sollten Sie vor der Formatierung kürzen:
const formatter = new Intl.ListFormat("en", { type: "conjunction" });
function formatUserList(users) {
if (users.length <= 3) {
return formatter.format(users);
}
const visible = users.slice(0, 2);
const remaining = users.length - 2;
return `${formatter.format(visible)}, and ${remaining} others`;
}
console.log(formatUserList(["Alice", "Bob", "Charlie", "David", "Eve"]));
// "Alice, Bob, and 3 others"
Dies erhält die Lesbarkeit und zeigt gleichzeitig die Gesamtanzahl an. Der genaue Schwellenwert hängt von Ihren Interface-Anforderungen ab.
Browser-Unterstützung und Fallbacks
Intl.ListFormat funktioniert in allen modernen Browsern seit April 2021. Die Unterstützung umfasst Chrome 72+, Firefox 78+, Safari 14.1+ und Edge 79+.
Prüfen Sie die Unterstützung mit Feature Detection:
if (typeof Intl.ListFormat !== "undefined") {
const formatter = new Intl.ListFormat("en");
return formatter.format(items);
} else {
// Fallback for older browsers
return items.join(", ");
}
Für eine breitere Kompatibilität verwenden Sie ein Polyfill wie @formatjs/intl-listformat. Installieren Sie es nur für Umgebungen, die es benötigen:
if (typeof Intl.ListFormat === "undefined") {
await import("@formatjs/intl-listformat/polyfill");
}
Angesichts der aktuellen Browser-Unterstützung können die meisten Anwendungen Intl.ListFormat direkt ohne Polyfills verwenden.
Häufige Fehler, die vermieden werden sollten
Das wiederholte Erstellen neuer Formatter verschwendet Ressourcen:
// Inefficient
function display(items) {
return new Intl.ListFormat("en").format(items);
}
// Efficient
const formatter = new Intl.ListFormat("en");
function display(items) {
return formatter.format(items);
}
Die Verwendung von array.join() für benutzerseitigen Text führt zu Lokalisierungsproblemen:
// Breaks in other languages
const text = items.join(", ");
// Works across languages
const formatter = new Intl.ListFormat(userLocale);
const text = formatter.format(items);
Die Annahme, dass englische Konjunktionsregeln universell gelten, führt zu falscher Ausgabe in anderen Locales. Übergeben Sie immer das Locale des Benutzers an den Konstruktor.
Das Nichtbehandeln leerer Arrays kann zu unerwarteter Ausgabe führen:
// Defensive
function formatItems(items) {
if (items.length === 0) {
return "No items";
}
return formatter.format(items);
}
Obwohl format([]) einen leeren String zurückgibt, verbessert eine explizite Behandlung leerer Zustände die Benutzererfahrung.
Wann Intl.ListFormat verwendet werden sollte
Verwenden Sie Intl.ListFormat immer dann, wenn mehrere Elemente in Fließtext angezeigt werden. Dies umfasst Navigationspfade, ausgewählte Filter, Validierungsfehler, Benutzerlisten, Kategorie-Tags und Feature-Listen.
Verwenden Sie es nicht für strukturierte Datenanzeigen wie Tabellen oder Optionsmenüs. Diese Komponenten haben ihre eigenen Formatierungsanforderungen außerhalb der Fließtext-Listenregeln.
Die API ersetzt manuelle String-Verkettungs- und Verbindungsmuster. Immer wenn Sie join(", ") für benutzerseitigen Text schreiben würden, überlegen Sie, ob Intl.ListFormat eine bessere Locale-Unterstützung bietet.