Comment choisir la forme plurielle pour les plages comme 1-3 articles

Utilisez JavaScript pour sélectionner la forme plurielle correcte lors de l'affichage de plages de nombres

Introduction

Les plages indiquent qu'une valeur se situe entre deux points. Les interfaces utilisateur affichent des plages dans des contextes comme les résultats de recherche montrant "10-15 correspondances trouvées", les systèmes d'inventaire indiquant "1-3 articles disponibles", ou les filtres affichant "Sélectionner 2-5 options". Ces plages combinent deux nombres avec un texte descriptif qui doit s'accorder grammaticalement avec la plage.

Lorsque vous affichez un seul compteur, vous choisissez entre les formes singulière et plurielle : "1 article" contre "2 articles". Les langues ont des règles qui déterminent quelle forme s'applique en fonction du compteur. Ces règles varient selon la langue. L'anglais utilise le singulier pour un et le pluriel pour tous les autres compteurs. Le polonais utilise différentes formes pour 1, 2-4, et 5 ou plus. L'arabe a six formes distinctes basées sur le compteur.

Les plages introduisent un défi différent. La forme plurielle dépend à la fois des valeurs de début et de fin, pas seulement d'un seul nombre. En anglais, "1-2 items" utilise le pluriel même si la plage commence à 1. Différentes langues ont différentes règles pour déterminer quelle forme plurielle s'applique à une plage. La méthode selectRange() sur Intl.PluralRules gère automatiquement ces règles spécifiques à chaque langue.

Pourquoi les plages nécessitent des règles de pluralisation différentes

L'utilisation de la méthode select() sur un seul nombre d'une plage ne fonctionne pas correctement pour toutes les langues. Vous pourriez penser à utiliser la valeur finale de la plage, mais cela produit des résultats incorrects dans de nombreuses langues.

Prenons l'exemple de l'anglais avec la plage 0-1. L'utilisation de select() sur la valeur finale renvoie "one", suggérant que vous devriez afficher "0-1 item". C'est grammaticalement incorrect. La forme correcte est "0-1 items" avec le pluriel.

const rules = new Intl.PluralRules("en-US");

console.log(rules.select(1));
// Sortie : "one"

// Mais "0-1 item" est incorrect
// Correct : "0-1 items"

Différentes langues ont des règles explicites pour les plages qui ne correspondent pas à leurs règles pour les compteurs uniques. En slovène, la plage 102-201 utilise la forme "few", tandis que les nombres individuels dans cette plage utilisent différentes formes.

const slRules = new Intl.PluralRules("sl");

console.log(slRules.select(102));
// Sortie : "few"

console.log(slRules.select(201));
// Sortie : "few"

console.log(slRules.selectRange(102, 201));
// Sortie : "few"

Certaines langues utilisent la valeur de début pour déterminer la forme, d'autres utilisent la valeur finale, et d'autres utilisent les deux valeurs ensemble. La méthode selectRange() encapsule ces règles spécifiques à chaque langue afin que vous n'ayez pas à les implémenter manuellement.

Créer une instance PluralRules pour les plages

Créez une instance Intl.PluralRules de la même manière que pour les nombres individuels. L'instance fournit à la fois select() pour les nombres individuels et selectRange() pour les plages.

const rules = new Intl.PluralRules("en-US");

Vous pouvez spécifier des options lors de la création de l'instance. Ces options s'appliquent à la fois aux nombres individuels et aux plages.

const rules = new Intl.PluralRules("en-US", {
  type: "cardinal"
});

L'option type est définie par défaut sur "cardinal", qui gère le comptage des objets. Vous pouvez également utiliser "ordinal" pour les nombres positionnels, bien que les plages ordinales soient moins courantes dans les interfaces utilisateur.

Réutilisez la même instance pour plusieurs appels. Créer une nouvelle instance pour chaque pluralisation est inefficace. Stockez l'instance dans une variable ou mettez-la en cache par locale.

Utiliser selectRange pour déterminer la catégorie de pluriel pour les plages

