Comment gérer le repli de locale lorsque la locale préférée n'est pas disponible

Sélectionner automatiquement les langues prises en charge lorsque les utilisateurs préfèrent des locales non prises en charge

Introduction

Toutes les applications web ne prennent pas en charge toutes les langues du monde. Lorsqu'un utilisateur préfère une langue que votre application ne prend pas en charge, vous avez besoin d'un mécanisme de repli pour afficher le contenu dans la meilleure langue alternative au lieu d'afficher des erreurs ou du texte non traduit.

Le repli de locale est le processus de sélection d'une locale alternative lorsque la locale préférée n'est pas disponible. L'API Intl de JavaScript gère cela automatiquement en acceptant plusieurs options de locale et en sélectionnant la première qu'elle prend en charge. Cela garantit que votre application affiche toujours un contenu correctement formaté, même lorsque la locale préférée exacte n'est pas disponible.

Cette leçon explique comment fonctionne le repli de locale en JavaScript, comment le mettre en œuvre efficacement et comment construire une logique de repli personnalisée pour les applications ayant des exigences spécifiques de prise en charge des locales.

Le problème des locales non prises en charge

Lorsque vous transmettez un identifiant de locale à une API Intl, l'environnement d'exécution JavaScript doit prendre en charge cette locale pour formater correctement le contenu. Si vous demandez un formatage pour le norvégien nynorsk mais que l'environnement ne prend en charge que le norvégien bokmål, le formateur doit pouvoir gérer cette situation avec élégance.

Sans mécanisme de repli, les applications échoueraient à afficher du contenu ou montreraient du texte non traduit lorsqu'elles rencontrent des locales non prises en charge. Les utilisateurs de régions avec des variantes linguistiques moins courantes connaîtraient des interfaces défectueuses.

Prenons l'exemple d'un utilisateur qui parle le français canadien. Si votre application ne prend en charge que le français européen, vous voudriez que le formateur utilise les conventions du français européen plutôt que d'échouer complètement. Bien que ce ne soit pas parfait, cela offre une meilleure expérience que l'absence totale de localisation.

Comment l'API Intl gère automatiquement le repli

Chaque constructeur Intl accepte soit une chaîne de locale unique, soit un tableau de chaînes de locale. Lorsque vous passez un tableau, l'environnement d'exécution évalue chaque locale dans l'ordre et utilise la première qu'il prend en charge.

const locales = ["fr-CA", "fr-FR", "en-US"];
const formatter = new Intl.DateTimeFormat(locales);

const date = new Date("2025-03-15");
console.log(formatter.format(date));
// Utilise fr-CA si disponible
// Se replie sur fr-FR si fr-CA n'est pas disponible
// Se replie sur en-US si aucune variante française n'est disponible

L'environnement d'exécution examine le tableau de gauche à droite. S'il prend en charge le français canadien, il l'utilise. Sinon, il essaie le français européen. Si aucune variante française n'est disponible, il se replie sur l'anglais américain.

Ce repli automatique signifie que vous n'avez pas besoin de vérifier manuellement la prise en charge ou de gérer les erreurs lors de la demande de locales spécifiques. L'API Intl garantit qu'elle sélectionnera une locale prise en charge ou se repliera sur la locale par défaut du système.

Fournir plusieurs options de locale

La façon la plus simple d'implémenter une solution de repli est de passer un tableau de locales par ordre de préférence. Cela fonctionne avec tous les constructeurs Intl, y compris DateTimeFormat, NumberFormat, Collator, et autres.

const locales = ["es-MX", "es-ES", "es", "en"];
const numberFormatter = new Intl.NumberFormat(locales, {
  style: "currency",
  currency: "USD"
});

console.log(numberFormatter.format(1234.56));
// Utilise l'espagnol mexicain si disponible
// Se replie sur l'espagnol européen
// Se replie sur l'espagnol générique
// Se replie sur l'anglais comme option finale

Ce modèle offre un chemin de dégradation élégant. Les utilisateurs obtiennent du contenu dans leur dialecte préféré si disponible, puis une variante plus large de leur langue, puis une langue de repli commune.

