Comment choisir la forme plurielle pour les plages comme 1-3 éléments

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 extrémités. Les interfaces utilisateur affichent des plages dans des contextes tels que les résultats de recherche indiquant « 10-15 correspondances trouvées », les systèmes d'inventaire indiquant « 1-3 éléments disponibles », ou les filtres indiquant « Sélectionnez 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 décompte, vous choisissez entre les formes singulier et pluriel : « 1 élément » versus « 2 éléments ». Les langues ont des règles qui déterminent quelle forme s'applique en fonction du décompte. Ces règles varient selon la langue. L'anglais utilise le singulier pour un et le pluriel pour tous les autres décomptes. Le polonais utilise des formes différentes pour 1, 2-4, et 5 ou plus. L'arabe possède six formes distinctes basées sur le décompte.

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 des règles différentes 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 de fin de la plage, mais cela produit des résultats incorrects dans de nombreuses langues.

Prenons l'anglais avec la plage 0-1. L'utilisation de select() sur la valeur de fin renvoie « one », suggérant que vous devriez afficher « 0-1 item ». Ceci est grammaticalement incorrect. La forme correcte est « 0-1 items » au pluriel.

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

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

// But "0-1 item" is 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 nombres individuels. En slovène, la plage 102-201 utilise la forme "few", tandis que les nombres individuels dans cette plage utilisent des formes différentes.

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

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

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

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

Certaines langues utilisent la valeur de début pour déterminer la forme, d'autres utilisent la valeur de fin, 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 besoin de 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 d'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 plurielle des 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 plurielle s'applique : "zero", "one", "two", "few", "many" ou "other".

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

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

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

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

En français, les plages utilisent presque toujours la catégorie "other", qui correspond à la forme plurielle. Cela correspond à la façon dont les francophones expriment naturellement les plages avec des noms au pluriel.

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

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

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

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

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

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

La valeur de retour est toujours l'un des six noms de catégories plurielles 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 de texte pour chaque catégorie plurielle 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. Le Map contient les traductions. La fonction les combine.

Pour les langues avec plus de catégories plurielles, 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. Consultez les règles plurielles Unicode CLDR ou testez avec l'API sur différentes plages pour identifier les catégories nécessaires.

Comment les 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 de la plage. Le portugais utilise "other" pour la plupart des plages. Le russe utilise "few" pour certaines plages.

Ces différences montrent pourquoi le codage en dur de la logique plurielle é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 de la locale, 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));
// Output: "1-3 items"

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

Le formateur de nombres ajoute des séparateurs de milliers. Les règles de pluralisation 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));
// Output: "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 pluralisation déterminent quelle forme de "Artikel" utiliser.

Comparer selectRange avec select pour les valeurs uniques

La méthode select() gère les comptes 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");

// Single count
console.log(rules.select(1));
// Output: "one"

console.log(rules.select(2));
// Output: "other"

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

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

Pour les comptes 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 compte unique 1 utilise la forme singulière.

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

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

// Single counts in Slovenian
console.log(slRules.select(1));
// Output: "one"

console.log(slRules.select(2));
// Output: "two"

console.log(slRules.select(5));
// Output: "few"

// Range in Slovenian
console.log(slRules.selectRange(102, 201));
// Output: "few"

Le slovène utilise "one", "two" et "few" pour différents comptes 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 que vous devriez 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 sous la forme « 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 souhaiterez peut-être détecter quand start est égal à end 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 se lit plus naturellement car il évite d'afficher « 1-1 » ou « 5-5 ».

Gérer les cas limites 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 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 transmettre à selectRange(). Ceci 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 pouvant ê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 offre une flexibilité dans la manière d'appeler la méthode, mais vous devriez privilégier le passage de types numériques réels lorsque cela est possible pour plus de clarté.

Gérer les plages décimales

La méthode selectRange() fonctionne avec des nombres décimaux. Ceci 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));
// Output: "other"

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

console.log(rules.selectRange(1.0, 1.5));
// Output: "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 de 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));
// Output: "1.5-2.5 kilometers"

console.log(formatRange(0.5, 1.0));
// Output: "0.5-1.0 kilometers"

Le formateur de nombres garantit un affichage décimal cohérent. Les règles de pluralisation déterminent la forme correcte en fonction des valeurs décimales.

Compatibilité et prise en charge des navigateurs

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

La prise en charge 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") {
  // Use selectRange for range pluralization
  console.log(rules.selectRange(1, 3));
} else {
  // Fall back to select with the end value
  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'une prise en charge complète pour les environnements plus anciens.

Quand utiliser selectRange plutôt que 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 « 10-15 correspondances trouvées », l'inventaire affichant « 1-3 articles en stock », ou les filtres affichant « Sélectionnez 2-5 options ».

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

Si votre plage est affichée en utilisant Intl.NumberFormat.formatRange() pour les nombres, utilisez selectRange() pour le texte d'accompagnement. Cela garantit 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, garantissant une gestion correcte pour toutes les langues.