La méthode selectRange() prend deux nombres représentant le début et la fin d'une plage. Elle renvoie une chaîne indiquant quelle catégorie de pluriel s'applique : "zero", "one", "two", "few", "many" ou "other".

const rules = new Intl.PluralRules("en-US");

console.log(rules.selectRange(0, 1));
// Sortie : "other"

console.log(rules.selectRange(1, 2));
// Sortie : "other"

console.log(rules.selectRange(5, 10));
// Sortie : "other"

En anglais, les plages utilisent presque toujours la catégorie "other", qui correspond à la forme plurielle. Cela correspond à la façon dont les anglophones expriment naturellement les plages avec des noms pluriels.

Les langues avec plus de formes plurielles renvoient différentes catégories selon leurs règles spécifiques.

const arRules = new Intl.PluralRules("ar-EG");

console.log(arRules.selectRange(0, 0));
// Sortie : "zero"

console.log(arRules.selectRange(1, 1));
// Sortie : "one"

console.log(arRules.selectRange(2, 2));
// Sortie : "two"

console.log(arRules.selectRange(3, 10));
// Sortie : "few"

La valeur de retour est toujours l'un des six noms de catégories de pluriel standard. Votre code associe ces catégories au texte localisé approprié.

Associer les catégories de plages aux chaînes localisées

Stockez les formes textuelles pour chaque catégorie de pluriel dans une structure de données. Utilisez la catégorie renvoyée par selectRange() pour rechercher le texte approprié.

const rules = new Intl.PluralRules("en-US");
const forms = new Map([
  ["one", "item"],
  ["other", "items"]
]);

function formatRange(start, end) {
  const category = rules.selectRange(start, end);
  const form = forms.get(category);
  return `${start}-${end} ${form}`;
}

console.log(formatRange(1, 3));
// Output: "1-3 items"

console.log(formatRange(0, 1));
// Output: "0-1 items"

console.log(formatRange(5, 10));
// Output: "5-10 items"

Ce modèle sépare la logique de pluralisation du texte localisé. L'instance Intl.PluralRules gère les règles linguistiques. La Map contient les traductions. La fonction les combine.

Pour les langues avec plus de catégories de pluriel, ajoutez des entrées pour chaque catégorie utilisée par la langue.

const arRules = new Intl.PluralRules("ar-EG");
const arForms = new Map([
  ["zero", "عناصر"],
  ["one", "عنصر"],
  ["two", "عنصران"],
  ["few", "عناصر"],
  ["many", "عنصرًا"],
  ["other", "عنصر"]
]);

function formatRange(start, end) {
  const category = arRules.selectRange(start, end);
  const form = arForms.get(category);
  return `${start}-${end} ${form}`;
}

console.log(formatRange(0, 0));
// Output: "0-0 عناصر"

console.log(formatRange(1, 1));
// Output: "1-1 عنصر"

Fournissez toujours du texte pour chaque catégorie utilisée par la langue. Vérifiez les règles de pluriel CLDR Unicode ou testez avec l'API sur différentes plages pour identifier quelles catégories sont nécessaires.

Comment différentes locales gèrent la pluralisation des plages

Chaque langue possède ses propres règles pour déterminer la forme plurielle des plages. Ces règles reflètent la façon dont les locuteurs natifs expriment naturellement les plages dans cette langue.

const enRules = new Intl.PluralRules("en-US");
console.log(enRules.selectRange(1, 3));
// Output: "other"

const slRules = new Intl.PluralRules("sl");
console.log(slRules.selectRange(102, 201));
// Output: "few"

const ptRules = new Intl.PluralRules("pt");
console.log(ptRules.selectRange(102, 102));
// Output: "other"

const ruRules = new Intl.PluralRules("ru");
console.log(ruRules.selectRange(1, 2));
// Output: "few"

L'anglais utilise systématiquement "other" pour les plages, rendant les plages toujours plurielles. Le slovène applique des règles plus complexes basées sur les nombres spécifiques dans la plage. Le portugais utilise "other" pour la plupart des plages. Le russe utilise "few" pour certaines plages.