L'ordre est important. Le runtime sélectionne la première locale qu'il prend en charge, donc placez les locales les plus spécifiques et préférées en premier.

Comprendre le fonctionnement de la correspondance des locales

Lorsque vous fournissez plusieurs locales, le runtime JavaScript utilise un algorithme de correspondance de locale pour sélectionner la meilleure option disponible. Cet algorithme compare vos locales demandées avec l'ensemble des locales prises en charge par le runtime.

Si une locale demandée correspond exactement à une locale prise en charge, le runtime l'utilise immédiatement. Si aucune correspondance exacte n'existe, le runtime peut sélectionner une locale apparentée basée sur les codes de langue et de région.

Par exemple, si vous demandez en-AU (anglais australien) mais que le runtime ne prend en charge que en-US et en-GB, il sélectionnera l'une de ces variantes d'anglais plutôt que de se replier sur une langue complètement différente.

const locales = ["en-AU", "en"];
const formatter = new Intl.DateTimeFormat(locales);

const resolvedLocale = formatter.resolvedOptions().locale;
console.log(resolvedLocale);
// Pourrait afficher "en-US" ou "en-GB" selon le runtime
// Le runtime a sélectionné une variante d'anglais prise en charge

La méthode resolvedOptions() renvoie la locale réelle que le formateur utilise. Cela vous permet de vérifier quelle locale a été sélectionnée après le repli.

Vérification des locales prises en charge

La méthode statique supportedLocalesOf() vérifie quelles locales d'une liste sont prises en charge par un constructeur Intl spécifique. Cette méthode renvoie un tableau contenant uniquement les locales prises en charge.

const requestedLocales = ["fr-CA", "fr-FR", "de-DE", "ja-JP"];
const supportedLocales = Intl.DateTimeFormat.supportedLocalesOf(requestedLocales);

console.log(supportedLocales);
// La sortie dépend de la prise en charge de l'environnement d'exécution
// Exemple : ["fr-FR", "de-DE", "ja-JP"]
// Le français canadien n'était pas pris en charge, les autres l'étaient

Cette méthode filtre les locales demandées pour montrer celles que l'environnement d'exécution peut utiliser sans recourir aux valeurs par défaut. Les locales non prises en charge sont supprimées du tableau renvoyé.

Vous pouvez utiliser cette méthode pour vérifier la prise en charge avant de créer des formateurs, ou pour afficher les options de langue disponibles pour les utilisateurs.

const availableLocales = ["en-US", "es-MX", "fr-FR", "de-DE", "ja-JP"];
const supported = Intl.NumberFormat.supportedLocalesOf(availableLocales);

console.log("Cet environnement d'exécution prend en charge :", supported);
// Montre lesquelles de vos locales d'application fonctionnent dans cet environnement

Chaque constructeur Intl possède sa propre méthode supportedLocalesOf() car la prise en charge des locales peut varier entre différentes fonctionnalités d'internationalisation. Un environnement d'exécution peut prendre en charge le français pour le formatage des nombres mais pas pour la segmentation de texte.

Construction de chaînes de repli à partir d'identifiants de locale

Lorsque vous savez que votre application prend en charge des locales spécifiques, vous pouvez construire une chaîne de repli qui devient progressivement moins spécifique. Ce modèle commence par un identifiant de locale complet et supprime des composants jusqu'à trouver une correspondance.

function buildFallbackChain(locale) {
  const chain = [locale];

  const parts = locale.split("-");
  if (parts.length > 1) {
    chain.push(parts[0]);
  }

  chain.push("en");

  return chain;
}

const fallbacks = buildFallbackChain("zh-Hans-CN");
console.log(fallbacks);
// ["zh-Hans-CN", "zh", "en"]

const formatter = new Intl.DateTimeFormat(fallbacks);
// Essaie le chinois simplifié pour la Chine
// Se replie sur le chinois générique
// Se replie sur l'anglais

Cette fonction crée une chaîne de repli en extrayant le code de langue d'un identifiant de locale et en ajoutant un repli final vers l'anglais. Vous pouvez étendre cette logique pour inclure des règles de repli plus sophistiquées basées sur les locales prises en charge par votre application.

