Comment formater des listes avec ou en JavaScript

Utilisez Intl.ListFormat avec le type disjonction pour formater correctement les alternatives dans n'importe quelle langue

Introduction

Les applications présentent souvent aux utilisateurs des choix ou des alternatives. Un composant de téléchargement de fichiers accepte les fichiers « PNG, JPEG ou SVG ». Un formulaire de paiement permet « carte de crédit, carte de débit ou PayPal » comme méthodes de paiement. Un message d'erreur suggère de corriger « nom d'utilisateur, mot de passe ou adresse e-mail » pour résoudre les échecs d'authentification.

Ces listes utilisent « ou » pour indiquer des alternatives. Les formater manuellement avec une concaténation de chaînes ne fonctionne pas dans d'autres langues car différentes langues ont des règles de ponctuation différentes, des mots différents pour « ou » et des conventions de placement de virgules différentes. L'API Intl.ListFormat avec le type disjonction formate correctement ces listes d'alternatives pour n'importe quelle langue.

Ce que sont les listes disjonctives

Une liste disjonctive présente des alternatives où généralement une seule option s'applique. Le mot « disjonction » signifie séparation ou alternatives. En français, les listes disjonctives utilisent « ou » comme conjonction :

const paymentMethods = ["credit card", "debit card", "PayPal"];
// Desired output: "credit card, debit card, or PayPal"

Cela diffère des listes conjonctives qui utilisent « et » pour indiquer que tous les éléments s'appliquent ensemble. Les listes disjonctives communiquent un choix, les listes conjonctives communiquent une combinaison.

Les contextes courants pour les listes disjonctives incluent les options de paiement, les restrictions de format de fichier, les suggestions de dépannage, les alternatives de filtres de recherche et toute interface où les utilisateurs sélectionnent une option parmi plusieurs possibilités.

Pourquoi le formatage manuel échoue

Les anglophones écrivent les listes disjonctives comme « A, B, or C » avec des virgules entre les éléments et « or » avant le dernier élément. Ce modèle ne fonctionne pas dans d'autres langues :

// Hardcoded English pattern
const items = ["apple", "orange", "banana"];
const text = items.slice(0, -1).join(", ") + ", or " + items[items.length - 1];
// "apple, orange, or banana"

Ce code produit une sortie incorrecte en espagnol, français, allemand et dans la plupart des autres langues. Chaque langue a des règles de formatage distinctes pour les listes disjonctives.

L'espagnol utilise "o" sans virgule avant :

Expected: "manzana, naranja o plátano"
English pattern produces: "manzana, naranja, or plátano"

Le français utilise "ou" sans virgule avant :

Expected: "pomme, orange ou banane"
English pattern produces: "pomme, orange, or banane"

L'allemand utilise "oder" sans virgule avant :

Expected: "Apfel, Orange oder Banane"
English pattern produces: "Apfel, Orange, or Banane"

Le japonais utilise la particule "か" (ka) avec une ponctuation différente :

Expected: "りんご、オレンジ、またはバナナ"
English pattern produces: "りんご、オレンジ、 or バナナ"

Ces différences vont au-delà du simple remplacement de mots. Le placement de la ponctuation, les règles d'espacement et les particules grammaticales varient tous selon la langue. La concaténation manuelle de chaînes ne peut pas gérer cette complexité.

Utilisation d'Intl.ListFormat avec le type disjonction

L'API Intl.ListFormat formate les listes selon les règles spécifiques à chaque langue. Définissez l'option type sur "disjunction" pour formater les listes alternatives :

const formatter = new Intl.ListFormat("en", { type: "disjunction" });

const paymentMethods = ["credit card", "debit card", "PayPal"];
console.log(formatter.format(paymentMethods));
// "credit card, debit card, or PayPal"

Le formateur gère n'importe quelle longueur de tableau :

const formatter = new Intl.ListFormat("en", { type: "disjunction" });

console.log(formatter.format([]));
// ""

console.log(formatter.format(["credit card"]));
// "credit card"

console.log(formatter.format(["credit card", "PayPal"]));
// "credit card or PayPal"

console.log(formatter.format(["credit card", "debit card", "PayPal"]));
// "credit card, debit card, or PayPal"

L'API applique automatiquement la ponctuation et la conjonction correctes pour chaque cas.