Ces différences montrent pourquoi coder en dur la logique de pluriel échoue pour les applications internationales. L'API encapsule la connaissance de la façon dont chaque langue gère les plages.

Combiner avec Intl.NumberFormat pour un formatage complet

Les applications réelles doivent formater à la fois les nombres et le texte. Utilisez Intl.NumberFormat pour formater les extrémités de la plage selon les conventions locales, puis utilisez selectRange() pour choisir la forme plurielle correcte.

const locale = "en-US";
const numberFormat = new Intl.NumberFormat(locale);
const pluralRules = new Intl.PluralRules(locale);
const forms = new Map([
  ["one", "item"],
  ["other", "items"]
]);

function formatRange(start, end) {
  const startFormatted = numberFormat.format(start);
  const endFormatted = numberFormat.format(end);
  const category = pluralRules.selectRange(start, end);
  const form = forms.get(category);
  return `${startFormatted}-${endFormatted} ${form}`;
}

console.log(formatRange(1, 3));
// Résultat : "1-3 items"

console.log(formatRange(1000, 5000));
// Résultat : "1,000-5,000 items"

Le formateur de nombres ajoute des séparateurs de milliers. Les règles de pluriel sélectionnent la forme correcte. La fonction combine les deux pour produire une sortie correctement formatée.

Différentes locales utilisent différentes conventions de formatage des nombres.

const locale = "de-DE";
const numberFormat = new Intl.NumberFormat(locale);
const pluralRules = new Intl.PluralRules(locale);
const forms = new Map([
  ["one", "Artikel"],
  ["other", "Artikel"]
]);

function formatRange(start, end) {
  const startFormatted = numberFormat.format(start);
  const endFormatted = numberFormat.format(end);
  const category = pluralRules.selectRange(start, end);
  const form = forms.get(category);
  return `${startFormatted}-${endFormatted} ${form}`;
}

console.log(formatRange(1000, 5000));
// Résultat : "1.000-5.000 Artikel"

L'allemand utilise des points comme séparateurs de milliers au lieu de virgules. Le formateur de nombres gère cela automatiquement. Les règles de pluriel déterminent quelle forme de "Artikel" utiliser.

Comparer selectRange avec select pour les valeurs uniques

La méthode select() gère les comptages uniques, tandis que selectRange() gère les plages. Utilisez select() lors de l'affichage d'une quantité unique et selectRange() lors de l'affichage d'une plage entre deux valeurs.

const rules = new Intl.PluralRules("en-US");

// Comptage unique
console.log(rules.select(1));
// Résultat : "one"

console.log(rules.select(2));
// Résultat : "other"

// Plage
console.log(rules.selectRange(1, 2));
// Résultat : "other"

console.log(rules.selectRange(0, 1));
// Résultat : "other"

Pour les comptages uniques, les règles dépendent uniquement de ce nombre. Pour les plages, les règles considèrent les deux extrémités. En anglais, une plage commençant à 1 utilise toujours la forme plurielle, même si le comptage unique 1 utilise la forme singulière.

Certaines langues montrent des différences plus marquées entre les règles de comptage unique et les règles de plage.

const slRules = new Intl.PluralRules("sl");

// Comptages uniques en slovène
console.log(slRules.select(1));
// Résultat : "one"

console.log(slRules.select(2));
// Résultat : "two"

console.log(slRules.select(5));
// Résultat : "few"

// Plage en slovène
console.log(slRules.selectRange(102, 201));
// Résultat : "few"

Le slovène utilise "one", "two" et "few" pour différents comptages uniques basés sur des règles complexes. Pour les plages, il applique une logique différente qui considère les deux nombres ensemble.

Gérer les plages où le début et la fin sont égaux

Lorsque les valeurs de début et de fin sont identiques, vous affichez une plage sans largeur. Certaines applications utilisent cela pour représenter une valeur exacte dans un contexte où des plages sont attendues.