Pour les applications qui prennent en charge plusieurs variantes d'une langue, vous pourriez vouloir vous replier sur des dialectes apparentés avant de passer à l'anglais.

function buildSmartFallbackChain(locale) {
  const chain = [locale];

  if (locale.startsWith("es-")) {
    chain.push("es-MX", "es-ES", "es");
  } else if (locale.startsWith("fr-")) {
    chain.push("fr-FR", "fr-CA", "fr");
  } else if (locale.startsWith("zh-")) {
    chain.push("zh-Hans-CN", "zh-Hant-TW", "zh");
  }

  const parts = locale.split("-");
  if (parts.length > 1 && !chain.includes(parts[0])) {
    chain.push(parts[0]);
  }

  if (!chain.includes("en")) {
    chain.push("en");
  }

  return chain;
}

const fallbacks = buildSmartFallbackChain("es-AR");
console.log(fallbacks);
// ["es-AR", "es-MX", "es-ES", "es", "en"]
// Essaie l'espagnol argentin
// Se replie sur l'espagnol mexicain
// Se replie sur l'espagnol européen
// Se replie sur l'espagnol générique
// Se replie sur l'anglais

Cette approche garantit que les utilisateurs voient le contenu dans un dialecte apparenté à leur langue avant de se replier sur l'anglais.

Sélection d'un algorithme de correspondance de locale

L'API Intl prend en charge deux algorithmes de correspondance de locale : lookup et best fit. Vous pouvez spécifier quel algorithme utiliser via l'option localeMatcher lors de la création des formateurs.

L'algorithme lookup suit la spécification BCP 47 Lookup. Il effectue une correspondance stricte en comparant systématiquement les identifiants de locale et en sélectionnant la première correspondance exacte.

const locales = ["de-DE", "en-US"];
const formatter = new Intl.NumberFormat(locales, {
  localeMatcher: "lookup"
});

console.log(formatter.resolvedOptions().locale);
// Utilise des règles de correspondance strictes

L'algorithme best fit permet au runtime de sélectionner une locale en utilisant sa propre logique de correspondance. Cet algorithme peut prendre des décisions intelligentes sur la locale qui répond le mieux aux besoins de l'utilisateur, même si ce n'est pas une correspondance exacte.

const locales = ["de-DE", "en-US"];
const formatter = new Intl.NumberFormat(locales, {
  localeMatcher: "best fit"
});

console.log(formatter.resolvedOptions().locale);
// Utilise l'algorithme best fit du runtime
// Peut sélectionner une locale apparentée de manière plus intelligente

L'algorithme par défaut est best fit. La plupart des applications devraient utiliser la valeur par défaut car elle fournit de meilleurs résultats à travers différents runtimes JavaScript. Utilisez lookup uniquement lorsque vous avez besoin d'un comportement de correspondance prévisible et strict.

Utilisation des préférences linguistiques du navigateur pour le fallback automatique

La propriété navigator.languages renvoie un tableau des langues préférées de l'utilisateur par ordre de préférence. Vous pouvez passer ce tableau directement aux constructeurs Intl pour implémenter un fallback automatique basé sur les paramètres du navigateur.

const formatter = new Intl.DateTimeFormat(navigator.languages);

const date = new Date("2025-03-15");
console.log(formatter.format(date));
// Utilise automatiquement la langue préférée prise en charge par l'utilisateur

Cette approche permet au navigateur de gérer toute la logique de fallback. Si la première préférence de l'utilisateur n'est pas prise en charge, l'API Intl essaie automatiquement sa deuxième préférence, puis la troisième, et ainsi de suite.

Ce modèle fonctionne bien lorsque vous souhaitez respecter toutes les préférences linguistiques de l'utilisateur sans construire manuellement des chaînes de fallback.

console.log(navigator.languages);
// ["fr-CA", "fr", "en-US", "en"]

const numberFormatter = new Intl.NumberFormat(navigator.languages, {
  style: "currency",
  currency: "USD"
});

console.log(numberFormatter.format(1234.56));
// Essaie d'abord le français canadien
// Revient à la liste complète des préférences de l'utilisateur