Comprendre les styles de disjonction

L'option style contrôle la verbosité du formatage. Trois styles existent : long, short et narrow. Le style long est celui par défaut.

const items = ["email", "phone", "SMS"];

const long = new Intl.ListFormat("en", {
  type: "disjunction",
  style: "long"
});
console.log(long.format(items));
// "email, phone, or SMS"

const short = new Intl.ListFormat("en", {
  type: "disjunction",
  style: "short"
});
console.log(short.format(items));
// "email, phone, or SMS"

const narrow = new Intl.ListFormat("en", {
  type: "disjunction",
  style: "narrow"
});
console.log(narrow.format(items));
// "email, phone, or SMS"

En anglais, les trois styles produisent un résultat identique pour les listes disjonctives. D'autres langues montrent plus de variation. L'allemand utilise "oder" dans le style long et peut abréger dans le style étroit. Les différences deviennent plus apparentes dans les langues avec plusieurs niveaux de formalité ou des mots de conjonction plus longs.

Le style étroit supprime généralement les espaces ou utilise des conjonctions plus courtes pour économiser de l'espace dans les mises en page contraintes. Utilisez le style long pour le texte standard, le style court pour les affichages modérément compacts et le style étroit pour les contraintes d'espace serrées comme les interfaces mobiles ou les tableaux compacts.

Comment les listes disjonctives apparaissent dans différentes langues

Chaque langue formate les listes disjonctives selon ses propres conventions. Intl.ListFormat gère ces différences automatiquement.

L'anglais utilise des virgules avec "or" :

const en = new Intl.ListFormat("en", { type: "disjunction" });
console.log(en.format(["PNG", "JPEG", "SVG"]));
// "PNG, JPEG, or SVG"

L'espagnol utilise des virgules avec "o" et aucune virgule avant la conjonction finale :

const es = new Intl.ListFormat("es", { type: "disjunction" });
console.log(es.format(["PNG", "JPEG", "SVG"]));
// "PNG, JPEG o SVG"

Le français utilise des virgules avec "ou" et aucune virgule avant la conjonction finale :

const fr = new Intl.ListFormat("fr", { type: "disjunction" });
console.log(fr.format(["PNG", "JPEG", "SVG"]));
// "PNG, JPEG ou SVG"

L'allemand utilise des virgules avec "oder" et aucune virgule avant la conjonction finale :

const de = new Intl.ListFormat("de", { type: "disjunction" });
console.log(de.format(["PNG", "JPEG", "SVG"]));
// "PNG, JPEG oder SVG"

Le japonais utilise une ponctuation et des particules différentes :

const ja = new Intl.ListFormat("ja", { type: "disjunction" });
console.log(ja.format(["PNG", "JPEG", "SVG"]));
// "PNG、JPEG、またはSVG"

Le chinois utilise des signes de ponctuation chinois :

const zh = new Intl.ListFormat("zh", { type: "disjunction" });
console.log(zh.format(["PNG", "JPEG", "SVG"]));
// "PNG、JPEG或SVG"

Ces exemples montrent comment l'API s'adapte aux conventions grammaticales et de ponctuation de chaque langue. Le même code fonctionne dans toutes les langues lorsque vous fournissez la locale appropriée.

Formater les options de paiement

Les formulaires de paiement présentent plusieurs choix de méthodes de paiement. Formatez-les avec des listes disjonctives :

const formatter = new Intl.ListFormat("en", { type: "disjunction" });

function getPaymentMessage(methods) {
  if (methods.length === 0) {
    return "No payment methods available";
  }

  return `Pay with ${formatter.format(methods)}.`;
}

const methods = ["credit card", "debit card", "PayPal", "Apple Pay"];
console.log(getPaymentMessage(methods));
// "Pay with credit card, debit card, PayPal, or Apple Pay."

Pour les applications internationales, transmettez la locale de l'utilisateur :

const userLocale = navigator.language; // e.g., "fr-FR"
const formatter = new Intl.ListFormat(userLocale, { type: "disjunction" });

function getPaymentMessage(methods) {
  if (methods.length === 0) {
    return "No payment methods available";
  }

  return `Pay with ${formatter.format(methods)}.`;
}

Cette approche fonctionne dans les flux de paiement, les sélecteurs de méthodes de paiement et toute interface où les utilisateurs choisissent comment payer.

