Posez votre question et obtenez un résumé du document en referencant cette page et le Provider AI de votre choix
Le contenu de cette page a été traduit à l'aide d'une IA.
Voir la dernière version du contenu original en anglaisSi vous avez une idée d’amélioration pour améliorer cette documentation, n’hésitez pas à contribuer en submitant une pull request sur GitHub.
Lien GitHub de la documentationCopier le Markdown du doc dans le presse-papiers
Format de Message ICU : la syntaxe et les pièges courants
ICU MessageFormat est une syntaxe de chaîne permettant à une traduction d'embarquer sa propre logique conditionnelle : pluriels, formes selon le genre, formatage de nombres et de dates. Elle part du principe que la grammaire appartient au traducteur, non au développeur qui écrirait if (count === 1). Cet article aborde la syntaxe, les aspects linguistiques qui mettent en échec les implémentations naïves et la façon dont l'écosystème JS gère ces problématiques.
Table des matières
Le problème, concrètement
Voici le code que presque tout le monde écrit au début :
Copier le code dans le presse-papiers
Cette approche fonctionne en anglais mais échoue partout ailleurs :
- Le russe et le polonais nécessitent trois ou quatre formes, et non deux.
- Le japonais n'en requiert qu'une seule, et l'espace concaténé est erroné.
- L'arabe en exige six, et le nombre lui-même devrait être rendu dans le système numérique adapté à la locale.
- Le français place une espace insécable avant certaines ponctuations, que votre
+ " "vient de casser.
Le problème de fond réside dans le découpage de la phrase en fragments isolés. Le traducteur se retrouve face à item et items sans contexte, incapable de réordonner la phrase. ICU MessageFormat résout ce problème en maintenant la phrase entière dans une chaîne traduisible unique tout en offrant des opérateurs logiques au traducteur.
Arguments simples
L'unité de base est un espace réservé (placeholder) entre accolades simples :
Copier le code dans le presse-papiers
Vous fournissez { name: "Alice" } au formatage pour obtenir Hello, Alice!. Les accolades constituent les seuls caractères spéciaux. Pour afficher une accolade littérale, entourez-la de guillemets simples : '{'.
C'est l'ensemble du système d'interpolation. Tout le reste dans ICU s'appuie sur cette base.
Pluriel
plural sélectionne une branche selon une valeur numérique :
Copier le code dans le presse-papiers
Trois points essentiels à connaître :
#est remplacé par la valeur formatée decount, adaptée à la locale. Ainsi,1234devient1,234enen-USet1 234enfr-FR.otherest obligatoire. Chaque implémentation ICU générera une erreur ou échouera à la validation en son absence. Il s'agit du repli lorsque aucune catégorie ne correspond.=0,=1, … ciblent des valeurs exactes et sont vérifiés avant les catégories CLDR. Utilisez-les pour des messages spécifiques ("Aucun message"), et non en remplacement deone.
Copier le code dans le presse-papiers
offset
offset:n soustrait n de la valeur avant la sélection de la catégorie et la substitution de #. Cela permet de gérer le modèle "Alice et 3 autres personnes ont aimé ceci" :
Copier le code dans le presse-papiers
Avec count: 4, # affichera 3. L'option offset est très utile mais parfois mal supportée selon les runtimes, pensez donc à vérifier votre environnement.
Les catégories de pluriel dépendent de la langue
C'est l'erreur la plus répandue. Les catégories zero, one, two, few, many, other ne sont pas des réceptacles universels valables pour toutes les langues. Chaque locale utilise un sous-ensemble, défini par les règles de pluriel CLDR, qui reposent sur la grammaire et non sur l'intuition.
Ouvrir le tableau dans une fenêtre modale pour voir tout le contenu clairement
| Langue | Tag | Catégories utilisées | Total |
|---|---|---|---|
| Japonais | ja | other | 1 |
| Chinois | zh | other | 1 |
| Anglais | en | one, other | 2 |
| Allemand | de | one, other | 2 |
| Français | fr | one, many, other | 3 |
| Tchèque | cs | one, few, many, other | 4 |
| Polonais | pl | one, few, many, other | 4 |
| Russe | ru | one, few, many, other | 4 |
| Arabe | ar | zero, one, two, few, many, other | 6 |
| Gallois | cy | zero, one, two, few, many, other | 6 |
Deux conséquences souvent inattendues :
onene signifie pas strictement "1". En russe,oneenglobe 1, 21, 31, 101 : tout nombre se terminant par 1 à l'exception de 11. En français,0relève de la catégorieone.- Ajouter une catégorie au texte source anglais n'a aucun effet. Le message anglais n'a besoin que de
oneetother. La traduction polonaise exige quatre branches, et cette structure réside dans la chaîne polonaise, non dans l'anglaise. Tout format contraignant chaque locale à partager la même structure de clés posera problème ici.
Vous pouvez vérifier le comportement réel d'un runtime sans rien installer :
Copier le code dans le presse-papiers
Intl.PluralRules fournit les données CLDR dans tous les navigateurs modernes et dans Node. Une bibliothèque revendiquant la pluralisation CLDR s'appuie presque systématiquement sur cette API native.
select et selectordinal
select permet de créer des branches à partir d'une chaîne arbitraire : un genre, un rôle, un statut ou un niveau d'abonnement.
Copier le code dans le presse-papiers
Les clés sont comparées littéralement et other reste obligatoire ici aussi. select est l'outil adapté dès que la structure d'une phrase dépend d'une valeur énumérée, car les langues ne s'accordent pas sur les catégories qui influencent leur grammaire.
selectordinal adopte la même forme que plural mais utilise les règles de pluriels ordinaux, distinctes de celles des nombres cardinaux :
Copier le code dans le presse-papiers
L'anglais emploie quatre catégories ordinales (1st, 2nd, 3rd, 4th) alors qu'il n'en utilise que deux pour les cardinaux. Cette asymétrie justifie l'existence de deux opérateurs distincts.
Arguments de nombres, dates et heures
ICU permet également de formater directement les valeurs interpolées :
Copier le code dans le presse-papiers
La syntaxe moderne s'appuie sur les skeletons, introduits avec ICU 60 et préfixés par ::. Les skeletons s'avèrent bien plus expressifs que les styles traditionnels :
Copier le code dans le presse-papiers
Le support des skeletons reste variable selon les solutions. FormatJS les gère intégralement, tandis que d'autres runtimes n'acceptent que les formes historiques number, currency ou date, long. Vérifiez la prise en charge de :: dans votre environnement avant mise en production.
Imbrication et lisibilité
ICU est composable. Une branche de pluriel peut contenir un select, qui lui-même peut contenir un autre pluriel :
Copier le code dans le presse-papiers
Il s'agit de l'exemple classique d'ICU, mais aussi de l'illustration parfaite des dérives de l'imbrication profonde. Dès le deuxième niveau, les erreurs d'accolades se multiplient et les éditeurs TMS peinent à assister le traducteur. Limitez l'imbrication à deux niveaux maximum. Si un troisième niveau s'avère nécessaire, scindez la phrase en deux messages distincts.
Prise en charge d'ICU par les bibliothèques JS
Ouvrir le tableau dans une fenêtre modale pour voir tout le contenu clairement
| Bibliothèque | Support ICU | Ce que vous écrivez réellement |
|---|---|---|
| react-intl (FormatJS) | Natif, complet | Chaînes ICU, y compris les skeletons et balises rich-text |
| next-intl | Natif | Chaînes ICU, via intl-messageformat de FormatJS |
| i18next | Nécessite un plugin | Suffixes key_one / key_other et {{name}}, ICU via i18next-icu |
| vue-i18n | Partiel / propriétaire | Interpolation {name} et branches de pluriel séparées par des barres |
Angular ($localize) | Sous-ensemble | ICU plural / select dans les templates, extrait vers XLIFF |
Quelques précisions pour une lecture éclairée du tableau :
- La syntaxe par défaut d'i18next n'est pas ICU, sans que ce soit un défaut. Les suffixes (
item_one,item_few) correspondent aux catégories deIntl.PluralRuleset sont souvent plus simples à manipuler dans un fichier JSON plat. Cependant,selectet les imbrications complexes n'en font pas partie, ce qui impose d'ajouteri18next-icuou de gérer la logique dans le code. - Les pluriels de vue-i18n s'appuient sur une fonction de règles propre à la locale plutôt que sur les catégories CLDR par défaut. Cela fonctionne, mais la règle se situe dans la configuration de l'application et non dans les données.
- FormatJS constitue la référence en JS. Lorsqu'on évoque "ICU MessageFormat" dans le contexte JavaScript, on fait généralement référence à ce que FormatJS prend en charge.
- La prise en charge intégrale d'ICU a un coût sur le bundle. Le parseur et la gestion des skeletons ajoutent environ 10 Ko de JavaScript compressé. Voir pourquoi ICU n'est pas fait pour JavaScript.
L'approche d'Intlayer
Intlayer n'utilise pas de DSL sous forme de chaîne de caractères. Les opérateurs logiques sont des fonctions déclarées dans un fichier de contenu TypeScript ou JavaScript. La structure est ainsi typée et chaque locale ne déclare que les catégories exigées par sa propre grammaire :
Copier le code dans le presse-papiers
Copier le code dans le presse-papiers
La correspondance avec les concepts ICU est directe :
Ouvrir le tableau dans une fenêtre modale pour voir tout le contenu clairement
| Concept ICU | Intlayer |
|---|---|
{name} | insert("Hello {{name}}") ou détection auto |
{count, plural, …} | plural({ one, few, many, other }) |
{value, select, …} | select({ draft, published, fallback }) |
branche de genre de select | gender({ male, female, fallback }) |
branche booléenne de select | cond({ true, false }) |
| plages numériques (non-CLDR) | enu({ "0": …, ">5": …, fallback: … }) |
{n, number, ::currency/EUR} | useCurrency()(1234.5, { currency: "EUR" }) |
plural délègue la sélection des catégories à Intl.PluralRules, ce qui permet d'appliquer la table CLDR ci-dessus sans altération. Le formatage reste découplé : nombres, dates, devises et listes sont gérés via des hooks de formatage plutôt que d'être intégrés directement dans le message textuel.
Limites objectives :
- Intlayer requiert une étape de build : le compilateur extrait les déclarations lors de la compilation. Si vous recherchez un simple JSON chargé dynamiquement au runtime, le paradigme est différent.
pluralne permet pas d'imbriquer unt()à l'intérieur de ses branches pour l'instant : vous enveloppezpluraldanst(), et non l'inverse.- L'écosystème est plus récent que celui d'i18next, avec moins d'intégrations TMS directes ou de retours d'expérience sur StackOverflow.
Pour les projets existants contenant déjà des chaînes ICU, l'adaptateur de compatibilité react-intl les analyse directement : plural, select, selectordinal, # et les arguments historiques number, date, time. Les skeletons et l'option offset: n'étant pas pris en charge par ce résolveur, vérifiez ces messages lors d'une migration. L'adaptateur i18next résout quant à lui les suffixes (key_one, key_male) via Intl.PluralRules.
Erreurs courantes
- Coder la logique de pluriel en dur dans le code. La condition
count === 1 ? a : bproduit un résultat erroné pour 8 des 10 langues listées dans le tableau précédent. Une fois la ternaire intégrée au code, le traducteur ne peut plus intervenir. - Concaténer des segments de texte traduits. L'ordre des mots, les accords grammaticaux et les espaces devant la ponctuation dépendent de la locale. Conservez toujours la phrase dans son ensemble.
- Omettre
other. Il s'agit d'une obligation de la spécification et non d'une convention facultative. La plupart des parseurs rejetteront le message, et les autres n'afficheront rien. - Supposer que vos catégories se transposent directement. Une source anglaise avec
oneetotherne signifie pas que le fichier polonais comportera deux branches. Chaque locale doit pouvoir déclarer ses propres règles. Consultez la déclaration de contenu par locale. - Utiliser
=1à la place deone.=1cible uniquement la valeur numérique 1. En russe, 21 exigeone, et la règle=1ne s'activera jamais pour ce cas. - Placer
#en dehors d'une branche de pluriel. Ce caractère n'a de signification spéciale qu'au sein d'un blocpluralouselectordinal. Ailleurs, il est traité comme un simple dièse. - Oublier que
#est déjà formaté. Si vous souhaitez afficher le nombre brut sans séparateur de milliers, interpolez plutôt l'argument par son nom.
Pour aller plus loin
Commentaires
Aucun commentaire pour le moment. Soyez le premier à partager vos pensées.
