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, en maturation, plutôt expressif, 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.
Avec de nombreux frameworks mobiles multiplateformes, wrappers de bureau, moteurs de jeu, et même des frameworks pour l’internet des objets (IoT), le monde appartient à JavaScript (nous ne faisons qu’y vivre).
Si vous êtes ici, c’est parce que vous voulez canaliser toute cette énergie 🔥 et apprendre à localiser les applications JavaScript afin de les rendre accessibles à un public mondial. Bonne nouvelle : ce guide couvre tout ce que vous devez savoir pour commencer la localisation JavaScript côté navigateur.
Ça va être rock && roll !

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

🗒 Remarque » Internet Explorer (IE), avec une part de marché mondiale de 2,15 %, peut être considéré comme un navigateur vieillissant. Pour des raisons de brièveté, nous ne détaillerons pas ici les solutions spécifiques à IE. 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 s’avérer être un bon choix pour votre projet), vous constaterez que JavaScript peut très bien faire l’affaire pour les projets de taille modeste. Développer la vôtre vous permettra de compiler des techniques d’internationalisation utilisables avec n’importe quelle bibliothèque que vous choisissez.

🤿 Pour approfondir » Notre article (en anglais), What Is I18n: A Simple Definition of Internationalization décrit de manière détaillée les concepts d’internationalisation (i18n) et de localisation (l10n).

✋🏽 Avertissement » Si vous concevez une application MPA (application multi-pages) traditionnelle, il arrive souvent qu’une grande partie de la localisation se fasse sur le serveur lui-même. Ici, nous nous concentrons exclusivement sur la localisation côté navigateur. Côté serveur, nous vous renvoyons à notre tutoriel consacré à l’internationalisation Node (en anglais) et à notre guide complet dédié à l’internationalisation JavaScript (en anglais).

Imaginons que vous avez une page que vous souhaitez localiser.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <!-- ... -->
  <title>My Appy Apperson</title>
</head>
<body>
  <div class="container">
    <h1>My Appy Apperson</h1>
    <p>Welcome to my little spot on the interwebs!</p>
  </div>
  <script src="js/scripts.js"></script>
</body>
</html>

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

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

Tout a l’air en ordre, mais l’application n’est pas prête pour le monde entier : tout le contenu est codé en dur en anglais. Nous allons y remédier en l’internationalisant un peu.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <!-- ... -->
  <title>My Appy Apperson</title>
</head>
<body>
  <div class="container">
    <h1 data-i18n-key="app-title">My Appy Apperson</h1>
    <p data-i18n-key="lead">Welcome to my little spot on the interwebs!</p>
  </div>
  <script src="js/scripts.js"></script>
</body>
</html>

Notez les attributs data-i18n-key que nous avons ajoutés aux conteneurs de texte ci-dessus. Nous pouvons les utiliser lorsque le document se charge et remplacer leur texte par des traductions. C’est ce que nous allons faire tout de suite.

// The active locale
const locale = "en";
// We can have as many locales here as we want,
// and use any locales we want. We have English
// and Arabic as locales here as examples.
const translations = {
  // English translations
  "en": {
    "app-title": "My Appy Apperson",
    "lead": "Welcome to my little spot on the interwebs!",
  },
  // Arabic translations
  "ar": {
    "app-title": "تطبيقي المطبق",
    "lead": "أهلاً بك في مكاني الصغير على النت.",
  },
};
// When the page content is ready...
document.addEventListener("DOMContentLoaded", () => {
  document
    // Find all elements that have the key attribute
    .querySelectorAll("[data-i18n-key]")
    .forEach(translateElement);
});
// Replace the inner text of the given HTML element
// with the translation in the active locale,
// corresponding to the element's data-i18n-key
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const translation = translations[locale][key];
  element.innerText = translation;
}

Maintenant, nous allons modifier la deuxième ligne ci-dessus en const locale = "ar"; et recharger la page. Lorsque l’événement DOMContentLoaded est déclenché, notre page affiche nos traductions en arabe.
Version arabe d'une application de démonstration en JavaScript | Phrase

🗒 Remarque » "en" et "ar" correspondent aux codes ISO 639-1 pour l’anglais et l’arabe, respectivement. On utilise généralement les codes ISO pour les langues et les pays lors de la localisation.

Chargement asynchrone des traductions

Notre solution d’internationalisation commence bien, mais pour le moment l’ajout de paramètres régionaux et de traductions n’est pas évolutif. À mesure que notre application s’enrichit, nous allons probablement vouloir séparer nos traductions dans des fichiers distincts pour chaque paramètre régional. Le fichier de traduction correspondant au paramètre régional actif peut alors être chargé sans qu’il soit nécessaire de charger les autres paramètres régionaux. C’est tout à fait faisable sans trop d’efforts.
Tout d’abord, nous allons déplacer les traductions de notre script principal vers des fichiers JSON, un pour chaque paramètre régional pris en charge.