Formater les restrictions de téléchargement de fichiers

Les composants de téléchargement de fichiers spécifient quels types de fichiers le système accepte :

const formatter = new Intl.ListFormat("en", { type: "disjunction" });

function getAcceptedFormatsMessage(formats) {
  if (formats.length === 0) {
    return "No file formats accepted";
  }

  if (formats.length === 1) {
    return `Accepted format: ${formats[0]}`;
  }

  return `Accepted formats: ${formatter.format(formats)}`;
}

const imageFormats = ["PNG", "JPEG", "SVG", "WebP"];
console.log(getAcceptedFormatsMessage(imageFormats));
// "Accepted formats: PNG, JPEG, SVG, or WebP"

const documentFormats = ["PDF", "DOCX"];
console.log(getAcceptedFormatsMessage(documentFormats));
// "Accepted formats: PDF or DOCX"

Ce modèle fonctionne pour les téléchargements d'images, les soumissions de documents et toute entrée de fichier avec des restrictions de format.

Formater les suggestions de dépannage

Les messages d'erreur suggèrent souvent plusieurs façons de résoudre un problème. Présentez ces suggestions sous forme de listes disjonctives :

const formatter = new Intl.ListFormat("en", { type: "disjunction" });

function getAuthenticationError(missingFields) {
  if (missingFields.length === 0) {
    return "Authentication failed";
  }

  return `Please check your ${formatter.format(missingFields)} and try again.`;
}

console.log(getAuthenticationError(["username", "password"]));
// "Please check your username or password and try again."

console.log(getAuthenticationError(["email", "username", "password"]));
// "Please check your email, username, or password and try again."

La liste disjonctive clarifie que les utilisateurs doivent corriger l'un des champs mentionnés, pas nécessairement tous.

Formatage des alternatives de filtres de recherche

Les interfaces de recherche affichent les filtres actifs. Lorsque les filtres présentent des alternatives, utilisez des listes disjonctives :

const formatter = new Intl.ListFormat("en", { type: "disjunction" });

function getFilterSummary(filters) {
  if (filters.length === 0) {
    return "No filters applied";
  }

  if (filters.length === 1) {
    return `Showing results for: ${filters[0]}`;
  }

  return `Showing results for: ${formatter.format(filters)}`;
}

const categories = ["Electronics", "Books", "Clothing"];
console.log(getFilterSummary(categories));
// "Showing results for: Electronics, Books, or Clothing"

Cela fonctionne pour les filtres de catégories, les sélections d'étiquettes et toute interface de filtre où les valeurs sélectionnées représentent des alternatives plutôt que des combinaisons.

Réutilisation des 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 at module level
const disjunctionFormatter = new Intl.ListFormat("en", { type: "disjunction" });

// Reuse in multiple functions
function formatPaymentMethods(methods) {
  return disjunctionFormatter.format(methods);
}

function formatFileTypes(types) {
  return disjunctionFormatter.format(types);
}

function formatErrorSuggestions(suggestions) {
  return disjunctionFormatter.format(suggestions);
}

Pour les applications prenant en charge plusieurs locales, stockez les formateurs dans un cache :

const formatters = new Map();

function getDisjunctionFormatter(locale) {
  if (!formatters.has(locale)) {
    formatters.set(
      locale,
      new Intl.ListFormat(locale, { type: "disjunction" })
    );
  }
  return formatters.get(locale);
}

const formatter = getDisjunctionFormatter("en");
console.log(formatter.format(["A", "B", "C"]));
// "A, B, or C"

Ce modèle réduit les coûts d'initialisation tout en prenant en charge plusieurs locales dans l'ensemble de l'application.

Utilisation de formatToParts pour un rendu personnalisé

La méthode formatToParts() renvoie un tableau d'objets représentant chaque élément de la liste formatée. Cela permet un style personnalisé :

const formatter = new Intl.ListFormat("en", { type: "disjunction" });
const parts = formatter.formatToParts(["PNG", "JPEG", "SVG"]);

console.log(parts);
// [
//   { type: "element", value: "PNG" },
//   { type: "literal", value: ", " },
//   { type: "element", value: "JPEG" },
//   { type: "literal", value: ", or " },
//   { type: "element", value: "SVG" }
// ]

