API Intl.ListFormat
Formatez des tableaux en listes lisibles adaptées aux paramètres régionaux
Introduction
Lors de l'affichage de plusieurs éléments aux utilisateurs, les développeurs joignent souvent des tableaux avec des virgules et ajoutent « et » avant le dernier élément :
const users = ["Alice", "Bob", "Charlie"];
const message = users.slice(0, -1).join(", ") + ", and " + users[users.length - 1];
// "Alice, Bob, and Charlie"
Cette approche code en dur les règles de ponctuation anglaises et ne fonctionne pas dans d'autres langues. Le japonais utilise des particules différentes, l'allemand a des règles d'espacement différentes et le chinois utilise des séparateurs différents. L'API Intl.ListFormat résout ce problème en formatant les listes selon les conventions de chaque paramètre régional.
Ce que fait Intl.ListFormat
Intl.ListFormat convertit des tableaux en listes lisibles qui suivent les règles grammaticales et de ponctuation de n'importe quelle langue. Il gère trois types de listes qui apparaissent dans toutes les langues :
- Les listes de conjonction utilisent « et » pour connecter les éléments (« A, B et C »)
- Les listes de disjonction utilisent « ou » pour présenter des alternatives (« A, B ou C »)
- Les listes d'unités formatent les mesures sans conjonctions (« 5 ft, 2 in »)
L'API connaît la façon dont chaque langue formate ces types de listes, de la ponctuation au choix des mots en passant par l'espacement.
Utilisation de base
Créez un formateur avec un paramètre régional et des options, puis appelez format() avec un tableau :
const formatter = new Intl.ListFormat("en", {
type: "conjunction",
style: "long"
});
const items = ["bread", "milk", "eggs"];
console.log(formatter.format(items));
// "bread, milk, and eggs"
Le formateur gère les tableaux de n'importe quelle longueur, y compris les cas limites :
formatter.format([]); // ""
formatter.format(["bread"]); // "bread"
formatter.format(["bread", "milk"]); // "bread and milk"
Les types de listes contrôlent les conjonctions
L'option type détermine quelle conjonction apparaît dans la liste formatée.
Listes de conjonction
Utilisez type: "conjunction" pour les listes où tous les éléments s'appliquent ensemble. C'est le type par défaut :
const formatter = new Intl.ListFormat("en", { type: "conjunction" });
console.log(formatter.format(["HTML", "CSS", "JavaScript"]));
// "HTML, CSS, and JavaScript"
Les utilisations courantes incluent l'affichage d'éléments sélectionnés, l'énumération de fonctionnalités et l'affichage de plusieurs valeurs qui s'appliquent toutes.
Listes de disjonction
Utilisez type: "disjunction" pour les listes présentant des alternatives ou des choix :
const formatter = new Intl.ListFormat("en", { type: "disjunction" });
console.log(formatter.format(["credit card", "debit card", "PayPal"]));
// "credit card, debit card, or PayPal"
Cela apparaît dans les listes d'options, les messages d'erreur avec plusieurs solutions et tout contexte où les utilisateurs choisissent un élément.
Listes d'unités
Utilisez type: "unit" pour les mesures et valeurs techniques qui doivent apparaître sans conjonctions :
const formatter = new Intl.ListFormat("en", { type: "unit" });
console.log(formatter.format(["5 feet", "2 inches"]));
// "5 feet, 2 inches"
Les listes d'unités fonctionnent pour les mesures, spécifications techniques et valeurs composées.
Les styles de liste contrôlent la verbosité
L'option style ajuste le niveau de verbosité du formatage. Trois styles existent : long, short et 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"
En anglais, long et short produisent un résultat identique pour la plupart des listes. Le style narrow omit la conjonction. D'autres langues montrent plus de variation entre les styles, particulièrement pour les listes de disjonction.
Comment les différentes langues formatent les listes
Chaque langue possède des règles de formatage de listes distinctes. Intl.ListFormat gère ces différences automatiquement.
L'anglais utilise des virgules, des espaces et des conjonctions :
const en = new Intl.ListFormat("en");
console.log(en.format(["Tokyo", "Paris", "London"]));
// "Tokyo, Paris, and London"
L'allemand utilise la même structure de virgules mais des conjonctions différentes :
const de = new Intl.ListFormat("de");
console.log(de.format(["Tokyo", "Paris", "London"]));
// "Tokyo, Paris und London"
Le japonais utilise des séparateurs et particules différents :
const ja = new Intl.ListFormat("ja");
console.log(ja.format(["東京", "パリ", "ロンドン"]));
// "東京、パリ、ロンドン"
Le chinois utilise une ponctuation entièrement différente :
const zh = new Intl.ListFormat("zh");
console.log(zh.format(["东京", "巴黎", "伦敦"]));
// "东京、巴黎和伦敦"
Ces différences s'étendent au-delà de la ponctuation aux règles d'espacement, au placement des conjonctions et aux particules grammaticales. Coder en dur une approche unique ne fonctionne pas pour les autres langues.
Utiliser formatToParts pour un rendu personnalisé
La méthode formatToParts() retourne un tableau d'objets au lieu d'une chaîne. Chaque objet représente un élément de la liste formatée :
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" }
// ]
Chaque partie possède un type et une value. Le type est soit "element" pour les éléments de liste, soit "literal" pour la ponctuation de formatage et les conjonctions.
Cette structure permet un rendu personnalisé où les éléments et les littéraux nécessitent des styles différents :
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>"
Cette approche maintient une ponctuation correcte selon la locale tout en appliquant une présentation personnalisée aux éléments de liste réels.
Réutiliser les formateurs pour les performances
La création d'instances Intl.ListFormat a un coût. Créez les formateurs une fois et réutilisez-les :
// 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);
}
Pour les applications avec plusieurs locales, stockez les formateurs dans une 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"]));
Ce pattern réduit les coûts d'initialisation répétés tout en prenant en charge plusieurs locales et configurations.
Formater les messages d'erreur
La validation de formulaire produit souvent plusieurs erreurs. Formatez-les avec des listes de disjonction pour présenter les options :
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."
La liste de disjonction clarifie que les utilisateurs doivent corriger l'un de ces problèmes, et le formatage s'adapte aux conventions de chaque locale.
Afficher les éléments sélectionnés
Lorsque les utilisateurs sélectionnent plusieurs éléments, formatez la sélection avec des listes de conjonction :
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"
Ce pattern fonctionne pour les sélections de fichiers, les choix de filtres, les sélections de catégories et toute interface de sélection multiple.
Gérer les longues listes
Pour les listes avec de nombreux éléments, envisagez de tronquer avant le formatage :
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"
Cela maintient la lisibilité tout en indiquant le nombre total. Le seuil exact dépend des contraintes de votre interface.
Support des navigateurs et solutions de repli
Intl.ListFormat fonctionne dans tous les navigateurs modernes depuis avril 2021. Le support inclut Chrome 72+, Firefox 78+, Safari 14.1+ et Edge 79+.
Vérifiez le support avec la détection de fonctionnalité :
if (typeof Intl.ListFormat !== "undefined") {
const formatter = new Intl.ListFormat("en");
return formatter.format(items);
} else {
// Fallback for older browsers
return items.join(", ");
}
Pour une compatibilité plus large, utilisez un polyfill comme @formatjs/intl-listformat. Installez-le uniquement pour les environnements qui en ont besoin :
if (typeof Intl.ListFormat === "undefined") {
await import("@formatjs/intl-listformat/polyfill");
}
Compte tenu de la prise en charge actuelle des navigateurs, la plupart des applications peuvent utiliser Intl.ListFormat directement sans polyfills.
Erreurs courantes à éviter
Créer de nouveaux formateurs de manière répétée gaspille des ressources :
// 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);
}
Utiliser array.join() pour du texte destiné aux utilisateurs crée des problèmes de localisation :
// Breaks in other languages
const text = items.join(", ");
// Works across languages
const formatter = new Intl.ListFormat(userLocale);
const text = formatter.format(items);
Supposer que les règles de conjonction anglaises s'appliquent universellement conduit à un résultat incorrect dans d'autres locales. Passez toujours la locale de l'utilisateur au constructeur.
Ne pas gérer les tableaux vides peut causer un résultat inattendu :
// Defensive
function formatItems(items) {
if (items.length === 0) {
return "No items";
}
return formatter.format(items);
}
Bien que format([]) retourne une chaîne vide, une gestion explicite de l'état vide améliore l'expérience utilisateur.
Quand utiliser Intl.ListFormat
Utilisez Intl.ListFormat chaque fois que vous affichez plusieurs éléments dans un texte. Cela inclut les fils d'Ariane de navigation, les filtres sélectionnés, les erreurs de validation, les listes d'utilisateurs, les balises de catégorie et les listes de fonctionnalités.
Ne l'utilisez pas pour les affichages de données structurées comme les tableaux ou les menus d'options. Ces composants ont leurs propres exigences de formatage en dehors des règles de liste en prose.
L'API remplace la concaténation manuelle de chaînes et les modèles de jointure. Chaque fois que vous écririez join(", ") pour du texte destiné aux utilisateurs, demandez-vous si Intl.ListFormat offre une meilleure prise en charge des locales.