Comment normaliser les identifiants de langue au format standard

Convertissez les identifiants de langue au format canonique avec la casse et l'ordre des composants appropriés

Introduction

Les identifiants de langue peuvent être écrits de nombreuses façons différentes tout en faisant référence à la même langue et au même pays. Par exemple, un utilisateur peut saisir EN-us, en-US ou en-us, et ces trois notations désignent toutes l’anglais américain. Lorsque vous stockez, comparez ou affichez ces identifiants, ces variations peuvent entraîner des incohérences.

La normalisation transforme les identifiants de langue en une forme canonique standard. Ce processus ajuste la casse des composants, trie les mots-clés d’extension par ordre alphabétique, et produit une représentation cohérente que vous pouvez utiliser dans toute votre application.

JavaScript offre des méthodes natives pour normaliser automatiquement les identifiants de langue. Ce guide vous explique ce que signifie la normalisation, comment l’appliquer dans votre code et à quels moments les identifiants normalisés renforcent la logique d’internationalisation.

Ce que signifie la normalisation des identifiants de langue

La normalisation transforme un identifiant de langue en sa forme canonique selon la norme BCP 47 et les spécifications Unicode. La forme canonique suit des règles précises pour la casse, l’ordre et la structure.

Un identifiant de langue normalisé suit les conventions suivantes :

  • Les codes de langue sont en minuscules
  • Les codes d’écriture commencent par une majuscule
  • Les codes de région sont en majuscules
  • Les codes de variante sont en minuscules
  • Les mots-clés d’extension sont triés par ordre alphabétique
  • Les attributs d’extension sont triés par ordre alphabétique

Ces règles permettent de créer une seule représentation standard pour chaque locale. Peu importe la façon dont un utilisateur écrit un identifiant de langue, la forme normalisée reste toujours la même.

Comprendre les règles de normalisation

Chaque composant d’un identifiant de langue suit une convention de casse spécifique dans sa forme canonique.

Casse des langues

Les codes de langue utilisent toujours des lettres minuscules :

en (correct)
EN (incorrect, but normalizes to en)
eN (incorrect, but normalizes to en)

Ceci s’applique aux codes de langue de deux ou trois lettres.

Casse des scripts

Les codes de script utilisent la casse en titre : la première lettre est en majuscule et les trois suivantes en minuscule :

Hans (correct)
hans (incorrect, but normalizes to Hans)
HANS (incorrect, but normalizes to Hans)

Parmi les codes de script courants : Latn pour le latin, Cyrl pour le cyrillique, Hans pour les sinogrammes simplifiés, et Hant pour les sinogrammes traditionnels.

Casse des régions

Les codes de région utilisent toujours des lettres majuscules :

US (correct)
us (incorrect, but normalizes to US)
Us (incorrect, but normalizes to US)

Ceci s’applique aux codes de pays de deux lettres utilisés dans la plupart des identifiants de paramètres régionaux.

Ordre des extensions

Les extensions Unicode contiennent des mots-clés qui définissent les préférences de formatage. Dans la forme canonique, ces mots-clés apparaissent par ordre alphabétique de leurs clés :

en-US-u-ca-gregory-nu-latn (correct)
en-US-u-nu-latn-ca-gregory (incorrect, but normalizes to first form)

La clé de calendrier ca précède la clé du système de numérotation nu par ordre alphabétique. Ainsi, ca-gregory apparaît en premier dans la forme normalisée.

Utiliser Intl.getCanonicalLocales pour normaliser

La méthode Intl.getCanonicalLocales() normalise les identifiants de paramètres régionaux et les retourne sous forme canonique. C’est la principale méthode de normalisation en JavaScript.

const normalized = Intl.getCanonicalLocales("EN-us");
console.log(normalized);
// ["en-US"]

Cette méthode accepte un identifiant de paramètres avec n’importe quelle casse et retourne la forme canonique correctement cédé.

Normalisation des codes de langue

La méthode convertit les codes de langue en minuscules :

const result = Intl.getCanonicalLocales("FR-fr");
console.log(result);
// ["fr-FR"]

Le code de langue FR devient fr dans la sortie.

Normalisation des codes de script

La méthode convertit les codes d’écriture en capitalisant la première lettre :

const result = Intl.getCanonicalLocales("zh-HANS-cn");
console.log(result);
// ["zh-Hans-CN"]

Le code d’écriture HANS devient Hans, et le code de région cn devient CN.

Normalisation des codes régionaux

La méthode convertit les codes régionaux en majuscules :

