Le guide ultime de la localisation JavaScript

Démarrez la localisation JavaScript de votre navigateur avec ce guide complet et préparez votre application pour les utilisateurs internationaux.

Étrange, mûrissant, sans doute expressif et incroyable, JavaScript est le langage de programmation le plus utilisé aujourd’hui. Étant la langue du navigateur — et effectuant un travail sur le serveur avec Node — JavaScript est partout dans les architectures web d’aujourd’hui.
Et avec de nombreux frameworks mobiles multiplateformes, wrappers de bureau, moteurs de jeu, et même des frameworks pour l’internet des objets (IoT), c’est vraiment le monde de JavaScript — nous ne faisons qu’y vivre.
Maintenant, bien sûr, vous êtes ici parce que vous voulez canaliser toute cette énergie 🔥 et apprendre à localiser des applications JavaScript, les rendant accessibles à un public mondial. N’ayez crainte : Ce guide couvrira tout ce que vous devez savoir pour commencer la localisation JavaScript côté navigateur.
C’est parti pour le rock && roll.

🔗 Ressource » Obtenez tout le code accompagnant cet article à partir de notre dépôt GitHub.

🗒 Note » Internet Explorer (IE), avec une part de marché mondiale de 2,15 %, peut être considéré comme un navigateur ancien. Pour des raisons de brièveté, nous omettons les solutions spécifiques à IE dans ce guide. Si vous prenez en charge IE, assurez-vous de vérifier si les fonctionnalités JavaScript intégrées que nous couvrons dans cet article nécessitent des versions dérivées (« forks ») ou des polyfills.

Overview

Comment puis-je localiser une page web avec JavaScript ?

Bien qu’il puisse être tentant de prendre une bibliothèque d’internationalisation (i18n) prête à l’emploi pour vos besoins de localisation — et cela pourrait en fait être le bon choix pour votre projet — vous constaterez que JavaScript pur peut très bien faire l’affaire pour des projets plus petits. Développer la vôtre vous donnera également un joli livre de recettes de techniques i18n que vous pouvez utiliser avec n’importe quelle bibliothèque que vous choisissez.

🤿 Allez plus loin » Notre article, Qu’est-ce que l’I18n : Une définition simple de l’internationalisation, entre dans plus de détails concernant ce que sont l’internationalisation (i18n) et la localisation (l10n).

✋🏽 Avis » Si vous construisez une application MPA (application multi-pages) traditionnelle, il arrive souvent qu’une grande partie de la localisation se fait sur le serveur lui-même. Ici, nous nous concentrons exclusivement sur la localisation côté navigateur. Nous avons également ce qu’il vous faut côté serveur, avec un tutoriel Node i18n et un guide i18n JavaScript full-stack.

D’accord, disons que vous avez une page que vous souhaitez localiser.

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <!-- ... -->
  <title>Mon Appy Apperson</title>
</head>
<body>
  <div class="container">
    <h1>Mon Appy Apperson</h1>
    <p>Bienvenue dans mon petit coin sur le web&nbsp;!</p>
  </div>
  <script src="js/scripts.js"></script>
</body>
</html>

Version anglaise d'une démo d'application JavaScript | Phrase

🔗 Ressource » Vous pouvez obtenir tout le code de l’application que nous construisons dans cette section à partir du dossier vanilla dans notre dépôt GitHub.
🔗 Ressource » J’utilise la bibliothèque Skeleton CSS au cas où vous vous demandiez.

Cela semble correct, mais ce n’est pas exactement prêt pour le monde entier, n’est-ce pas ? Tout le contenu est codé en dur en anglais. Faisons un peu d’i18n ici.

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <!-- ... -->
  <title>Mon Appy Apperson</title>
</head>
<body>
  <div class="container">
    <h1 data-i18n-key="app-title">Mon Appy Apperson</h1>
    <p data-i18n-key="lead">Bienvenue dans mon petit coin sur le web !</p>
  </div>
  <script src="js/scripts.js"></script>
</body>
</html>

Notez les attributs data-i18n-key que nous avons ajoutés à nos conteneurs de texte ci-dessus. Nous pouvons les utiliser lorsque le document se charge et remplacer leur texte par des traductions. En fait, faisons juste cela.

// Le paramètre linguistique actif
const locale = "en";
// Nous pouvons avoir autant de paramètres régionaux ici que nous le souhaitons,
// et utiliser tous les paramètres régionaux que nous voulons. L'anglais est disponible.
// et l'arabe comme paramètres régionaux ici à titre d'exemple.
const translations = {
  // Traductions anglaises
  "en": {
    "app-title": "Mon Appy Apperson",
    "lead": "Bienvenue dans mon petit coin sur le web !"
  },
  // Traductions arabes
  "ar": {
    "app-title": "Titre de l'application",
    "lead": "أهلاً بك في مكاني الصغير على النت.",
  },
};
// Lorsque le contenu de la page est prêt...
document.addEventListener("DOMContentLoaded", () => {
  document
    // Trouver tous les éléments qui ont l'attribut clé
    .querySelectorAll("[data-i18n-key]")
    .forEach(translateElement);
});
// Remplacer le texte interne de l'élément HTML donné
// avec la traduction dans les paramètres régionaux actifs,
// correspondant à la clé i18n de données de l'élément
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const translation = translations[locale][key];
  element.innerText = translation;
}

Avec cela en place, modifions la deuxième ligne ci-dessus en const locale = "ar"; et rechargeons la page. Lorsque l’événement DOMContentLoaded est déclenché, notre page adopte nos traductions en arabe.
Version arabe d'une application de démonstration en JavaScript | Phrase

🗒 Note » « en » et « ar » ci-dessus sont les codes ISO 639-1 pour l’anglais et l’arabe, respectivement. Il est standard d’utiliser des codes ISO pour langues et pays lors de la localisation :

Chargement asynchrone des traductions

Nous avons bien commencé avec notre solution i18n. Cependant, l’ajout de paramètres régionaux et de traductions n’est pas évolutif pour le moment. À mesure que notre application grandit, nous souhaiterions probablement diviser nos traductions en fichiers distincts pour chaque paramètre régional. Le fichier de traduction correspondant au paramètre régional actif pourrait alors être chargé sans le coût de chargement des autres paramètres régionaux. Nous pouvons mettre cela en œuvre sans trop d’effort.
Tout d’abord, déplaçons nos traductions de notre script principal vers des fichiers JSON, un pour chaque paramètre régional que nous prenons en charge.

{
  "app-title": "Mon Appy Apperson",
  "lead": "Bienvenue dans mon petit coin sur le web !"
}
{
  "app-title": "تطبيقي المطبق",
  "lead": "أهلاً بك في مكاني الصغير على النت."
}

Retravaillons notre script pour charger les fichiers JSON de manière asynchrone lorsque cela est nécessaire.

// Le paramètre régional affiché en premier par notre application
const defaultLocale = "en";
// Le paramètre linguistique actif
let locale;
// Rempli avec les traductions des paramètres régionaux actifs
let translations = {};
// Lorsque le contenu de la page est prêt...
document.addEventListener("DOMContentLoaded", () => {
  // Traduire la page vers le paramètre régional par défaut
  setLocale(defaultLocale);
});
// Charger les traductions pour le paramètre régional donné et traduire
// la page dans le paramètre régional
async function setLocale(newLocale) {
  if (newLocale === locale) return;
  const newTranslations =
    await fetchTranslationsFor(newLocale);
  locale = newLocale;
  translations = newTranslations;
  translatePage();
}
// Récupérer l'objet JSON des traductions pour le paramètre régional donné
// le paramètre régional sur le réseau
async function fetchTranslationsFor(newLocale) {
  const response = await fetch(`/lang/${newLocale}.json`);
  return await response.json();
}
// Remplacer le texte intérieur de chaque élément qui a un
// attribut data-i18n-key avec la traduction correspondante
// à sa clé data-i18n-key
function translatePage() {
  document
    .querySelectorAll("[data-i18n-key]")
    .forEach(translateElement);
}
// Remplacer le texte interne de l'élément HTML donné
// avec la traduction dans le paramètre régional actif,
// correspondant à la clé data-i18n-key de l'élément
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const translation = translations[key];
  element.innerText = translation;
}

Si nous rechargeons notre page maintenant, elle a exactement le même aspect qu’avant. Cependant, en coulisses, nous avons rendu notre application beaucoup plus évolutive et facile à maintenir.

🗒 Note » Nous utilisons le Fetch API pratique, intégré dans les navigateurs modernes, pour récupérer nos fichiers JSON via le réseau.

Créer un sélecteur de paramètre régional

