Comment personnaliser les locales avec les extensions Unicode

Ajouter des systèmes de calendrier, des formats de numérotation et des préférences d'affichage de l'heure aux identifiants de locale

Introduction

Un identifiant de locale comme en-US indique à JavaScript quelle langue et quelle région utiliser pour le formatage. Cependant, il ne précise pas quel système de calendrier utiliser, quel format de numérotation afficher, ou si l'heure doit être affichée au format 12 heures ou 24 heures. Ces préférences de formatage varient selon le choix de l'utilisateur, et pas seulement selon l'emplacement.

Les extensions Unicode résolvent ce problème. Elles vous permettent d'ajouter des préférences de formatage directement aux identifiants de locale. Au lieu d'utiliser des options de configuration distinctes pour chaque formateur, vous encodez les préférences une seule fois dans la chaîne de locale elle-même.

Ce guide explique comment fonctionnent les extensions Unicode, quels types d'extensions sont disponibles et quand les utiliser dans votre code d'internationalisation.

Que sont les extensions Unicode

Les extensions Unicode sont des balises supplémentaires que vous ajoutez aux identifiants de locale pour spécifier les préférences de formatage. Elles suivent un format standard défini dans BCP 47, la même spécification qui définit les identifiants de locale.

Une extension commence par -u- suivi de paires clé-valeur. Le u signifie Unicode. Chaque clé comporte deux lettres, et les valeurs varient selon le type de clé.

const locale = "en-US-u-ca-gregory-hc-h12";

Cet identifiant de locale spécifie l'anglais américain avec le calendrier grégorien et l'affichage de l'heure au format 12 heures.

Comment ajouter des extensions aux chaînes de locale

Les extensions apparaissent à la fin d'un identifiant de locale, après les composants de langue, de script et de région. Le marqueur -u- sépare l'identifiant principal des extensions.

La structure de base suit ce modèle :

langue-région-u-clé-valeur-clé-valeur

Chaque paire clé-valeur spécifie une préférence de formatage. Vous pouvez inclure plusieurs paires clé-valeur dans une seule chaîne de locale.

const japanese = new Intl.Locale("ja-JP-u-ca-japanese-nu-jpan");
console.log(japanese.calendar); // "japanese"
console.log(japanese.numberingSystem); // "jpan"

L'ordre des paires clé-valeur n'a pas d'importance. "en-u-ca-gregory-nu-latn" et "en-u-nu-latn-ca-gregory" sont tous deux valides et équivalents.

Extensions de calendrier

La clé ca spécifie quel système de calendrier utiliser pour le formatage des dates. Différentes cultures utilisent différents systèmes de calendrier, et certains utilisateurs préfèrent les calendriers non grégoriens pour des raisons religieuses ou culturelles.

Les valeurs de calendrier courantes incluent :

  • gregory pour le calendrier grégorien
  • buddhist pour le calendrier bouddhiste
  • islamic pour le calendrier islamique
  • hebrew pour le calendrier hébraïque
  • chinese pour le calendrier chinois
  • japanese pour le calendrier impérial japonais