{
  "app-title": "My Appy Apperson",
  "lead": "Welcome to my little spot on the interwebs!"
}
{
  "app-title": "تطبيقي المطبق",
  "lead": "أهلاً بك في مكاني الصغير على النت."
}

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

// The locale our app first shows
const defaultLocale = "en";
// The active locale
let locale;
// Gets filled with active locale translations
let translations = {};
// When the page content is ready...
document.addEventListener("DOMContentLoaded", () => {
  // Translate the page to the default locale
  setLocale(defaultLocale);
});
// Load translations for the given locale and translate
// the page to this locale
async function setLocale(newLocale) {
  if (newLocale === locale) return;
  const newTranslations =
    await fetchTranslationsFor(newLocale);
  locale = newLocale;
  translations = newTranslations;
  translatePage();
}
// Retrieve translations JSON object for the given
// locale over the network
async function fetchTranslationsFor(newLocale) {
  const response = await fetch(`/lang/${newLocale}.json`);
  return await response.json();
}
// Replace the inner text of each element that has a
// data-i18n-key attribute with the translation corresponding
// to its data-i18n-key
function translatePage() {
  document
    .querySelectorAll("[data-i18n-key]")
    .forEach(translateElement);
}
// Replace the inner text of the given HTML element
// with the translation in the active locale,
// corresponding to the element's data-i18n-key
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const translation = translations[key];
  element.innerText = translation;
}

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

🗒 Remarque » Nous utilisons l’API Fetch, intégrée aux navigateurs modernes, pour récupérer nos fichiers JSON via le réseau.

Création d’un sélecteur de paramètre régional

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

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
  <title>My Appy Apperson</title>
</head>
<body>
  <div class="container">
    <nav class="navbar">
      <div class="container">
        <ul class="navbar-list navbar-left">
          <!-- Nav links -->
        </ul>
        <div class="navbar-right">
          <!-- ... -->
          <select data-i18n-switcher class="locale-switcher">
            <option value="en">English</option>
            <option value="ar">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">My Appy Apperson</h1>
    <p data-i18n-key="lead">Welcome to my little spot on the interwebs!</p>
  </div>
  <script src="js/scripts.js"></script>
</body>
</html>

Un simple <select> suffit ici. Nous pouvons utiliser un attribut data-i18n-switcher pour que notre JavaScript puisse s’y connecter et charger les paramètres régionaux sélectionnés par l’utilisateur.

const defaultLocale = "en";
let locale;
// ...
// When the page content is ready...
document.addEventListener("DOMContentLoaded", () => {
  setLocale(defaultLocale);
  bindLocaleSwitcher(defaultLocale);
});
// ...
// Whenever the user selects a new locale, we
// load the locale's translations and update
// the page
function bindLocaleSwitcher(initialValue) {
  const switcher =
    document.querySelector("[data-i18n-switcher]");
  switcher.value = initialValue;
  switcher.onchange = (e) => {
    // Set the locale to the selected option[value]
    setLocale(e.target.value);
  };
}

L’événement onchange permet de mettre à jour les traductions de notre page selon la valeur <option> sélectionnée. Et voilà. Les visiteurs de notre site peuvent maintenant sélectionner leurs propres paramètres régionaux.
Application de démonstration avec gestionnaire d'événement onchange | Phrase

📣 Remerciements » Un grand merci à 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 navigator.language.
La liste navigator.languages, autre grand classique (bien qu’expérimentale au moment où j’écris ces lignes), 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étecter les paramètres régionaux du navigateur.

/**
 * Retrieve user-preferred locales from the browser
 *
 * @param {boolean} languageCodeOnly - when true, returns
 * ["en", "fr"] instead of ["en-US", "fr-FR"]
 * @returns array | undefined
 */
function browserLocales(languageCodeOnly = false) {
  return navigator.languages.map((locale) =>
    languageCodeOnly ? locale.split("-")[0] : locale,
  );
}

Maintenant, imaginons que l’utilisateur ait 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 pour la première fois notre page.

// The locale our app first shows
const defaultLocale = "en";
const supportedLocales = ["en", "ar"];
// ...
document.addEventListener("DOMContentLoaded", () => {
  const initialLocale =
    supportedOrDefault(browserLocales(true));
  setLocale(initialLocale);
  bindLocaleSwitcher(initialLocale);
});
// ...
function isSupported(locale) {
  return supportedLocales.indexOf(locale) > -1;
}
// Retrieve the first locale we support from the given
// array, or return our default locale
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 vers les premiers paramètres régionaux de la liste des préférences de l’utilisateur, avec une solution de secours élégante.