L'API Intl évalue chaque langue dans le tableau et sélectionne la première qu'elle prend en charge, ce qui en fait une solution robuste pour gérer diverses préférences utilisateur.

Combiner la prise en charge des applications avec les préférences des utilisateurs

Pour les applications avec une prise en charge limitée des paramètres régionaux, vous pouvez filtrer les préférences des utilisateurs pour qu'elles correspondent à vos paramètres régionaux pris en charge avant de créer des formateurs. Cela garantit que vous n'essayez d'utiliser que les paramètres régionaux que votre application peut gérer.

const supportedLocales = ["en-US", "es-MX", "fr-FR", "de-DE"];

function findBestLocale(userPreferences, appSupported) {
  const preferences = userPreferences.map(pref => {
    const parts = pref.split("-");
    return [pref, parts[0]];
  }).flat();

  for (const pref of preferences) {
    if (appSupported.includes(pref)) {
      return pref;
    }
  }

  return appSupported[0];
}

const userLocale = findBestLocale(navigator.languages, supportedLocales);
const formatter = new Intl.DateTimeFormat(userLocale);

console.log(formatter.resolvedOptions().locale);
// Le paramètre régional sélectionné correspond à la fois aux préférences de l'utilisateur et à la prise en charge de l'application

Cette fonction trouve la première correspondance entre les préférences de l'utilisateur et les paramètres régionaux pris en charge, y compris en essayant les codes de langue sans codes de région pour une correspondance plus large.

Pour une correspondance plus sophistiquée, vous pouvez vérifier lesquelles des préférences de l'utilisateur sont prises en charge par l'API Intl dans cet environnement d'exécution.

const supportedLocales = ["en-US", "es-MX", "fr-FR", "de-DE"];

function findBestSupportedLocale(userPreferences, appSupported) {
  const runtimeSupported = Intl.DateTimeFormat.supportedLocalesOf(appSupported);

  for (const pref of userPreferences) {
    if (runtimeSupported.includes(pref)) {
      return pref;
    }

    const lang = pref.split("-")[0];
    const match = runtimeSupported.find(s => s.startsWith(lang));
    if (match) {
      return match;
    }
  }

  return runtimeSupported[0] || "en";
}

const userLocale = findBestSupportedLocale(navigator.languages, supportedLocales);
const formatter = new Intl.DateTimeFormat(userLocale);

Cette approche garantit que le paramètre régional sélectionné est pris en charge à la fois par votre application et par l'environnement d'exécution JavaScript.

Gestion des cas où aucun paramètre régional ne correspond

Si aucun des paramètres régionaux demandés n'est pris en charge, l'API Intl se replie sur le paramètre régional par défaut du système. Cette valeur par défaut varie selon l'environnement et est déterminée par le système d'exploitation, le navigateur ou la configuration de Node.js.

const unsupportedLocales = ["non-existent-locale"];
const formatter = new Intl.DateTimeFormat(unsupportedLocales);

console.log(formatter.resolvedOptions().locale);
// Affiche le paramètre régional par défaut du système
// Peut être "en-US" ou un autre paramètre régional selon le système

La valeur par défaut du système garantit que les formateurs fonctionnent toujours, même avec des listes de paramètres régionaux complètement invalides ou non prises en charge. Votre application ne générera pas d'erreurs dues à des problèmes de paramètres régionaux.

Pour garantir une solution de repli spécifique au lieu de vous fier à la valeur par défaut du système, incluez toujours un paramètre régional largement pris en charge comme en ou en-US à la fin de votre tableau de paramètres régionaux.

const locales = ["xyz-INVALID", "en"];
const formatter = new Intl.DateTimeFormat(locales);

console.log(formatter.resolvedOptions().locale);
// Utilisera "en" puisque le premier paramètre régional est invalide
// Repli garanti vers l'anglais au lieu de la valeur par défaut du système

Ce modèle rend le comportement de votre application plus prévisible dans différents environnements.

Modèles pratiques pour les applications en production

Lors de la création d'applications en production, combinez plusieurs stratégies de repli pour assurer une gestion robuste des paramètres régionaux sur des bases d'utilisateurs diverses.