const result = Intl.getCanonicalLocales("en-gb");
console.log(result);
// ["en-GB"]

Le code régional gb devient GB dans le résultat.

Normalisation des extensions de mots-clés

La méthode trie les mots-clés d’extension par ordre alphabétique :

const result = Intl.getCanonicalLocales("en-US-u-nu-latn-hc-h12-ca-gregory");
console.log(result);
// ["en-US-u-ca-gregory-hc-h12-nu-latn"]

Les mots-clés passent de nu-latn-hc-h12-ca-gregory à ca-gregory-hc-h12-nu-latn car ca précède hc, qui lui-même précède nu dans l’ordre alphabétique.

Normalisation de plusieurs identifiants de locale

La méthode Intl.getCanonicalLocales() accepte un tableau d’identifiants de locale et les normalise tous :

const locales = ["EN-us", "fr-FR", "ZH-hans-cn"];
const normalized = Intl.getCanonicalLocales(locales);
console.log(normalized);
// ["en-US", "fr-FR", "zh-Hans-CN"]

Chaque locale du tableau est convertie dans sa forme canonique.

Suppression des doublons

La méthode retire les identifiants de locale en double après normalisation. Si plusieurs valeurs en entrée se normalisent dans la même forme canonique, le résultat ne contient qu’un seul exemplaire :

const locales = ["en-US", "EN-us", "en-us"];
const normalized = Intl.getCanonicalLocales(locales);
console.log(normalized);
// ["en-US"]

Les trois entrées représentent la même locale, donc la sortie contient un seul identifiant normalisé.

Cette opération de suppression des doublons est utile lors du traitement des saisies utilisateur ou lors de la fusion de listes de locales provenant de plusieurs sources.

Gestion des identifiants invalides

Si un identifiant de locale dans le tableau est invalide, la méthode déclenche une RangeError :

try {
  Intl.getCanonicalLocales(["en-US", "invalid", "fr-FR"]);
} catch (error) {
  console.error(error.message);
  // "invalid is not a structurally valid language tag"
}

Lorsque vous normalisez des listes saisies par les utilisateurs, validez ou interceptez les erreurs séparément pour chaque locale afin d’identifier précisément les identifiants invalides.

Utilisation de Intl.Locale pour la normalisation

Le constructeur Intl.Locale normalise également les identifiants de langue lors de la création d'objets de locale. Vous pouvez accéder à la forme normalisée via la méthode toString().

const locale = new Intl.Locale("EN-us");
console.log(locale.toString());
// "en-US"

Le constructeur accepte toute casse valide et produit un objet de locale normalisé.

Accès aux composants normalisés

Chaque propriété de l'objet locale retourne la version normalisée de ce composant :

const locale = new Intl.Locale("ZH-hans-CN");

console.log(locale.language);
// "zh"

console.log(locale.script);
// "Hans"

console.log(locale.region);
// "CN"

console.log(locale.baseName);
// "zh-Hans-CN"

Les propriétés language, script et region utilisent toutes la casse correcte de la forme canonique.

Normalisation avec des options

Quand vous créez un objet locale avec des options, le constructeur normalise à la fois l'identifiant de base et les options :

const locale = new Intl.Locale("EN-us", {
  calendar: "gregory",
  numberingSystem: "latn",
  hourCycle: "h12"
});

console.log(locale.toString());
// "en-US-u-ca-gregory-hc-h12-nu-latn"

Les mots-clés d'extension apparaissent par ordre alphabétique dans la sortie, même si l'objet options ne spécifie pas d'ordre particulier.

Pourquoi la normalisation est importante

La normalisation assure la cohérence dans votre application. Lorsque vous stockez, affichez ou comparez des identifiants de langue, utiliser la forme canonique évite de subtils bugs et améliore la fiabilité.

Stockage cohérent

Lors du stockage des identifiants de langue dans des bases de données, fichiers de configuration ou stockage local, les formes normalisées évitent les doublons :

const userPreferences = new Set();

function saveUserLocale(identifier) {
  const normalized = Intl.getCanonicalLocales(identifier)[0];
  userPreferences.add(normalized);
}

saveUserLocale("en-US");
saveUserLocale("EN-us");
saveUserLocale("en-us");

console.log(userPreferences);
// Set { "en-US" }

Sans normalisation, l'ensemble contiendrait trois entrées pour la même locale. Avec la normalisation, il n'en contient qu'une seule correctement.

Comparaison fiable