Nos utilisateurs n’ont pas encore la possibilité d’utiliser nos incroyables capacités asynchrones. Devons-nous construire un menu déroulant pour changer de langue pour eux ?
Nous allons ajouter une barre de navigation et y intégrer notre sélecteur.

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
  <title>Mon Appy Apperson</title>
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <ul class="navbar-list navbar-left">
          <!-- Liens de navigation -->
        </ul>
        <div class="navbar-right">
          <!-- ... -->
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">Anglais</option>
            <option value="ar">Arabe (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">My Appy Apperson</h1>
    <p data-i18n-key="lead">Bienvenue dans mon petit coin sur le web&nbsp;!</p>
  </div>
  <script src="js/scripts.js"></script>
</body>
</html>

Un simple <select> peut nous aider ici. Nous pouvons utiliser un attribut data-i18n-switcher pour que notre JavaScript puisse s’y connecter et charger le paramètre régional sélectionné par l’utilisateur.

const defaultLocale = "en";
let locale;
// ...
// Lorsque le contenu de la page est prêt...
document.addEventListener("DOMContentLoaded", () => {
  setLocale(defaultLocale);
  bindLocaleSwitcher(defaultLocale);
});
// ...
// Chaque fois que l'utilisateur sélectionne un nouveau paramètre régional, nous
// chargeons les traductions du paramètre régional et mettons à jour
// la page
function bindLocaleSwitcher(initialValue) {
  const switcher =
    document.querySelector("[data-i18n-switcher]");
  switcher.value = initialValue;
  switcher.onchange = (e) => {
    // Définir le paramètre régional sur l'option sélectionnée[value]
    setLocale(e.target.value);
  };
}

L’événement onchange permet de mettre à jour les traductions de notre page selon la valeur de l’<option> sélectionnée. Et voilà. Notre visiteur du site peut maintenant sélectionner son propre paramètre régional.
Application de démonstration avec gestionnaire d'événement onchange | Phrase

📣 Shout out » à Hary Murdiono JS du Noun Project pour son icône de traduction.

Détection des paramètres régionaux préférés de l’utilisateur à partir du navigateur

Parfois, il est judicieux de deviner les paramètres régionaux préférés de l’utilisateur avant de lui donner la possibilité de sélectionner manuellement les siens. La plupart des gens ont l’interface utilisateur de leur navigateur réglée dans la langue de leur choix, souvent la langue du système d’exploitation.
Cette langue d’interface utilisateur du navigateur peut être trouvée dans l’objet navigator, plus précisément dans la chaîne standard navigator.language.
Le tableau navigator.languages, également standard — bien que expérimental au moment où j’écris ceci — devrait contenir la langue de l’interface utilisateur comme première entrée, en plus de toutes les langues que l’utilisateur a explicitement définies dans les paramètres de langues préférées de son navigateur.
Une petite fonction qui interroge navigator.languages peut nous aider à démarrer avec la détection des paramètres régionaux du navigateur.

/**
 * Récupérer les paramètres régionaux préférés de l'utilisateur à partir du navigateur
 *
 * @param {boolean} languageCodeOnly - lorsque la valeur est vraie, retourne
 * ["en", "fr"] au lieu de ["en-US", "fr-FR"]
 * @returns tableau | undefined
 */
function browserLocales(languageCodeOnly = false) {
  return navigator.languages.map((locale) =>
    languageCodeOnly ? locale.split("-")[0] : locale,
  );
}

Maintenant, disons que l’utilisateur a le français (Canada) et le chinois (simplifié) dans ses paramètres de navigateur.
Paramètres régionaux du navigateur | Phrase
Dans ce cas, browserLocales() renverra ["fr-CA", "zh-CN"]. Si nous appelons browserLocales(true), nous obtiendrons ["fr", "zh"].
Nous pouvons maintenant utiliser cette nouvelle fonction pour détecter les paramètres régionaux préférés de l’utilisateur lorsque nous chargeons d’abord notre page.

// Le paramètre régional que notre application affiche en premier
const defaultLocale = "en";
// Paramètres régionaux pris en charge
const supportedLocales = ["en", "ar"];
// ...
document.addEventListener("DOMContentLoaded", () => {
  const initialLocale =
    supportedOrDefault(browserLocales(true));
  setLocale(initialLocale);
  bindLocaleSwitcher(initialLocale);
});
// ...
function isSupported(locale) {
  return supportedLocales.indexOf(locale) > -1;
}
// Récupérer le premier paramètre régional que nous prenons en charge à partir du tableau donné
// ou retourner notre paramètre régional par défaut
function supportedOrDefault(locales) {
  return locales.find(isSupported) || defaultLocale;
}
// ...
function browserLocales(languageCodeOnly = false) {
  return navigator.languages.map((locale) =>
    languageCodeOnly ? locale.split("-")[0] : locale,
  );
}

Remarquez que nous avons introduit le concept de supportedLocales ; ce sont les seuls paramètres régionaux pour lesquels nous avons des traductions. Avec eux, nous pouvons revenir à nos paramètres régionaux par défaut si aucun des paramètres régionaux préférés de l’utilisateur n’est dans notre liste prise en charge.
Notre application sera maintenant traduite dans le premier paramètre régional de la liste préférée de l’utilisateur, avec une solution de repli élégante.

🤿 Allez plus loin » Nous couvrons la détection des paramètres régionaux à la fois sur le navigateur et le serveur en profondeur dans Détection de la préférence de langue du navigateur avec JavaScript.

Gestion de la direction : de droite à gauche et langues de droite à gauche

L’arabe, l’hébreu, le persan, l’ourdou et d’autres langues utilisent des scripts qui s’écrivent de droite à gauche. Bien que les langues s’écrivant de gauche à droite (LTR) soient beaucoup plus nombreuses que celles s’écrivant de droite à gauche (RTL), il est bon de savoir comment prendre en charge ces dernières. Heureusement, beaucoup de travail est fait par le navigateur ici ; nous devons juste définir l’attribut <html dir> dans nos pages.

// ...
// Charger les traductions pour les paramètres régionaux donnés et traduire
// la page pour ce paramètre régional
async function setLocale(newLocale) {
  if (newLocale === locale) return;
  const newTranslations = await fetchTranslationsFor(
    newLocale,
  );
  locale = newLocale;
  translations = newTranslations;
  // Définir l'attribut <html dir>
  document.documentElement.dir = dir(newLocale);
  // Pas nécessaire pour le flux de direction, mais par précaution...
  document.documentElement.lang = newLocale;
  translatePage();
}
// ...
function dir(locale) {
  return locale === "ar" ? "rtl" : "ltr";
}
// ...

L’attribut <html dir> peut prendre les valeurs "ltr" ou "rtl". Nous fournissons cette valeur via une très simple fonction dir() et définissons l’attribut chaque fois que nous changeons de locale.

Attributs HTML des outils de développement du navigateur | Phrase
Si nous ouvrons nos outils de développement du navigateur, nous pouvons voir les <html> attributs se mettre à jour lorsque nous changeons de langue

Le navigateur affichera automatiquement le document de droite à gauche lorsque nous définissons <html dir="rtl">. Cependant, l’un de nos styles directionnels personnalisés, par exemple margin-left: 20px, nécessitera un CSS inversé spécifique RTL. Ce n’est généralement pas trop compliqué; c’est juste un peu en dehors du champ d’application de cet article.

🤿 Allez plus loin » Lisez-en plus sur le CSS localisé dans Comment utiliser un fichier CSS pour la localisation du site ?

Avec notre nouveau code en place, nous obtenons une page arabe qu’Avicenne approuverait !
Application de démonstration avec texte arabe aligné à droite | Phrase

Messages de traduction de base

Avant de passer à des messages plus complexes, comme ceux avec des valeurs interpolées et des pluriels, revenons brièvement sur la façon dont nous avons implémenté nos messages de traduction.

// Dans notre page HTML
<h1 data-i18n-key="app-title">Mon Appy Apperson</h1>
// Dans notre JavaScript
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const translation = translations[key];
  element.innerText = translation;
}
// Étant donné que nous avons chargé les traductions arabes depuis ar.json :
translations = {
  "titre de l'application": "تطبيقي المطبق",
};
translateElement(document.querySelector("[data-i18n-key='lead']"));
// rend à :
<h1 data-i18n-key="app-title">تطبيقي المطبق</h1>

C’est notre système de traduction en résumé.

Interpolation

Que se passe-t-il lorsque nous avons des valeurs qui changent à l’exécution et qui doivent être injectées dans nos messages ? Un exemple courant est le nom de l’utilisateur actuellement connecté. Nous devrons mettre à jour notre système de traduction pour gérer des cas comme ceux-ci.

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
    <h1 data-i18n-key="app-title">Mon Appy Apperson</h1>
    <p
      data-i18n-key="lead"
      data-i18n-opt='{"username": "Swoodesh"}'
    >
      Bienvenue dans mon petit coin sur le web, {username} !
    </p>
  </div>
  <script src="js/scripts.js"></script>
</body>
</html>

Nous indiquons un espace réservé pour les valeurs que nous voulons interpoler dans nos messages avec la syntaxe {variable}. Un nouvel attribut data-i18n-opt stocke des paires clé/valeur d’interpolation dans un objet JSON valide.
Bien sûr, nous aurons besoin de l’espace réservé dans nos fichiers de langue.

{
  Responsable "Bienvenue dans mon petit coin sur le web, {username} !"
}
{
  "lead": "Bienvenue dans mon petit coin sur Internet, {username}.",
}

Nous pouvons maintenant modifier notre fonction translateElement pour gérer les interpolations.

// ...
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const translation = translations[key];
  const options = JSON.parse(
    element.getAttribute("data-i18n-opt")
  );
  element.innerText = options ? interpolate(translation, options) : translation;
    ? interpolate(translation, options)
    : translation;
}
// Convertir un message comme "Bonjour, {name}" en "Bonjour, Chad"
// étant donné l'objet d'interpolations {name: "Chad"}
function interpolate(message, interpolations) {
  return Object.keys(interpolations).reduce(
    (interpolated, key) =>
      interpolated.replace(
        new RegExp(`{\s*${key}\s*}`, "g"),
        interpolations[key],
      ),
    message,
  );
}
// ...

Si nous détectons un attribut data-i18n-opt sur l’élément donné à translateElement(), nous exécutons son message traduit à travers une nouvelle fonction interpolate() avant de mettre à jour l’élément. Maintenant, lorsque nous chargeons notre page, nous voyons le message avec la valeur interpolée.
Application de démonstration avec interpolation dans les paramètres régionaux anglais | Phrase
Application de démonstration avec interpolation dans les paramètres régionaux arabes | Phrase
Bien sûr, avoir des valeurs statiques dans le HTML est d’une utilité limitée pour nous. Idéalement, nous voulons pouvoir interpoler dynamiquement avec JavaScript. Ce n’est pas trop difficile à coder.

Traduire dynamiquement après le chargement de la page

Essayons d’extraire une fonction de traduction générale à partir de translateElement().

// ...
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const options =
    JSON.parse(element.getAttribute("data-i18n-opt")) || {};
  element.innerText = translate(key, options);
}
function translate(key, interpolations = {}) {
  return interpolate(translations[clé], interpolations);
}
// ...

Nous avons simplement extrait le code qui gère la récupération d’un message dans la locale active, avec des interpolations, dans une nouvelle fonction translate(). Nous pouvons maintenant utiliser cette fonction pour mettre à jour la traduction d’un élément après le chargement de la page. Disons que nous voulons mettre à jour notre texte principal après que l’utilisateur se soit connecté. Pas de problème.

const element =
  document.querySelector("[data-i18n-key='lead']");
// Notre nouvelle fonction nous sert bien ici
element.innerText =
  translate("lead", { username: "Maggie" });
// Stocker les interpolations mises à jour dans le document
// au cas où l'élément serait redessiné à l'avenir
element.setAttribute(
  "data-i18n-opt",
  JSON.stringify({ username: "Maggie" }),
);

Application de démonstration avec traduction dynamique en Anglais | Phrase
Maintenant, nous pouvons mettre à jour les traductions des éléments à tout moment depuis notre JavaScript.

Pluriels

Non seulement nous devons souvent présenter différents messages en fonction d’un compteur — comme « 1 abonné » ou « 20 000 abonnéss » — différentes langues ont des règles de pluriel différentes. Alors que l’anglais a deux formes plurielles : one et other, l’arabe a, par exemple, six formes plurielles. Historiquement, cela signifiait que la mise en œuvre du support pluriel pour les applications front-end n’était pas très facile. Heureusement, le maintenant-standard Intl.PluralRules object permet de traiter rapidement les pluriels.
Disons que nous sommes des rédacteurs prolifiques, et nous voulons faire savoir au monde combien d’articles nous avons effectivement rédigés.

<p
  data-i18n-key="article-plural"
  data-i18n-opt='{"count": 122}'
>
  {count} articles écrits et ce n'est pas fini.
</p>

Notez que nous utilisons une convention consistant à terminer notre clé de message pluriel par -plural. Et, bien sûr, nous avons besoin d’un entier count requis pour sélectionner la bonne forme plurielle. En parlant de formes plurielles, ajoutons-les.

{
  // L’anglais a deux formes plurielles
  "article-plural": {
    "one": "{count} article et ça continue de s'ajouter",
    "other": "{count} articles et ce n'est pas fini"
  }
}
{
  // L'arabe a six formes plurielles
  "article-plural": {
    "zero": "Aucun article",
    "one": "Article {count}",
    "two": "Deux articles",
    "few": "{count} articles",
    "many": "{count} articles",
    "other": "{count} article"
  }
}

Maintenant, mettons à jour la fonction translate pour gérer les messages au pluriel.

// ...
function translate(key, interpolations = {}) {
  const message = translations[key];
  if (key.endsWith("-plural")) {
    return interpolate(
      pluralFormFor(message, interpolations.count),
      interpolations,
    );
  }
  return interpolate(message, interpolations);
}
// ...
/*
  Étant donné un objet de formulaires comme
  {
    "zero": « Aucun article »,
    "one": « Un article »,
    "other": "{count} articles"
  } et un nombre de 3, renvoie "3 articles"
*/
function pluralFormFor(forms, count) {
  const matchingForm = new Intl.PluralRules(locale).select(count);
  retournez forms[matchingForm];
}
// ...

La sauce magique ici, c’est la partie qui lit new Intl.PluralRules(locale).select(...). L’objet intégré Intl.PluralRules, lorsqu’on lui fournit un locale, connaît les règles de pluriel de ce locale. Par exemple, en passant "ar" au constructeur, puis en appelant select(5) sur l’objet retourné, renvoie "few"—la forme correcte ici.
Donc, avec très peu de lignes de code, nous avons entièrement globalisé le support pluriel 🙌

Pluriel en arabe et en anglais | PhraseNotre message au pluriel affiché en anglais et en arabe, pour différents nombres

Formatage des nombres

Trois cent mille euros s’écrit « €300,000.00 » en anglais (États-Unis), « 300.000,00 € » en allemand (Allemagne), « €3,00,000.00 » en hindi (indien)—remarquez les virgules dans ce dernier—et « ٣٠٠٬٠٠٠٫٠٠ € » en arabe (Égypte). Comment gérons-nous tous ces formats ? Pas de soucis ; un autre objet Intl qui fait partie de la norme JavaScript moderne est exactement ce qu’il fallait. Intl.NumberFormat à la rescousse !
En tant qu’entrepreneurs en herbe, disons que nous voulons créer un site Web de suivi NFT, avec des statistiques numériques, bien sûr.

<p
  data-i18n-key="nyan-cat-price"
  data-i18n-opt='{"price": {"number" : 5300}}'
>
  Nyan Cat (Officiel) NFT : {price}
</p>

Notre data-i18n-opt identifie la valeur numérique du {price} dans nos fichiers de paramètres régionaux. Nous rechercherons cette clé number lorsque nous mettrons à jour notre code d’interpolation dans un instant. Tout d’abord, fournissez vos traductions de messages.

{
  "nyan-cat-price": "Nyan Cat (Officiel) NFT : {price}"
}
{
  "nyan-cat-price": "نيان كات NFT: {price}"
}

OK, mettons à jour notre JavaScript pour que cela fonctionne.