Un modèle courant crée des formateurs avec une chaîne de repli complète qui inclut les préférences de l'utilisateur, les paramètres régionaux pris en charge par l'application et un repli final garanti.

class LocaleManager {
  constructor(supportedLocales) {
    this.supportedLocales = supportedLocales;
    this.defaultLocale = "en-US";
  }

  buildLocaleChain(userPreference) {
    const chain = [];

    if (userPreference) {
      chain.push(userPreference);

      const lang = userPreference.split("-")[0];
      if (lang !== userPreference) {
        chain.push(lang);
      }
    }

    chain.push(...navigator.languages);
    chain.push(...this.supportedLocales);
    chain.push(this.defaultLocale);

    const unique = [...new Set(chain)];
    return unique;
  }

  createDateFormatter(userPreference, options = {}) {
    const locales = this.buildLocaleChain(userPreference);
    return new Intl.DateTimeFormat(locales, options);
  }

  createNumberFormatter(userPreference, options = {}) {
    const locales = this.buildLocaleChain(userPreference);
    return new Intl.NumberFormat(locales, options);
  }
}

const manager = new LocaleManager(["en-US", "es-MX", "fr-FR", "de-DE"]);
const dateFormatter = manager.createDateFormatter("pt-BR");

console.log(dateFormatter.resolvedOptions().locale);
// Utilise une chaîne de repli complète
// Essaie le portugais pour le Brésil
// Se replie sur le portugais
// Se replie à travers navigator.languages
// Se replie à travers les paramètres régionaux pris en charge
// Garanti d'utiliser en-US si rien d'autre ne correspond

Cette classe encapsule la logique de repli des paramètres régionaux et assure un comportement cohérent dans toute votre application.

Pour les applications qui doivent répondre aux changements de paramètres régionaux de l'utilisateur en temps réel, combinez le gestionnaire de paramètres régionaux avec des écouteurs d'événements.

class LocaleAwareFormatter {
  constructor(supportedLocales) {
    this.supportedLocales = supportedLocales;
    this.updateFormatters();

    window.addEventListener("languagechange", () => {
      this.updateFormatters();
    });
  }

  updateFormatters() {
    const locales = [...navigator.languages, ...this.supportedLocales, "en"];

    this.dateFormatter = new Intl.DateTimeFormat(locales);
    this.numberFormatter = new Intl.NumberFormat(locales);
  }

  formatDate(date) {
    return this.dateFormatter.format(date);
  }

  formatNumber(number) {
    return this.numberFormatter.format(number);
  }
}

const formatter = new LocaleAwareFormatter(["en-US", "es-MX", "fr-FR"]);
console.log(formatter.formatDate(new Date()));
// Se met à jour automatiquement lorsque l'utilisateur modifie ses préférences linguistiques

Ce modèle crée des formateurs qui restent synchronisés avec les changements de langue du navigateur, garantissant que votre application affiche toujours le contenu selon les préférences actuelles.

Résumé

Le repli de locale garantit que votre application affiche du contenu correctement formaté même lorsque les utilisateurs préfèrent des locales que vous ne prenez pas explicitement en charge. L'API Intl gère automatiquement le repli en acceptant des tableaux de préférences de locale et en sélectionnant la première option prise en charge.

Concepts clés :

  • Passez des tableaux de locales aux constructeurs Intl pour un repli automatique
  • L'environnement d'exécution sélectionne la première locale qu'il prend en charge dans le tableau
  • Utilisez supportedLocalesOf() pour vérifier quelles locales sont disponibles
  • Construisez des chaînes de repli qui progressent des locales spécifiques aux locales générales
  • L'option localeMatcher contrôle l'algorithme de correspondance
  • Passez directement navigator.languages pour une gestion automatique des préférences utilisateur
  • Incluez toujours un repli final largement pris en charge comme l'anglais
  • L'environnement d'exécution se replie sur les paramètres par défaut du système lorsqu'aucune locale ne correspond

Utilisez le repli automatique avec des tableaux de locales pour la plupart des applications. Implémentez une logique de repli personnalisée lorsque vous avez besoin d'un contrôle spécifique sur les locales à essayer et dans quel ordre.