La comparaison d'identifiants de langue nécessite une normalisation. Deux identifiants qui ne diffèrent que par la casse représentent la même locale :

function isSameLocale(locale1, locale2) {
  const normalized1 = Intl.getCanonicalLocales(locale1)[0];
  const normalized2 = Intl.getCanonicalLocales(locale2)[0];
  return normalized1 === normalized2;
}

console.log(isSameLocale("en-US", "EN-us"));
// true

console.log(isSameLocale("en-US", "en-GB"));
// false

La comparaison directe de chaînes non normalisées produit des résultats incorrects.

Affichage cohérent

Lorsque vous affichez des identifiants de langue aux utilisateurs ou lors du débogage, les formes normalisées assurent un formatage homogène :

function displayLocale(identifier) {
  try {
    const normalized = Intl.getCanonicalLocales(identifier)[0];
    return `Current locale: ${normalized}`;
  } catch (error) {
    return "Invalid locale identifier";
  }
}

console.log(displayLocale("EN-us"));
// "Current locale: en-US"

console.log(displayLocale("zh-HANS-cn"));
// "Current locale: zh-Hans-CN"

Les utilisateurs voient des identifiants de langue correctement formatés, quel que soit le format d'entrée.

Applications pratiques

La normalisation permet de résoudre des problèmes fréquents liés à l'utilisation des identifiants de locale dans des applications réelles.

Normaliser les saisies utilisateur

Lorsque les utilisateurs saisissent des identifiants de locale dans des formulaires ou paramètres, normalisez les saisies avant de les enregistrer :

function processLocaleInput(input) {
  try {
    const normalized = Intl.getCanonicalLocales(input)[0];
    return {
      success: true,
      locale: normalized
    };
  } catch (error) {
    return {
      success: false,
      error: "Please enter a valid locale identifier"
    };
  }
}

const result = processLocaleInput("fr-ca");
console.log(result);
// { success: true, locale: "fr-CA" }

Cela garantit un format cohérent dans votre base de données ou votre configuration.

Créer des tables de correspondance de locales

Pour créer des tables de recherche pour les traductions ou les données spécifiques à une locale, utilisez des clés normalisées :

const translations = new Map();

function addTranslation(locale, key, value) {
  const normalized = Intl.getCanonicalLocales(locale)[0];

  if (!translations.has(normalized)) {
    translations.set(normalized, {});
  }

  translations.get(normalized)[key] = value;
}

addTranslation("en-us", "hello", "Hello");
addTranslation("EN-US", "goodbye", "Goodbye");

console.log(translations.get("en-US"));
// { hello: "Hello", goodbye: "Goodbye" }

Les deux appels à addTranslation utilisent la même clé normalisée, donc les traductions sont stockées dans le même objet.

Fusionner des listes de locales

Lorsque vous combinez des identifiants de locale provenant de plusieurs sources, normalisez-les et supprimez les doublons :

function mergeLocales(...sources) {
  const allLocales = sources.flat();
  const normalized = Intl.getCanonicalLocales(allLocales);
  return normalized;
}

const userLocales = ["en-us", "fr-FR"];
const appLocales = ["EN-US", "de-de"];
const systemLocales = ["en-US", "es-mx"];

const merged = mergeLocales(userLocales, appLocales, systemLocales);
console.log(merged);
// ["en-US", "fr-FR", "de-DE", "es-MX"]

La méthode supprime les doublons et harmonise la casse sur l'ensemble des sources.

Créer des interfaces de sélection de locales

Lorsque vous développez des menus déroulants ou des interfaces de sélection, normalisez les identifiants de locale à afficher :

function buildLocaleOptions(locales) {
  const normalized = Intl.getCanonicalLocales(locales);

  return normalized.map(locale => {
    const localeObj = new Intl.Locale(locale);
    const displayNames = new Intl.DisplayNames([locale], {
      type: "language"
    });

    return {
      value: locale,
      label: displayNames.of(localeObj.language)
    };
  });
}

const options = buildLocaleOptions(["EN-us", "fr-FR", "DE-de"]);
console.log(options);
// [
//   { value: "en-US", label: "English" },
//   { value: "fr-FR", label: "French" },
//   { value: "de-DE", label: "German" }
// ]

Les valeurs normalisées offrent des identifiants cohérents pour les envois de formulaire.

Valider les fichiers de configuration

Lors du chargement des identifiants de locale depuis des fichiers de configuration, normalisez-les à l'initialisation :