Chaque partie a un type et une value. Le type est soit "element" pour les éléments de liste, soit "literal" pour la ponctuation et les conjonctions.

Utilisez ceci pour appliquer différents styles aux éléments et aux littéraux :

const formatter = new Intl.ListFormat("en", { type: "disjunction" });
const formats = ["PNG", "JPEG", "SVG"];

const html = formatter.formatToParts(formats)
  .map(part => {
    if (part.type === "element") {
      return `<code>${part.value}</code>`;
    }
    return part.value;
  })
  .join("");

console.log(html);
// "<code>PNG</code>, <code>JPEG</code>, or <code>SVG</code>"

Cette approche maintient la ponctuation et les conjonctions correctes selon la locale tout en appliquant une présentation personnalisée aux éléments réels.

Prise en charge des navigateurs et compatibilité

Intl.ListFormat fonctionne dans tous les navigateurs modernes depuis avril 2021. La prise en charge inclut Chrome 72+, Firefox 78+, Safari 14.1+ et Edge 79+.

Vérifiez la prise en charge avant d'utiliser l'API :

if (typeof Intl.ListFormat !== "undefined") {
  const formatter = new Intl.ListFormat("en", { type: "disjunction" });
  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 là où c'est nécessaire :

if (typeof Intl.ListFormat === "undefined") {
  await import("@formatjs/intl-listformat/polyfill");
}

const formatter = new Intl.ListFormat("en", { type: "disjunction" });

Compte tenu de la prise en charge actuelle des navigateurs, la plupart des applications peuvent utiliser Intl.ListFormat directement sans polyfills.

Erreurs courantes à éviter

L'utilisation du type conjonction au lieu de disjonction produit un sens incorrect :

// Wrong: suggests all methods required
const wrong = new Intl.ListFormat("en", { type: "conjunction" });
console.log(`Pay with ${wrong.format(["credit card", "debit card"])}`);
// "Pay with credit card and debit card"

// Correct: suggests choosing one method
const correct = new Intl.ListFormat("en", { type: "disjunction" });
console.log(`Pay with ${correct.format(["credit card", "debit card"])}`);
// "Pay with credit card or debit card"

Créer de nouveaux formateurs de manière répétée gaspille des ressources :

// Inefficient
function formatOptions(options) {
  return new Intl.ListFormat("en", { type: "disjunction" }).format(options);
}

// Efficient
const formatter = new Intl.ListFormat("en", { type: "disjunction" });
function formatOptions(options) {
  return formatter.format(options);
}

Coder en dur « ou » dans les chaînes empêche la localisation :

// Breaks in other languages
const text = items.join(", ") + ", or other options";

// Works across languages
const formatter = new Intl.ListFormat(userLocale, { type: "disjunction" });
const allItems = [...items, "other options"];
const text = formatter.format(allItems);

Ne pas gérer les tableaux vides peut causer une sortie inattendue :

// Defensive
function formatPaymentMethods(methods) {
  if (methods.length === 0) {
    return "No payment methods available";
  }
  return formatter.format(methods);
}

Bien que format([]) retourne une chaîne vide, la gestion explicite d'un état vide améliore l'expérience utilisateur.

Quand utiliser les listes disjonctives

Utilisez les listes disjonctives lors de la présentation d'alternatives ou de choix où généralement une seule option s'applique. Cela inclut la sélection de méthode de paiement, les restrictions de format de fichier, les suggestions d'erreur d'authentification, les options de filtre de recherche et les choix de type de compte.

N'utilisez pas les listes disjonctives lorsque tous les éléments doivent s'appliquer ensemble. Utilisez plutôt les listes conjonctives. Par exemple, « Le nom, l'e-mail et le mot de passe sont requis » utilise la conjonction car tous les champs doivent être fournis, pas seulement un.

N'utilisez pas les listes disjonctives pour des énumérations neutres sans implications de choix. Les mesures et spécifications techniques utilisent généralement des listes unitaires au lieu de disjonction ou conjonction.

L'API remplace les modèles de concaténation de chaînes manuels pour les alternatives. Chaque fois que vous écririez du code qui joint des éléments avec « ou » pour du texte destiné aux utilisateurs, demandez-vous si Intl.ListFormat avec le type disjonction offre une meilleure prise en charge des paramètres régionaux.