🤿 Pour approfondir » Nous expliquons en détails comment détecter les paramètres régionaux à la fois sur le navigateur et le serveur dans l’article (en anglais) Detecting browser language preference with JavaScript.

Gestion de la direction : les langues s’écrivant de droite à gauche et 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 pour nous, le gros du travail est effectué par le navigateur ; nous avons juste à définir l’attribut <html dir> dans nos pages.

// ...
// Load translations for the given locale and translate
// the page to this locale
async function setLocale(newLocale) {
  if (newLocale === locale) return;
  const newTranslations = await fetchTranslationsFor(
    newLocale,
  );
  locale = newLocale;
  translations = newTranslations;
  // Set <html dir> attribute
  document.documentElement.dir = dir(newLocale);
  // Not necessary for direction flow, but for good measure...
  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 fonction dir() très simple et nous définissons l’attribut chaque fois que nous changeons de paramètres régionaux.

Attributs HTML des outils de développement du navigateur | Phrase
Si nous ouvrons nos outils de développement du navigateur, nous pouvons voir les attributs <html> 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.

🤿 Pour approfondir » Lisez-en plus sur le CSS localisé dans l’article (en anglais) How Do I Use a CSS File for Site Localization?

Grâce à notre nouveau code, nous obtenons une page en arabe qui aurait pu être lue par Avicenne !
Application de démonstration avec le texte en 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.

// In our HTML page
<h1 data-i18n-key="app-title">My Appy Apperson</h1>
// In our JavaScript
function translateElement(element) {
  const key = element.getAttribute("data-i18n-key");
  const translation = translations[key];
  element.innerText = translation;
}
// Given that we've loaded Arabic translations from ar.json:
translations = {
  "app-title": "تطبيقي المطبق",
};
translateElement(document.querySelector("[data-i18n-key='lead']"));
// renders to:
<h1 data-i18n-key="app-title">تطبيقي المطبق</h1>

That’s our translation system in a nutshell.

Interpolation

What happens when we have values that change at runtime and need to be injected into our messages? A common example is the name of the currently logged in user. We’ll have to update our translation system to handle cases like these.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
    <h1 data-i18n-key="app-title">My Appy Apperson</h1>
    <p
      data-i18n-key="lead"
      data-i18n-opt='{"username": "Swoodesh"}'
    >
      Welcome to my little spot on the interwebs, {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.

{
  "lead": "Welcome to my little spot on the interwebs, {username}!",
}
{
  "lead": "أهلاً بك في مكاني الصغير على النت يا {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;
}
// Convert a message like "Hello, {name}" to "Hello, Chad"
// given the interpolations object {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.

Traduction dynamique après chargement de la page

Nous allons 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[key], interpolations);
}
// ...

Nous avons simplement extrait le code qui gère la récupération d’un message dans les paramètres régionaux actifs, 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. Supposons que nous voulions mettre à jour notre texte principal après que l’utilisateur se connecte. Ce n’est pas bien compliqué.

const element =
  document.querySelector("[data-i18n-key='lead']");
// Our new function is serving us well here
element.innerText =
  translate("lead", { username: "Maggie" });
// Store the updated interpolations in the document
// in case the element is re-rendered in the future
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.

Formes plurielles

Non seulement nous devons souvent présenter différents messages en fonction d’un compteur(exemple : « 1 abonné » ou « 20 000 abonnés« ), sachant queles langues n’ont pas toutes les mêmes règles de pluriel. L’anglais a deux formes plurielles (one et other) alors que l’arabe, par exemple, en a six. Par le passé, cela signifiait qu’il n’était pas simple de mettre en œuvre la prise en charge des formes plurielles pour les applications front-end. Heureusement, aujourd’hui l’objet Intl.PluralRules permet de traiter rapidement les pluriels.
Supposons que nous soyons des rédacteurs prolifiques et que nous voulions faire savoir au monde combien d’articles nous avons rédigés.

<p
  data-i18n-key="article-plural"
  data-i18n-opt='{"count": 122}'
>
  {count} articles written and counting.
</p>

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

{
  // English has two plural forms
  "article-plural": {
    "one": "{count} article and counting",
    "other": "{count} articles and counting"
  }
}
{
  // Arabic has six plural forms
  "article-plural": {
    "zero": "لا توجد مقالات",
    "one": "مقال {count}",
    "two": "مقالان",
    "few": "{count} مقالات",
    "many": "{count} مقال",
    "other": "{count} مقال"
  }
}

Maintenant, nous allons mettre à 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);
}
// ...
/*
  Given a forms object like
  {
    "zero": "No articles",
    "one": "One article",
    "other": "{count} articles"
  } and a count of 3, returns "3 articles"
*/
function pluralFormFor(forms, count) {
  const matchingForm = new Intl.PluralRules(locale).select(count);
  return forms[matchingForm];
}
// ...