function loadLocaleConfig(config) {
  const validatedConfig = {
    defaultLocale: null,
    supportedLocales: []
  };

  try {
    validatedConfig.defaultLocale = Intl.getCanonicalLocales(
      config.defaultLocale
    )[0];
  } catch (error) {
    console.error("Invalid default locale:", config.defaultLocale);
    validatedConfig.defaultLocale = "en-US";
  }

  config.supportedLocales.forEach(locale => {
    try {
      const normalized = Intl.getCanonicalLocales(locale)[0];
      validatedConfig.supportedLocales.push(normalized);
    } catch (error) {
      console.warn("Skipping invalid locale:", locale);
    }
  });

  return validatedConfig;
}

const config = {
  defaultLocale: "en-us",
  supportedLocales: ["EN-us", "fr-FR", "invalid", "de-DE"]
};

const validated = loadLocaleConfig(config);
console.log(validated);
// {
//   defaultLocale: "en-US",
//   supportedLocales: ["en-US", "fr-FR", "de-DE"]
// }

Cela permet de détecter rapidement les erreurs de configuration et garantit que votre application utilise des identifiants normalisés valides.

Normalisation et correspondance des locales

La normalisation est essentielle pour les algorithmes de correspondance de locale. Pour trouver la meilleure correspondance avec la préférence d'un utilisateur, comparez les formes normalisées :

function findBestMatch(userPreference, availableLocales) {
  const normalizedPreference = Intl.getCanonicalLocales(userPreference)[0];
  const normalizedAvailable = Intl.getCanonicalLocales(availableLocales);

  if (normalizedAvailable.includes(normalizedPreference)) {
    return normalizedPreference;
  }

  const preferenceLocale = new Intl.Locale(normalizedPreference);

  const languageMatch = normalizedAvailable.find(available => {
    const availableLocale = new Intl.Locale(available);
    return availableLocale.language === preferenceLocale.language;
  });

  if (languageMatch) {
    return languageMatch;
  }

  return normalizedAvailable[0];
}

const available = ["en-us", "fr-FR", "DE-de"];
console.log(findBestMatch("EN-GB", available));
// "en-US"

La normalisation garantit que la logique de correspondance fonctionne correctement, quel que soit le format de casse de l'entrée.

La normalisation ne change pas le sens

La normalisation affecte uniquement la représentation d’un identifiant de paramètre régional. Elle ne modifie pas la langue, l’écriture ou la région représentée par cet identifiant.

const locale1 = new Intl.Locale("en-us");
const locale2 = new Intl.Locale("EN-US");

console.log(locale1.language === locale2.language);
// true

console.log(locale1.region === locale2.region);
// true

console.log(locale1.toString() === locale2.toString());
// true

Les deux identifiants se réfèrent à l’anglais américain. La normalisation garantit simplement qu’ils sont rédigés de la même façon.

Cela est différent d'opérations comme maximize() et minimize(), qui ajoutent ou retirent des composants et peuvent modifier la spécificité de l’identifiant.

Compatibilité avec les navigateurs

La méthode Intl.getCanonicalLocales() fonctionne dans tous les navigateurs modernes. Chrome, Firefox, Safari et Edge offrent une prise en charge complète.

Node.js prend en charge Intl.getCanonicalLocales() à partir de la version 9, avec une prise en charge complète dès la version 10 et suivantes.

Le constructeur Intl.Locale et son comportement de normalisation fonctionnent dans tous les navigateurs qui prennent en charge l’API Intl.Locale. Cela inclut les versions récentes de Chrome, Firefox, Safari et Edge.

Résumé

La normalisation convertit les identifiants régionaux en leur forme canonique en appliquant les règles standards de casse et en triant les extensions par ordre alphabétique. Cela crée des représentations cohérentes à stocker, comparer et afficher de façon fiable.

Concepts clés :

  • La forme canonique utilise les minuscules pour les langues, la casse « Title » pour les écritures et les majuscules pour les régions
  • Les extensions sont triées par ordre alphabétique dans la forme canonique
  • La méthode Intl.getCanonicalLocales() normalise les identifiants et supprime les doublons
  • Le constructeur Intl.Locale génère également une sortie normalisée
  • La normalisation ne change pas le sens d’un identifiant régional
  • Utilisez des identifiants normalisés pour le stockage, la comparaison et l'affichage

La normalisation est une opération fondamentale pour toute application qui gère des identifiants régionaux. Elle évite les bugs liés à une casse incohérente et garantit que votre logique d’internationalisation manipule ces identifiants de façon fiable.