Retour au cours

frontend / javascript

Intl : internationalisation native

Leçon 301 exercice

Explication

Ce que vous allez apprendre

  • Formater un nombre et une date selon les conventions d'une locale avec Intl.NumberFormat/Intl.DateTimeFormat
  • Expliquer pourquoi un formatage manuel par manipulation de chaînes est fragile à l'international
  • Générer un texte relatif naturel ("il y a 3 jours") avec Intl.RelativeTimeFormat
  • Gérer l'accord grammatical au pluriel selon la langue avec Intl.PluralRules
  • Trier des chaînes de caractères en respectant les conventions linguistiques avec Intl.Collator

Dans quel contexte ?

Une application e-commerce affiche "1234.5 €" à ses clients français au lieu de "1 234,50 €", et son tri alphabétique des noms de produits place systématiquement "Éclair" après "Zèbre" à cause de l'accent, perturbant les utilisateurs qui cherchent leurs produits. Ces deux problèmes viennent du même réflexe évitable : formater et trier "à la main" au lieu d'utiliser Intl.NumberFormat("fr-FR") et Intl.Collator("fr"), qui encapsulent déjà correctement ces conventions culturelles et linguistiques.

1. Rien ne s'écrit pareil partout

Un nombre, une date ou une liste ne s'écrivent pas de la même façon partout dans le monde : la virgule décimale française devient un point en anglais américain, l'ordre jour/mois/année s'inverse selon les régions.

Prérequis

Aucune connaissance préalable spécifique n'est nécessaire, mais avoir déjà manipulé des dates JavaScript natives (Date) aide à comprendre les exemples de formatage de dates de cette leçon.

2. Pourquoi le formatage manuel ne suffit pas

Formater ces valeurs "à la main" avec des manipulations de chaînes est fragile et rapidement incorrect dès qu'une application vise plusieurs pays.

3. Intl encapsule ces règles pour vous

L'espace de noms Intl, natif du navigateur et de Node.js, encapsule ces règles culturelles. Intl.NumberFormat et Intl.DateTimeFormat couvrent le cas le plus fréquent : afficher un nombre ou une date selon les conventions d'une locale.

API IntlRôle
Intl.NumberFormatFormater un nombre ou une devise selon une locale
Intl.DateTimeFormatFormater une date selon une locale
Intl.RelativeTimeFormatProduire un texte relatif ("il y a 3 jours")
Intl.PluralRulesDéterminer la forme grammaticale plurielle correcte
Intl.CollatorTrier du texte selon les conventions linguistiques

4. Des besoins plus subtils, déjà résolus

Intl.RelativeTimeFormat produit un texte naturel ("il y a 3 jours") plutôt qu'une date brute. Intl.PluralRules détermine la bonne forme grammaticale d'un mot selon la quantité et la langue.

5. Un exemple qui montre la difficulté réelle

Cette règle de pluriel varie radicalement d'une langue à l'autre, et aucune formule simple ne peut la généraliser — d'où l'intérêt de s'appuyer sur une API déjà correcte plutôt que de la réinventer.

Astuce