const islamicLocale = new Intl.Locale("ar-SA-u-ca-islamic");
const date = new Date("2025-03-15");
const formatter = new Intl.DateTimeFormat(islamicLocale, {
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(formatter.format(date));
// Output: "٢٠ رمضان ١٤٤٦ هـ"

Ceci formate la date selon le calendrier islamique. La même date grégorienne apparaît comme une année, un mois et un jour différents dans le système de calendrier islamique.

Le calendrier bouddhiste est couramment utilisé en Thaïlande. Il compte les années à partir de la naissance de Bouddha en 543 av. J.-C., ce qui fait que les années bouddhistes sont en avance de 543 ans sur les années grégoriennes.

const buddhistLocale = new Intl.Locale("th-TH-u-ca-buddhist");
const formatter = new Intl.DateTimeFormat(buddhistLocale, {
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(formatter.format(new Date("2025-03-15")));
// Output: "15 มีนาคม 2568"

L'année 2025 dans le calendrier grégorien correspond à l'année 2568 dans le calendrier bouddhiste.

Extensions de système de numération

La clé nu spécifie quel système de numération utiliser pour afficher les nombres. Bien que la plupart des locales utilisent les chiffres arabes occidentaux (0-9), de nombreuses régions ont leurs propres systèmes de numération traditionnels.

Les valeurs courantes de système de numération incluent :

  • latn pour les chiffres arabes occidentaux (0-9)
  • arab pour les chiffres indo-arabes
  • hanidec pour les chiffres décimaux chinois
  • deva pour les chiffres devanagari
  • thai pour les chiffres thaïlandais
const arabicLocale = new Intl.Locale("ar-EG-u-nu-arab");
const number = 123456;
const formatter = new Intl.NumberFormat(arabicLocale);

console.log(formatter.format(number));
// Output: "١٢٣٬٤٥٦"

Les chiffres indo-arabes ont un aspect différent des chiffres occidentaux mais représentent les mêmes valeurs. Le nombre 123456 apparaît comme ١٢٣٬٤٥٦.

Les chiffres thaïlandais fournissent un autre exemple :

const thaiLocale = new Intl.Locale("th-TH-u-nu-thai");
const formatter = new Intl.NumberFormat(thaiLocale);

console.log(formatter.format(123456));
// Output: "๑๒๓,๔๕๖"

De nombreuses locales arabes prennent en charge à la fois les chiffres indo-arabes et les chiffres latins. Les utilisateurs peuvent choisir leur système préféré en fonction de leurs préférences personnelles ou du contexte.

Extensions de cycle horaire

La clé hc spécifie comment afficher l'heure. Certaines régions préfèrent le format 12 heures avec les indicateurs AM et PM, tandis que d'autres préfèrent le format 24 heures. Le cycle horaire détermine également comment apparaît minuit.

Quatre valeurs de cycle horaire sont disponibles :

  • h12 utilise les heures 1-12 avec minuit à 12:00 AM
  • h11 utilise les heures 0-11 avec minuit à 0:00 AM
  • h23 utilise les heures 0-23 avec minuit à 0:00
  • h24 utilise les heures 1-24 avec minuit à 24:00

Les valeurs h12 et h11 représentent le format 12 heures, tandis que h23 et h24 représentent le format 24 heures. La différence réside dans le fait que la plage horaire commence à 0 ou à 1.

const us12Hour = new Intl.Locale("en-US-u-hc-h12");
const japan11Hour = new Intl.Locale("ja-JP-u-hc-h11");
const europe23Hour = new Intl.Locale("en-GB-u-hc-h23");

const date = new Date("2025-03-15T00:30:00");

console.log(new Intl.DateTimeFormat(us12Hour, { hour: "numeric", minute: "numeric" }).format(date));
// Output: "12:30 AM"

console.log(new Intl.DateTimeFormat(japan11Hour, { hour: "numeric", minute: "numeric" }).format(date));
// Output: "0:30 AM"

console.log(new Intl.DateTimeFormat(europe23Hour, { hour: "numeric", minute: "numeric" }).format(date));
// Output: "00:30"

Le format h12 affiche minuit comme 12:30 AM, tandis que h11 l'affiche comme 0:30 AM. Le format h23 l'affiche comme 00:30 sans AM ou PM.

La plupart des applications utilisent soit h12 soit h23. Le format h11 est principalement utilisé au Japon, et h24 est rarement utilisé en pratique.

Extensions de collation

La clé co spécifie les règles de collation pour trier les chaînes de caractères. La collation détermine l'ordre des caractères lors du tri de texte. Différentes langues et régions ont différentes conventions de tri.

Les valeurs de collation courantes incluent :

  • standard pour la collation Unicode standard
  • phonebk pour l'ordre de l'annuaire téléphonique (allemand)
  • pinyin pour l'ordre Pinyin (chinois)
  • stroke pour l'ordre par nombre de traits (chinois)

La collation de l'annuaire téléphonique allemand traite les trémas différemment de la collation standard. L'ordre de l'annuaire téléphonique développe ä en ae, ö en oe, et ü en ue pour les besoins du tri.

const names = ["Müller", "Meyer", "Möller", "Mueller"];

const standard = new Intl.Collator("de-DE");
const phonebook = new Intl.Collator("de-DE-u-co-phonebk");

console.log(names.sort((a, b) => standard.compare(a, b)));
// Output: ["Meyer", "Möller", "Mueller", "Müller"]

console.log(names.sort((a, b) => phonebook.compare(a, b)));
// Output: ["Meyer", "Möller", "Mueller", "Müller"]

La collation chinoise offre plusieurs systèmes d'ordonnancement. L'ordre Pinyin trie par prononciation, tandis que l'ordre par traits trie par le nombre de coups de pinceau utilisés pour écrire chaque caractère.

const pinyinCollator = new Intl.Collator("zh-CN-u-co-pinyin");
const strokeCollator = new Intl.Collator("zh-CN-u-co-stroke");

Les extensions de collation n'affectent que l'API Intl.Collator et les méthodes comme Array.prototype.sort() lorsqu'elles sont utilisées avec des collateurs.

Extensions de priorité de casse

La clé kf détermine si les lettres majuscules ou minuscules sont triées en premier dans la collation. Cette préférence varie selon la langue et le cas d'utilisation.

Trois valeurs sont disponibles :

  • upper pour trier les lettres majuscules avant les minuscules
  • lower pour trier les lettres minuscules avant les majuscules
  • false pour utiliser l'ordre de casse par défaut de la locale
const words = ["apple", "Apple", "APPLE", "banana"];

const upperFirst = new Intl.Collator("en-US-u-kf-upper");
const lowerFirst = new Intl.Collator("en-US-u-kf-lower");

console.log(words.sort((a, b) => upperFirst.compare(a, b)));
// Output: ["APPLE", "Apple", "apple", "banana"]

console.log(words.sort((a, b) => lowerFirst.compare(a, b)));
// Output: ["apple", "Apple", "APPLE", "banana"]

L'ordre de priorité de casse affecte la collation lorsque les mots sont par ailleurs identiques à l'exception de la casse. Il détermine l'ordre de tri secondaire après la comparaison des caractères de base.

Extensions de collation numérique

La clé kn active la collation numérique, qui trie les séquences numériques par leur valeur numérique au lieu de lexicographiquement. Sans collation numérique, "10" est trié avant "2" car "1" vient avant "2" dans l'ordre des caractères.

La collation numérique accepte deux valeurs :

  • true pour activer la collation numérique
  • false pour désactiver la collation numérique (par défaut)
const items = ["item1", "item10", "item2", "item20"];

const standard = new Intl.Collator("en-US");
const numeric = new Intl.Collator("en-US-u-kn-true");

console.log(items.sort((a, b) => standard.compare(a, b)));
// Output: ["item1", "item10", "item2", "item20"]

console.log(items.sort((a, b) => numeric.compare(a, b)));
// Output: ["item1", "item2", "item10", "item20"]

Avec la collation numérique activée, "item2" est correctement trié avant "item10" car 2 est inférieur à 10. Cela produit l'ordre de tri attendu pour les chaînes contenant des nombres.

La collation numérique est utile pour trier les noms de fichiers, les numéros de version, les adresses et tout texte contenant des nombres intégrés.

Utilisation d'objets d'options au lieu de chaînes d'extension

Au lieu d'encoder les extensions dans la chaîne de paramètres régionaux, vous pouvez les passer comme options au constructeur Intl.Locale. Cette approche sépare les paramètres régionaux de base des préférences de formatage.

const locale = new Intl.Locale("ja-JP", {
  calendar: "japanese",
  numberingSystem: "jpan",
  hourCycle: "h11"
});

console.log(locale.toString());
// Résultat : "ja-JP-u-ca-japanese-hc-h11-nu-jpan"

Le constructeur convertit automatiquement les options en balises d'extension. Les deux approches produisent des objets de paramètres régionaux identiques.

L'approche par objet d'options offre plusieurs avantages. Elle rend le code plus lisible en utilisant des noms de propriétés complets au lieu de codes à deux lettres. Elle facilite également la construction dynamique de paramètres régionaux à partir de données de configuration.

const userPreferences = {
  language: "ar",
  region: "SA",
  calendar: "islamic",
  numberingSystem: "arab"
};

const locale = new Intl.Locale(`${userPreferences.language}-${userPreferences.region}`, {
  calendar: userPreferences.calendar,
  numberingSystem: userPreferences.numberingSystem
});

Vous pouvez également passer des options directement aux constructeurs de formateurs :

const formatter = new Intl.DateTimeFormat("th-TH", {
  calendar: "buddhist",
  numberingSystem: "thai",
  year: "numeric",
  month: "long",
  day: "numeric"
});

Cela combine les options de formatage spécifiques aux paramètres régionaux avec les options de présentation dans un seul appel de constructeur.

Quand utiliser les extensions par rapport aux options de formateur

Les extensions et les options de formateur servent des objectifs différents. Comprendre quand utiliser chaque approche vous aide à écrire un code plus propre et plus maintenable.

Utilisez les extensions dans la chaîne de paramètres régionaux lorsque les préférences de formatage sont inhérentes aux paramètres régionaux de l'utilisateur. Si un utilisateur thaïlandais souhaite toujours voir le calendrier bouddhiste et les chiffres thaïlandais, encodez ces préférences dans son identifiant de paramètres régionaux.

const userLocale = "th-TH-u-ca-buddhist-nu-thai";

Cela vous permet de passer les paramètres régionaux à n'importe quel formateur sans répéter les préférences :

const dateFormatter = new Intl.DateTimeFormat(userLocale);
const numberFormatter = new Intl.NumberFormat(userLocale);

Les deux formateurs utilisent automatiquement le calendrier bouddhiste et les chiffres thaïlandais.

Utilisez les options de formateur lorsque les préférences de formatage sont spécifiques à un cas d'utilisation. Si vous souhaitez afficher le calendrier islamique dans une partie de votre application mais le calendrier grégorien ailleurs, passez l'option de calendrier au formateur spécifique.

const islamicFormatter = new Intl.DateTimeFormat("ar-SA", {
  calendar: "islamic"
});

const gregorianFormatter = new Intl.DateTimeFormat("ar-SA", {
  calendar: "gregory"
});

Le même identifiant de paramètres régionaux produit un formatage différent en fonction de l'option de calendrier.

Les extensions dans la chaîne de paramètres régionaux agissent comme des valeurs par défaut. Les options de formateur remplacent ces valeurs par défaut lorsqu'elles sont spécifiées. Cela vous permet d'utiliser les préférences de l'utilisateur comme référence tout en personnalisant des formateurs spécifiques.

const locale = "en-US-u-hc-h23";
const formatter12Hour = new Intl.DateTimeFormat(locale, {
  hourCycle: "h12"
});

L'utilisateur préfère le format 24 heures, mais ce formateur spécifique remplace cette préférence pour afficher le format 12 heures.

Lecture des valeurs d'extension à partir des locales

L'objet Intl.Locale expose les valeurs d'extension sous forme de propriétés. Vous pouvez lire ces propriétés pour inspecter ou valider les préférences de formatage d'une locale.

const locale = new Intl.Locale("ar-SA-u-ca-islamic-nu-arab-hc-h12");

console.log(locale.calendar); // "islamic"
console.log(locale.numberingSystem); // "arab"
console.log(locale.hourCycle); // "h12"

Ces propriétés renvoient les valeurs d'extension si elles sont présentes, ou undefined si l'extension n'est pas spécifiée.

Vous pouvez utiliser ces propriétés pour créer des interfaces de configuration ou valider les préférences utilisateur :

function describeLocalePreferences(localeString) {
  const locale = new Intl.Locale(localeString);

  return {
    language: locale.language,
    region: locale.region,
    calendar: locale.calendar || "default",
    numberingSystem: locale.numberingSystem || "default",
    hourCycle: locale.hourCycle || "default"
  };
}

console.log(describeLocalePreferences("th-TH-u-ca-buddhist-nu-thai"));
// Sortie : { language: "th", region: "TH", calendar: "buddhist", numberingSystem: "thai", hourCycle: "default" }

Les propriétés collation, caseFirst et numeric correspondent aux clés d'extension co, kf et kn :

const locale = new Intl.Locale("de-DE-u-co-phonebk-kf-upper-kn-true");

console.log(locale.collation); // "phonebk"
console.log(locale.caseFirst); // "upper"
console.log(locale.numeric); // true

Notez que la propriété numeric renvoie un booléen, pas une chaîne de caractères. La valeur true indique que le classement numérique est activé.

Combinaison de plusieurs extensions

Vous pouvez combiner plusieurs extensions dans un seul identifiant de locale. Cela vous permet de spécifier toutes les préférences de formatage en une seule fois.

const locale = new Intl.Locale("ar-SA-u-ca-islamic-nu-arab-hc-h12-co-standard");

const dateFormatter = new Intl.DateTimeFormat(locale, {
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric"
});

const date = new Date("2025-03-15T14:30:00");
console.log(dateFormatter.format(date));
// La sortie utilise le calendrier islamique, les chiffres arabo-indiens et le format 12 heures

Chaque clé d'extension ne peut apparaître qu'une seule fois dans une chaîne de locale. Si vous spécifiez la même clé plusieurs fois, la dernière valeur prévaut.

const locale = new Intl.Locale("en-US-u-hc-h23-hc-h12");
console.log(locale.hourCycle); // "h12"

Lors de la construction programmatique des locales, assurez-vous que chaque clé d'extension n'apparaît qu'une seule fois pour éviter toute ambiguïté.

Cas d'utilisation pratiques

Les extensions Unicode résolvent des problèmes concrets dans les applications internationalisées. Comprendre les cas d'utilisation courants vous aide à appliquer efficacement ces extensions.

Stockage des préférences utilisateur

Stockez les préférences de formatage de l'utilisateur dans une seule chaîne de paramètres régionaux au lieu de plusieurs champs de configuration :

function saveUserPreferences(userId, localeString) {
  const locale = new Intl.Locale(localeString);

  return {
    userId,
    language: locale.language,
    region: locale.region,
    localeString: locale.toString(),
    preferences: {
      calendar: locale.calendar,
      numberingSystem: locale.numberingSystem,
      hourCycle: locale.hourCycle
    }
  };
}

const preferences = saveUserPreferences(123, "ar-SA-u-ca-islamic-nu-arab-hc-h12");

Cette approche stocke les préférences de formatage sous forme d'une chaîne unique tout en offrant un accès structuré aux composants individuels.

Construction de sélecteurs de paramètres régionaux

Permettez aux utilisateurs de choisir leurs préférences de formatage via une interface utilisateur en construisant des chaînes de paramètres régionaux avec extensions :

function buildLocaleFromUserInput(language, region, preferences) {
  const options = {};

  if (preferences.calendar) {
    options.calendar = preferences.calendar;
  }

  if (preferences.numberingSystem) {
    options.numberingSystem = preferences.numberingSystem;
  }

  if (preferences.hourCycle) {
    options.hourCycle = preferences.hourCycle;
  }

  const locale = new Intl.Locale(`${language}-${region}`, options);
  return locale.toString();
}

const userLocale = buildLocaleFromUserInput("th", "TH", {
  calendar: "buddhist",
  numberingSystem: "thai",
  hourCycle: "h23"
});

console.log(userLocale);
// Résultat : "th-TH-u-ca-buddhist-hc-h23-nu-thai"

Respect des calendriers religieux

Les applications destinées aux communautés religieuses doivent prendre en charge leurs systèmes de calendrier :

function createReligiousCalendarFormatter(religion, baseLocale) {
  const calendars = {
    jewish: "hebrew",
    muslim: "islamic",
    buddhist: "buddhist"
  };

  const calendar = calendars[religion];
  if (!calendar) {
    return new Intl.DateTimeFormat(baseLocale);
  }

  const locale = new Intl.Locale(baseLocale, { calendar });
  return new Intl.DateTimeFormat(locale, {
    year: "numeric",
    month: "long",
    day: "numeric"
  });
}

const jewishFormatter = createReligiousCalendarFormatter("jewish", "en-US");
console.log(jewishFormatter.format(new Date("2025-03-15")));
// Résultat : "15 Adar II 5785"

Tri avec règles personnalisées

Utilisez les extensions de collation pour implémenter un tri spécifique à la langue :

function sortNames(names, locale, collationType) {
  const localeWithCollation = new Intl.Locale(locale, {
    collation: collationType
  });

  const collator = new Intl.Collator(localeWithCollation);
  return names.sort((a, b) => collator.compare(a, b));
}

const germanNames = ["Müller", "Meyer", "Möller", "Mueller"];
const sorted = sortNames(germanNames, "de-DE", "phonebk");
console.log(sorted);

Affichage des chiffres traditionnels

Affichez les nombres dans les systèmes de numération traditionnels pour un affichage culturellement approprié :

function formatTraditionalNumber(number, locale, numberingSystem) {
  const localeWithNumbering = new Intl.Locale(locale, {
    numberingSystem
  });

  return new Intl.NumberFormat(localeWithNumbering).format(number);
}

console.log(formatTraditionalNumber(123456, "ar-EG", "arab"));
// Résultat : "١٢٣٬٤٥٦"

console.log(formatTraditionalNumber(123456, "th-TH", "thai"));
// Résultat : "๑๒๓,๔๕๖"

Compatibilité des navigateurs

Les extensions Unicode fonctionnent dans tous les navigateurs modernes. Chrome, Firefox, Safari et Edge prennent en charge la syntaxe d'extension dans les identifiants de locale et les propriétés correspondantes sur les objets Intl.Locale.

La disponibilité de valeurs d'extension spécifiques dépend de l'implémentation du navigateur. Tous les navigateurs prennent en charge les valeurs courantes comme gregory pour le calendrier, latn pour le système de numération, et h12 ou h23 pour le cycle horaire. Les valeurs moins courantes comme les calendriers chinois traditionnels ou les systèmes de numération des langues minoritaires peuvent ne pas fonctionner dans tous les navigateurs.

Testez vos identifiants de locale dans les navigateurs cibles lorsque vous utilisez des valeurs d'extension moins courantes. Utilisez les propriétés Intl.Locale pour vérifier si le navigateur a reconnu vos valeurs d'extension :

const locale = new Intl.Locale("zh-CN-u-ca-chinese");
console.log(locale.calendar);
// Si le navigateur prend en charge le calendrier chinois : "chinese"
// Si le navigateur ne le prend pas en charge : undefined

Node.js prend en charge les extensions Unicode à partir de la version 12, avec une prise en charge complète de toutes les propriétés dans la version 18 et ultérieures.

Résumé

Les extensions Unicode vous permettent d'ajouter des préférences de formatage aux identifiants de locale. Au lieu de configurer chaque formateur séparément, vous encodez les préférences une seule fois dans la chaîne de locale.

Concepts clés :

  • Les extensions commencent par -u- suivi de paires clé-valeur
  • La clé ca spécifie le système de calendrier
  • La clé nu spécifie le système de numération
  • La clé hc spécifie le format du cycle horaire
  • La clé co spécifie les règles de collation
  • La clé kf spécifie l'ordre de priorité des majuscules
  • La clé kn active la collation numérique
  • Vous pouvez utiliser des chaînes d'extension ou des objets d'options
  • Les extensions agissent comme des valeurs par défaut que les options du formateur peuvent remplacer
  • L'objet Intl.Locale expose les extensions sous forme de propriétés

Utilisez les extensions Unicode pour stocker les préférences des utilisateurs, respecter les calendriers culturels, afficher des chiffres traditionnels et implémenter le tri spécifique à la locale. Elles fournissent une méthode standard pour personnaliser le comportement de formatage dans le code d'internationalisation JavaScript.