La petite dose de magie ici, c’est le morceau de code qui lit new Intl.PluralRules(locale).select(...). L’objet intégré Intl.PluralRules, lorsqu’on lui fournit des paramètres régionaux, en connaît les règles relatives aux formes plurielles. Par exemple, en passant "ar" au constructeur, puis en appelant select(5) sur l’objet retourné, cela renvoie la forme correcte "few".
Avec seulement quelques lignes de code, nous avons entièrement globalisé la prise en charge des formes plurielles 🙌

Formes plurielles 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) (on notera les virgules) et « ٣٠٠٬٠٠٠٫٠٠ € » en arabe (Égypte). Comment gérer tous ces formats ? Pas de soucis ! Nous pouvons compter sur un autre objet Intl qui fait partie de la norme JavaScript moderne. Intl.NumberFormat va faire le travail pour nous !
Imaginons que nous soyons des entrepreneurs débutants et que nous voulions créer un site web de suivi NFT avec des statistiques numériques.

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

data-i18n-opt identifie la valeur numérique de {price} dans nos fichiers de paramètres régionaux. Nous allons rechercher cette clé number lorsque nous allons mettre à jour notre code d’interpolation dans un instant. Tout d’abord, nous allons fournir les traductions de nos messages.

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

Nous devons mettre à 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,
  );
}
/*
  Given a value object like
  {
    "number" : 300000,
    "style": "currency",
    "currency": "EUR"
  } and that the active locale is "en", returns "€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 formateur de nombre, qui utilise l’objet intégré Intl.NumberFormat. 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 arabe | Phrase

✋🏽 Avertissement » Il est préférable de passer des paramètres régionaux entièrement qualifiés, 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 pourra utiliser par défaut "en-US", tandis qu’un autre choisira "en-UK". Alors nous utilisons une carte fullyQualifiedLocaleDefaults dans notre fonction formatNumber() pour obtenir un formatage cohérent d’un navigateur à l’autre.

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 (Official) NFT: {price}
</p>

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

🔗 Ressources » Découvrez notre article (en anglais) Concise Guide to Number Localization.

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. L’objet intégré Intl.DateTimeFormat permet de gérer l’essentiel du travail en ce qui concerne le formatage des dates.
Imaginons que nous voulions indiquer 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"
  }}'
>
  Published on {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": "Published {publishDate}"
}
{
  //...
  "publish-date": "نشر {publishDate}"
}

Maintenant, nous allons mettre à 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(
    (interpolated, key) => {
      const value = formatDate(
        formatNumber(interpolations[key]),
      );
      return interpolated.replace(
        new RegExp(`{\s*${key}\s*}`, "g"),
        value,
      );
    },
    message,
  );
}
// ...
/*
  Given a value object like
  {
    "date": "2021-12-05 15:29:00",
    "dateStyle": "long",
    "timeStyle": "short"
  } and that the current locale is en,
  returns "December 5, 2021 at 3:29 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 formateur de nombres, nous faisons un autre passage à travers notre nouveau formateur 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.

✋🏽 Avertissement » 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.

Voilà, 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"
  }}'
>
  Published on {publishDate}
</p>

Application de démonstration avec formatage de date américain | Phrase
Application de démonstration avec formatage de date arabe | Phrase

🤿 Pour approfondir » Si vous recherchez des fonctionnalités de formatage de dates plus robustes, consultez l’article (en anglais) What Is the Best JavaScript Date and Time Library? L’article (en anglais) Human-friendly Way to Display Dates in TypeScript/JavaScript vous permet d’obtenir un format du type « il y a 1 heure ».

🔗 Ressources » Si vous utilisez un framework déclaratif comme React, vous pouvez aller plus loin avec ce que nous avons construit dans cette section et l’article (en anglais) Roll Your Own JavaScript i18n Library with TypeScript.

Quelles bibliothèques d’internationalisation JavaScript est-il recommandé d’utiliser ?

Nous avons expliqué ci-dessus comment créer votre propre bibliothèque i18n JavaScript. Cependant, il peut s’avérer plus judicieux pour votre projet d’adopter une bibliothèque d’internationalisation prête à l’emploi. Les options ne manquent pas. Dans cet article, nous allons passer en revue l’utilisation des bibliothèques Polyglot, i18next et Globalize.
Pour une sélection encore plus large, ces articles peuvent vous être utiles :

🗒 Remarque » 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 d’internationalisation 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 formes plurielles. Cependant, comme nous l’avons mentionné précédemment, constructeur Intl.PluralRules est désormais pris en charge par tous les navigateurs modernes et permet de résoudre facilement 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 auraient encore besoin d’un guide pour Polyglot.

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

Sans plus attendre, nous allons localiser notre application de démonstration avec la bibliothèque de localisation d’Airbnb :

<!DOCTYPE html>
<html lang="en">
<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">
              Home
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n-key="about" class="navbar-link">
              About
            </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">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">With Polyglot</h1>
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Cadence"}'>
      Welcome to my little spot on the interwebs, %{username}!
    </p>
    <p
      data-i18n-key="article-plural"
      data-i18n-opt='{"smart_count": 2}'
    >
      %{smart_count} articles written and counting.
    </p>
  </div>
</body>
</html>

Application de démonstration JavaScript avec Polyglot | Phrase
Passons maintenant à la localisation avec Polyglot.

Installation

Polyglot a quelques dépendances NPM, donc il a besoin que Node soit installé localement. Node étant installé, 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

Maintenant, nous allons 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 le 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"
  }
}

🗒 Remarque : 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>

Ceci étant fait, 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 comment procéder :

// 1. Import the library
import Polyglot from "node-polyglot";
// 2. Create an instance
const polyglot = new Polyglot();
// 3. Add translation messages for the active locale
polyglot.extend({
  "app-title": "With Polyglot",
});
// 4. Use the messages to translate page elements
const element = document.querySelector(
  "[data-i18n-key='app-title']",
);
// polyglot.t() resolves a translation message given
// a key
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.

✋🏽 Avertissement » 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.

{
  "app-title": "With Polyglot",
  "home": "Home",
  "about": "About"
}
{
  "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();
// Load translation messages from the network
async function loadTranslations(locale) {
  return await fetch(`/lang/${locale}.json`).then(
    (response) => response.json(),
  );
}
// Translate all elements on the page that have our custom
// data-i18n-key attribute
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();
})();

Nous obtenons ainsi le rendu suivant dans le navigateur.
Application de démonstration en anglais avec Polyglot, éléments de menu et titre principal manquants | Phrase
Nos éléments de menu de navigation et le titre principal sont traduits dans nos paramètres régionaux 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 en arabe avec Polyglot, éléments de menu et titre principal manquants | Phrase

Sélecteur de langue

Notre application est maintenant plus évolutive puisque seules les traductions des paramètres régionaux actifs sont chargées. Nous allons maintenant construire un sélecteur de langue. Nous avons déjà le HTML pour le sélecteur dans notre application :

<!DOCTYPE html>
<html lang="en">
<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">English</option>
            <option value="ar">Arabic (العربية)</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();
// ...
// Load translations for the given locale and translate
// page elements for this locale
async function loadAndTranslate(locale) {
  const translations = await loadTranslations(locale);
  polyglot.replace(translations);
  translatePage();
}
// Whenever the user switches the active locale, load
// this locale's messages into the page
function bindLocaleSwitcher(initialValue) {
  const switcher = document.querySelector(
    "[data-i18n-switcher]",
  );
  switcher.value = initialValue;
  switcher.onchange = (e) => {
    loadAndTranslate(e.target.value);
  };
}
// 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.

✋🏽 Avertissement » 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.

🗒 Remarque » 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"}'>
      Welcome to my little spot on the interwebs, %{username}!
    </p>
<!-- ... -->
{
  // ...
  "lead": "Welcome to my little spot on the interwebs, %{username}!"
}
{
  // ...
  "lead": "أهلاً بك في مكاني الصغير على النت يا %{username}."
}

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

// ...
// Translate all elements on the page that have a
// data-i18n-key attribute
function translatePage() {
  const translatableElements = document.querySelectorAll(
    "[data-i18n-key]",
  );
  translatableElements.forEach((el) => {
    const key = el.getAttribute("data-i18n-key");
    // Extract interpolation key/values from the HTML and
    // parse them to JSON
    const interpolations = el.getAttribute("data-i18n-opt");
    const parsedInterpolations = interpolations
      ? JSON.parse(interpolations)
      : {};
    // Pass the parsed interpolations to polyglot.t(),
    // which automatically handles substitutions
    el.innerHTML = polyglot.t(key, parsedInterpolations);
  });
}
// ...

Grâce au code ci-dessus, nous avons maintenant notre paragraphe d’introduction interpolé dans les paramètres régionaux actifs.
Application de démonstration des paramètres régionaux anglais avec Polyglot avec paragraphe d'introduction correct | Phrase
Application de démonstration des paramètres régionaux arabes avec Polyglot avec paragraphe d'introduction correct | Phrase

Formes plurielles

Imaginons que nous voulions indiquer à l’utilisateur actuellement connecté combien de messages il ou elle a reçus : Par exemple, « Vous avez 1 nouveau message » ou « Vous avez 12 nouveaux messages ». Polyglot gère bien les pluriels de cette manière. Nous avons 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}'
    >
      You have %{smart_count} new 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": "You have %{smart_count} new message |||| You have %{smart_count} new messages"
}

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

{
  // ...
  "new-messages": "لا توجد لديك رسائل جديدة |||| لديك رسالة جديدة |||| لديك رسالتان جداد |||| لديك %{smart_count} رسائل جديدة |||| لديك %{smart_count} رسالة جديدة |||| لديك %{smart_count} رسالة جديدة"
}

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

À noter : par défaut, Polyglot n’est pas conscient 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.

// ...
// Load translations for the given locale and translate
// page elements for this locale
async function loadAndTranslate(locale) {
  const translations = await loadTranslations(locale);
  polyglot.locale(locale);
  polyglot.replace(translations);
  translatePage();
}
// ...

Voilà, c’est terminé ! Notre application prend désormais en charge les pluriels de manière avancée.
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

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

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

Comment localiser une page web avec i18next ?

Au moment où j’écris ces lignes, i18next est l’une des bibliothèques i18n JavaScript les plus populaires. La bibliothèque learn once, [use] everywhere 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. Nous vous recommandons donc d’utiliser i18next.
Bon, trêve de bavardages ! Nous allons reprendre notre petite démonstration et la localiser en utilisant i18next.

<!DOCTYPE html>
<html lang="en">
<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">
              Home
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n-key="about" class="navbar-link">
              About
            </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">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">With i18next</h1>
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Zelda"}'>
      Welcome to my little spot on the interwebs, {{username}}!
    </p>
    <p data-i18n-key="new-messages" data-i18n-opt='{"count": 12}'>
      You have {{count}} new 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"
  }
}

🗒 Remarque » 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({
  // The active locale
  lng: "en",
  // Enabled useful console output when developing
  debug: true,
  // Translation messages, keyed by locale code
  resources: {
    en: {
      // By default, i18next expects messages under the
      // "translation" namespace
      translation: {
        "app-title": "With Polyglot",
        home: "Home",
        about: "About",
      },
    },
    ar: {
      translation: {
        "app-title": "مع بوليجلوت",
        home: "الرئيسية",
        about: "نبذة عنا",
      },
    },
  },
});
// Translate page elements
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

🗒 Remarque » 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",
  "home": "Home",
  "about": "About"
}
{
  "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. Nous allons l’installer.

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";
// We make the function async so we can await
// the translation file as it pipes down the
// network
async function initI18next() {
  // We use() the backend and await it to load
  // the translations from the network
  await i18next.use(HttpApi).init({
    lng: "en",
    debug: true,
    // Remove inlined `resources`
    // Disable loading of dev locale
    fallbackLng: false,
    // Configure Http backend
    backend: {
      loadPath: "/lang/{{lng}}.json",
    },
  });
}
// Quick refactor of the page translation code
// to a function
function translatePageElements() {
  const translatableElements = document.querySelectorAll(
    "[data-i18n-key]",
  );
  translatableElements.forEach((el) => {
    const key = el.getAttribute("data-i18n-key");
    el.innerHTML = i18next.t(key);
  });
}
// Init
(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 presque terminé. 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 solution 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",
    },
  });
}
// ...

✋🏽 Avertissement » 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
    .use(HttpApi)
    .use(LanguageDetector)
    .init({
      debug: true,
      supportedLngs: ["en", "ar"],
      fallbackLng: "en",
      // Allow "en" to be used for
      // "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.

🔗 Ressources » 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.

✋🏽 Avertissement » 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.

🤿 Pour approfondir » Nous avons un guide dédié Comment détecter 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="en">
<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">English</option>
            <option value="ar">Arabic (العربية)</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);
})();

✋🏽 Avertissement » 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"}'>
      Welcome to my little spot on the interwebs, {{username}}!
    </p>
<!-- ... -->
{
  // ...
  "lead": "Welcome to my little spot on the interwebs, {{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

Formes plurielles

En coulisses, 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}'>
      You have {{count}} new 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": "You have {{count}} new message",
  "new-messages_other": "You have {{count}} new 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": "لا توجد لديك رسائل جديدة",
  "new-messages_one": "لديك رسالة جديدة",
  "new-messages_two": "لديك رسالتان جداد",
  "new-messages_few": "لديك {{count}} رسائل جديدة",
  "new-messages_many": "لديك {{count}} رسالة جديدة",
  "new-messages_other": "لديك {{count}} رسالة جديدة"
}

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

🔗 Ressources » 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é :

🗒 Remarque » 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.

🤿 Pour approfondir » 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>
        Welcome to my little spot on the interwebs, user
      </p>
      <p>You have count new 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">
              Home
            </a>
          </li>
          <li className="navbar-item">
            <a href="#" className="navbar-link">
              About
            </a>
          </li>
        </ul>
        <div className="navbar-end">
          <LocaleSwitcher />
        </div>
      </div>
    </nav>
  );
}
export default Navbar;
function LocaleSwitcher() {
  return (
    <>
      <img
        alt="Translation icon"
        src="img/translation-icon@2x.png"
        className="translation-icon"
      />
      <select className="locale-switcher">
        <option value="en">English</option>
        <option value="ar">Arabic (العربية)</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({
  resources: {
    en: {
      translation: {
        "app-title": "With React",
      },
    },
    ar: {
      translation: {
        "app-title": "مع ريأكت",
      },
    },
  },
  lng: "en",
  debug: true,
  interpolation: {
    escapeValue: false,
  },
});
export default i18next;

🗒 Remarque » 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. Nous allons l’installer.

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": "With React",
  "home": "Home",
  "about": "About"
}

Notre fichier arabe suit la même convention :

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

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({
    // Remove `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

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="Translation icon"
        className="translation-icon"
      />
      <select
        className="locale-switcher"
        value={i18n.language}
        onChange={(e) =>
          i18n.changeLanguage(e.target.value)
        }
      >
        <option value="en">English</option>
        <option value="ar">Arabic (العربية)</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

🔗 Ressources » 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.

🗒 Remarque » 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="en">
<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">
              Home
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n="about" class="navbar-link">
              About
            </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">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n="app-title">jQuery i18n</h1>
    <p data-i18n="lead" data-i18n-options='{"username": "Jackie"}'>
      Welcome to my little spot on the interwebs, {{username}}!
    </p>
    <p data-i18n="new-messages" data-i18n-options='{"count": 3}'>
      You have {{count}} new messages
    </p>
  </div>
</body>
</html>

🗒 Remarque » 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.

🔗 Ressources » 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="en">
<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: {
      translation: {
        "app-title": "جي كويري + آي إيتين نيكست",
      },
    },
  },
});
// Initialiser le plug-in jQuery i18next
jqueryI18next.init(i18next, $);
// Translate page elements
$("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 :

// In our JavaScript
i18next.init({
  lng: "en",
  resources: {
    en: {
      translation: {
        "app-title": "jQuery + i18next",
      },
    },
    ar: {
      translation: {
        "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="en">
<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": "Home",
  "about": "About"
}
{
  "app-title": "مع جي كويري و آي إيتين نيكست",
  "home": "الرئيسية",
  "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.

// Wait for translations to come down the network
// before initializing the jQuery plugin
async function initI18n() {
  // Use Http backend plugin to download translations
  await i18next.use(i18nextHttpBackend).init({
    lng: "en",
  // Remove `resources` option, since our translations
  // are in JSON files now
  });
  jqueryI18next.init(i18next, $);
}
// Refactor to function
function translatePage() {
  $("body").localize();
}
// Init
(async function () {
  // Wait for i18next to initialize before
  // translating page elements
  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="en">
<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">English</option>
            <option value="ar">Arabic (العربية)</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 au paramètre linguistique sélectionné par l’utilisateur.

// ...
function bindLocaleSwitcher() {
  const $switcher = $("[data-i18n-switcher]");
  // Initial value
  $switcher.val(i18next.language);
  $switcher.on("change", async function () {
    // Changing the active locale will cause its
    // translations to load from the network, so
    // we wait for that load before refreshing
    // page elements
    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"}'>
      Welcome to my little spot on the interwebs, {{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": "With jQuery + i18next",
  "home": "Home",
  "about": "About",
  "lead": "Welcome to my little spot on the interwebs, {{username}}!"
}
{
  "app-title": "مع جي كويري و آي إيتين نيكست",
  "home": "الرئيسية",
  "about": "نبذة عنا",
  "lead": "أهلاً بك في مكاني الصغير على النت يا {{username}}."
}

Et voilà. Interpolations interpolées.

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

Formes plurielles

Nous utilisons le même attribut data-i18n-options pour fournir une variable numérique spéciale count lorsque nous avons des messages au pluriel.

🗒 Remarque » 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}'>
      You have {{count}} new 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": "You have {{count}} new 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

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

🔗 Ressources » 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.

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

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

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

<!DOCTYPE html>
<html lang="en">
<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">
              Home
            </a>
          </li>
          <li class="navbar-item">
            <a href="#" data-i18n-key="about" class="navbar-link">
              About
            </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">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <h1 data-i18n-key="app-title">With Globalize</h1>
    <p data-i18n-key="lead" data-i18n-opt='{"username": "Stella"}'>
      Welcome to my little spot on the interwebs, {username}!
    </p>
    <p data-i18n-key="new-messages" data-i18n-opt='{"count": 100}'>
      You have # new 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/          << renamed from cldr-x.x.x
│   ├── cldr-json/     << renamed from cldr-x.x.x-json-full
│   └── globalize/     << renamed from globalize.x.x
└── index.html

🔗 Ressources » 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="en">
<head>
  <!-- ... -->
</head>
<body>
  <div class="container">
    <!-- ... -->
  </div>
  <!-- Globalize requirements -->
  <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>
  <!-- Our app entry point -->
  <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().

// We'll add more to this list as we go
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 () {
  // Load supplemental requirements
  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. Load translation messages
  Globalize.loadMessages({
    en: {
      "app-title": "Hello Globalize!",
    },
    ar: {
      "app-title": "أهلاً جلوبالايز",
    },
  });
  // 2. Set the default locale
  Globalize.locale("en");
  // 3. Use formatMessage() to get translation by key
  document.querySelector(
    "[data-i18n-key='app-title']"
  ).textContent = Globalize.formatMessage("app-title");
})();

🔗 Ressources » 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") {
        // Show console warnings on missing message
        // instead of grinding to a halt.
        console.warn(error.message);
        // Show key value on page for missing message
        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 expects the locale code to be the top-level key
  "en": {
    "app-title": "With Globalize",
    "home": "Home",
    "about": "About"
  }
}
{
  "ar": {
    "app-title": "مع جلوبالايز",
    "home": "الرئيسية",
    "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="en">
<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">English</option>
            <option value="ar">Arabic (العربية)</option>
          </select>
        </div>
      </div>
    </nav>
    <!-- ... -->
  </div>
  <!-- ... -->
  <script src="./index.js"></script>
</body>
</html>

Some JavaScripting will get the switcher switching.

const defaultLocale = "en";
// ...
function bindLocaleSwitcher() {
  const switcher = document.querySelector(
    "[data-i18n-switcher]"
  );
  // Globalize.locale() returns the active locale
  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: ["languages"],
};
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();
}
// Given options = {
//   localenames: ["languages"],
//   dates: ["ca-generic", "ca-gregorian"],
// }
// and, locale = "en", returns an array like
// [
//    "/lib/cldr-json/cldr-localenames-full/main/en/languages.json",
//    "/lib/cldr-json/cldr-dates-full/main/en/ca-generic.json",
//    "/lib/cldr-json/cldr-dates-full/main/en/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;
    // Get CLDR main data by path
    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

🔗 Ressources » 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": {
    // ...
    "lead": "Welcome to my little spot on the interwebs, {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"}'>
      Welcome to my little spot on the interwebs, {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

Formes plurielles

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="en">
<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 = [
  "likelySubtags",
  "plurals",
  "ordinals",
];
// ...

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": {
    // ...
    "new-messages": [
      // We can call `count` anything we want, as long as we
      // match the key in our call to formatMessage()
      "You have {count, plural,",
      "    one {# new message}",
      "  other {# new messages}",
      "}"
    ]
  }
}

🗒 Remarque » 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": {
    // ...
    "new-messages": [
      "{count, plural, ",
      "   zero {لا توجد لديك رسائل جديدة}",
      "    one {لديك رسالة جديدة}",
      "    two {لديك رسالتان جداد}",
      "    few {لديك # رسائل جديدة}",
      "   many {لديك # رسالة جديدة}",
      "  other {لديك # رسالة جديدة}",
      "}"
    ]
  }
}

🤿 Go deeper » We cover ICU plurals in more detail in The Missing Guide to the ICU Message Format.

Of course, we need to make sure that count is supplied to Globalize.formatMessage().

<!-- ... -->
    <p data-i18n-key="new-messages" data-i18n-opt='{"count": 110}'>
      You have # new 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
🔗 Ressources » Obtenez tout le code de démonstration Globalize de notre dépôt GitHub.

🗒 Remarque » 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.

🔗 Ressources » 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.

🔗 Ressources » 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

Blog post

Localiser les jeux Unity avec le plug-in officiel Phrase

Vous voulez localiser votre jeu Unity sans avoir à jongler avec les fichiers .csv ? Découvrez comment le plug-in officiel Phrase Strings pour Unity simplifie le flux de travaux de localisation de votre jeu, de la configuration du tableau de chaînes à l’importation directe des traductions dans votre projet. Que vous développiez pour l’allemand, le serbe ou d’autres langues, ce guide vous explique comment démarrer rapidement et localiser comme un professionnel.

Software localization blog category featured image | Phrase

Blog post

Traduire du contenu Flutter en over-the-air avec Phrase

Arrêtez d’attendre l’approbation du magasin d’applications mobiles. Avec Phrase Over the Air, générez du nouveau contenu pour les utilisateurs de vos applications Flutter instantanément à travers le monde.

Software localization blog category featured image | Phrase

Blog post

Guide ultime de la localisation Flutter

Décodons ensemble les secrets de la localisation Flutter afin que vous permettre de parler la langue de vos utilisateurs et de continuer à coder votre chemin vers le succès à l’international.

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étecter les 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 les détecter correctement.