const rules = new Intl.PluralRules("en-US");

console.log(rules.selectRange(5, 5));
// Output: "other"

console.log(rules.selectRange(1, 1));
// Output: "one"

Lorsque les deux valeurs sont égales à 1, l'anglais renvoie "one", suggérant d'utiliser la forme singulière. Lorsque les deux valeurs sont n'importe quel autre nombre, l'anglais renvoie "other", suggérant la forme plurielle.

Ce comportement est logique si vous affichez la plage comme "1-1 élément" ou simplement "1 élément". Pour les valeurs autres que 1, vous affichez "5-5 éléments" ou "5 éléments".

En pratique, vous pourriez vouloir détecter quand le début égale la fin et afficher une valeur unique au lieu d'une plage.

const rules = new Intl.PluralRules("en-US");
const forms = new Map([
  ["one", "item"],
  ["other", "items"]
]);

function formatRange(start, end) {
  if (start === end) {
    const category = rules.select(start);
    const form = forms.get(category);
    return `${start} ${form}`;
  }

  const category = rules.selectRange(start, end);
  const form = forms.get(category);
  return `${start}-${end} ${form}`;
}

console.log(formatRange(1, 1));
// Output: "1 item"

console.log(formatRange(5, 5));
// Output: "5 items"

console.log(formatRange(1, 3));
// Output: "1-3 items"

Cette approche utilise select() pour les valeurs égales et selectRange() pour les plages réelles. Le résultat est plus naturel car il évite d'afficher "1-1" ou "5-5".

Gérer les cas particuliers avec selectRange

La méthode selectRange() valide ses entrées. Si l'un des paramètres est undefined, null, ou ne peut pas être converti en un nombre valide, la méthode génère une erreur.

const rules = new Intl.PluralRules("en-US");

try {
  console.log(rules.selectRange(1, undefined));
} catch (error) {
  console.log(error.name);
  // Output: "TypeError"
}

try {
  console.log(rules.selectRange(NaN, 5));
} catch (error) {
  console.log(error.name);
  // Output: "RangeError"
}

Validez vos entrées avant de les passer à selectRange(). C'est particulièrement important lorsque vous travaillez avec des entrées utilisateur ou des données provenant de sources externes.

function formatRange(start, end) {
  if (typeof start !== "number" || typeof end !== "number") {
    throw new Error("Start and end must be numbers");
  }

  if (isNaN(start) || isNaN(end)) {
    throw new Error("Start and end must be valid numbers");
  }

  const category = rules.selectRange(start, end);
  const form = forms.get(category);
  return `${start}-${end} ${form}`;
}

La méthode accepte des nombres, des valeurs BigInt, ou des chaînes qui peuvent être analysées comme des nombres.

const rules = new Intl.PluralRules("en-US");

console.log(rules.selectRange(1, 5));
// Output: "other"

console.log(rules.selectRange(1n, 5n));
// Output: "other"

console.log(rules.selectRange("1", "5"));
// Output: "other"

Les entrées de type chaîne sont analysées comme des nombres. Cela permet une certaine flexibilité dans la façon dont vous appelez la méthode, mais il est préférable de passer des types numériques réels lorsque c'est possible pour plus de clarté.

Gérer les plages décimales

La méthode selectRange() fonctionne avec les nombres décimaux. Cela est utile lors de l'affichage de plages de quantités fractionnaires comme des mesures ou des statistiques.

const rules = new Intl.PluralRules("en-US");

console.log(rules.selectRange(1.5, 2.5));
// Résultat : "other"

console.log(rules.selectRange(0.5, 1.0));
// Résultat : "other"

console.log(rules.selectRange(1.0, 1.5));
// Résultat : "other"

L'anglais traite toutes ces plages décimales comme plurielles. D'autres langues peuvent avoir des règles différentes pour les plages décimales.

Lors du formatage des plages décimales, combinez selectRange() avec Intl.NumberFormat configuré pour la précision décimale appropriée.