Ne codez jamais vous-même la règle "ajouter un 's' si n > 1" : certaines langues ont plus de deux formes plurielles (l'arabe en a six). Intl.PluralRules(locale).select(n) renvoie la catégorie correcte (one, few, many, other...) pour n'importe quelle langue prise en charge.

6. Trier du texte correctement aussi

Intl.Collator trie des chaînes en respectant les conventions linguistiques réelles d'une langue, là où le tri par défaut de sort() classe les caractères accentués de façon souvent incohérente.

Cette leçon est une invitation à ne jamais réinventer ces règles soi-même : chaque cas semble simple isolément, mais leur combinaison à l'échelle internationale est un piège classique déjà résolu.

Commandes & code

Intl : internationalisation native

L'API standard du navigateur/Node pour formater dates, nombres et textes selon une locale.

js
// --- Intl.NumberFormat : nombres, devises, pourcentages, unites ---
const nombre = 1234567.891;

console.log(new Intl.NumberFormat("fr-FR").format(nombre));           // "1 234 567,891"
console.log(new Intl.NumberFormat("en-US").format(nombre));            // "1,234,567.891"
console.log(new Intl.NumberFormat("de-DE").format(nombre));             // "1.234.567,891"

const formatteurEUR = new Intl.NumberFormat("fr-FR", {
    style: "currency",
    currency: "EUR",
});
console.log(formatteurEUR.format(49.9));      // "49,90 €"

const formatteurUSD = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
console.log(formatteurUSD.format(49.9));       // "$49.90"

console.log(new Intl.NumberFormat("fr-FR", { style: "percent" }).format(0.256));    // "26 %"

console.log(
    new Intl.NumberFormat("fr-FR", { notation: "compact" }).format(1_500_000)
);   // "1,5 M" -- notation compacte pour l'affichage de statistiques

console.log(
    new Intl.NumberFormat("en-US", { style: "unit", unit: "kilometer-per-hour" }).format(80)
);   // "80 km/h"

// --- Intl.DateTimeFormat : dates et heures localisees ---
const maintenant = new Date(2024, 5, 15, 14, 30);

console.log(new Intl.DateTimeFormat("fr-FR").format(maintenant));       // "15/06/2024"
console.log(new Intl.DateTimeFormat("en-US").format(maintenant));        // "6/15/2024"

const formatteurComplet = new Intl.DateTimeFormat("fr-FR", {
    weekday: "long", year: "numeric", month: "long", day: "numeric",
    hour: "2-digit", minute: "2-digit",
});
console.log(formatteurComplet.format(maintenant));   // "samedi 15 juin 2024 a 14:30"

// Plage de dates formatee de facon compacte
const debut = new Date(2024, 5, 15);
const fin = new Date(2024, 5, 20);
console.log(new Intl.DateTimeFormat("fr-FR").formatRange(debut, fin));   // "15-20/06/2024"

// --- Intl.RelativeTimeFormat : "il y a 3 jours", "dans 2 heures" ---
const rtf = new Intl.RelativeTimeFormat("fr", { numeric: "auto" });
console.log(rtf.format(-3, "day"));       // "il y a 3 jours"
console.log(rtf.format(2, "hour"));         // "dans 2 heures"
console.log(rtf.format(-1, "day"));          // "hier" (numeric: "auto" utilise les formes idiomatiques)
console.log(rtf.format(0, "day"));            // "aujourd'hui"

// --- Intl.ListFormat : listes grammaticalement correctes ---
const fruits = ["pommes", "bananes", "cerises"];
console.log(new Intl.ListFormat("fr", { type: "conjunction" }).format(fruits));
// "pommes, bananes et cerises"
console.log(new Intl.ListFormat("fr", { type: "disjunction" }).format(fruits));
// "pommes, bananes ou cerises"
console.log(new Intl.ListFormat("en", { type: "conjunction" }).format(fruits));
// "pommes, bananes, and cerises"

// --- Intl.PluralRules : regles de pluriel correctes selon la locale (pas juste s/pas de s) ---
const pr = new Intl.PluralRules("fr");
console.log(pr.select(0));      // "one" (en francais, 0 se comporte comme le singulier)
console.log(pr.select(1));       // "one"
console.log(pr.select(2));        // "other"

function pluraliser(n, singulier, pluriel) {
    return `${n} ${pr.select(n) === "one" ? singulier : pluriel}`;
}
console.log(pluraliser(0, "article", "articles"));    // "0 article"
console.log(pluraliser(5, "article", "articles"));      // "5 articles"

// --- Intl.Collator : tri de chaines respectant les regles linguistiques de la locale ---
const mots = ["cote", "coté", "côte", "côté"];
console.log(mots.sort());                                        // tri par code point brut, souvent incorrect
console.log(mots.sort(new Intl.Collator("fr").compare));           // tri linguistiquement correct

// Tri insensible a la casse ET aux accents, tres utile pour une recherche utilisateur
const collatorRecherche = new Intl.Collator("fr", { sensitivity: "base" });
console.log(collatorRecherche.compare("cafe", "CAFÉ"));    // 0 -- considere egal pour la recherche

// --- Intl.Segmenter : decouper un texte en mots/phrases selon la locale (gere les emojis, CJK...) ---
const segmenteur = new Intl.Segmenter("fr", { granularity: "word" });
const segments = Array.from(segmenteur.segment("Bonjour le monde !"));
console.log(segments.filter((s) => s.isWordLike).map((s) => s.segment));   // ["Bonjour", "le", "monde"]

// --- Detection de la locale/langue preferee de l'utilisateur ---
console.log(navigator.language);          // "fr-FR"
console.log(navigator.languages);          // ["fr-FR", "fr", "en-US", "en"]

Résumé

  • Intl.NumberFormat/DateTimeFormat évitent tout formatage manuel fragile et respectent les conventions de chaque locale.
  • Intl.RelativeTimeFormat et Intl.ListFormat produisent un texte idiomatique ("il y a 3 jours", "A, B et C").
  • Intl.PluralRules gère les règles de pluriel réelles d'une langue, pas juste l'ajout d'un "s".
  • Intl.Collator trie des chaînes selon les règles linguistiques de la locale, contrairement à Array.sort() par défaut.

Exercices pratiques

1 disponible
1

Mission : réparer un tri et un pluriel codés à la main

Objectif : Diagnostiquer un tri alphabétique incorrect et une pluralisation codée à la main, puis les remplacer par les bonnes API Intl.

Contexte

Un catalogue produit trie ses articles ainsi :

js
produits.sort((a, b) => (a.nom > b.nom ? 1 : -1));

Le tri place systématiquement "Éclair" après "Zèbre". Par ailleurs, une fonction affiche ${n} article(s) codée en dur pour tous les cas de quantité.

Résoudre l’exercice →