// ...
const fullyQualifiedLocaleDefaults = {
  en: "en-US",
  ar: "ar-EG",
};
// ...
function interpolate(message, interpolations) {
  return Object.keys(interpolations).reduce(
    (interpolated, key) => {
      const value = formatNumber(interpolations[key]);
      return interpolated.replace(
        new RegExp(`{\s*${key}\s*}`, "g"),
        value,
      );
    },
    message,
  );
}
/*
  Étant donné un objet valeur tel que
  {
    "number" : 300 000,
    "style": "currency",
    "currency": EUR
  } et que le locale actif est "en", retourne "€300,000.00"
*/
function formatNumber(value) {
  if (typeof value === "object" && value.number) {
    const { number, ...options } = value;
    return new Intl.NumberFormat(
      fullyQualifiedLocaleDefaults[locale],
      options,
    ).format(number);
  } else {
    return value;
  }
}
// ...

Lorsque nous interpolons nos messages traduits, nous passons d’abord la valeur à échanger à travers un formatteur de nombre, qui utilise l’objet intégré Intl.NumberFormat. Donc, encore une fois, avec très peu de lignes de code, nous avons localisé le formatage des nombres.
Application de démonstration avec format de nombre américain | Phrase
Application de démonstration avec formatage des nombres en arabe | Phrase

✋🏽 Attention » Il est préférable de passer un locale entièrement qualifié, comme "en-US", au constructeur Intl.NumberFormat(). Si nous passons juste un code de langue, comme "en", chaque navigateur décidera quelle région utiliser pour formater ses nombres : Un navigateur peut utiliser par défaut "en-US", tandis qu’un autre choisit "en-UK". Alors nous utilisons une fullyQualifiedLocaleDefaults map dans notre fonction formatNumber() pour obtenir un formatage cohérent entre les navigateurs.

Parce que nous passons toutes les options définies dans notre objet d’interpolations au constructeur Intl.NumberFormat(), nous pouvons utiliser ses nombreuses options de format chaque fois que nous le souhaitons.

<p
  data-i18n-key="nyan-cat-price"
  data-i18n-opt='{"price": {"
    "number" : 5300,
    "style": "currency",
    "currency": EUR
  }}'
 >
  Nyan Cat (Officiel) NFT : {price}
</p>

Exemple de format de nombre américain | PhraseExemple de format de nombre arabe | Phrase

🔗 Ressource » Découvrez notre Guide concis de la localisation des nombres.

Formatage des dates

Tout comme les nombres, le formatage des dates est spécifique à la région. Le 5 décembre 2021, sous sa forme courte, s’écrit « 12/5/2021 » en anglais (US), et « 5.12.2021 » en allemand (Allemagne), par exemple. Et encore une fois, un objet intégré pratique Intl.DateTimeFormat peut gérer l’essentiel du travail en ce qui concerne le formatage des dates.
Disons que nous voulons montrer la date et l’heure de publication d’un de nos articles.

<p
  data-i18n-key="publish-date"
  data-i18n-opt='{"publishDate": {"
    "date": 2021-12-05 15:29:00
  }}'
>
  Publié le {publishDate}
</p>

La clé spéciale date dans notre objet data-i18n-opt contient la valeur de date et d’heure que nous voulons formater. Comme d’habitude, nous allons ajouter nos messages localisés.

{
  // ...
  "publish-date": "Publié {publishDate}"
}
{
  //...
  "publish-date": "نشر {publishDate}"
}

Maintenant, mettons à jour notre système de traduction pour rechercher les clés date et formater leurs valeurs au format localisé.

// ...
function interpolate(message, interpolations) {
  return Object.keys(interpolations).reduce(
    (interpolé, clé) => {
      const value = formatDate(
        formatNumber(interpolations[key]),
      );
      return interpolated.replace(
        new RegExp(`{\s*${key}\s*}`, "g"),
        value,
      );
    },
    message,
  );
}
// ...
/*
  Étant donné un objet de valeur comme
  {
    "date": 2021-12-05 15:29:00,
    "dateStyle": "long",
    "timeStyle": "short"
  } et que les paramètres régionaux actuels sont "en",
  retourne "5 décembre 2021 à 15h29 PM"
*/
function formatDate(value) {
  if (typeof value === "object" && value.date) {
    const { date, ...options } = value;
    const parsedDate =
      typeof date === "string" ? Date.parse(date) : date;
    return new Intl.DateTimeFormat(
      fullyQualifiedLocaleDefaults[locale],
      options,
    ).format(parsedDate);
  } else {
    return value;
  }
}
// ...

Après avoir passé notre objet de valeur à notre formatteur de nombres, nous faisons un autre passage à travers notre nouveau formatteur de dates. Les options de formatage de date sont passées au constructeur Intl.DateTimeFormat, permettant une flexibilité appréciable dans le formatage des dates.

✋🏽 Attention » Nous utilisons Date.parse() ci-dessus pour nous assurer que notre chaîne date est convertie en un objet Date, sinon Intl.DateTimeFormat lancera une erreur.

Ainsi, nous avons un formatage de date localisé 👍

<p
  data-i18n-key="publish-date"
  data-i18n-opt='{"publishDate": {"
    "date": 2021-12-05 15:29:00,
    "dateStyle": "long",
    "timeStyle": "short"
  }}'
>
  Publié le {publishDate}
</p>

Application de démonstration avec formatage de date US | Phrase
Application de démonstration avec formatage de date arabe | Phrase

🤿 Plongez plus profondément » Si vous recherchez des fonctionnalités de formatage de date plus robustes, consultez notre récapitulatif, Quelle est la meilleure bibliothèque JavaScript pour les dates et heures ?<6> Et notre Manière conviviale d’afficher des dates en TypeScript/JavaScript vous permet d’obtenir un format comme « il y a 1 heure ».

🔗 Ressource » Si vous utilisez un framework déclaratif comme React, vous pouvez aller plus loin avec ce que nous avons construit dans cette section et Créer votre propre bibliothèque i18n JavaScript avec TypeScript.

Quelles sont de bonnes bibliothèques i18n JavaScript que je peux utiliser ?

Nous avons expliqué ci-dessus comment créer votre propre bibliothèque i18n JavaScript. Cependant, il pourrait être plus judicieux pour votre projet d’adopter une bibliothèque i18n prête à l’emploi. Les options ne manquent pas ici, et dans cet article, nous allons passer en revue l’utilisation des bibliothèques Polyglot, i18next et Globalize.
Pour une sélection encore plus large, nos articles populaires peuvent vous mettre sur la bonne voie :

🗒 Note » Si vous travaillez avec l’ancien gettext, jetez un œil à la bibliothèque Jed.

Comment localiser une page web avec Polyglot ?

Maintenue par Airbnb, Polyglot est une petite bibliothèque i18n qui résout certains problèmes de localisation auparavant non pris en charge par les bibliothèques standard de JavaScript. Parmi les fonctionnalités de Polyglot, la plus remarquable est son excellente gestion des pluriels. Cependant, comme nous l’avons mentionné plus tôt, le maintenant intégré constructeur Intl.PluralRules est pris en charge par tous les navigateurs modernes et résout habilement le problème de la pluralisation. Cependant, la dernière version de cet article mettait fortement en avant Polyglot ; donc nous voulions inclure cette section au cas où certains de nos lecteurs souhaiteraient toujours un guide Polyglot.

🗒 Note » À moins que votre cas d’utilisation ne nécessite Polyglot, consultez l’alternative bibliothèque i18next dans la section suivante avant de choisir une solution de localisation.

