Comment choisir la bonne forme plurielle pour différentes langues ?
Utilisez Intl.PluralRules de JavaScript pour sélectionner entre un élément, deux éléments, quelques éléments, plusieurs éléments selon les règles spécifiques à chaque langue
Introduction
Lorsque vous affichez du texte avec des quantités, vous avez besoin de messages différents pour différents décomptes. En anglais, on écrit "1 file" mais "2 files". L'approche la plus simple consiste à concaténer un nombre avec un mot et à ajouter un "s" lorsque nécessaire.
function formatFileCount(count) {
return count === 1 ? `${count} file` : `${count} files`;
}
Cette approche échoue de trois façons. Premièrement, elle produit un anglais incorrect pour zéro ("0 files" devrait sans doute être "no files"). Deuxièmement, elle ne fonctionne pas avec des pluriels complexes comme "1 child, 2 children" ou "1 person, 2 people". Troisièmement, et c'est le plus critique, d'autres langues ont des règles de pluriel entièrement différentes que ce code ne peut pas gérer.
JavaScript fournit Intl.PluralRules pour résoudre ce problème. Cette API détermine quelle forme plurielle utiliser pour n'importe quel nombre dans n'importe quelle langue, en suivant la norme CLDR d'Unicode utilisée par les systèmes de traduction professionnels dans le monde entier.
Pourquoi différentes langues nécessitent différentes formes plurielles
L'anglais utilise deux formes plurielles. On écrit "1 book" et "2 books". Le mot change lorsque le décompte est exactement un par rapport à tout autre nombre.
D'autres langues fonctionnent différemment. Le polonais utilise trois formes basées sur des règles complexes. Le russe utilise quatre formes. L'arabe utilise six formes. Certaines langues n'utilisent qu'une seule forme pour toutes les quantités.
Voici des exemples montrant comment le mot pour "pomme" change en fonction de la quantité dans différentes langues :
Anglais : 1 apple, 2 apples, 5 apples, 0 apples
Polonais : 1 jabłko, 2 jabłka, 5 jabłek, 0 jabłek
Russe : 1 яблоко, 2 яблока, 5 яблок, 0 яблок
Arabe : Utilise six formes différentes selon que vous avez zéro, un, deux, quelques-uns, beaucoup ou d'autres quantités
Le CLDR d'Unicode définit les règles exactes pour savoir quand utiliser chaque forme dans chaque langue. Vous ne pouvez pas mémoriser ces règles ou les coder en dur dans votre application. Vous avez besoin d'une API qui les connaît.
Quelles sont les catégories de pluriel CLDR
La norme Unicode CLDR définit six catégories de pluriel qui couvrent toutes les langues :
zero: utilisé dans certaines langues pour exactement zéro élémentone: utilisé pour les formes singulièrestwo: utilisé dans les langues avec une forme duellefew: utilisé pour les petites quantités dans certaines languesmany: utilisé pour les grandes quantités ou fractions dans certaines languesother: la forme par défaut, utilisée lorsqu'aucune autre catégorie ne s'applique
Chaque langue utilise la catégorie other. La plupart des langues n'utilisent que deux ou trois catégories au total. Les catégories ne correspondent pas directement aux quantités. Par exemple, en polonais, le nombre 5 utilise la catégorie many, mais c'est aussi le cas pour 0, 25 et 1,5.
Les règles spécifiques qui déterminent quels nombres correspondent à quelles catégories diffèrent selon la langue. JavaScript gère cette complexité grâce à l'API Intl.PluralRules.
Comment déterminer quelle forme plurielle utiliser
L'objet Intl.PluralRules détermine à quelle catégorie de pluriel appartient un nombre dans une langue spécifique. Vous créez un objet PluralRules avec une locale, puis appelez sa méthode select() avec un nombre.
const rules = new Intl.PluralRules('en-US');
console.log(rules.select(0)); // "other"
console.log(rules.select(1)); // "one"
console.log(rules.select(2)); // "other"
console.log(rules.select(5)); // "other"
En anglais, select() renvoie "one" pour le nombre 1 et "other" pour tout le reste.
Le polonais utilise trois catégories avec des règles plus complexes :
const rules = new Intl.PluralRules('pl-PL');
console.log(rules.select(0)); // "many"
console.log(rules.select(1)); // "one"
console.log(rules.select(2)); // "few"
console.log(rules.select(5)); // "many"
console.log(rules.select(22)); // "few"
console.log(rules.select(25)); // "many"
L'arabe utilise six catégories :
const rules = new Intl.PluralRules('ar-EG');
console.log(rules.select(0)); // "zero"
console.log(rules.select(1)); // "one"
console.log(rules.select(2)); // "two"
console.log(rules.select(3)); // "few"
console.log(rules.select(11)); // "many"
console.log(rules.select(100)); // "other"
La méthode select() renvoie une chaîne identifiant la catégorie. Vous utilisez cette chaîne pour choisir le message approprié à afficher.
Comment associer les catégories de pluriel aux messages
Après avoir déterminé la catégorie de pluriel, vous devez sélectionner le message correct à afficher à l'utilisateur. Créez un objet qui associe chaque catégorie à son message, puis utilisez la chaîne de catégorie pour rechercher le message.
const messages = {
one: '{count} fichier',
other: '{count} fichiers'
};
function formatFileCount(count, locale) {
const rules = new Intl.PluralRules(locale);
const category = rules.select(count);
const message = messages[category];
return message.replace('{count}', count);
}
console.log(formatFileCount(1, 'en-US')); // "1 fichier"
console.log(formatFileCount(5, 'en-US')); // "5 fichiers"
Ce modèle fonctionne pour n'importe quelle langue. Pour le polonais, vous fournissez des messages pour les trois catégories utilisées par la langue :
const messages = {
one: '{count} plik',
few: '{count} pliki',
many: '{count} plików'
};
function formatFileCount(count, locale) {
const rules = new Intl.PluralRules(locale);
const category = rules.select(count);
const message = messages[category];
return message.replace('{count}', count);
}
console.log(formatFileCount(1, 'pl-PL')); // "1 plik"
console.log(formatFileCount(2, 'pl-PL')); // "2 pliki"
console.log(formatFileCount(5, 'pl-PL')); // "5 plików"
console.log(formatFileCount(22, 'pl-PL')); // "22 pliki"
La structure du code reste identique pour toutes les langues. Seul l'objet messages change. Cette séparation permet aux traducteurs de fournir les messages corrects pour leur langue sans modifier le code.
Comment gérer les catégories de pluriel manquantes
Votre objet messages peut ne pas inclure les six catégories possibles. La plupart des langues n'utilisent que deux ou trois catégories. Lorsque select() renvoie une catégorie qui n'est pas dans votre objet messages, utilisez la catégorie other comme solution de repli.
function formatFileCount(count, locale, messages) {
const rules = new Intl.PluralRules(locale);
const category = rules.select(count);
const message = messages[category] || messages.other;
return message.replace('{count}', count);
}
const englishMessages = {
one: '{count} fichier',
other: '{count} fichiers'
};
console.log(formatFileCount(1, 'en-US', englishMessages)); // "1 fichier"
console.log(formatFileCount(5, 'en-US', englishMessages)); // "5 fichiers"
Ce modèle garantit que votre code fonctionne même lorsque l'objet messages est incomplet. La catégorie other existe toujours dans chaque langue, ce qui en fait une solution de repli sûre.
Comment utiliser les règles de pluriel avec les nombres ordinaux
Le constructeur Intl.PluralRules accepte une option type qui modifie la façon dont les catégories sont déterminées. Le type par défaut est "cardinal", utilisé pour compter les éléments. Définissez type: "ordinal" pour déterminer les formes plurielles pour les nombres ordinaux comme "1er", "2e", "3e".
const cardinalRules = new Intl.PluralRules('en-US', { type: 'cardinal' });
console.log(cardinalRules.select(1)); // "one"
console.log(cardinalRules.select(2)); // "other"
console.log(cardinalRules.select(3)); // "other"
const ordinalRules = new Intl.PluralRules('en-US', { type: 'ordinal' });
console.log(ordinalRules.select(1)); // "one"
console.log(ordinalRules.select(2)); // "two"
console.log(ordinalRules.select(3)); // "few"
console.log(ordinalRules.select(4)); // "other"
Les règles cardinales déterminent "1 élément, 2 éléments". Les règles ordinales déterminent "1ère place, 2e place, 3e place, 4e place". Les catégories retournées diffèrent car les modèles grammaticaux diffèrent.
Comment formater les quantités fractionnaires
La méthode select() fonctionne avec les nombres décimaux. Différentes langues ont des règles spécifiques pour la façon dont les fractions correspondent aux catégories plurielles.
const rules = new Intl.PluralRules('en-US');
console.log(rules.select(1)); // "one"
console.log(rules.select(1.0)); // "one"
console.log(rules.select(1.5)); // "other"
console.log(rules.select(0.5)); // "other"
En anglais, 1.0 utilise la forme singulière, mais 1.5 utilise le pluriel. Certaines langues ont des règles différentes pour les fractions, les traitant comme une catégorie distincte.
const messages = {
one: '{count} fichier',
other: '{count} fichiers'
};
const rules = new Intl.PluralRules('en-US');
const count = 1.5;
const category = rules.select(count);
const message = messages[category];
console.log(message.replace('{count}', count)); // "1.5 fichiers"
Passez le nombre décimal directement à select(). La méthode applique automatiquement les règles de langue correctes.
Comment créer un formateur de pluriel réutilisable
Au lieu de répéter le même modèle dans toute votre application, créez une fonction réutilisable qui encapsule la logique de sélection plurielle.
class PluralFormatter {
constructor(locale) {
this.locale = locale;
this.rules = new Intl.PluralRules(locale);
}
format(count, messages) {
const category = this.rules.select(count);
const message = messages[category] || messages.other;
return message.replace('{count}', count);
}
}
const formatter = new PluralFormatter('en-US');
const fileMessages = {
one: '{count} fichier',
other: '{count} fichiers'
};
const itemMessages = {
one: '{count} élément',
other: '{count} éléments'
};
console.log(formatter.format(1, fileMessages)); // "1 fichier"
console.log(formatter.format(5, fileMessages)); // "5 fichiers"
console.log(formatter.format(1, itemMessages)); // "1 élément"
console.log(formatter.format(3, itemMessages)); // "3 éléments"
Cette classe crée l'objet PluralRules une seule fois et le réutilise pour plusieurs opérations de formatage. Vous pouvez l'étendre pour prendre en charge des fonctionnalités plus avancées comme le formatage du compteur avec Intl.NumberFormat avant de l'insérer dans le message.
Comment intégrer les règles de pluriel avec les systèmes de traduction
Les systèmes de traduction professionnels stockent les messages avec des espaces réservés pour les catégories de pluriel. Lorsque vous traduisez un texte, vous fournissez toutes les formes plurielles dont votre langue a besoin.
const translations = {
'en-US': {
fileCount: {
one: '{count} file',
other: '{count} files'
},
downloadComplete: {
one: 'Download of {count} file complete',
other: 'Download of {count} files complete'
}
},
'pl-PL': {
fileCount: {
one: '{count} plik',
few: '{count} pliki',
many: '{count} plików'
},
downloadComplete: {
one: 'Pobieranie {count} pliku zakończone',
few: 'Pobieranie {count} plików zakończone',
many: 'Pobieranie {count} plików zakończone'
}
}
};
class Translator {
constructor(locale, translations) {
this.locale = locale;
this.translations = translations[locale] || {};
this.rules = new Intl.PluralRules(locale);
}
translate(key, count) {
const messages = this.translations[key];
if (!messages) return key;
const category = this.rules.select(count);
const message = messages[category] || messages.other;
return message.replace('{count}', count);
}
}
const translator = new Translator('en-US', translations);
console.log(translator.translate('fileCount', 1)); // "1 file"
console.log(translator.translate('fileCount', 5)); // "5 files"
console.log(translator.translate('downloadComplete', 1)); // "Download of 1 file complete"
console.log(translator.translate('downloadComplete', 5)); // "Download of 5 files complete"
const polishTranslator = new Translator('pl-PL', translations);
console.log(polishTranslator.translate('fileCount', 1)); // "1 plik"
console.log(polishTranslator.translate('fileCount', 2)); // "2 pliki"
console.log(polishTranslator.translate('fileCount', 5)); // "5 plików"
Ce modèle sépare les données de traduction de la logique du code. Les traducteurs fournissent les messages pour chaque catégorie de pluriel utilisée par leur langue. Votre code applique les règles automatiquement.
Comment vérifier quelles catégories de pluriel utilise une locale
La méthode resolvedOptions() renvoie des informations sur l'objet PluralRules, mais elle ne liste pas les catégories utilisées par la locale. Pour trouver toutes les catégories qu'une locale utilise, testez une plage de nombres et collectez les catégories uniques renvoyées.
function getPluralCategories(locale) {
const rules = new Intl.PluralRules(locale);
const categories = new Set();
for (let i = 0; i <= 100; i++) {
categories.add(rules.select(i));
categories.add(rules.select(i + 0.5));
}
return Array.from(categories).sort();
}
console.log(getPluralCategories('en-US')); // ["one", "other"]
console.log(getPluralCategories('pl-PL')); // ["few", "many", "one"]
console.log(getPluralCategories('ar-EG')); // ["few", "many", "one", "other", "two", "zero"]
Cette technique teste les entiers et les valeurs fractionnaires sur une plage donnée. Elle capture les catégories que votre objet de messages doit inclure pour une locale spécifique.