const locale = "en-US";
const numberFormat = new Intl.NumberFormat(locale, {
  minimumFractionDigits: 1,
  maximumFractionDigits: 1
});
const pluralRules = new Intl.PluralRules(locale);
const forms = new Map([
  ["one", "kilometer"],
  ["other", "kilometers"]
]);

function formatRange(start, end) {
  const startFormatted = numberFormat.format(start);
  const endFormatted = numberFormat.format(end);
  const category = pluralRules.selectRange(start, end);
  const form = forms.get(category);
  return `${startFormatted}-${endFormatted} ${form}`;
}

console.log(formatRange(1.5, 2.5));
// Résultat : "1.5-2.5 kilometers"

console.log(formatRange(0.5, 1.0));
// Résultat : "0.5-1.0 kilometers"

Le formateur de nombres assure un affichage décimal cohérent. Les règles de pluriel déterminent la forme correcte basée sur les valeurs décimales.

Support et compatibilité des navigateurs

La méthode selectRange() est relativement nouvelle par rapport au reste de l'API Intl. Elle est devenue disponible en 2023 dans le cadre de la spécification Intl.NumberFormat v3.

Le support des navigateurs inclut Chrome 106 et versions ultérieures, Firefox 116 et versions ultérieures, Safari 15.4 et versions ultérieures, et Edge 106 et versions ultérieures. La méthode n'est pas disponible dans Internet Explorer ou les versions plus anciennes des navigateurs.

Pour les applications ciblant les navigateurs modernes, vous pouvez utiliser selectRange() sans polyfill. Si vous devez prendre en charge des navigateurs plus anciens, vérifiez l'existence de la méthode avant de l'utiliser.

const rules = new Intl.PluralRules("en-US");

if (typeof rules.selectRange === "function") {
  // Utiliser selectRange pour la pluralisation des plages
  console.log(rules.selectRange(1, 3));
} else {
  // Se rabattre sur select avec la valeur finale
  console.log(rules.select(3));
}

Cette approche de repli utilise select() sur la valeur finale lorsque selectRange() n'est pas disponible. Ce n'est pas linguistiquement parfait pour toutes les langues, mais cela fournit une approximation raisonnable pour les navigateurs plus anciens.

Des polyfills sont disponibles via des packages comme @formatjs/intl-pluralrules si vous avez besoin d'un support complet pour les environnements plus anciens.

Quand utiliser selectRange versus select

Utilisez selectRange() lorsque votre interface utilisateur affiche explicitement une plage avec les valeurs de début et de fin visibles pour l'utilisateur. Cela inclut des contextes comme les résultats de recherche affichant "Trouvé 10-15 correspondances", l'inventaire indiquant "1-3 articles en stock", ou les filtres montrant "Sélectionnez 2-5 options".

Utilisez select() lors de l'affichage d'un seul compteur, même si ce compteur représente une valeur approximative ou résumée. Par exemple, "Environ 10 résultats" utilise select(10) car vous affichez un seul nombre, pas une plage.

Si votre plage est affichée en utilisant Intl.NumberFormat.formatRange() pour les nombres, utilisez selectRange() pour le texte d'accompagnement. Cela assure la cohérence entre le formatage des nombres et la pluralisation du texte.

const locale = "en-US";
const numberFormat = new Intl.NumberFormat(locale);
const pluralRules = new Intl.PluralRules(locale);
const forms = new Map([
  ["one", "result"],
  ["other", "results"]
]);

function formatSearchResults(start, end) {
  const rangeFormatted = numberFormat.formatRange(start, end);
  const category = pluralRules.selectRange(start, end);
  const form = forms.get(category);
  return `Found ${rangeFormatted} ${form}`;
}

console.log(formatSearchResults(10, 15));
// Output: "Found 10–15 results"

Ce modèle utilise formatRange() de Intl.NumberFormat pour formater les nombres et selectRange() de Intl.PluralRules pour choisir le texte. Les deux méthodes opèrent sur des plages, assurant un traitement correct pour toutes les langues.