Sans plus tarder, localisons notre petite application de démonstration avec la bibliothèque de localisation d’Airbnb :

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <ul class="navbar-list navbar-start">
          <li class="navbar-item">
            <a href="#" data-i18n-key="home" class="navbar-link">Accueil</a>
              Accueil
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n-key="about" class="navbar-link">
              À propos :
            </a>
          </li>
        </ul>
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">English</option>
            <option value="ar">Arabe (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">Avec Polyglot</h1>
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Cadence"}'>
      Bienvenue dans mon petit coin sur le web, %{username} !
    </p>
    <p
      data-i18n-key="article-plural"
      data-i18n-opt='{"smart_count": 2}'
    >
      %{smart_count} articles rédigés et ce n'est pas fini.
    </p>
  </div>
</body>
</html>

JavaScript demo app with Polyglot | Phrase
D’accord, passons à la localisation de ce petit projet avec Polyglot.

Installation

Polyglot a quelques dépendances NPM, donc il a besoin que Node soit installé localement. Avec Node en place, nous pouvons initialiser un fichier package.json pour notre application de démonstration en exécutant ce qui suit depuis la ligne de commande.

npm init -y

Afin de regrouper nos dépendances NPM dans un fichier que les navigateurs peuvent lire, nous allons installer le bundler de modules Webpack en tant que dépendances de développement. Le serveur de développement Webpack nous aidera avec le rechargement à chaud de notre bundle dans le navigateur pendant que nous développons.

npm install --save-dev webpack webpack-cli webpack-dev-server

D’accord, maintenant installons la star du spectacle : Polyglot.

npm install node-polyglot

Un index.js servira de point d’entrée pour notre application, et nous pouvons l’utiliser pour faire un test rapide des installations des bibliothèques.

import Polyglot from "node-polyglot";
console.log({ Polyglot });

Un script start dans notre package.json facilitera notre développement en lançant le serveur de développement avec notre config personnalisée.

{
  "name": "polyglot-demo",
  // ...
  "scripts": {
    "start": "webpack-dev-server --config webpack.config.js"
  },
  // ...
  "devDependencies": {
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1",
    "webpack-dev-server": "^4.6.0"
  },
  "dependencies" : {
    "node-polyglot": "^2.4.2"
  }
}

🗒 Note : Nous utilisons un fichier webpack.config.js relativement simple pour regrouper notre application et configurer le serveur de développement. Consultez-le dans notre dépôt Git sur GitHub.

Maintenant, nous allons intégrer notre JS regroupé juste avant la balise de fermeture </body> dans notre fichier public/index.html.

<script src="./bundle.js"></script>

Avec cela en place, nous devrions être en mesure d’exécuter notre script start depuis la ligne de commande pour initialiser le serveur de développement Webpack.

npm start

Si tout se passe bien, notre application devrait s’ouvrir automatiquement dans le navigateur. Si nous ouvrons les outils de développement de notre navigateur, nous devrions voir des journaux de console similaires à ceux ci-dessous.
Journaux de console des outils de développement du navigateur | Phrase

Traductions de base

Passons à l’utilisation de base de Polyglot. Voici la recette :

// 1. Importer la bibliothèque
import Polyglot from "node-polyglot";
// 2. Créer une instance
const polyglot = new Polyglot();
// 3. Ajouter des messages de traduction pour les paramètres régionaux actifs
polyglot.extend({
  "titre de l’application": "Avec Polyglot",
});
// 4. Utiliser les messages pour traduire les éléments de la page
const element = document.querySelector(
  "[data-i18n-key='app-title']",
);
// polyglot.t() renvoie un message de traduction spécifié
// une clé
element.innerHTML = polyglot.t("app-title");

Pour changer de paramètres régionaux, nous pouvons recharger la page avec les messages du nouveau paramètre régional.

✋🏽 Attention » Nous utilisons innerHTML dans cet article pour définir le contenu de l’élément. Faites attention à cet attribut en production ; assurez-vous d’assainir tout HTML que vous injectez en utilisant innerHTML afin d’éviter les attaques XSS (cross-site scripting).

import Polyglot from "node-polyglot";
const polyglot = new Polyglot();
polyglot.extend({
  "app-title": "مع بوليجلوت",
});
const element = document.querySelector(
  "[data-i18n-key='app-title']",
);
element.innerHTML = polyglot.t("app-title");

Chargement asynchrone des fichiers de traduction

Bien que ce qui précède fonctionne bien pour les plus petites applications, nous pourrions faire un peu mieux en séparant nos messages de traduction dans des fichiers JSON distincts par paramètres régionaux.

{
  "titre de l'application": "Avec Polyglot",
  "home": "Accueil", Accueil,
  "about": À propos
}
{
  "app-title": "مع بوليجلوت",
  "home": "الرئيسية",
  "about": "نبذة عنا"
}

Maintenant, nous pouvons configurer des paramètres régionaux par défaut pour notre application et charger ses traductions depuis le réseau lorsque notre page se charge.

import Polyglot from "node-polyglot";
const defaultLocale = "en";
const polyglot = new Polyglot();
// Charger les messages de traduction depuis le réseau
async function loadTranslations(locale) {
  return await fetch(`/lang/${locale}.json`).then(
    (réponse) => réponse.json(),
  );
}
// Traduire tous les éléments de la page qui ont notre personnalisé
// attribut data-i18n-key
function translatePage() {
  const translatableElements = document.querySelectorAll(
    "[data-i18n-key]",
  );
  translatableElements.forEach((el) => {
    const key = el.getAttribute("data-i18n-key");
    el.innerHTML = polyglot.t(key);
  });
}
// Init
(async function () {
  const translations = await loadTranslations(
    defaultLocale,
  );
  polyglot.extend(translations);
  translatePage();
})();

Avec cela, nous obtenons le rendu suivant dans le navigateur.
Application de démonstration en anglais avec Polyglot, paramètres régionaux, éléments de menu manquants et titre principal | Phrase
Nos éléments de menu de navigation et le titre principal sont traduits dans notre paramètre régional par défaut, l’anglais. Cependant, remarquez les erreurs Polyglot dans la console, et comment les clés manquantes (lead et article-plural) affichent la valeur de la clé elle-même. Nous ajouterons des traductions pour ces clés dans un instant pour corriger cela.
Si nous modifions defaultLocale en "ar", nous obtenons le rendu suivant.
Application de démonstration des paramètres régionaux arabes avec Polyglot, éléments de menu manquants et titre principal | Phrase

Sélecteur de langue

Notre application est maintenant plus évolutive puisque seules les traductions du paramètre régional actif sont chargées. Utilisons cela pour construire un sélecteur de langue . Nous avons déjà le HTML pour le sélecteur dans notre application :

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
		<!-- ... -->
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">Anglais</option>
            <option value="ar">Arabe (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <!-- ... -->
  <script src="./bundle.js"></script>
</body>
</html>

Le fait d’interagir avec cet élément <select> depuis notre JavaScript nous permet d’ajouter notre comportement de changement de paramètres régionaux.

import Polyglot from "node-polyglot";
const defaultLocale = "en";
const polyglot = new Polyglot();
// ...
// Charger les traductions pour les paramètres régionaux donnés et traduire
// les éléments de page pour ces paramètres régionaux
async function loadAndTranslate(locale) {
  const translations = await loadTranslations(locale);
  polyglot.replace(translations);
  translatePage();
}
// Chaque fois que l'utilisateur change les paramètres régionaux actifs, chargez
// les messages de ces paramètres régionaux dans la page
function bindLocaleSwitcher(initialValue) {
  const switcher = document.querySelector(
    "[data-i18n-switcher]",
  );
  switcher.value = initialValue;
  switcher.onchange = (e) => {
    loadAndTranslate(e.target.value);
  };
}
// Init
.init({
loadAndTranslate(defaultLocale);
bindLocaleSwitcher(defaultLocale);

Nous avons refactorisé le code qui charge nos messages de traduction et traduit nos éléments de page en une fonction réutilisable loadAndTranslate(). Une nouvelle fonction bindLocaleSwitcher() s’intègre dans le sélecteur de langue <select> ; elle utilise loadAndTranslate() pour actualiser nos traductions en fonction des paramètres régionaux sélectionnés par l’utilisateur.

✋🏽 Avis » polyglot.extend() va ajouter des messages de traduction à ceux déjà chargés, donc nous utilisons polyglot.replace() à la place pour nous assurer que nous ne chargeons que les traductions du paramètre régional actif.

Cela devrait faire fonctionner notre élégant sélecteur de paramètres régionaux.
Application de démonstration avec Polyglot avec sélecteur de paramètres régionaux fixe | Phrase

Interpolation

Notre texte principal inclut le nom de l’utilisateur actuellement connecté (fictif, bien sûr). Ce type de valeur interpolée est géré par Polyglot en utilisant une syntaxe spéciale %{variable} par défaut.

🗒 Note » Vous pouvez modifier les caractères qui désignent les valeurs interpolées en utilisant l’option interpolation passée au constructeur Polyglot.

<!-- ... -->
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Cadence"}'>
      Bienvenue dans mon petit coin sur le web, %{username} !
    </p>
<!-- ... -->
{
  // ...
  "lead": "Bienvenue dans mon petit coin sur le web, %{username}!"
}
{
  // ...
  "lead": "أهلاً بك في مكاني الصغير على النت يا %{username}."
}

Quelques lignes de code peuvent être ajoutées à notre fonction translatePage() pour prendre en charge les interpolations.

// ...
// Traduisez tous les éléments de la page qui ont un
// attribut data-i18n-key
function translatePage() {
  const translatableElements = document.querySelectorAll(
    "[data-i18n-key]",
  );
  translatableElements.forEach((el) => {
    const key = el.getAttribute("data-i18n-key");
    // Extraire les clés/valeurs d'interpolation du HTML et
    // les analyser en JSON
    const interpolations = el.getAttribute("data-i18n-opt");
    const parsedInterpolations = interpolations
      ? JSON.parse(interpolations)
      : {};
    // Passez les interpolations analysées à polyglot.t(),
    // qui gère automatiquement les substitutions
    el.innerHTML = polyglot.t(key, parsedInterpolations);
  });
}
// ...

Avec le code ci-dessus en place, nous avons maintenant notre paragraphe d’introduction interpolé rendu dans le paramètre régional actif.
Application de démonstration des paramètres régionaux anglais avec Polyglot avec paragraphe d'introduction fixe | Phrase
Application de démonstration des paramètres régionaux arabes avec Polyglot avec paragraphe d'introduction fixe | Phrase

Pluriels

Disons que nous voulons montrer à l’utilisateur actuellement connecté combien de messages il ou elle a reçus : “Vous avez 1 nouveau message” ou “Vous avez 12 nouveaux messages”, par exemple. Polyglot gère bien les pluriels de cette manière. Nous devons juste ajouter nos messages de traduction avec la valeur numérique interpolée spéciale smart_count.

<!-- ... -->
    <p
      data-i18n-key="new-messages"
      data-i18n-opt='{"smart_count": 12}'
    >
      Vous avez %{smart_count} nouveaux messages
    </p>
<!-- ... -->

Polyglot utilise smart_count pour sélectionner la forme plurielle correcte d’un message de traduction en fonction des paramètres régionaux actifs. Les formes plurielles sont séparées par quatre pipes |||| dans nos messages. L’anglais a un et autre formes plurielles, et nous devons les fournir dans l’ordre :

{
  // ...
  "new-messages": "Vous avez %{smart_count}\u00a0nouveau message |||| Vous avez %{smart_count}\u00a0nouveaux messages"
}

L’arabe a six formes plurielles, et nous les ajoutons à nos messages de la même manière.

{
  // ...
  "new-messages": "Vous n'avez pas de nouveaux messages |||| Vous avez un nouveau message |||| Vous avez deux nouveaux messages |||| Vous avez %{smart_count} nouveaux messages |||| Vous avez %{smart_count} nouveau message |||| Vous avez %{smart_count} nouveau message"
}

🗒 Note » Consultez la section Comment localiser une page web avec JavaScript ? ➞ Pluriels pour plus de détails sur les formes plurielles.

Une chose de plus : par défaut, Polyglot est joyeusement inconscient des paramètres régionaux actifs, donc il ne connaîtra pas les règles plurielles de ces paramètres à moins que nous ne spécifiions explicitement les paramètres régionaux lors du chargement.

// ...
// Charger les traductions pour les paramètres régionaux donnés et traduire
// les éléments de page pour ces paramètres régionaux
async function loadAndTranslate(locale) {
  const translations = await loadTranslations(locale);
  polyglot.locale(locale);
  polyglot.replace(translations);
  translatePage();
}
// ...

Et c’est tout ! Nous avons maintenant un support avancé des pluriels dans notre application.
Application de démonstration des paramètres régionaux anglais avec le comptage intelligent de Polyglot | Phrase
Application de démonstration des paramètres régionaux arabes avec le comptage intelligent de Polyglot | Phrase

🔗 Ressource » Obtenez tout le code de notre application Polyglot sur notre dépôt GitHub.

🔗 Ressource » La documentation officielle de Polyglot est aussi concise que la bibliothèque elle-même.

Comment localiser une page web avec i18next ?

Alors que j’écris cela, i18next est l’une des bibliothèques i18n JavaScript les plus populaires. La bibliothèque « apprendre une fois, [utiliser] partout » fonctionne de manière autonome et avec une multitude de frameworks JavaScript. Un riche écosystème de plug-ins signifie que vous êtes souvent à une installation NPM de résoudre un problème i18n courant. Tout cela fait d’i18next une recommandation facile :
Bon, ça suffit les bavardages. Revisitons notre petite démonstration et localisons-la en utilisant i18next.

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <ul class="navbar-list navbar-start">
          <li class="navbar-item">
            <a href="#" data-i18n-key="home" class="navbar-link">Accueil</a>
              Accueil
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n-key="about" class="navbar-link">
              À propos
            </a>
          </li>
        </ul>
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">Anglais</option>
            <option value="ar">Arabe (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">Avec i18next</h1>
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Zelda"}'>
      Bienvenue dans mon petit coin sur le web, {{username}}!
    </p>
    <p data-i18n-key="new-messages" data-i18n-opt='{"count": 12}'>
      Vous avez {{count}} nouveaux messages
    </p>
  </div>
  <script src="./bundle.js"></script>
</body>
</html>

Pas grand-chose de nouveau ici. Passons à la localisation.

Installation

Nous allons utiliser Node et son gestionnaire de paquets NPM pour installer i18next. Tout d’abord, créons un fichier package.json pour suivre les dépendances du projet et les scripts NPM en exécutant ce qui suit depuis la ligne de commande.

npm init -y

Le Webpack bundler nous permettra de regrouper i18next, ses plug-ins et notre JavaScript personnalisé et de les servir dans un seul fichier au navigateur. Installons Webpack, ainsi que son serveur de développement, qui dispose d’une fonctionnalité de rechargement à chaud pratique qui facilite le développement :

npm install --save-dev webpack webpack-cli webpack-dev-server

Nous ne pouvons pas oublier notre bibliothèque i18n, bien sûr :

npm install i18next

Un script pratique npm start peut servir de raccourci pour lancer notre serveur de développement.

{
  "name": "i18next-demo",
  //...
  "scripts": {
    "start": "webpack-dev-server --config webpack.config.js"
  },
  // ...
  "devDependencies": {
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1",
    "webpack-dev-server": "^4.6.0"
  },
  "dependencies" : {
    "i18next": "^21.6.3"
  }
}

🗒 Note » Nous utilisons un fichier webpack.config.js relativement simple pour regrouper notre application et configurer le serveur de développement. Consultez-le dans notre dépôt Git sur GitHub.

Créons un index.js point d’entrée pour notre application et testons i18next pour nous assurer qu’il est installé correctement.

import i18next from "i18next";
console.log({ i18next });

Maintenant, lorsque nous exécutons npm start depuis la ligne de commande, nous devrions voir le serveur Webpack démarrer et charger notre application dans notre navigateur automatiquement.
Application de démonstration avec le serveur de développement Webpack chargé | Phrase

Messages de base de traduction

i18next est flexible dans la façon dont il accepte les messages de traduction. Nous faisons la plupart de notre configuration lors de l’initialisation de la bibliothèque avec i18next.init(...) . La configuration la plus basique consiste à inclure nos messages de traduction sous une option resources.

import i18next from "i18next";
i18next.init({
  // Le paramètre linguistique actif
  lng: "en",
  // Activer une sortie console utile lors du développement
  debug: true,
  // Messages de traduction, associés à chaque code de paramètres régionaux
  resources: {
    en: {
      // Par défaut, i18next s'attend à des messages sous le
      // espace de noms « translation »
      translation: {
        "app-title": "Avec Polyglot",
        home: "Home",
        about: "About",
      },
    },
    ar: {
      translation: {
        "app-title": "مع بوليجلوت",
        home: "الرئيسية",
        about: "نبذة عنا",
      },
    },
  },
});
// Traduire les éléments de la page
const translatableElements = document.querySelectorAll(
  "[data-i18n-key]",
);
translatableElements.forEach((el) => {
  const key = el.getAttribute("data-i18n-key");
  el.innerHTML = i18next.t(key);
});

Avec cela, nous ne devrions voir aucun changement lorsque notre application se rechargera dans le navigateur. Cependant, si nous modifions lng en "ar", nous voyons les traductions arabes suivantes.
Démo d'application avec paramètres régionaux arabes et clé manquante | Phrase

🗒 Note » L’option debug: true permet d’activer des journaux de console très pratiques dans le navigateur. Notez les messages clés manquants ci-dessus, par exemple.

Chargement asynchrone des traductions

En tant que développeurs tournés vers l’avenir, rendons notre application plus évolutive en divisant nos traductions en fichiers séparés, un par paramètres régionaux.

{
  "app-title": "Avec i18next",
  Accueil Accueil,
  À propos : À propos
}
{
  "app-title": "مع آي أيتين نيكست",
  "home": "الرئيسية",
  "about": "نبذة عنا"
}

i18next est mature et couvre beaucoup de bases ; donc nous n’avons pas besoin d’écrire notre propre code pour charger des fichiers de traduction depuis le réseau. Le backend HTTP officiel s’intègre à la bibliothèque et fait tout le travail pour nous. Installez-le.

npm install i18next-http-backend

Nous pouvons maintenant intégrer le backend dans notre index.js et utiliser() lors de l’initialisation d’i18next.

import i18next from "i18next";
import HttpApi from "i18next-http-backend";
// Nous rendons la fonction asynchrone pour pouvoir utiliser await
// le fichier de traduction tel qu'il est transmis
// réseau
async function initI18next() {
  // Nous utilisons le backend et attendons qu'il charge
  // les traductions depuis le réseau
  await i18next.use(HttpApi).init({
    lng: "en",
    debug: true,
    // Retirer les `ressources` intégrées
    // Désactiver le chargement du paramètre linguistique de développement
    fallbackLng: false,
    // Configurer le backend HTTP
    backend: {
      loadPath: "/lang/{{lng}}.json",
    },
  });
}
// Refactorisation rapide du code de traduction de la page
// à une fonction
function translatePageElements() {
  const translatableElements = document.querySelectorAll(
    "[data-i18n-key]",
  );
  translatableElements.forEach((el) => {
    const key = el.getAttribute("data-i18n-key");
    el.innerHTML = i18next.t(key);
  });
}
// Initialisation
(async function () {
  await initI18next();
  translatePageElements();
})();

L’option backend.loadPath remplace le chemin de fichier de traduction par défaut du backend : Un espace réservé spécial {{lng}} est remplacé par les paramètres régionaux actifs. Par exemple, lorsque notre application se charge pour la première fois, le backend cherchera /lang/en.json, puisque nous avons spécifié les paramètres régionaux par défaut comme en plus tôt dans notre configuration.
C’est à peu près tout. Nos traductions se chargent maintenant depuis le réseau au lieu d’être intégrées dans notre code.

Paramètres régionaux pris en charge et de secours

Nous voulons souvent spécifier une liste de paramètres régionaux que notre application prend en charge et un paramètre régional de secours lorsqu’une traduction est manquante. Nous pouvons utiliser les options de configuration supportLngs et fallbackLng d’i18next, respectivement, pour y parvenir.

// ...
async function initI18next() {
  await i18next.use(HttpApi).init({
    lng: "en",
    debug: true,
    supportedLngs: ["en", "ar"],
    fallbackLng: "en",
    backend: {
      loadPath: "/lang/{{lng}}.json",
    },
  });
}
// ...

✋🏽 Attention » Le paramètre linguistique de secours sera toujours chargé, peu importe le paramètre linguistique actif.

Détection automatique des paramètres régionaux de l’utilisateur

Il est courant de vouloir détecter les paramètres du navigateur de l’utilisateur et d’utiliser ses paramètres régionaux si nous les prenons en charge. C’est normalement un peu délicat, mais encore une fois, i18next a un plug-in officiel qui peut nous aider rapidement ici. Nous pouvons commencer par l’installer.

npm install i18next-browser-languagedetector

Tout comme le backend HTTP, nous importer le plug-in de détection et l'utiliser() lors de l’initialisation.

import i18next from "i18next";
import HttpApi from "i18next-http-backend";
import LanguageDetector from "i18next-browser-languagedetector";
async function initI18next() {
  await i18next
    .utiliser(HttpApi)
    .utiliser(LanguageDetector)
    .init({
      debug: true,
      supportedLngs: ["en", "ar"],
      fallbackLng: "en",
      // Autoriser l'utilisation de "en" pour
      // « en-US », « en-CA », etc.
      nonExplicitSupportedLngs: true,
      backend: {
        loadPath: "/lang/{{lng}}.json",
      },
    });
}
// ...

Et c’est tout ce qu’il faut pour obtenir une détection automatique solide des paramètres régionaux avec i18next.

🔗 Ressource » Vous vous demandez peut-être quels critères le détecteur de paramètres régionaux utilise pour déterminer les paramètres régionaux de l’utilisateur. Nous couvrons cela en détail dans notre Guide de la localisation React avec i18next.

✋🏽 Attention » Le détecteur de paramètres régionaux stockera les paramètres régionaux qu’il détecte dans le stockage des paramètres régionaux du navigateur par défaut, et utilisera cette valeur lorsque l’utilisateur visitera notre site à nouveau.

🤿 Aller plus loin » Nous avons un guide dédié à la détection de la préférence de langue du navigateur avec JavaScript, qui pourrait vous intéresser.

Sélecteur de langue

La détection automatique des paramètres régionaux est utile, mais il nous faut souvent une interface utilisateur permettant à nos utilisateurs de définir explicitement leur langue de prédilection. Nous avons déjà le balisage pour un sélecteur de paramètres régionaux configuré.

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
      <!-- ... -->
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">Anglais</option>
            <option value="ar">Arabe (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <!-- ... -->
  </div>
  <script src="./bundle.js"></script>
</body>
</html>

Intégrons ce HTML et utilisons la fonction i18next.changeLanguage() pour définir le paramètre régional actif en fonction du choix de l’utilisateur. Après le chargement des messages des paramètres régionaux, nous pouvons enchaîner translatePageElements pour réafficher la page avec les traductions mises à jour.

// ...
function bindLocaleSwitcher(initialValue) {
  const switcher = document.querySelector(
    "[data-i18n-switcher]",
  );
  switcher.value = initialValue;
  switcher.onchange = (e) => {
    i18next
      .changeLanguage(e.target.value)
      .then(translatePageElements);
  };
}
// Init
(async function () {
  await initI18next();
  translatePageElements();
  bindLocaleSwitcher(i18next.resolvedLanguage);
})();

✋🏽 Alerte » Le détecteur de paramètres régionaux pourrait avoir détecté des paramètres régionaux que nous ne prenons pas en charge, et cette valeur existera dans i18next.language. Nous utilisons i18next.resolvedLanguage ci-dessus pour nous assurer que nous utilisons le paramètre régional actif, pris en charge lors de l’initialisation de notre sélecteur de paramètres régionaux.

Et voilà ! Une interface utilisateur de changement de langue :
Application de démonstration Polyglot avec une interface utilisateur de changement de langue | Phrase

Interpolation

Par défaut, i18next utilise une syntaxe {{variable}} pour désigner les valeurs interpolées dans les messages de traduction.

<!-- ... -->
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Zelda"}'>
      Bienvenue dans mon petit coin sur le web, {{username}} !
    </p>
<!-- ... -->
{
  // ...
  "lead": "Bienvenue sur mon petit coin du web, {{username}} !"
}
{
  // ...
  "lead": "أهلاً بك في مكاني الصغير على النت يا {{username}}."
}

Nous pouvons tirer ces valeurs dynamiques de nos attributs HTML et les transmettre à i18next.t(), qui gère l’interpolation pour nous.

// ...
function translatePageElements() {
  const translatableElements = document.querySelectorAll(
    "[data-i18n-key]",
  );
  translatableElements.forEach((el) => {
    const key = el.getAttribute("data-i18n-key");
    const interpolations = el.getAttribute("data-i18n-opt");
    const parsedInterpolations = interpolations
      ? JSON.parse(interpolations)
      : {};
    el.innerHTML = i18next.t(key, parsedInterpolations);
  });
}
// ...

Avec cela en place, nos valeurs dynamiques sont remplacées dans nos messages.
Application de démonstration avec Polyglot en paramètres régionaux anglais et paragraphe d'introduction interpolé | Phrase
Application de démonstration avec Polyglot en paramètres régionaux arabes et paragraphe d'introduction interpolé | Phrase

Pluriels

Sous le capot, i18next essaie d’utiliser le standard Intl.PluralRules pour gérer les pluriels. Une variable interpolée spéciale count dirige le choix de la forme plurielle, en fonction du paramètre régional actif.

<!-- ... -->
    <p data-i18n-key="new-messages" data-i18n-opt='{"count": 12}'>
      Vous avez {{count}} nouveaux messages
    </p>
<!-- ... -->

i18next utilise une convention message_form pour les clés de message pluriel. Par exemple, pour gérer les formes one et other dans une traduction anglaise new-messages, nous pouvons spécifier ce qui suit.

{
  // ...
  "new-messages_one": "Vous avez {{count}} nouveau message",
  "new-messages_other": "Vous avez {{count}} nouveaux messages"
}

L’arabe a six formes plurielles, et nous pouvons les spécifier à peu près de la même manière.

{
  // ...
  "new-messages_zero": "Vous n'avez pas de nouveaux messages",
  "new-messages_one": "Vous avez un nouveau message",
  "new-messages_two": "Vous avez deux nouveaux messages",
  "new-messages_few": "Vous avez {{count}} nouveaux messages",
  "new-messages_many": "Vous avez {{count}} nouveaux messages",
  "new-messages_other": "Vous avez {{count}} nouveaux messages"
}

Avec peu d’effort, notre application peut afficher des messages au pluriel.
Application de démonstration avec Polyglot en paramètres régionaux anglais et pluralisation | Phrase
Application de démonstration avec Polyglot en paramètres régionaux arabes et pluralisation | Phrase

🔗 Ressource » Obtenez tout le code que nous avons couvert ci-dessus depuis notre dépôt GitHub.

Comment localiser une application React, Angular ou Vue ?

Ces dernières années, des frameworks déclaratifs comme React, Angular, Vue.js et d’autres ont pris d’assaut le monde du web front-end. Nous couvrons ces frameworks en profondeur sur notre blog. Comme React est le plus populaire parmi les poids lourds, nous allons vous fournir ici dans un instant un guide rapide pour localiser les applications React. Et pour d’autres frameworks déclaratifs, consultez les articles approfondis suivants.

Articles sur la localisation Angular

Articles de localisation Vue.js

Articles de localisation pour d’autres frameworks

Nous réalisons que certains de nos lecteurs pourraient utiliser Svelte, Next ou un autre framework, donc nous écrivons toujours sur les nouveautés les plus récentes. Voici une sélection de guides pour localiser l’application que vous construisez dans votre framework préféré :

🗒 Note » Pour être plus précis : Si vous allez travailler avec des numéros de téléphone internationaux, assurez-vous de jeter un œil à la libphonenumber library. C’est indépendant du framework !

Comment localiser une application React avec i18next ?

Comme promis, nous allons passer en revue rapidement un guide pour localiser nos applications React avec la bibliothèque i18next. Nous avons déjà couvert i18next plus tôt dans cet article, donc nous allons nous concentrer sur son intégration à React ici.

🤿 Allez plus loin » Un guide de la localisation React avec i18next va plus loin et plus en profondeur que notre bref aperçu ici.

Tout d’abord, nous allons prendre notre précieuse application de démonstration et la diviser en composants React.

import "./App.css";
import Navbar from "./layout/Navbar";
function App() {
  return (
    <div className="container">
      <Navbar />
      <h1>React i18n</h1>
      <p>
        Bienvenue dans mon petit coin sur le web, utilisateur
      </p>
      <p>Vous avez count nouveaux messages</p>
    </div>
  );
}
export default App;
import LocaleSwitcher from "../features/LocaleSwitcher";
function Navbar() {
  return (
    <nav className="navbar">
      <div className="container">
        <ul className="navbar-list navbar-start">
          <li className="navbar-item">
            <a href="#" className="navbar-link">
              Accueil
            </a>
          </li>
          <li className="navbar-item">
            <a href="#" className="navbar-link">
              À propos de
            </a>
          </li>
        </ul>
        <div className="navbar-end">
          <LocaleSwitcher />
        </div>
      </div>
    </nav>
  );
}
export default Navbar;
function LocaleSwitcher() {
  return (
    <>
      <img
        alt="Icône de traduction"
        src="img/translation-icon@2x.png"
        className="translation-icon"
      />
      <select className="locale-switcher">
        <option value="en">Anglais</option>
        <option value="ar">Arabe (العربية)</option>
      </select>
    </>
  );
}
export default LocaleSwitcher;

Le sélecteur de paramètres régionaux fait peu de choses pour le moment, mais nous y remédierons bientôt. Pour l’instant, nous avons un Starter solide à localiser.
Application de démonstration React en anglais avec la bibliothèque i18n | Phrase

Installation de la bibliothèque

En plus d’i18next, nous allons récupérer le cadre d’intégration officiel react-i18next , qui facilite l’utilisation d’i18next avec React. Depuis la racine du projet, exécutons ce qui suit dans la ligne de commande pour installer les bibliothèques.

npm install i18next react-i18next

Ensuite, initialisons i18next, en utilisant() l’intégration React comme nous le faisons. La manière la plus basique de fournir des traductions à i18next est d’utiliser l’option resources lors de l’initialisation.

import i18next from "i18next";
import { initReactI18next } from "react-i18next";
i18next.use(initReactI18next).init({
  Ressources : {
    en: {
      translation: {
        "app-title": « Avec React »,
      },
    },
    ar: {
      translation: {
        "app-title": "مع ريأكت",
      },
    },
  },
  lng: "en",
  debug: true,
  interpolation: {
    escapeValue: false,
  },
});
export default i18next;

🗒 Note » Nous avons défini interpolation.escapeValue sur false pour désactiver l’échappement par défaut que i18next effectue pour se protéger contre les attaques XSS, puisque React le fait déjà pour nous.

Importons notre module dans notre fichier racine index.js afin de pouvoir initialiser i18next lorsque notre application se charge.

import React from "react";
import ReactDOM from "react-dom";
import "./lib/skeleton/normalize.css";
import "./lib/skeleton/skeleton.css";
import "./services/i18n";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root"),
);
// ...

Traductions de base

La fonction de traduction familière i18next.t() peut être utilisée dans nos composants React. Nous devons juste importer le hook React useTranslation afin de rendre t disponible.

import { useTranslation } from "react-i18next";
import Navbar from "./layout/Navbar";
import "./App.css";
function App() {
  const { t } = useTranslation();
  return (
    <div className="container">
      <Navbar />
      <h1>{t("app-title")}</h1>
      // ...
    </div>
  );
}
export default App;

Lorsque notre application se recharge, tout devrait être identique. Cependant, si nous modifions la valeur lng en "ar" dans notre initialiseur src/services/i18n.js, nous devrions voir le titre de notre application localisé en arabe.
Application de démonstration React avec bibliothèque i18n et titre en arabe | Phrase

Chargement asynchrone de fichiers de traduction

Rendons notre application plus évolutive en divisant nos traductions en fichiers par paramètres régionaux. Le plug-in officiel i18next-http-backend, pratique, fait de cela une tâche rapide pour nous. Installons-le.

npm install i18next-http-backend

Le backend cherchera des fichiers à /locales/{{lng}}/{{ns}}.json par défaut, où {{lng}} correspond aux paramètres régionaux actifs, et {{ns}} correspond à l’espace de noms actif. Puisque l’espace de noms par défaut est translation, nous pouvons mettre nos messages de traduction en anglais dans public/locales/en/translation.json.

{
  "app-title": « Avec React »,
  "home": Accueil,
  "about": À propos
}

Notre fichier arabe suit la même convention :

{
  "app-title": "مع ريأكت",
  "home": "Accueil",
  "about": "À propos de"
}

Nous devons maintenant juste importer le backend et utiliser() lors de l’initialisation d’i18next. Nous voudrons également retirer nos traductions en ligne sous la clé resources, puisque le plugin chargera maintenant nos messages de traduction depuis le réseau.

import i18next from "i18next";
import { initReactI18next } from "react-i18next";
import HttpApi from "i18next-http-backend";
i18next
  .use(initReactI18next)
  .use(HttpApi)
  .init({
    // Retirer `resources`
    lng: "en",
    debug: true,
    interpolation: {
      escapeValue: false,
    },
  });
export default i18next;

Lorsque notre application se recharge, nous devrions voir exactement le même rendu localisé. Bien sûr, les traductions des paramètres régionaux actifs sont maintenant transmises sur le réseau, donc notre application est plus légère au chargement, et plus facile à mettre à l’échelle et à maintenir.
Application de démonstration React avec chargement de fichier de traduction asynchrone | Phrase<2>

Sélecteur de langue

Construire une interface utilisateur de changement de langue est un jeu d’enfant avec React et i18next. Nous pouvons mettre à jour notre ComposantLocaleSwitcher, contrôlant le <select> à l’intérieur pour modifier les paramètres régionaux actifs selon le choix de l’utilisateur.

import { useTranslation } from "react-i18next";
function LocaleSwitcher() {
  const { i18n } = useTranslation();
  return (
    <>
      <img
        src="img/translation-icon@2x.png"
        alt="Icône de traduction"
        className="translation-icon"
      />
      <select
        className="locale-switcher"
        value={i18n.language}
        onChange={(e) =>
          i18n.changeLanguage(e.target.value)
        }
      >
        <option value="en">Anglais</option>
        <option value="ar">Arabe (العربية)</option>
      </select>
    </>
  );
}
export default LocaleSwitcher;

L’intégration React d’i18next garantit que les traductions sont re-rendues lorsque les paramètres régionaux actifs sont modifiés.
Application de démonstration React avec sélecteur de langue fonctionnel | Phrase

🔗 Ressource » Obtenez le code pour tout ce que nous avons construit ci-dessus depuis notre dépôt GitHub.

Articles de localisation React

Nous aimons écrire sur React dans notre blog, donc nous sommes heureux de vous donner une sélection de nos analyses approfondies et de nos tutoriels basés sur React, tous centrés sur la localisation :

Comment localiser une page web avec jQuery et i18next ?

Bien que ce ne soit plus aussi en vogue qu’il y a quelques années, jQuery reste l’une des bibliothèques JavaScript les plus populaires et est encore largement utilisée aujourd’hui. Vous constaterez que la localisation des applications jQuery est assez facile avec la bibliothèque i18next . Un plugin jQuery i18next officiel nécessite très peu de travail pour être configuré, alors utilisons-le pour localiser notre fidèle application de démonstration.

🗒 Note » Nous avons couvert i18next en détail plus tôt dans cet article, donc nous allons nous concentrer sur l’intégration jQuery ici.

Si vous avez suivi, le Starter suivant vous semblera familier. Elle sert de bonne base pour notre travail de localisation.

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <ul class="navbar-list navbar-start">
          <li class="navbar-item">
            <a href="#" data-i18n="home" class="navbar-link">
              Accueil
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n="about" class="navbar-link">
              À propos de
            </a>
          </li>
        </ul>
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">Anglais</option>
            <option value="ar">Arabe (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n="app-title">jQuery i18n</h1>
    <p data-i18n="lead" data-i18n-options='{"username": "Jackie"}'>
      Bienvenue dans mon petit coin sur le web, {{username}} !
    </p>
    <p data-i18n="new-messages" data-i18n-options='{"count": '>
      Vous avez {{count}} nouveaux messages
    </p>
  </div>
</body>
</html>

🗒 Note » Par défaut, le plug-in jQuery i18next utilise data-i18n (pas notre précédent data-i18n-key) pour ses clés de traduction. Ce peut être modifié dans les options du plugin.

Démo jQuery | Phrase

Il est temps de localiser ? Allons-y !

Installation

La manière la plus simple d’installer i18next et son plug-in jQuery est de télécharger leurs fichiers de distribution minifiés et de les inclure dans notre HTML. Vous pouvez récupérer les fichiers aux emplacements suivants.

🔗 Ressource » Consultez la documentation officielle du plug-in jQuery i18next sur GitHub.

Après avoir téléchargé les fichiers ci-dessus, nous pouvons les placer dans un répertoire js/lib dans notre projet et les intégrer dans notre page HTML principale.

<!DOCTYPE html>
<html lang="fr">
<head>
   <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
  </div>
  <script src="./js/lib/jquery-3.6.0.min.js"></script>
  <script src="./js/lib/i18next.min.js"></script>
  <script src="./js/lib/jquery-i18next.min.js"></script>
  <!-- Notre JavaScript personnalisé, va arriver dans un instant … -->
  <script src="./js/scripts.js"></script>
</body>
</html>

Traductions de base

Avec nos bibliothèques installées, nous pouvons maintenant écrire un code de configuration pour faire fonctionner la localisation de base.

// Initialiser i18next
i18next.init({
  lng: "en",     // Paramètre régional initial
  debug: true,   // Fournit des messages utiles dans la console
  resources: {   // Traductions
    en: {
      translation: {
        "app-title": "jQuery + i18next",
      },
    },
    ar: {
      Traduction {
        "app-title": "جي كويري + آي إيتين نيكست",
      },
    },
  },
});
// Initialiser le plug-in jQuery i18next
jqueryI18next.init(i18next, $);
// Traduire les éléments de la page
$("body").localize();

Nous avons couvert i18next.init(...) plus tôt dans cet article. Notez qu’ici nous avons également un appel jQueryI18next.init(...). Dans sa forme la plus basique, la fonction init du plug-in jQuery i18next prend l’instance active i18next ainsi qu’une référence à l’objet jQuery, $.
Lorsque le plug-in est initialisé, il ajoute une fonction localize() à jQuery. Appeler $(selector).localize() provoque la localisation de tous les éléments sous la hiérarchie sélectionnée par le plug-in. Pour chaque élément, si un attribut data-i18n est trouvé, sa traduction correspondante dans les paramètres régionaux actifs est échangée.
Par exemple :

// Dans notre JavaScript
i18next.init({
  lng: "en",
  Ressources : {
    en: {
      Traduction {
        "app-title": "jQuery + i18next",
      },
    },
    ar: {
      Traduction {
        "app-title": "جي كويري + آي إيتين نيكست",
      },
    },
  },
});
jqueryI18next.init(i18next, $);
$("#main-title").localize();
// Dans notre HTML
<h1 id="main-title" data-i18n="app-title"></h1>
// S'affiche comme:
<h1 id="main-title" data-i18n="app-title">jQuery + i18next</h1>

Simple et agréable 😊

application de démonstration jQuery avec la bibliothèque i18next chargée avec le paramètre linguistique anglais | Phrase

Et si nous modifions nos paramètres régionaux initiaux en arabe en changeant lng en "ar", nous obtenons un titre en arabe à la place.

application de démonstration jQuery avec la bibliothèque i18next chargée en paramètres régionaux arabes | Phrase

Chargement asynchrone de fichiers de traduction

Que diriez-vous de diviser nos fichiers de traduction en fichiers séparés, un par paramètres régionaux ? J’entends que vous vous le demandez. Pas de soucis, le plug-in HTTP officiel i18next nous couvre.
Pour installer le plug-in, nous pouvons récupérer le script de distribution minifié depuis GitHub et le placer dans notre répertoire js/lib. Bien sûr, nous voudrons aussi l’intégrer dans notre&nbsp;HTML.

<!DOCTYPE html>
<html lang="fr">
<head>
   <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
  </div>
  <script src="./js/lib/jquery-3.6.0.min.js"></script>
  <script src="./js/lib/i18next.min.js"></script>
  <script src="./js/lib/i18nextHttpBackend.min.js"></script>
  <script src="./js/lib/jquery-i18next.min.js"></script>
  <script src="./js/scripts.js"></script>
</body>
</html>

Maintenant, nous pouvons rendre notre application plus évolutive en déplaçant nos traductions dans des fichiers JSON séparés.

{
  "app-title": "Avec jQuery + i18next",
  "home": Accueil,
  "about": À propos
}
{
  "app-title": "مع جي كويري و آي إيتين نيكست",
  "home": "Accueil",
  "about": "نبذة عنا"
}

Nous devrons retravailler notre code de configuration pour utiliser() le plugin HTTP lors de l’initialisation d’i18next. Nous voudrons également attendre que le fichier de traduction de nos paramètres régionaux initiaux soit téléchargé avant d’essayer de traduire nos éléments de page.

// Attendre que les traductions arrivent sur le réseau
// avant d'initialiser le plug-in jQuery
async function initI18n() {
  // Utiliser le plug-in Http backend pour télécharger les traductions
  await i18next.use(i18nextHttpBackend).init({
    lng: "en",
  // Retirer l'option `resources`, puisque nos traductions
  // sont maintenant dans des fichiers JSON
  });
  jqueryI18next.init(i18next, $);
}
// Refactorer en fonction
function translatePage() {
  $("body").localize();
}
// Initialisation
(async function () {
  // Attendre que i18next s'initialise avant
  // Traduction des éléments de la page
  await initI18n();
  translatePage();
})();

Si nous rechargeons notre application, nous ne remarquons aucune différence dans la sortie. Cependant, un examen plus attentif de l’onglet Réseau de nos outils de développement révèle une solution de fichier de traduction plus maintenable « télécharger quand vous en avez besoin ».

Démo jQuery avec chargement asynchrone du fichier de traduction | Phrase

Sélecteur de langue

Vous avez peut-être remarqué que nous avons du code HTML ressemblant à un sélecteur de langue dans notre démo.

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <!-- ... -->
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">anglais</option>
            <option value="ar">Arabe (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <!-- ... -->
  </div>
  <!-- ... -->
</body>
</html>

Connectons l’élément <select> ci-dessus afin que notre application change ses traductions pour celles correspondant aux paramètres régionaux sélectionnés par l’utilisateur.

// ...
function bindLocaleSwitcher() {
  const $switcher = $("[data-i18n-switcher]");
  // Valeur initiale
  $switcher.val(i18next.language);
  $switcher.on("change", async function () {
    // Modifier le paramètre linguistique actif entraînera son
    // traductions à charger depuis le réseau, donc
    // nous attendons ce chargement avant de rafraîchir
    // les éléments de la page
    await i18next.changeLanguage($switcher.val());
    translatePage();
  });
}
(async function () {
  await initI18n();
  translatePage();
  bindLocaleSwitcher();
})();

Et voilà, nous avons un sélecteur de langue fonctionnel.

application de démonstration jQuery avec sélecteur de langue | Phrase

Interpolation

La gestion des valeurs dynamiques dans nos messages de traduction est prête à l’emploi avec i18next. Nous devons simplement fournir une carte JSON data-i18n-options dans l’élément pour lequel nous avons des interpolations.

<!-- ... -->
    <p data-i18n="lead" data-i18n-options='{"username": "Jackie"}'>
      Bienvenue dans mon petit coin sur le web, {{username}} !
    </p>
<!-- ... -->

Le plug-in jQuery i18next ne cherche pas data-i18n-options par défaut ; nous devons utiliser l’option de configuration useOptionsAttr pour activer l’interpolation automatique.

async function initI18n() {
  await i18next.use(i18nextHttpBackend).init({ lng: "en" });
  jqueryI18next.init(i18next, $, { useOptionsAttr: true });
}
// ...

Bien sûr, nous voulons nous assurer que nous avons les {{variable}} placeholders que i18next attend dans nos messages de traduction.

{
  "app-title": "Avec jQuery + i18next",
  Accueil : Accueil,
  À propos : À propos,
  "lead": "Introduction" "Bienvenue dans mon petit coin sur le web, {{username}} !"
}
{
  "app-title": "مع جي كويري و آي إيتين نيكست",
  "home": "Accueil",
  "about": "À propos",
  "lead": "أهلاً بك في مكاني الصغير على النت يا {{username}}."
}

Et voilà. Interpolations interpolées.

Paragraphe d'introduction avec interpolation | Phrase
Paragraphe d'introduction en arabe avec interpolation | Phrase

Pluriels

Nous utilisons le même attribut data-i18n-options<1>} pour fournir une variable spéciale count<2>} lorsque nous avons des messages au pluriel.

🗒 Note » Consultez la section i18next ➞ Pluriels ci-dessus pour plus de détails sur le fonctionnement de la bibliothèque avec le pluriel.

<!-- ... -->
    <p data-i18n="new-messages" data-i18n-options='{"count": 3}'>
      Vous avez {{count}} nouveaux messages
    </p>
<!-- ... -->

Nous utilisons une convention key_form lors de la spécification de nos messages au pluriel. L'anglais a deux formes plurielles : one et other.

{
  // ...
  "new-messages_one": "You have {{count}} new message",
  "new-messages_other": "Vous avez {{count}} nouveaux messages"
}

L'arabe a six formes plurielles, et elles sont gérées automatiquement par i18next.

{
  // ...
  "new-messages_zero": "لا توجد لديك رسائل جديدة",
  "new-messages_one": "لديك رسالة جديدة",
  "new-messages_two": "لديك رسالتان جداد",
  "new-messages_few": "لديك {{count}} رسائل جديدة",
  "new-messages_many": "لديك {{count}} رسالة جديدة",
  "new-messages_other": "لديك {{count}} رسالة جديدة"
}

Ainsi, sans code supplémentaire, notre application gère la pluralisation complexe.

Application de démonstration jQuery en anglais avec paramètres régionaux et pluralisation | Phrase
Application de démonstration jQuery en anglais avec paramètres régionaux et pluralisation | Phrase

🔗 Ressource » Obtenez le code complet fonctionnel de l'application ci-dessus depuis notre référentiel GitHub.

🔗 Ressource » Si vous cherchez une alternative à i18next, consultez le guide avancé de jQuery i18n qui utilise la bibliothèque jQuery.i18n.

Comment localiser une page web avec le format ICU en utilisant Globalize ?

Si vous souhaitez une couverture de localisation presque exhaustive des Composants Internationaux pour Unicode (ICU) et du Référentiel de données de paramètres régionaux communs (CLDR) dans votre application JavaScript, la bibliothèque Globalize répondra certainement à vos besoins. Passons en revue comment installer Globalize et localiser notre humble application de démonstration.

🔗 Ressource » Le guide manquant sur le format de message ICU couvre ce que sont l'ICU et le CLDR plus en détail.

🗒 Note » Contrairement à de nombreuses autres bibliothèques que nous couvrons dans cet article, Globalize prend en charge Internet Explorer 9+.

Quelle démonstration ? dites-vous. Notre fidèle page unique, bien sûr.

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <ul class="navbar-list navbar-start">
          <li class="navbar-item">
            <a href="#" data-i18n-key="home" class="navbar-link">Accueil
              Accueil
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n-key="about" class="navbar-link">
              À propos
            </a>
          </li>
        </ul>
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">Anglais</option>
            <option value="ar">arabe (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">Avec Globalize</h1>
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Stella"}'>
      Bienvenue dans mon petit coin sur le web, {username} !
    </p>
    <p data-i18n-key="new-messages" data-i18n-opt='{"count": 100}'>
      Vous avez # nouveaux messages
    </p>
  </div>
</body>
</html>
Application de démonstration Globalize | Phrase

Commençons la localisation.

Installation

Globalize est très modulaire, donc il peut être un peu encombrant à installer avec du JavaScript pur. Cependant, c'est une recette relativement simple.

Cela devrait nous donner trois fichiers ZIP. Décompressons-les, renommons leurs répertoires de premier niveau décompressés, et déplaçons-les dans notre répertoire de projet. Je les ai placés sous un répertoire /lib dans mon projet, donc mon projet ressemble maintenant à :

.
├── css/
├── img/
├── lib/
│   ├── cldr/          << renommé depuis cldr-x.x.x
│   ├── cldr-json/     << renommé depuis cldr-x.x.x-json-full
│   └── globalize/     << renommé depuis globalize.x.x
└── index.html

🔗 Ressource » La documentation officielle passe en revue différentes manières d'installer Globalize.

Utiliser l'outil des exigences pour déterminer les scripts requis

Nous avons toujours besoin de certaines parties de Globalize et cldr.js ; d'autres dépendront des fonctionnalités de localisation de notre application. Un outil pratique, So What’cha Want, peut nous indiquer quels fichiers intégrer dans notre projet en fonction des fonctionnalités que nous avons sélectionnées. Nous pouvons commencer par désélectionner toutes les fonctionnalités sauf message.

So What’cha Want Globalize Capture d'écran | Phrase

Notez les deux listes de fichiers en bas de la page. La liste à gauche indique quels fichiers importer de Globalize et cldr.js ; nous pouvons utiliser les balises <script> pour cela.

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
  </div>
  <!-- Exigences de Globalize -->
  <script src="./lib/cldr/dist/cldr.js"></script>
  <script src="./lib/cldr/dist/cldr/event.js"></script>
  <script src="./lib/cldr/dist/cldr/supplemental.js"></script>
  <script src="./lib/globalize/dist/globalize.js"></script>
  <script src="./lib/globalize/dist/globalize/message.js"></script>
  <!-- Notre point d’entrée de l’application -->
  <script src="./index.js"></script>
</body>
</html>

La liste en bas à droite de So What'cha Want contient des données JSON CLDR dont nous avons besoin. Ce JSON, nous souhaitons le récupérer dans notre code et le transmettre à Globalize via sa fonction load().

// Nous ajouterons davantage à cette liste au fur et à mesure
const supplementals = ["likelySubtags"];
async function loadIntoGlobalize(featureUrls) {
  await Promise.all(
    featureUrls.map((url) => fetchJson(url))
  ).then((downloaded) =>
    downloaded.forEach((feature) => Globalize.load(feature))
  );
}
function supplementalUrlsFor(options) {
  return options.map(
    (feature) =>
      `/lib/cldr-json/cldr-core/supplemental/${feature}.json`
  );
}
async function fetchJson(url) {
  const response = await fetch(url);
  return await response.json();
}
(async function () {
  // Charger les exigences supplémentaires
  await loadIntoGlobalize(
    supplementalUrlsFor(supplementals)
  );
})();

C'est à peu près tout pour la configuration. OK, ce n'est pas la bibliothèque la plus facile à installer, mais pour un projet qui nécessite une localisation à toute épreuve, cela en vaut la peine.

Traductions de base

Utiliser Globalize pour traduire les éléments de la page est un processus simple en trois étapes.

// ...
(async function () {
  await loadIntoGlobalize(
    supplementalUrlsFor(supplementals)
  );
  // 1. Charger les messages de traduction
  Globalize.loadMessages({
    en: {
      "app-title": "Bonjour Globalize!",
    },
    ar : {
      "app-title": "أهلاً جلوبالايز",
    },
  });
  // 2. Définir les paramètres régionaux par défaut
  Globalize.locale("en");
  // 3. Utilisez formatMessage() pour obtenir la traduction par clé
  document.querySelector(
    "[data-i18n-key='app-title']"
  ).textContent = Globalize.formatMessage("app-title");
})();

🔗 Ressource » La documentation officielle de Globalize a une section API pratique qui présente la liste des fonctions disponibles.

Disco. Notre titre est affiché en utilisant les traductions de notre paramètre régional par défaut.

Application de démonstration Globalize version anglaise | Phrase

Si nous modifions l'appel ci-dessus, de sorte qu'il se lise Globalize.locale("ar"), nous obtenons notre titre d'application en arabe.

Application de démonstration Globalize traduction en arabe | Phrase

Gestion des erreurs de messages manquants

Généralisons le code qui traduit les éléments de la page en écrivant la fonction translatePageElements(). Contrairement à d'autres bibliothèques i18n, Globalize lancera une erreur et s'arrêtera s'il rencontre un message manquant pour une clé donnée à formatMessage(). Cependant, un simple try/catch suffit à atténuer le problème.

// ...
function translatePageElements() {
  const elements = document.querySelectorAll(
    "[data-i18n-key]"
  );
  elements.forEach((element) => {
    const key = element.getAttribute("data-i18n-key");
    try {
      element.innerHTML = Globalize.formatMessage(key);
    } catch (error) {
      if (error.code === "E_MISSING_MESSAGE") {
        // Afficher les avertissements de la console en cas de message manquant
        // au lieu de s'arrêter complètement.
        console.warn(error.message);
        // Afficher la valeur de la clé sur la page pour le message manquant
        element.innerHTML = key;
      } else {
        console.error(error);
      }
    }
  });
}
(async function () {
  // ...
  Globalize.loadMessages(/* ... */);
  Globalize.locale("en");
  translatePageElements();
})();

Moins "plantage sur message manquant", plus "afficher la valeur de la clé et avertir dans la console".

Application de démonstration Globalize avec avertissement dans le code | Phrase

Chargement asynchrone de fichiers de traduction

Comme nous l'avons fait avec d'autres solutions dans cet article, divisons nos messages de traduction en fichiers JSON par paramètres régionaux pour l'évolutivité et la maintenabilité.

{
  // Globalize s'attend à ce que le code de paramètres régionaux soit la clé de niveau supérieur
  "en": {
    "app-title": "Avec Globalize",
    "home": "Accueil",
    "about": "À propos"
  }
}
{
  "ar": {
    "app-title": "مع جلوبالايز",
    "home": "Accueil",
    "about": "نبذة عنا"
  }
}

Une fonction réutilisable setLocale() peut charger notre fichier de traduction, configurer Globalize et actualiser nos éléments de page.

const defaultLocale = "en";
// ...
 async function setLocale(locale) {
  const messages = await fetchJson(`/lang/${locale}.json`);
  Globalize.loadMessages(messages);
  Globalize.locale(locale);
  translatePageElements();
}
// ...
(async function () {
  await loadIntoGlobalize(
    supplementalUrlsFor(supplementals)
  );
  setLocale(defaultLocale);
})();

Chargement de fichier de traduction asynchrone : c'est fait.

Sélecteur de langue

Nous pouvons utiliser la fonction setLocale() que nous venons d'écrire pour que notre interface de changement de langue fonctionne. Vous vous souvenez peut-être que notre application de démonstration a déjà un peu de HTML pour le sélecteur .

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <!-- ... -->
        <div class="navbar-end">
          <img src="img/translation-icon@2x.png" class="translation-icon" />
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">Anglais</option>
            <option value="ar">Arabe (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <!-- ... -->
  </div>
  <!-- ... -->
  <script src="./index.js"></script>
</body>
</html>

Un peu de JavaScripting fera basculer le sélecteur.

const defaultLocale = "en";
// ...
function bindLocaleSwitcher() {
  const switcher = document.querySelector(
    "[data-i18n-switcher]"
  );
  Globalize.locale() renvoie la locale active.
  switcher.value = Globalize.locale().locale;
  switcher.onchange = (e) => {
    setLocale(e.target.value);
  };
}
(async function () {
  await loadIntoGlobalize(
    supplementalUrlsFor(supplementals)
  );
  await setLocale(defaultLocale);
  bindLocaleSwitcher(defaultLocale);
})();

Et avec cela, nos utilisateurs peuvent choisir la langue de leur choix.

Application de démonstration Globalize avec sélecteur de langue | Phrase

Affichage des noms des paramètres régionaux dans les paramètres régionaux actifs

L'une des fonctionnalités les plus puissantes de l'utilisation d'une bibliothèque ICU comme Globalize est l'accès à une variété immensément riche de données de localisation CLDR. Par exemple, nous pouvons afficher les langues dans notre sélecteur de paramètres régionaux dans le paramètre régional actif—l'anglais serait «الإنجليزية» en arabe, par exemple. Nous devrions intégrer les données CLDR principales et ensuite mettre à jour le texte de notre select > option.

const defaultLocale = "ar";
const mains = {
    localenames: ["langues"],
};
const supplementals = ["likelySubtags"];
async function setLocale(locale) {
  const messages = await fetchJson(`/lang/${locale}.json`);
  Globalize.loadMessages(messages);
  await loadIntoGlobalize(mainUrlsFor(mains, locale));
  Globalize.locale(locale);
  translatePageElements();
  setLocaleSwitcherDisplayNames();
}
// Options données = {
//   localenames: ["languages"],
//   dates: ["ca-generic", "ca-gregorian"],
// }
// et, paramètres régionaux = "fr", retourne un tableau comme
// [
//    "/lib/cldr-json/cldr-localenames-full/main/en/languages.json",
//    "/lib/cldr-json/cldr-dates-full/main/fr/ca-generic.json",
//    "/lib/cldr-json/cldr-dates-full/main/fr/ca-gregorian.json"
// ]
function mainUrlsFor(options, locale) {
  const result = [];
  Object.keys(options).forEach((key) => {
    options[key].forEach((collection) => {
      result.push(
        `/lib/cldr-json/cldr-${key}-full/main/${locale}/${collection}.json`
      );
    });
  });
  return result;
}
function setLocaleSwitcherDisplayNames() {
  const options = document.querySelectorAll(
    "[data-i18n-switcher] option"
  );
  options.forEach((option) => {
    const localeCode = option.value;
    // Obtenez les données principales CLDR par chemin
    option.textContent = Globalize.cldr.main(
      `localeDisplayNames/languages/${localeCode}`
    );
  });
}

Nous pouvons utiliser notre code principal de chargement JSON chaque fois que nous voulons utiliser les données principales JSON CLDR. Sinon, un peu de manipulation du DOM lorsqu’un nouveau paramètre régional est choisi nous donne des noms de paramètres régionaux localisés (tellement méta !).

Application de démonstration Globalize avec des noms de paramètres régionaux localisés | Phrase

🔗 Ressource » La documentation officielle de cldr.js explique en détail comment récupérer les données JSON CLDR.

Interpolation

La gestion des valeurs dynamiques dans notre message est gérée par le format message ICU via une syntaxe {variable}.

{
  "en": {
    // ...
    Responsable "Bienvenue dans mon petit coin sur le web, {username}!"
  }
}
{
  "ar": {
    
    "lead": "أهلاً بك في مكاني الصغير على النت يا {username}."
  }
}

Nous pouvons fournir des paires de substitution clé/valeur dans notre HTML.

<!-- ... -->
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Stella"}'>
      Bienvenue dans mon petit coin sur le web, {username}!
    </p>
<!-- ... -->

Et maintenant, nous devons juste lire ces paires clé/valeur et les fournir à Globalize.formatMessage().

// ...
function translatePageElements() {
  const elements = document.querySelectorAll(
    "[data-i18n-key]"
  );
  elements.forEach((element) => {
    const key = element.getAttribute("data-i18n-key");
    const interpolations =
      element.getAttribute("data-i18n-opt");
    const parsedInterpolations = interpolations
      ? JSON.parse(interpolations)
      : {};
    try {
      element.innerHTML = Globalize.formatMessage(
        key,
        parsedInterpolations
      );
    catch (error) {
      if (error.code === "E_MISSING_MESSAGE") {
        console.warn(error.message);
      } else {
        console.error(error);
      }
    }
  });
}
// ...

Pas de problème.

Application de démonstration Globalize avec interpolation dans le paramètre linguistique anglais | Phrase
Application de démonstration Globalize avec interpolation dans les paramètres régionaux arabes | Phrase

Pluriels

La gestion des pluriels dans le format ICU est sans égal et couvre des formes plurielles complexes, comme celles en russe ou en arabe. Nous devrons configurer la fonctionnalité des pluriels avant de pouvoir l'utiliser.

Ajout des exigences relatives au pluriel

Allons sur Alors, qu'est-ce que vous voulez pour déterminer ce dont nous aurons besoin si nous devons activer pluriel.

Alors, qu'est-ce que vous souhaitez pour la capture d'écran Globalize | Phrase

Pas trop mala: d'abord, nous devrons ajouter une balise <script> pour la fonctionnalité.

<!DOCTYPE html>
<html lang="fr">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
  </div>
  <script src="./lib/cldr/dist/cldr.js"></script>
  <script src="./lib/cldr/dist/cldr/event.js"></script>
  <script src="./lib/cldr/dist/cldr/supplemental.js"></script>
  <script src="./lib/globalize/dist/globalize.js"></script>
  <script src="./lib/globalize/dist/globalize/message.js"></script>
  <script src="./lib/globalize/dist/globalize/plural.js"></script>
  <script src="./index.js"></script>
</body>
</html>

Nous aurons également besoin du JSON supplémentaire pluriels, et probablement du ordinaux si nous voulons couvrir le format comme « 3e » et « 4e » dans nos messages. Nous devrons simplement ajouter les exigences à notre tableau de configuration supplementals. Notre application est déjà configurée pour charger le JSON correspondant pour nous au démarrage.

const defaultLocale = "ar";
const supplementals = [
  "sous-étiquettes probables",
  « pluriels »,
  « ordinaux »,
];
// ...

Maintenant, nous pouvons ajouter nos messages pluriels. La syntaxe plurielle ICU est largement intuitive : une variable count détermine la forme plurielle choisie, et un symbole spécial # est remplacé par la valeur count lors de l'affichage.

{
  "en": {
    // ...
    "nouveaux-messages": [
      // Nous pouvons appeler `count` comme nous le voulons, tant que nous...
      // correspondance à la clé dans notre appel à formatMessage()
      "Vous avez {count, plural,
          one {# nouveau message}
        other {# nouveaux messages}
      }
    ]
  }
}

🗒 Note » Globalize nous permet de diviser les messages multilignes en utilisant des tableaux.

Les six formes plurielles en arabe sont gérées par le format ICU.

{
  "ar": {
    // ...
    "nouveaux-messages": [
      "{count, plural, ",
         zero {لا توجد لديك رسائل جديدة}
          one {لديك رسالة جديدة}
          two {لديك رسالتان جداد}
          few {لديك # رسائل جديدة}
         many {لديك # رسالة جديدة}
        other {لديك # رسالة جديدة}
      }
    ]
  }
}

🤿 Allez plus loin » Nous couvrons les pluriels ICU plus en détail dans Le Guide Manquant sur le format de message ICU.

Bien sûr, nous devons nous assurer que count est fourni à Globalize.formatMessage().

<!-- ... -->
    <p data-i18n-key="new-messages" data-i18n-opt='{"count": 110}'>
      Vous avez # nouveaux messages
    </p>
<!-- ... -->

Et c'est à peu près tout pour les pluriels.

Application de démonstration Globalize avec pluralisation en anglais | Phrase
Application de démonstration Globalize avec pluralisation en arabe | Phrase
🔗 Ressource » Obtenez tout le code de démonstration Globalize de notre dépôt GitHub.

🗒 Note » L'implémentation ICU de Globalize couvre le formatage complet des dates et des nombres. Jetez un œil à la documentation officielle pour plus d'informations.

🔗 Ressource » Nous couvrons plus d'options d'installation et de cas d'utilisation de Globalize dans JS I18n avec Globalize.js.

Pour conclure notre guide de localisation JavaScript

Et c'est à peu près tout pour celui-ci. Nous espérons que vous avez apprécié cette incursion dans certaines des solutions de localisation JavaScript les plus populaires.

🔗 Ressource » Si vous travaillez avec Ruby on Rails, vous pourriez apprécier La localisation de JavaScript dans les applications Rails.

Et si vous souhaitez faire passer votre processus de localisation au niveau supérieur, jetez un œil à Phrase. Phrase prend en charge le format ICU, tous les autres formats de traduction que nous avons couverts ici et bien d'autres. Avec son CLI et la synchronisation avec Bitbucket, GitHub et GitLab, votre i18n peut être en pilote automatique. La console web complète de Phrase, avec apprentissage automatique et suggestions intelligentes, est un plaisir à utiliser pour les traducteurs. Une fois les traductions prêtes, elles peuvent se synchroniser automatiquement avec votre projet. Vous le configurez et l'oubliez, ce qui vous permet de vous concentrer sur le code que vous aimez.

Découvrez toutes les fonctionnalités de Phrase pour les développeurs et voyez par vous-même comment cela peut rationaliser vos flux de travaux de localisation de logiciels.

Posts associés

Software localization blog category featured image | Phrase

Blog post

Traduction over-the-air Flutter avec Phrase

Arrêtez d'attendre l'approbation du magasin d'applications mobiles et obtenez du contenu frais pour vos utilisateurs d'applications Flutter instantanément à travers le monde—avec Phrase over-the-air.

Software localization blog category featured image | Phrase

Blog post

Le guide ultime de la localisation Flutter

Décodons les secrets de la localisation Flutter afin que vous puissiez parler la langue de vos utilisateurs et continuer à coder votre chemin vers la domination mondiale.

Software localization blog category featured image | Phrase

Blog post

Comment utiliser le plug-in de localisation de Phrase Strings pour WordPress

Apprenez à traduire les pages WordPress ou encore des articles dans plusieurs langues avec l'intégration Phrase Strings pour WordPress.

Software localization blog category featured image | Phrase

Blog post

Détection des paramètres régionaux d’un utilisateur dans une application web

L'un des problèmes les plus courants dans le développement d'applications web est la détection des paramètres régionaux d'un utilisateur. Voici comment le faire correctement.

Software localization blog category featured image | Phrase

Blog post

Traduction de l’application iOS over-the-air avec des chaînes Phrase

Si vous voulez vous assurer que tous vos utilisateurs d'application obtiennent le bon texte, la fonctionnalité over-the-air de Phrase peut vous aider à publier vos mises à jour de traduction instantanément.