{"id":133371,"date":"2022-09-14T07:00:43","date_gmt":"2022-09-14T05:00:43","guid":{"rendered":"https:\/\/phrase.com\/blog\/posts\/le-guide-ultime-de-la-localisation-javascript\/"},"modified":"2026-02-26T16:50:39","modified_gmt":"2026-02-26T15:50:39","slug":"step-step-guide-javascript-localization","status":"publish","type":"post","link":"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/","title":{"rendered":"Guide ultime de la localisation JavaScript"},"content":{"rendered":"<p>\u00c9trange, en maturation, plut\u00f4t expressif, incroyable\u00a0: JavaScript est le <a href=\"https:\/\/insights.stackoverflow.com\/survey\/2020#technology-programming-scripting-and-markup-languages-professional-developers\">langage de programmation le plus utilis\u00e9 aujourd&rsquo;hui<\/a>. \u00c9tant la langue du navigateur (et effectuant un travail sur le serveur avec <a href=\"https:\/\/nodejs.org\/en\/\">Node<\/a>) JavaScript est partout dans les architectures web d&rsquo;aujourd&rsquo;hui.<br \/>\nAvec de nombreux frameworks mobiles multiplateformes, wrappers de bureau, moteurs de jeu, et m\u00eame des frameworks pour l&rsquo;internet des objets (IoT), le monde appartient \u00e0 JavaScript (nous ne faisons qu\u2019y vivre).<br \/>\nSi vous \u00eates ici, c&rsquo;est parce que vous voulez canaliser toute cette \u00e9nergie \ud83d\udd25 et apprendre \u00e0 localiser les applications JavaScript afin de les rendre accessibles \u00e0 un public mondial. Bonne nouvelle\u00a0: ce guide couvre tout ce que vous devez savoir pour commencer la localisation JavaScript c\u00f4t\u00e9 navigateur.<br \/>\n\u00c7a va \u00eatre rock <code>&amp;&amp;<\/code> roll\u00a0!<\/p>\n<p>\ud83d\udd17 <em>Ressources\u00a0\u00bb<\/em> Obtenez tout le code accompagnant cet article \u00e0 partir de notre <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\">d\u00e9p\u00f4t GitHub<\/a>.<\/p>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> Internet Explorer (IE), avec une <a href=\"https:\/\/kinsta.com\/browser-market-share\/\">part de march\u00e9 mondiale de 2,15\u00a0%<\/a>, peut \u00eatre consid\u00e9r\u00e9 comme un navigateur vieillissant. Pour des raisons de bri\u00e8vet\u00e9, nous ne d\u00e9taillerons pas ici les solutions sp\u00e9cifiques \u00e0 IE. Si vous <em>prenez en charge<\/em> IE, assurez-vous de v\u00e9rifier si les fonctionnalit\u00e9s JavaScript int\u00e9gr\u00e9es que nous couvrons dans cet article n\u00e9cessitent des versions d\u00e9riv\u00e9es (forks) ou des polyfills.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_81 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Overview<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#comment-puis-je-localiser-une-page-web-avec-javascript\" >Comment puis-je localiser une page web avec JavaScript\u00a0?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#chargement-asynchrone-des-traductions\" >Chargement asynchrone des traductions<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#creation-dun-selecteur-de-parametre-regional\" >Cr\u00e9ation d&rsquo;un s\u00e9lecteur de param\u00e8tre r\u00e9gional<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#detection-des-parametres-regionaux-preferes-de-lutilisateur-a-partir-du-navigateur\" >D\u00e9tection des param\u00e8tres r\u00e9gionaux pr\u00e9f\u00e9r\u00e9s de l\u2019utilisateur \u00e0 partir du navigateur<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#gestion-de-la-direction-les-langues-secrivant-de-droite-a-gauche-et-de-droite-a-gauche\" >Gestion de la direction : les langues s&rsquo;\u00e9crivant de droite \u00e0 gauche et de droite \u00e0 gauche<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#messages-de-traduction-de-base\" >Messages de traduction de base<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#interpolation\" >Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#traduction-dynamique-apres-chargement-de-la-page\" >Traduction dynamique apr\u00e8s chargement de la page<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#formes-plurielles\" >Formes plurielles<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#formatage-des-nombres\" >Formatage des nombres<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#formatage-des-dates\" >Formatage des dates<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#quelles-bibliotheques-dinternationalisation-javascript-est-il-recommande-dutiliser\" >Quelles biblioth\u00e8ques d&rsquo;internationalisation JavaScript est-il recommand\u00e9 d&rsquo;utiliser\u00a0?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#comment-localiser-une-page-web-avec-polyglot\" >Comment localiser une page web avec Polyglot ?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#installation\" >Installation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#traductions-de-base\" >Traductions de base<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#chargement-asynchrone-des-fichiers-de-traduction\" >Chargement asynchrone des fichiers de traduction<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#selecteur-de-langue\" >S\u00e9lecteur de langue<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#interpolation-2\" >Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#formes-plurielles-2\" >Formes plurielles<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#comment-localiser-une-page-web-avec-i18next\" >Comment localiser une page web avec i18next\u00a0?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-21\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#installation-2\" >Installation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-22\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#messages-de-base-de-traduction\" >Messages de base de traduction<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-23\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#chargement-asynchrone-des-traductions-2\" >Chargement asynchrone des traductions<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-24\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#parametres-regionaux-pris-en-charge-et-solution-de-secours\" >Param\u00e8tres r\u00e9gionaux pris en charge et solution de secours<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-25\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#detection-automatique-des-parametres-regionaux-de-lutilisateur\" >D\u00e9tection automatique des param\u00e8tres r\u00e9gionaux de l\u2019utilisateur<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-26\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#selecteur-de-langue-2\" >S\u00e9lecteur de langue<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-27\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#interpolation-3\" >Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-28\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#formes-plurielles-3\" >Formes plurielles<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-29\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#comment-localiser-une-application-react-angular-ou-vue\" >Comment localiser une application React, Angular ou Vue ?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-30\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#articles-sur-la-localisation-angular\" >Articles sur la localisation Angular<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-31\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#articles-de-localisation-vuejs\" >Articles de localisation Vue.js<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-32\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#articles-de-localisation-pour-dautres-frameworks\" >Articles de localisation pour d&rsquo;autres frameworks<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-33\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#comment-localiser-une-application-react-avec-i18next\" >Comment localiser une application React avec i18next ?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-34\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#installation-de-la-bibliotheque\" >Installation de la biblioth\u00e8que<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-35\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#traductions-de-base-2\" >Traductions de base<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-36\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#chargement-asynchrone-de-fichiers-de-traduction\" >Chargement asynchrone de fichiers de traduction<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-37\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#selecteur-de-langue-3\" >S\u00e9lecteur de langue<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-38\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#articles-de-localisation-react\" >Articles de localisation React<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-39\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#comment-localiser-une-page-web-avec-jquery-et-i18next\" >Comment localiser une page web avec jQuery et i18next ?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-40\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#installation-3\" >Installation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-41\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#traductions-de-base-3\" >Traductions de base<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-42\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#chargement-asynchrone-de-fichiers-de-traduction-2\" >Chargement asynchrone de fichiers de traduction<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-43\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#selecteur-de-langue-4\" >S\u00e9lecteur de langue<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-44\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#interpolation-4\" >Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-45\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#formes-plurielles-4\" >Formes plurielles<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-46\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#comment-localiser-une-page-web-avec-le-format-icu-en-utilisant-globalize\" >Comment localiser une page web avec le format ICU en utilisant Globalize ?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-47\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#installation-4\" >Installation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-48\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#traductions-de-base-4\" >Traductions de base<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-49\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#gestion-des-erreurs-de-messages-manquants\" >Gestion des erreurs de messages manquants<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-50\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#chargement-asynchrone-de-fichiers-de-traduction-3\" >Chargement asynchrone de fichiers de traduction<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-51\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#selecteur-de-langue-5\" >S\u00e9lecteur de langue<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-52\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#interpolation-5\" >Interpolation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-53\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#formes-plurielles-5\" >Formes plurielles<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-54\" href=\"https:\/\/phrase.com\/fr\/blog\/posts\/step-step-guide-javascript-localization\/#pour-conclure-notre-guide-de-localisation-javascript\" >Pour conclure notre guide de localisation JavaScript<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"comment-puis-je-localiser-une-page-web-avec-javascript\"><\/span>Comment puis-je localiser une page web avec JavaScript\u00a0?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Bien qu&rsquo;il puisse \u00eatre tentant de prendre une biblioth\u00e8que d&rsquo;internationalisation (i18n) pr\u00eate \u00e0 l&#8217;emploi pour vos besoins de localisation (et cela pourrait s&rsquo;av\u00e9rer \u00eatre un bon choix pour votre projet), vous constaterez que JavaScript peut tr\u00e8s bien faire l&rsquo;affaire pour les projets de taille modeste. D\u00e9velopper la v\u00f4tre vous permettra de compiler des techniques d&rsquo;internationalisation utilisables avec <em>n&rsquo;importe quelle<\/em> biblioth\u00e8que que vous choisissez.<\/p>\n<p>\ud83e\udd3f <em>Pour approfondir \u00bb<\/em><i> <\/i>Notre article (en anglais), <a href=\"https:\/\/phrase.com\/fr\/blog\/posts\/i18n-a-simple-definition\/\">What Is I18n: A Simple Definition of Internationalization<\/a> d\u00e9crit de mani\u00e8re d\u00e9taill\u00e9e les concepts d&rsquo;internationalisation (i18n) et de localisation (l10n).<\/p>\n<p>\u270b\ud83c\udffd <em>Avertissement \u00bb<\/em> Si vous concevez une application MPA (application multi-pages) traditionnelle, il arrive souvent qu&rsquo;une grande partie de la localisation se fasse sur le serveur lui-m\u00eame. Ici, nous nous concentrons exclusivement sur la localisation c\u00f4t\u00e9 navigateur. C\u00f4t\u00e9 serveur, nous vous renvoyons \u00e0 notre <a href=\"https:\/\/phrase.com\/blog\/posts\/nodejs-tutorial-on-creating-multilingual-web-app\/\">tutoriel consacr\u00e9 \u00e0 l&rsquo;internationalisation Node<\/a> (en anglais) et \u00e0 notre <a href=\"https:\/\/phrase.com\/blog\/posts\/full-stack-javascript-i18n\/\">guide complet d\u00e9di\u00e9 \u00e0 l&rsquo;internationalisation JavaScript<\/a> (en anglais).<\/p>\n<p>Imaginons que vous avez une page que vous souhaitez localiser.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\" data-enlighter-group=\"e17c768d-5eb9-41e9-a82c-c8758e6dff71\" data-enlighter-title=\"index.html\" data-enlighter-linenumbers=\"false\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;meta charset=\"UTF-8\"&gt;\n  &lt;!-- ... --&gt;\n  &lt;title&gt;My Appy Apperson&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;h1&gt;My Appy Apperson&lt;\/h1&gt;\n    &lt;p&gt;Welcome to my little spot on the interwebs!&lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;script src=\"js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15564 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-en.png\" alt=\"Version anglaise d&apos;une d\u00e9mo d&apos;application JavaScript | Phrase\" width=\"998\" height=\"316\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-en.png 998w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-en-300x95.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-en-768x243.png 768w\" sizes=\"auto, (max-width: 998px) 100vw, 998px\" \/><\/p>\n<p>\ud83d\udd17 <em>Ressources\u00a0\u00bb<\/em> Vous pouvez obtenir tout le code de l&rsquo;application que nous concevons dans cette section \u00e0 partir du <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/vanilla\">dossier vanilla dans notre d\u00e9p\u00f4t GitHub<\/a>.<br \/>\n\ud83d\udd17 <em>Ressources\u00a0\u00bb<\/em> J&rsquo;utilise la biblioth\u00e8que <a href=\"http:\/\/getskeleton.com\/\">Skeleton CSS<\/a> au cas o\u00f9 vous vous demandiez.<\/p>\n<p>Tout a l&rsquo;air en ordre, mais l&rsquo;application n&rsquo;est pas pr\u00eate pour le monde entier\u00a0: tout le contenu est cod\u00e9 en dur en anglais. Nous allons y rem\u00e9dier en l&rsquo;internationalisant un peu.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-highlight=\"10,11\" data-enlighter-group=\"9ae201e0-a675-4f5e-8ebb-1c941d5f6f4b\" data-enlighter-title=\"index.html\" data-enlighter-linenumbers=\"false\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;meta charset=\"UTF-8\"&gt;\n  &lt;!-- ... --&gt;\n  &lt;title&gt;My Appy Apperson&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;My Appy Apperson&lt;\/h1&gt;\n    &lt;p data-i18n-key=\"lead\"&gt;Welcome to my little spot on the interwebs!&lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;script src=\"js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Notez les attributs <code>data-i18n-key<\/code> que nous avons ajout\u00e9s aux conteneurs de texte ci-dessus. Nous pouvons les utiliser lorsque le document se charge et remplacer leur texte par des traductions. C&rsquo;est ce que nous allons faire tout de suite.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"eb3e63cb-cd04-47cf-a52d-41474533e624\" data-enlighter-title=\"\/js\/scripts.js\">\/\/ The active locale\nconst locale = \"en\";\n\/\/ We can have as many locales here as we want,\n\/\/ and use any locales we want. We have English\n\/\/ and Arabic as locales here as examples.\nconst translations = {\n  \/\/ English translations\n  \"en\": {\n    \"app-title\": \"My Appy Apperson\",\n    \"lead\": \"Welcome to my little spot on the interwebs!\",\n  },\n  \/\/ Arabic translations\n  \"ar\": {\n    \"app-title\": \"\u062a\u0637\u0628\u064a\u0642\u064a \u0627\u0644\u0645\u0637\u0628\u0642\",\n    \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a.\",\n  },\n};\n\/\/ When the page content is ready...\ndocument.addEventListener(\"DOMContentLoaded\", () =&gt; {\n  document\n    \/\/ Find all elements that have the key attribute\n    .querySelectorAll(\"[data-i18n-key]\")\n    .forEach(translateElement);\n});\n\/\/ Replace the inner text of the given HTML element\n\/\/ with the translation in the active locale,\n\/\/ corresponding to the element's data-i18n-key\nfunction translateElement(element) {\n  const key = element.getAttribute(\"data-i18n-key\");\n  const translation = translations[locale][key];\n  element.innerText = translation;\n}\n<\/pre>\n<p>Maintenant, nous allons modifier la deuxi\u00e8me ligne ci-dessus en <code>const locale = \"ar\";<\/code> et recharger la page. Lorsque l&rsquo;\u00e9v\u00e9nement <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Window\/DOMContentLoaded_event\">DOMContentLoaded<\/a> est d\u00e9clench\u00e9, notre page affiche nos traductions en arabe.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15566 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-ar.png\" alt=\"Version arabe d&apos;une application de d\u00e9monstration en JavaScript | Phrase\" width=\"572\" height=\"272\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-ar.png 572w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/vanilla-ar-300x143.png 300w\" sizes=\"auto, (max-width: 572px) 100vw, 572px\" \/><\/p>\n<p>\ud83d\uddd2 <em>Remarque\u00a0\u00bb<\/em> <code>\"en\"<\/code> et <code>\"ar\"<\/code> correspondent aux <a href=\"https:\/\/www.loc.gov\/standards\/iso639-2\/php\/code_list.php\">codes ISO 639-1<\/a> pour l&rsquo;anglais et l&rsquo;arabe, respectivement. On utilise g\u00e9n\u00e9ralement les codes ISO pour les <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/locale\/#language-code\">langues<\/a> et les <a href=\"https:\/\/unicode-org.github.io\/icu\/userguide\/locale\/#country-code\">pays<\/a> lors de la localisation.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"chargement-asynchrone-des-traductions\"><\/span>Chargement asynchrone des traductions<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Notre solution d&rsquo;internationalisation commence bien, mais pour le moment l&rsquo;ajout de param\u00e8tres r\u00e9gionaux et de traductions n&rsquo;est pas \u00e9volutif. \u00c0 mesure que notre application s&rsquo;enrichit, nous allons probablement vouloir s\u00e9parer nos traductions dans des fichiers distincts pour chaque param\u00e8tre r\u00e9gional. Le fichier de traduction correspondant au param\u00e8tre r\u00e9gional actif peut alors \u00eatre charg\u00e9 sans qu&rsquo;il soit n\u00e9cessaire de charger les autres param\u00e8tres r\u00e9gionaux. C&rsquo;est tout \u00e0 fait faisable sans trop d\u2019efforts.<br \/>\nTout d&rsquo;abord, nous allons d\u00e9placer les traductions de notre script principal vers des fichiers JSON, un pour chaque param\u00e8tre r\u00e9gional pris en charge.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"6063d19b-b975-4367-8a9f-2be85e5d7770\" data-enlighter-title=\"\/lang\/en.json\">{\n  \"app-title\": \"My Appy Apperson\",\n  \"lead\": \"Welcome to my little spot on the interwebs!\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"c6570b0b-a294-414a-b00a-5ab4e1103c72\" data-enlighter-title=\"\/lang\/ar.json\">{\n  \"app-title\": \"\u062a\u0637\u0628\u064a\u0642\u064a \u0627\u0644\u0645\u0637\u0628\u0642\",\n  \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a.\"\n}\n<\/pre>\n<p>Nous allons retravailler notre script pour charger les fichiers JSON de mani\u00e8re asynchrone lorsque cela est n\u00e9cessaire.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"eecf242f-182d-40ed-bc04-f77f3246859a\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"2,5,8,13,18,19,20,21,22,23,24,25,26,27,28,32,33,34,35,40-44,51\">\/\/ The locale our app first shows\nconst defaultLocale = \"en\";\n\/\/ The active locale\nlet locale;\n\/\/ Gets filled with active locale translations\nlet translations = {};\n\/\/ When the page content is ready...\ndocument.addEventListener(\"DOMContentLoaded\", () =&gt; {\n  \/\/ Translate the page to the default locale\n  setLocale(defaultLocale);\n});\n\/\/ Load translations for the given locale and translate\n\/\/ the page to this locale\nasync function setLocale(newLocale) {\n  if (newLocale === locale) return;\n  const newTranslations =\n    await fetchTranslationsFor(newLocale);\n  locale = newLocale;\n  translations = newTranslations;\n  translatePage();\n}\n\/\/ Retrieve translations JSON object for the given\n\/\/ locale over the network\nasync function fetchTranslationsFor(newLocale) {\n  const response = await fetch(`\/lang\/${newLocale}.json`);\n  return await response.json();\n}\n\/\/ Replace the inner text of each element that has a\n\/\/ data-i18n-key attribute with the translation corresponding\n\/\/ to its data-i18n-key\nfunction translatePage() {\n  document\n    .querySelectorAll(\"[data-i18n-key]\")\n    .forEach(translateElement);\n}\n\/\/ Replace the inner text of the given HTML element\n\/\/ with the translation in the active locale,\n\/\/ corresponding to the element's data-i18n-key\nfunction translateElement(element) {\n  const key = element.getAttribute(\"data-i18n-key\");\n  const translation = translations[key];\n  element.innerText = translation;\n}\n<\/pre>\n<p>Si nous rechargeons notre page maintenant, elle aura exactement le m\u00eame aspect qu&rsquo;avant. Cependant, en coulisses, nous avons rendu notre application beaucoup plus \u00e9volutive et facile \u00e0 mettre \u00e0 jour.<\/p>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> Nous utilisons l&rsquo;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Fetch_API\">API Fetch<\/a>, int\u00e9gr\u00e9e aux navigateurs modernes, pour r\u00e9cup\u00e9rer nos fichiers JSON via le r\u00e9seau.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"creation-dun-selecteur-de-parametre-regional\"><\/span>Cr\u00e9ation d&rsquo;un s\u00e9lecteur de param\u00e8tre r\u00e9gional<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Nos utilisateurs n&rsquo;ont pas encore la possibilit\u00e9 d&rsquo;utiliser nos incroyables capacit\u00e9s asynchrones. Et si nous ajoutions un menu d\u00e9roulant pour leur permettre de changer de langue\u00a0?<br \/>\nNous allons ajouter une barre de navigation et y int\u00e9grer notre s\u00e9lecteur.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-highlight=\"9,18,19,20,21,24\" data-enlighter-group=\"ffdcbc91-e87f-487d-b549-2a88f94eb91a\" data-enlighter-title=\"index.html\" data-enlighter-linenumbers=\"false\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n  &lt;title&gt;My Appy Apperson&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;ul class=\"navbar-list navbar-left\"&gt;\n          &lt;!-- Nav links --&gt;\n        &lt;\/ul&gt;\n        &lt;div class=\"navbar-right\"&gt;\n          &lt;!-- ... --&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;My Appy Apperson&lt;\/h1&gt;\n    &lt;p data-i18n-key=\"lead\"&gt;Welcome to my little spot on the interwebs!&lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;script src=\"js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Un simple <code>&lt;select&gt;<\/code> suffit ici. Nous pouvons utiliser un attribut <code>data-i18n-switcher<\/code> pour que notre JavaScript puisse s&rsquo;y connecter et charger les param\u00e8tres r\u00e9gionaux s\u00e9lectionn\u00e9s par l&rsquo;utilisateur.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"f468dc96-a9e9-47c3-84a4-e59ac95a6d68\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"10,18,19,20,21,22,23,24,25,26,27,28\">const defaultLocale = \"en\";\nlet locale;\n\/\/ ...\n\/\/ When the page content is ready...\ndocument.addEventListener(\"DOMContentLoaded\", () =&gt; {\n  setLocale(defaultLocale);\n  bindLocaleSwitcher(defaultLocale);\n});\n\/\/ ...\n\/\/ Whenever the user selects a new locale, we\n\/\/ load the locale's translations and update\n\/\/ the page\nfunction bindLocaleSwitcher(initialValue) {\n  const switcher =\n    document.querySelector(\"[data-i18n-switcher]\");\n  switcher.value = initialValue;\n  switcher.onchange = (e) =&gt; {\n    \/\/ Set the locale to the selected option[value]\n    setLocale(e.target.value);\n  };\n}\n<\/pre>\n<p>L&rsquo;\u00e9v\u00e9nement <code>onchange<\/code> permet de mettre \u00e0 jour les traductions de notre page selon la valeur <code>&lt;option&gt;<\/code> s\u00e9lectionn\u00e9e. Et voil\u00e0. Les visiteurs de notre site peuvent maintenant s\u00e9lectionner leurs propres param\u00e8tres r\u00e9gionaux.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15567 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/locale-switcher.gif\" alt=\"Application de d\u00e9monstration avec gestionnaire d&apos;\u00e9v\u00e9nement onchange | Phrase\" width=\"600\" height=\"204\" \/><\/p>\n<p>\ud83d\udce3 <em>Remerciements \u00bb<\/em> Un grand merci \u00e0 <a href=\"https:\/\/thenounproject.com\/search\/?q=translation&amp;i=4380109%0A\">Hary Murdiono JS du Noun Project<\/a> pour son <em>ic\u00f4ne de traduction<\/em>.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"detection-des-parametres-regionaux-preferes-de-lutilisateur-a-partir-du-navigateur\"><\/span>D\u00e9tection des param\u00e8tres r\u00e9gionaux pr\u00e9f\u00e9r\u00e9s de l\u2019utilisateur \u00e0 partir du navigateur<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Parfois, il est judicieux de deviner les param\u00e8tres r\u00e9gionaux pr\u00e9f\u00e9r\u00e9s de l&rsquo;utilisateur avant de lui donner la possibilit\u00e9 de s\u00e9lectionner manuellement les siens. La plupart des gens ont l&rsquo;interface utilisateur de leur navigateur r\u00e9gl\u00e9e dans la langue de leur choix, souvent la langue du syst\u00e8me d&rsquo;exploitation.<br \/>\nCette langue d&rsquo;interface utilisateur du navigateur peut \u00eatre trouv\u00e9e dans l&rsquo;objet <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Navigator\/language\">navigator<\/a>, plus pr\u00e9cis\u00e9ment dans la cha\u00eene <code>navigator.language<\/code>.<br \/>\nLa liste <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Navigator\/languages\">navigator.languages<\/a>, autre grand classique (bien qu&rsquo;exp\u00e9rimentale au moment o\u00f9 j&rsquo;\u00e9cris ces lignes), devrait contenir la langue de l&rsquo;interface utilisateur comme premi\u00e8re entr\u00e9e, en plus de toutes les langues que l&rsquo;utilisateur a explicitement d\u00e9finies dans les param\u00e8tres de langues pr\u00e9f\u00e9r\u00e9es de son navigateur.<br \/>\nUne petite fonction qui interroge <code>navigator.languages<\/code> peut nous aider \u00e0 d\u00e9tecter les param\u00e8tres r\u00e9gionaux du navigateur.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"905d80b4-e5a7-4a8e-ba58-54f9ba5f8ee9\" data-enlighter-title=\"\/js\/scripts.js\">\/**\n * Retrieve user-preferred locales from the browser\n *\n * @param {boolean} languageCodeOnly - when true, returns\n * [\"en\", \"fr\"] instead of [\"en-US\", \"fr-FR\"]\n * @returns array | undefined\n *\/\nfunction browserLocales(languageCodeOnly = false) {\n  return navigator.languages.map((locale) =&gt;\n    languageCodeOnly ? locale.split(\"-\")[0] : locale,\n  );\n}\n<\/pre>\n<p>Maintenant, imaginons que l\u2019utilisateur ait le fran\u00e7ais (Canada) et le chinois (simplifi\u00e9) dans ses param\u00e8tres de navigateur.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15568 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/browser-locale-prefs-1024x605.png\" alt=\"Param\u00e8tres r\u00e9gionaux du navigateur | Phrase\" width=\"1024\" height=\"605\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/browser-locale-prefs-1024x605.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/browser-locale-prefs-300x177.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/browser-locale-prefs-768x454.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/browser-locale-prefs.png 1438w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\nDans ce cas, <code>browserLocales()<\/code> renverra <code>[\"fr-CA\", \"zh-CN\"]<\/code>. Si nous appelons <code>browserLocales(true)<\/code>, nous obtiendrons <code>[\"fr\", \"zh\"]<\/code>.<br \/>\nNous pouvons maintenant utiliser cette nouvelle fonction pour d\u00e9tecter les param\u00e8tres r\u00e9gionaux pr\u00e9f\u00e9r\u00e9s de l&rsquo;utilisateur lorsque nous chargeons pour la premi\u00e8re fois notre page.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"72b255d6-6feb-43d4-8437-db90b0dbc34e\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"4,9,10,12,14,19,20,21,25,26,27\">\/\/ The locale our app first shows\nconst defaultLocale = \"en\";\nconst supportedLocales = [\"en\", \"ar\"];\n\/\/ ...\ndocument.addEventListener(\"DOMContentLoaded\", () =&gt; {\n  const initialLocale =\n    supportedOrDefault(browserLocales(true));\n  setLocale(initialLocale);\n  bindLocaleSwitcher(initialLocale);\n});\n\/\/ ...\nfunction isSupported(locale) {\n  return supportedLocales.indexOf(locale) &gt; -1;\n}\n\/\/ Retrieve the first locale we support from the given\n\/\/ array, or return our default locale\nfunction supportedOrDefault(locales) {\n  return locales.find(isSupported) || defaultLocale;\n}\n\/\/ ...\nfunction browserLocales(languageCodeOnly = false) {\n  return navigator.languages.map((locale) =&gt;\n    languageCodeOnly ? locale.split(\"-\")[0] : locale,\n  );\n}\n<\/pre>\n<p>Remarquez que nous avons introduit le concept de <code>supportedLocales<\/code> ; ce sont les seuls param\u00e8tres r\u00e9gionaux pour lesquels nous avons des traductions. Avec eux, nous pouvons revenir \u00e0 nos param\u00e8tres r\u00e9gionaux par d\u00e9faut si aucun des param\u00e8tres r\u00e9gionaux pr\u00e9f\u00e9r\u00e9s de l&rsquo;utilisateur n&rsquo;est dans notre liste prise en charge.<br \/>\nNotre application sera maintenant traduite vers les premiers param\u00e8tres r\u00e9gionaux de la liste des pr\u00e9f\u00e9rences de l&rsquo;utilisateur, avec une solution de secours \u00e9l\u00e9gante.<\/p>\n<p>\ud83e\udd3f <em>Pour approfondir \u00bb<\/em> Nous expliquons en d\u00e9tails comment d\u00e9tecter les param\u00e8tres r\u00e9gionaux \u00e0 la fois sur le navigateur et le serveur dans l&rsquo;article (en anglais) <a href=\"https:\/\/phrase.com\/fr\/blog\/posts\/detecting-a-users-locale\/\">Detecting browser language preference with JavaScript<\/a>.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"gestion-de-la-direction-les-langues-secrivant-de-droite-a-gauche-et-de-droite-a-gauche\"><\/span>Gestion de la direction : les langues s&rsquo;\u00e9crivant de droite \u00e0 gauche et de droite \u00e0 gauche<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>L&rsquo;arabe, l&rsquo;h\u00e9breu, le persan, l&rsquo;ourdou et d&rsquo;autres langues <a href=\"https:\/\/www.worldatlas.com\/articles\/which-languages-are-written-from-right-to-left.html\">utilisent des scripts qui s&rsquo;\u00e9crivent de droite \u00e0 gauche<\/a>. Bien que les langues s&rsquo;\u00e9crivant de gauche \u00e0 droite (LTR) soient beaucoup plus nombreuses que celles s&rsquo;\u00e9crivant de droite \u00e0 gauche (RTL), il est bon de savoir comment prendre en charge ces derni\u00e8res. Heureusement pour nous, le gros du travail est effectu\u00e9 par le navigateur ; nous avons juste \u00e0 d\u00e9finir l&rsquo;attribut <code>&lt;html dir&gt;<\/code> dans nos pages.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"6a86b7f1-65ce-4b15-891b-0425324892e3\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"16,26,27,28\">\/\/ ...\n\/\/ Load translations for the given locale and translate\n\/\/ the page to this locale\nasync function setLocale(newLocale) {\n  if (newLocale === locale) return;\n  const newTranslations = await fetchTranslationsFor(\n    newLocale,\n  );\n  locale = newLocale;\n  translations = newTranslations;\n  \/\/ Set &lt;html dir&gt; attribute\n  document.documentElement.dir = dir(newLocale);\n  \/\/ Not necessary for direction flow, but for good measure...\n  document.documentElement.lang = newLocale;\n  translatePage();\n}\n\/\/ ...\nfunction dir(locale) {\n  return locale === \"ar\" ? \"rtl\" : \"ltr\";\n}\n\/\/ ...\n<\/pre>\n<p>L&rsquo;attribut <code>&lt;html dir&gt;<\/code> peut prendre les valeurs <code>\"ltr\"<\/code> ou <code>\"rtl\"<\/code>. Nous fournissons cette valeur via une fonction <code>dir()<\/code> tr\u00e8s simple et nous d\u00e9finissons l&rsquo;attribut chaque fois que nous changeons de param\u00e8tres r\u00e9gionaux.<\/p>\n<p style=\"text-align: center\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15569 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dom-lang-dir.png\" alt=\"Attributs HTML des outils de d\u00e9veloppement du navigateur | Phrase\" width=\"322\" height=\"208\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dom-lang-dir.png 322w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dom-lang-dir-300x194.png 300w\" sizes=\"auto, (max-width: 322px) 100vw, 322px\" \/><br \/>\n<em>Si nous ouvrons nos outils de d\u00e9veloppement du navigateur, nous pouvons voir les attributs <code>&lt;html&gt;<\/code> se mettre \u00e0 jour lorsque nous changeons de langue<\/em><\/p>\n<p>Le navigateur affichera automatiquement le document de droite \u00e0 gauche lorsque nous d\u00e9finissons <code>&lt;html dir=\"rtl\"&gt;<\/code>. Cependant, l&rsquo;un de nos styles directionnels personnalis\u00e9s, par exemple <code>margin-left: 20px<\/code>, n\u00e9cessitera un CSS invers\u00e9 sp\u00e9cifique RTL. Ce n&rsquo;est g\u00e9n\u00e9ralement pas trop compliqu\u00e9; c&rsquo;est juste un peu en dehors du champ d&rsquo;application de cet article.<\/p>\n<p>\ud83e\udd3f <em>Pour approfondir \u00bb<\/em> Lisez-en plus sur le CSS localis\u00e9 dans l&rsquo;article (en anglais) <a href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/\">How Do I Use a CSS File for Site Localization?<\/a><\/p>\n<p>Gr\u00e2ce \u00e0 notre nouveau code, nous obtenons une page en arabe qui aurait pu \u00eatre lue par Avicenne\u00a0!<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15570 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/ar-rtl-1024x348.png\" alt=\"Application de d\u00e9monstration avec le texte en arabe align\u00e9 \u00e0 droite | Phrase\" width=\"1024\" height=\"348\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/ar-rtl-1024x348.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/ar-rtl-300x102.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/ar-rtl-768x261.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/ar-rtl.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"messages-de-traduction-de-base\"><\/span>Messages de traduction de base<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Avant de passer \u00e0 des messages plus complexes, comme ceux avec des valeurs interpol\u00e9es et des pluriels, revenons bri\u00e8vement sur la fa\u00e7on dont nous avons impl\u00e9ment\u00e9 nos messages de traduction.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">\/\/ In our HTML page\n&lt;h1 data-i18n-key=\"app-title\"&gt;My Appy Apperson&lt;\/h1&gt;\n\/\/ In our JavaScript\nfunction translateElement(element) {\n  const key = element.getAttribute(\"data-i18n-key\");\n  const translation = translations[key];\n  element.innerText = translation;\n}\n\/\/ Given that we've loaded Arabic translations from ar.json:\ntranslations = {\n  \"app-title\": \"\u062a\u0637\u0628\u064a\u0642\u064a \u0627\u0644\u0645\u0637\u0628\u0642\",\n};\ntranslateElement(document.querySelector(\"[data-i18n-key='lead']\"));\n\/\/ renders to:\n&lt;h1 data-i18n-key=\"app-title\"&gt;\u062a\u0637\u0628\u064a\u0642\u064a \u0627\u0644\u0645\u0637\u0628\u0642&lt;\/h1&gt;\n<\/pre>\n<p>That\u2019s our translation system in a nutshell.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"interpolation\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>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\u2019ll have to update our translation system to handle cases like these.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-highlight=\"12,13,14,15,16,17\" data-enlighter-group=\"269a8ae8-8a14-4a95-9863-a481f6ae89f3\" data-enlighter-title=\"index.html\" data-enlighter-linenumbers=\"false\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;!-- ... --&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;My Appy Apperson&lt;\/h1&gt;\n    &lt;p\n      data-i18n-key=\"lead\"\n      data-i18n-opt='{\"username\": \"Swoodesh\"}'\n    &gt;\n      Welcome to my little spot on the interwebs, {username}!\n    &lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;script src=\"js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Nous indiquons un espace r\u00e9serv\u00e9 pour les valeurs que nous voulons interpoler dans nos messages avec la syntaxe <code>{variable}<\/code>. Un nouvel attribut <code>data-i18n-opt<\/code> stocke des paires cl\u00e9\/valeur d&rsquo;interpolation dans un objet JSON valide.<br \/>\nBien s\u00fbr, nous aurons besoin de l&rsquo;espace r\u00e9serv\u00e9 dans nos fichiers de langue.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"f78af90a-df90-420c-a3c7-7a289dcede90\" data-enlighter-title=\"\/lang\/en.json\">{\n  \"lead\": \"Welcome to my little spot on the interwebs, {username}!\",\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"9082479b-aff1-4962-b14e-d522963e7eb8\" data-enlighter-title=\"\/lang\/ar.json\">{\n  \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a \u064a\u0627 {username}.\",\n}\n<\/pre>\n<p>Nous pouvons maintenant modifier notre fonction <code>translateElement<\/code> pour g\u00e9rer les interpolations.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"13c0b59f-dfdf-45cf-8d56-911e8a61422e\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"7,8,9,11,12,13,18,19,20,21,22,23,24,25,26,27\">\/\/ ...\nfunction translateElement(element) {\n  const key = element.getAttribute(\"data-i18n-key\");\n  const translation = translations[key];\n  const options = JSON.parse(\n    element.getAttribute(\"data-i18n-opt\")\n  );\n  element.innerText = options\n    ? interpolate(translation, options)\n    : translation;\n}\n\/\/ Convert a message like \"Hello, {name}\" to \"Hello, Chad\"\n\/\/ given the interpolations object {name: \"Chad\"}\nfunction interpolate(message, interpolations) {\n  return Object.keys(interpolations).reduce(\n    (interpolated, key) =&gt;\n      interpolated.replace(\n        new RegExp(`{\\s*${key}\\s*}`, \"g\"),\n        interpolations[key],\n      ),\n    message,\n  );\n}\n\/\/ ...\n<\/pre>\n<p>Si nous d\u00e9tectons un attribut <code>data-i18n-opt<\/code> sur l&rsquo;\u00e9l\u00e9ment donn\u00e9 \u00e0 <code>translateElement()<\/code>, nous ex\u00e9cutons son message traduit \u00e0 travers une nouvelle fonction <code>interpolate()<\/code> avant de mettre \u00e0 jour l&rsquo;\u00e9l\u00e9ment. Maintenant, lorsque nous chargeons notre page, nous voyons le message avec la valeur interpol\u00e9e.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15571 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-en-1024x348.png\" alt=\"Application de d\u00e9monstration avec interpolation dans les param\u00e8tres r\u00e9gionaux anglais | Phrase\" width=\"1024\" height=\"348\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-en-1024x348.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-en-300x102.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-en-768x261.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-en.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15572 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-ar-1024x348.png\" alt=\"Application de d\u00e9monstration avec interpolation dans les param\u00e8tres r\u00e9gionaux arabes | Phrase\" width=\"1024\" height=\"348\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-ar-1024x348.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-ar-300x102.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-ar-768x261.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/interpolation-ar.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\nBien s\u00fbr, avoir des valeurs statiques dans le HTML est d&rsquo;une utilit\u00e9 limit\u00e9e pour nous. Id\u00e9alement, nous voulons pouvoir interpoler dynamiquement avec JavaScript. Ce n&rsquo;est pas trop difficile \u00e0 coder.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"traduction-dynamique-apres-chargement-de-la-page\"><\/span>Traduction dynamique apr\u00e8s chargement de la page<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Nous allons extraire une fonction de traduction g\u00e9n\u00e9rale \u00e0 partir de <code>translateElement()<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"6597c244-ae4d-4095-a22e-3f67cadd8248\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"6,7,9,12,13,14\">\/\/ ...\nfunction translateElement(element) {\n  const key = element.getAttribute(\"data-i18n-key\");\n  const options =\n    JSON.parse(element.getAttribute(\"data-i18n-opt\")) || {};\n  element.innerText = translate(key, options);\n}\nfunction translate(key, interpolations = {}) {\n  return interpolate(translations[key], interpolations);\n}\n\/\/ ...\n<\/pre>\n<p>Nous avons simplement extrait le code qui g\u00e8re la r\u00e9cup\u00e9ration d&rsquo;un message dans les param\u00e8tres r\u00e9gionaux actifs, avec des interpolations, dans une nouvelle fonction <code>translate()<\/code>. Nous pouvons maintenant utiliser cette fonction pour mettre \u00e0 jour la traduction d&rsquo;un \u00e9l\u00e9ment apr\u00e8s le chargement de la page. Supposons que nous voulions mettre \u00e0 jour notre texte principal apr\u00e8s que l\u2019utilisateur se connecte. Ce n&rsquo;est pas bien compliqu\u00e9.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">const element =\n  document.querySelector(\"[data-i18n-key='lead']\");\n\/\/ Our new function is serving us well here\nelement.innerText =\n  translate(\"lead\", { username: \"Maggie\" });\n\/\/ Store the updated interpolations in the document\n\/\/ in case the element is re-rendered in the future\nelement.setAttribute(\n  \"data-i18n-opt\",\n  JSON.stringify({ username: \"Maggie\" }),\n);\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15573 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dynamic-interpolation-1024x348.png\" alt=\"Application de d\u00e9monstration avec traduction dynamique en anglais | Phrase\" width=\"1024\" height=\"348\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dynamic-interpolation-1024x348.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dynamic-interpolation-300x102.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dynamic-interpolation-768x261.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/dynamic-interpolation.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\nMaintenant, nous pouvons mettre \u00e0 jour les traductions des \u00e9l\u00e9ments \u00e0 tout moment depuis notre JavaScript.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"formes-plurielles\"><\/span>Formes plurielles<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Non seulement nous devons souvent pr\u00e9senter diff\u00e9rents messages en fonction d&rsquo;un compteur(exemple\u00a0: \u00ab\u00a01 abonn\u00e9\u00a0\u00bb ou \u00ab\u00a020 000 abonn\u00e9<em>s<\/em>\u00ab\u00a0), sachant que<a href=\"https:\/\/unicode-org.github.io\/cldr-staging\/charts\/latest\/supplemental\/language_plural_rules.html\">les langues n&rsquo;ont pas toutes les m\u00eames r\u00e8gles de pluriel<\/a>. L&rsquo;anglais a deux formes plurielles (<em>one<\/em> et <em>other<\/em>) alors que l&rsquo;arabe, par exemple, en a six. Par le pass\u00e9, cela signifiait qu&rsquo;il n&rsquo;\u00e9tait pas simple de mettre en \u0153uvre la prise en charge des formes plurielles pour les applications front-end. Heureusement, aujourd&rsquo;hui l&rsquo;objet <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/PluralRules\">Intl.PluralRules<\/a> permet de traiter rapidement les pluriels.<br \/>\nSupposons que nous soyons des r\u00e9dacteurs prolifiques et que nous voulions faire savoir au monde combien d\u2019articles nous avons r\u00e9dig\u00e9s.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;p\n  data-i18n-key=\"article-plural\"\n  data-i18n-opt='{\"count\": 122}'\n&gt;\n  {count} articles written and counting.\n&lt;\/p&gt;\n<\/pre>\n<p>Notez que nous utilisons une convention consistant \u00e0 terminer notre cl\u00e9 de message pluriel par <code>-plural<\/code>. Bien s\u00fbr, nous avons besoin d&rsquo;un entier <code>count<\/code> pour s\u00e9lectionner la bonne forme plurielle. En parlant des formes plurielles, nous allons maintenant les ajouter.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"75fdda27-47a8-4750-9436-e1a6d3120c1f\" data-enlighter-title=\"\/lang\/en.json\">{\n  \/\/ English has two plural forms\n  \"article-plural\": {\n    \"one\": \"{count} article and counting\",\n    \"other\": \"{count} articles and counting\"\n  }\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"f45a1ae7-53e1-4816-85d4-c79c3bd740d8\" data-enlighter-title=\"\/lang\/ar.json\">{\n  \/\/ Arabic has six plural forms\n  \"article-plural\": {\n    \"zero\": \"\u0644\u0627 \u062a\u0648\u062c\u062f \u0645\u0642\u0627\u0644\u0627\u062a\",\n    \"one\": \"\u0645\u0642\u0627\u0644 {count}\",\n    \"two\": \"\u0645\u0642\u0627\u0644\u0627\u0646\",\n    \"few\": \"{count} \u0645\u0642\u0627\u0644\u0627\u062a\",\n    \"many\": \"{count} \u0645\u0642\u0627\u0644\",\n    \"other\": \"{count} \u0645\u0642\u0627\u0644\"\n  }\n}\n<\/pre>\n<p>Maintenant, nous allons mettre \u00e0 jour la fonction <code>translate<\/code> pour g\u00e9rer les messages au pluriel.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"62d15884-1e5b-4035-b937-4091ed4ac4b9\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"6,7,8,9,10,11,26,27,28,29,30\">\/\/ ...\nfunction translate(key, interpolations = {}) {\n  const message = translations[key];\n  if (key.endsWith(\"-plural\")) {\n    return interpolate(\n      pluralFormFor(message, interpolations.count),\n      interpolations,\n    );\n  }\n  return interpolate(message, interpolations);\n}\n\/\/ ...\n\/*\n  Given a forms object like\n  {\n    \"zero\": \"No articles\",\n    \"one\": \"One article\",\n    \"other\": \"{count} articles\"\n  } and a count of 3, returns \"3 articles\"\n*\/\nfunction pluralFormFor(forms, count) {\n  const matchingForm = new Intl.PluralRules(locale).select(count);\n  return forms[matchingForm];\n}\n\/\/ ...\n<\/pre>\n<p>La petite dose de magie ici, c&rsquo;est le morceau de code qui lit <code>new Intl.PluralRules(locale).select(...)<\/code>. L&rsquo;objet int\u00e9gr\u00e9 <code>Intl.PluralRules<\/code>, lorsqu\u2019on lui fournit des param\u00e8tres r\u00e9gionaux, en conna\u00eet les r\u00e8gles relatives aux formes plurielles. Par exemple, en passant <code>\"ar\"<\/code> au constructeur, puis en appelant <code>select(5)<\/code> sur l&rsquo;objet retourn\u00e9, cela renvoie la forme correcte <code>\"few\"<\/code>.<br \/>\nAvec seulement quelques lignes de code, nous avons enti\u00e8rement globalis\u00e9 la prise en charge des formes plurielles \ud83d\ude4c<\/p>\n<p style=\"text-align: center\" data-wp-editing=\"1\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15574 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/plural-forms-1024x372.png\" alt=\"Formes plurielles en arabe et en anglais | Phrase\" width=\"1024\" height=\"372\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/plural-forms-1024x372.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/plural-forms-300x109.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/plural-forms-768x279.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/plural-forms.png 1229w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><em>Notre message au pluriel affich\u00e9 en anglais et en arabe, pour diff\u00e9rents nombres<\/em><\/p>\n<h3><span class=\"ez-toc-section\" id=\"formatage-des-nombres\"><\/span>Formatage des nombres<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Trois cent mille euros s\u2019\u00e9crit \u00ab\u00a0\u20ac300,000.00\u00a0\u00bb en anglais (\u00c9tats-Unis), \u00ab\u00a0300.000,00 \u20ac\u00a0\u00bb en allemand (Allemagne), \u00ab\u00a0\u20ac3,00,000.00\u00a0\u00bb en hindi (indien) (on notera les virgules) et \u00ab\u00a0\u0663\u0660\u0660\u066c\u0660\u0660\u0660\u066b\u0660\u0660 \u20ac\u00a0\u00bb en arabe (\u00c9gypte). Comment g\u00e9rer tous ces formats\u00a0? Pas de soucis\u00a0! Nous pouvons compter sur un autre objet <code>Intl<\/code> qui fait partie de la norme JavaScript moderne. <code>Intl.NumberFormat<\/code> va faire le travail pour nous\u00a0!<br \/>\nImaginons que nous soyons des entrepreneurs d\u00e9butants et que nous voulions cr\u00e9er un site web de suivi NFT avec des statistiques num\u00e9riques.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;p\n  data-i18n-key=\"nyan-cat-price\"\n  data-i18n-opt='{\"price\": {\"number\" : 5300}}'\n&gt;\n  Nyan Cat (Official) NFT: {price}\n&lt;\/p&gt;\n<\/pre>\n<p><code>data-i18n-opt<\/code> identifie la valeur num\u00e9rique de <code>{price}<\/code> dans nos fichiers de param\u00e8tres r\u00e9gionaux. Nous allons rechercher cette cl\u00e9 <code>number<\/code> lorsque nous allons mettre \u00e0 jour notre code d\u2019interpolation\u00a0dans un instant. Tout d&rsquo;abord, nous allons fournir les traductions de nos messages.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"15136bc5-7049-41a1-a400-c01c52f8df32\" data-enlighter-title=\"\/lang\/en.json\">{\n  \"nyan-cat-price\": \"Nyan Cat (Official) NFT: {price}\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"8be65390-6187-4d54-bf60-d3d7fb8bcf39\" data-enlighter-title=\"\/lang\/ar.json\">{\n  \"nyan-cat-price\": \"\u0646\u064a\u0627\u0646 \u0643\u0627\u062a NFT: {price}\"\n}\n<\/pre>\n<p>Nous devons mettre \u00e0 jour notre JavaScript pour que cela fonctionne.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"eebd5a57-2ac6-40b8-9138-1b9ffa458059\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"3,4,5,6,13,32-43\">\/\/ ...\nconst fullyQualifiedLocaleDefaults = {\n  en: \"en-US\",\n  ar: \"ar-EG\",\n};\n\/\/ ...\nfunction interpolate(message, interpolations) {\n  return Object.keys(interpolations).reduce(\n    (interpolated, key) =&gt; {\n      const value = formatNumber(interpolations[key]);\n      return interpolated.replace(\n        new RegExp(`{\\s*${key}\\s*}`, \"g\"),\n        value,\n      );\n    },\n    message,\n  );\n}\n\/*\n  Given a value object like\n  {\n    \"number\" : 300000,\n    \"style\": \"currency\",\n    \"currency\": \"EUR\"\n  } and that the active locale is \"en\", returns \"\u20ac300,000.00\"\n*\/\nfunction formatNumber(value) {\n  if (typeof value === \"object\" &amp;&amp; value.number) {\n    const { number, ...options } = value;\n    return new Intl.NumberFormat(\n      fullyQualifiedLocaleDefaults[locale],\n      options,\n    ).format(number);\n  } else {\n    return value;\n  }\n}\n\/\/ ...\n<\/pre>\n<p>Lorsque nous interpolons nos messages traduits, nous passons d&rsquo;abord la valeur \u00e0 \u00e9changer \u00e0 travers un formateur de nombre, qui utilise l&rsquo;objet int\u00e9gr\u00e9 <code>Intl.NumberFormat<\/code>. Encore une fois, avec tr\u00e8s peu de lignes de code, nous avons localis\u00e9 le formatage des nombres.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15576 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-en-1024x450.png\" alt=\"Application de d\u00e9monstration avec format de nombre am\u00e9ricain | Phrase\" width=\"1024\" height=\"450\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-en-1024x450.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-en-300x132.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-en-768x337.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-en.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15575 size-large\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-ar-1024x450.png\" alt=\"Application de d\u00e9monstration avec formatage des nombres arabe | Phrase\" width=\"1024\" height=\"450\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-ar-1024x450.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-ar-300x132.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-ar-768x337.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-format-ar.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p>\u270b\ud83c\udffd <em>Avertissement\u00a0\u00bb<\/em> Il est pr\u00e9f\u00e9rable de passer des param\u00e8tres r\u00e9gionaux enti\u00e8rement qualifi\u00e9s, comme <code>\"en-US\"<\/code>, au constructeur <code>Intl.NumberFormat()<\/code>. Si nous passons juste un code de langue, comme <code>\"en\"<\/code>, chaque navigateur d\u00e9cidera quelle <em>r\u00e9gion<\/em> utiliser pour formater ses nombres\u00a0: un navigateur pourra utiliser par d\u00e9faut <code>\"en-US\"<\/code>, tandis qu&rsquo;un autre choisira <code>\"en-UK\"<\/code>. Alors nous utilisons une carte <code>fullyQualifiedLocaleDefaults<\/code> dans notre fonction <code>formatNumber()<\/code> pour obtenir un formatage coh\u00e9rent d&rsquo;un navigateur \u00e0 l&rsquo;autre.<\/p>\n<p>Parce que nous passons toutes les options d\u00e9finies dans notre objet d&rsquo;interpolations au constructeur <code>Intl.NumberFormat()<\/code>, nous pouvons utiliser ses <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/NumberFormat\/NumberFormat#parameters\">nombreuses options de format<\/a> chaque fois que nous le souhaitons.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;p\n  data-i18n-key=\"nyan-cat-price\"\n  data-i18n-opt='{\"price\": {\n    \"number\" : 5300,\n    \"style\": \"currency\",\n    \"currency\": \"EUR\"\n  }}'\n &gt;\n  Nyan Cat (Official) NFT: {price}\n&lt;\/p&gt;\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15578 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-en.png\" alt=\"Exemple de format de nombre am\u00e9ricain\u00a0| Phrase\" width=\"632\" height=\"94\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-en.png 632w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-en-300x45.png 300w\" sizes=\"auto, (max-width: 632px) 100vw, 632px\" \/><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15577 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-ar.png\" alt=\"Exemple de format de nombre arabe\u00a0| Phrase\" width=\"420\" height=\"104\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-ar.png 420w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/11\/number-options-ar-300x74.png 300w\" sizes=\"auto, (max-width: 420px) 100vw, 420px\" \/><\/p>\n<p>\ud83d\udd17 <em>Ressources\u00a0\u00bb<\/em> D\u00e9couvrez notre article (en anglais) <a href=\"https:\/\/phrase.com\/blog\/posts\/number-localization\/\">Concise Guide to Number Localization<\/a>.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"formatage-des-dates\"><\/span>Formatage des dates<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Tout comme les nombres, le formatage des dates est sp\u00e9cifique \u00e0 la r\u00e9gion. Le 5 d\u00e9cembre 2021, sous sa forme courte, s&rsquo;\u00e9crit \u00ab\u00a012\/5\/2021\u00a0\u00bb en anglais (US) et \u00ab\u00a05.12.2021\u00a0\u00bb en allemand (Allemagne), par exemple. L&rsquo;objet int\u00e9gr\u00e9 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/DateTimeFormat\/DateTimeFormat\">Intl.DateTimeFormat<\/a> permet de g\u00e9rer l&rsquo;essentiel du travail en ce qui concerne le formatage des dates.<br \/>\nImaginons que nous voulions indiquer la date et l&rsquo;heure de publication d&rsquo;un de nos articles.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;p\n  data-i18n-key=\"publish-date\"\n  data-i18n-opt='{\"publishDate\": {\n    \"date\": \"2021-12-05 15:29:00\"\n  }}'\n&gt;\n  Published on {publishDate}\n&lt;\/p&gt;\n<\/pre>\n<p>La cl\u00e9 sp\u00e9ciale <code>date<\/code> dans notre objet <code>data-i18n-opt<\/code> contient la valeur de date et d&rsquo;heure que nous voulons formater. Comme d&rsquo;habitude, nous allons ajouter nos messages localis\u00e9s.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"2a53594b-f0b3-42fa-8a05-45366d8f704a\" data-enlighter-title=\"\/lang\/en.json\">{\n  \/\/ ...\n  \"publish-date\": \"Published {publishDate}\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"b7482e03-350d-4750-a944-9eeb661556a1\" data-enlighter-title=\"\/lang\/ar.json\">{\n  \/\/...\n  \"publish-date\": \"\u0646\u0634\u0631 {publishDate}\"\n}\n<\/pre>\n<p>Maintenant, nous allons mettre \u00e0 jour notre syst\u00e8me de traduction pour rechercher les cl\u00e9s <code>date<\/code> et formater leurs valeurs au format localis\u00e9.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"9f97809e-bb75-48e8-85ab-d99b7aee1937\" data-enlighter-title=\"\/js\/scripts.js\" data-enlighter-highlight=\"6,7,8,30,31,32,34,35,37,38,39,40,41,42,43,44\">\/\/ ...\nfunction interpolate(message, interpolations) {\n  return Object.keys(interpolations).reduce(\n    (interpolated, key) =&gt; {\n      const value = formatDate(\n        formatNumber(interpolations[key]),\n      );\n      return interpolated.replace(\n        new RegExp(`{\\s*${key}\\s*}`, \"g\"),\n        value,\n      );\n    },\n    message,\n  );\n}\n\/\/ ...\n\/*\n  Given a value object like\n  {\n    \"date\": \"2021-12-05 15:29:00\",\n    \"dateStyle\": \"long\",\n    \"timeStyle\": \"short\"\n  } and that the current locale is en,\n  returns \"December 5, 2021 at 3:29 PM\"\n*\/\nfunction formatDate(value) {\n  if (typeof value === \"object\" &amp;&amp; value.date) {\n    const { date, ...options } = value;\n    const parsedDate =\n      typeof date === \"string\" ? Date.parse(date) : date;\n    return new Intl.DateTimeFormat(\n      fullyQualifiedLocaleDefaults[locale],\n      options,\n    ).format(parsedDate);\n  } else {\n    return value;\n  }\n}\n\/\/ ...\n<\/pre>\n<p>Apr\u00e8s avoir pass\u00e9 notre objet de valeur \u00e0 notre formateur de nombres, nous faisons un autre passage \u00e0 travers notre nouveau formateur de dates. Les options de formatage de date sont pass\u00e9es au constructeur <code>Intl.DateTimeFormat<\/code>, permettant une <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/DateTimeFormat\/DateTimeFormat#parameters\">flexibilit\u00e9 appr\u00e9ciable dans le formatage des dates<\/a>.<\/p>\n<p>\u270b\ud83c\udffd <em>Avertissement \u00bb<\/em> Nous utilisons <code>Date.parse()<\/code> ci-dessus pour nous assurer que notre cha\u00eene <code>date<\/code> est convertie en un <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Date\">objet Date<\/a>, sinon <code>Intl.DateTimeFormat<\/code> lancera une erreur.<\/p>\n<p>Voil\u00e0, nous avons un formatage de date localis\u00e9 \ud83d\udc4d<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;p\n  data-i18n-key=\"publish-date\"\n  data-i18n-opt='{\"publishDate\": {\n    \"date\": \"2021-12-05 15:29:00\",\n    \"dateStyle\": \"long\",\n    \"timeStyle\": \"short\"\n  }}'\n&gt;\n  Published on {publishDate}\n&lt;\/p&gt;\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15816 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-en.png\" alt=\"Application de d\u00e9monstration avec formatage de date am\u00e9ricain | Phrase\" width=\"1330\" height=\"696\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-en-300x157.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-en-1024x536.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-en-768x402.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15817 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-ar.png\" alt=\"Application de d\u00e9monstration avec formatage de date arabe\u00a0| Phrase\" width=\"1330\" height=\"696\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-ar-300x157.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-ar-1024x536.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/date-format-ar-768x402.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<p>\ud83e\udd3f <em>Pour approfondir \u00bb<\/em> Si vous recherchez des fonctionnalit\u00e9s de formatage de dates plus robustes, consultez l&rsquo;article (en anglais) <a href=\"https:\/\/phrase.com\/blog\/posts\/best-javascript-date-time-libraries\/\">What Is the Best JavaScript Date and Time Library?<\/a> L&rsquo;article (en anglais) <a href=\"https:\/\/phrase.com\/blog\/posts\/a-human-friendly-way-to-display-dates-in-typescript-javascript\/\">Human-friendly Way to Display Dates in TypeScript\/JavaScript<\/a> vous permet d&rsquo;obtenir un format du type \u00ab\u00a0il y a 1 heure\u00a0\u00bb.<\/p>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> Si vous utilisez un framework d\u00e9claratif comme React, vous pouvez aller plus loin avec ce que nous avons construit dans cette section et l&rsquo;article (en anglais) <a href=\"https:\/\/phrase.com\/blog\/posts\/roll-your-own-javascript-i18n-library-with-typescript-part-1\/\">Roll Your Own JavaScript i18n Library with TypeScript<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"quelles-bibliotheques-dinternationalisation-javascript-est-il-recommande-dutiliser\"><\/span>Quelles biblioth\u00e8ques d&rsquo;internationalisation JavaScript est-il recommand\u00e9 d&rsquo;utiliser\u00a0?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Nous avons expliqu\u00e9 ci-dessus comment cr\u00e9er votre propre biblioth\u00e8que i18n JavaScript. Cependant, il peut s&rsquo;av\u00e9rer plus judicieux pour votre projet d&rsquo;adopter une biblioth\u00e8que d&rsquo;internationalisation pr\u00eate \u00e0 l&#8217;emploi. Les options ne manquent pas. Dans cet article, nous allons passer en revue l&rsquo;utilisation des biblioth\u00e8ques Polyglot, i18next et Globalize.<br \/>\nPour une s\u00e9lection encore plus large, ces articles peuvent vous \u00eatre utiles :<\/p>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/the-best-javascript-i18n-libraries\/\">The Best JavaScript I18n Libraries<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/best-javascript-date-time-libraries\/\">What Is the Best JavaScript Date and Time Library?<\/a><\/li>\n<\/ul>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> Si vous travaillez avec l&rsquo;ancien gettext, jetez un \u0153il \u00e0 la <a href=\"https:\/\/github.com\/messageformat\/Jed\">biblioth\u00e8que Jed<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"comment-localiser-une-page-web-avec-polyglot\"><\/span>Comment localiser une page web avec Polyglot ?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Maintenue par Airbnb, <a href=\"https:\/\/airbnb.io\/polyglot.js\/\">Polyglot<\/a> est une petite biblioth\u00e8que d&rsquo;internationalisation qui r\u00e9sout certains probl\u00e8mes de localisation auparavant non pris en charge par les biblioth\u00e8ques standard de JavaScript. Parmi les fonctionnalit\u00e9s de Polyglot, la plus remarquable est son excellente gestion des formes plurielles. Cependant, comme nous l&rsquo;avons mentionn\u00e9 pr\u00e9c\u00e9demment, <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/PluralRules\/PluralRules\">constructeur Intl.PluralRules<\/a> est d\u00e9sormais pris en charge par tous les navigateurs modernes et permet de r\u00e9soudre facilement le probl\u00e8me de la pluralisation. Cependant, la derni\u00e8re version de cet article mettait fortement en avant Polyglot ; donc nous voulions inclure cette section au cas o\u00f9 certains de nos lecteurs auraient encore besoin d&rsquo;un guide pour Polyglot.<\/p>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> \u00c0 moins que votre cas d&rsquo;utilisation ne n\u00e9cessite Polyglot, consultez la <a href=\"#How_do_I_localize_a_web_page_with_i18next\">biblioth\u00e8que d&rsquo;internationalisation dans la section suivante<\/a> avant de choisir une solution de localisation.<\/p>\n<p>Sans plus attendre, nous allons localiser notre application de d\u00e9monstration avec la biblioth\u00e8que de localisation d&rsquo;Airbnb :<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"125ae0d3-e7f7-4d16-b5c2-2b8f1ebc5198\" data-enlighter-title=\"public\/index.html\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;ul class=\"navbar-list navbar-start\"&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"home\" class=\"navbar-link\"&gt;\n              Home\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"about\" class=\"navbar-link\"&gt;\n              About\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n        &lt;\/ul&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;With Polyglot&lt;\/h1&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Cadence\"}'&gt;\n      Welcome to my little spot on the interwebs, %{username}!\n    &lt;\/p&gt;\n    &lt;p\n      data-i18n-key=\"article-plural\"\n      data-i18n-opt='{\"smart_count\": 2}'\n    &gt;\n      %{smart_count} articles written and counting.\n    &lt;\/p&gt;\n  &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15818 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-before-i18n.png\" alt=\"Application de d\u00e9monstration JavaScript avec Polyglot | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-before-i18n.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-before-i18n-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-before-i18n-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-before-i18n-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\nPassons maintenant \u00e0 la localisation avec Polyglot.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"installation\"><\/span>Installation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Polyglot a quelques d\u00e9pendances <a href=\"https:\/\/www.npmjs.com\/\">NPM<\/a>, donc il a besoin que <a href=\"https:\/\/nodejs.org\/en\/\">Node<\/a> soit install\u00e9 localement. Node \u00e9tant install\u00e9, nous pouvons initialiser un fichier <code>package.json<\/code> pour notre application de d\u00e9monstration en ex\u00e9cutant ce qui suit depuis la ligne de commande.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm init -y\n<\/pre>\n<p>Afin de regrouper nos d\u00e9pendances NPM dans un fichier que les navigateurs peuvent lire, nous allons installer le bundler de modules <a href=\"https:\/\/webpack.js.org\/\">Webpack<\/a> en tant que d\u00e9pendances de d\u00e9veloppement. Le <a href=\"https:\/\/github.com\/webpack\/webpack-dev-server\">serveur de d\u00e9veloppement Webpack<\/a> nous aidera avec le rechargement \u00e0 chaud de notre bundle dans le navigateur pendant que nous d\u00e9veloppons.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install --save-dev webpack webpack-cli webpack-dev-server\n<\/pre>\n<p>Maintenant, nous allons installons la star du spectacle : Polyglot.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install node-polyglot\n<\/pre>\n<p>Un <code>index.js<\/code> servira de point d&rsquo;entr\u00e9e pour notre application, et nous pouvons l&rsquo;utiliser pour faire un test rapide des installations des biblioth\u00e8ques.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"13c36abe-a6bd-4f2f-a7d5-0661798eb598\" data-enlighter-title=\"src\/index.js\">import Polyglot from \"node-polyglot\";\nconsole.log({ Polyglot });\n<\/pre>\n<p>Un script <code>start<\/code> dans notre <code>package.json<\/code> facilitera le d\u00e9veloppement en lan\u00e7ant le serveur de d\u00e9veloppement avec notre config personnalis\u00e9e.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"d15750f8-71f2-491d-bf0e-ad4c16fc24bb\" data-enlighter-title=\"package.json\" data-enlighter-highlight=\"7\">{\n  \"name\": \"polyglot-demo\",\n  \/\/ ...\n  \"scripts\": {\n    \"start\": \"webpack-dev-server --config webpack.config.js\"\n  },\n  \/\/ ...\n  \"devDependencies\": {\n    \"webpack\": \"^5.65.0\",\n    \"webpack-cli\": \"^4.9.1\",\n    \"webpack-dev-server\": \"^4.6.0\"\n  },\n  \"dependencies\": {\n    \"node-polyglot\": \"^2.4.2\"\n  }\n}\n<\/pre>\n<p>\ud83d\uddd2 <em>Remarque\u00a0: <\/em> Nous utilisons un fichier <code>webpack.config.js<\/code> relativement simple pour regrouper notre application et configurer le serveur de d\u00e9veloppement. <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/blob\/main\/polyglot\/webpack.config.js\">Consultez-le dans notre d\u00e9p\u00f4t Git sur GitHub<\/a>.<\/p>\n<p>Maintenant, nous allons int\u00e9grer notre JS regroup\u00e9 juste avant la balise de fermeture <code>&lt;\/body&gt;<\/code> dans notre fichier <code>public\/index.html<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">&lt;script src=\".\/bundle.js\"&gt;&lt;\/script&gt;\n<\/pre>\n<p>Ceci \u00e9tant fait, nous devrions \u00eatre en mesure d&rsquo;ex\u00e9cuter notre script <code>start<\/code> depuis la ligne de commande pour initialiser le serveur de d\u00e9veloppement Webpack.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm start\n<\/pre>\n<p>Si tout se passe bien, notre application devrait s&rsquo;ouvrir automatiquement dans le navigateur. Si nous ouvrons les outils de d\u00e9veloppement de notre navigateur, nous devrions voir des journaux de console similaires \u00e0 ceux ci-dessous.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15819 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-webpack-server-browser-output.png\" alt=\"Journaux de console des outils de d\u00e9veloppement du navigateur\u00a0| Phrase\" width=\"1430\" height=\"342\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-webpack-server-browser-output.png 1430w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-webpack-server-browser-output-300x72.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-webpack-server-browser-output-1024x245.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-webpack-server-browser-output-768x184.png 768w\" sizes=\"auto, (max-width: 1430px) 100vw, 1430px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"traductions-de-base\"><\/span>Traductions de base<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Passons \u00e0 l\u2019utilisation de base de Polyglot. Voici comment proc\u00e9der :<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">\/\/ 1. Import the library\nimport Polyglot from \"node-polyglot\";\n\/\/ 2. Create an instance\nconst polyglot = new Polyglot();\n\/\/ 3. Add translation messages for the active locale\npolyglot.extend({\n  \"app-title\": \"With Polyglot\",\n});\n\/\/ 4. Use the messages to translate page elements\nconst element = document.querySelector(\n  \"[data-i18n-key='app-title']\",\n);\n\/\/ polyglot.t() resolves a translation message given\n\/\/ a key\nelement.innerHTML = polyglot.t(\"app-title\");\n<\/pre>\n<p>Pour changer de param\u00e8tres r\u00e9gionaux, nous pouvons recharger la page avec les messages du nouveau param\u00e8tre r\u00e9gional.<\/p>\n<p>\u270b\ud83c\udffd <em>Avertissement\u00a0\u00bb<\/em> Nous utilisons <code>innerHTML<\/code> dans cet article pour d\u00e9finir le contenu de l&rsquo;\u00e9l\u00e9ment. Faites attention \u00e0 cet attribut en production ; assurez-vous d&rsquo;assainir tout HTML que vous injectez en utilisant <code>innerHTML<\/code> afin d&rsquo;\u00e9viter les attaques XSS (cross-site scripting).<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-highlight=\"6\">import Polyglot from \"node-polyglot\";\nconst polyglot = new Polyglot();\npolyglot.extend({\n  \"app-title\": \"\u0645\u0639 \u0628\u0648\u0644\u064a\u062c\u0644\u0648\u062a\",\n});\nconst element = document.querySelector(\n  \"[data-i18n-key='app-title']\",\n);\nelement.innerHTML = polyglot.t(\"app-title\");\n<\/pre>\n<h3><span class=\"ez-toc-section\" id=\"chargement-asynchrone-des-fichiers-de-traduction\"><\/span>Chargement asynchrone des fichiers de traduction<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Bien que ce qui pr\u00e9c\u00e8de fonctionne bien pour les plus petites applications, nous pourrions faire un peu mieux en s\u00e9parant nos messages de traduction dans des fichiers JSON distincts par param\u00e8tres r\u00e9gionaux.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"d3cbfa7d-9584-422b-ae6b-da4ae0f6239e\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \"app-title\": \"With Polyglot\",\n  \"home\": \"Home\",\n  \"about\": \"About\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"33d548fc-48fe-4e4f-a852-51da5f540ca6\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \"app-title\": \"\u0645\u0639 \u0628\u0648\u0644\u064a\u062c\u0644\u0648\u062a\",\n  \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n  \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\"\n}\n<\/pre>\n<p>Maintenant, nous pouvons configurer des param\u00e8tres r\u00e9gionaux par d\u00e9faut pour notre application et charger ses traductions depuis le r\u00e9seau lorsque notre page se charge.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"72b66df6-d597-40ec-bbda-ef066a489055\" data-enlighter-title=\"src\/index.js\">import Polyglot from \"node-polyglot\";\nconst defaultLocale = \"en\";\nconst polyglot = new Polyglot();\n\/\/ Load translation messages from the network\nasync function loadTranslations(locale) {\n  return await fetch(`\/lang\/${locale}.json`).then(\n    (response) =&gt; response.json(),\n  );\n}\n\/\/ Translate all elements on the page that have our custom\n\/\/ data-i18n-key attribute\nfunction translatePage() {\n  const translatableElements = document.querySelectorAll(\n    \"[data-i18n-key]\",\n  );\n  translatableElements.forEach((el) =&gt; {\n    const key = el.getAttribute(\"data-i18n-key\");\n    el.innerHTML = polyglot.t(key);\n  });\n}\n\/\/ Init\n(async function () {\n  const translations = await loadTranslations(\n    defaultLocale,\n  );\n  polyglot.extend(translations);\n  translatePage();\n})();\n<\/pre>\n<p>Nous obtenons ainsi le rendu suivant dans le navigateur.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15820 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-en.png\" alt=\"Application de d\u00e9monstration en anglais avec Polyglot, \u00e9l\u00e9ments de menu et titre principal manquants | Phrase\" width=\"1430\" height=\"978\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-en.png 1430w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-en-300x205.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-en-1024x700.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-en-768x525.png 768w\" sizes=\"auto, (max-width: 1430px) 100vw, 1430px\" \/><br \/>\nNos \u00e9l\u00e9ments de menu de navigation et le titre principal sont traduits dans nos param\u00e8tres r\u00e9gionaux par d\u00e9faut (l&rsquo;anglais). Cependant, remarquez les erreurs Polyglot dans la console, et comment les cl\u00e9s manquantes (<code>lead<\/code> et <code>article-plural<\/code>) affichent la valeur de la cl\u00e9 elle-m\u00eame. Nous ajouterons des traductions pour ces cl\u00e9s dans un instant pour corriger cela.<br \/>\nSi nous modifions <code>defaultLocale<\/code> en <code>\"ar\"<\/code>, nous obtenons le rendu suivant.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15821 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-ar.png\" alt=\"Application de d\u00e9monstration en arabe avec Polyglot, \u00e9l\u00e9ments de menu et titre principal manquants | Phrase\" width=\"1436\" height=\"978\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-ar.png 1436w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-ar-300x204.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-ar-1024x697.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-async-ar-768x523.png 768w\" sizes=\"auto, (max-width: 1436px) 100vw, 1436px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"selecteur-de-langue\"><\/span>S\u00e9lecteur de langue<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Notre application est maintenant plus \u00e9volutive puisque seules les traductions des param\u00e8tres r\u00e9gionaux actifs sont charg\u00e9es. Nous allons maintenant construire un s\u00e9lecteur de langue. Nous avons d\u00e9j\u00e0 le HTML pour le s\u00e9lecteur dans notre application\u00a0:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"dc20fe15-b6c0-48a7-bca9-d5e1be114c97\" data-enlighter-title=\"public\/index.html\" data-enlighter-highlight=\"18,19,20,21\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n\t\t&lt;!-- ... --&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;!-- ... --&gt;\n  &lt;script src=\".\/bundle.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Le fait d&rsquo;interagir avec cet \u00e9l\u00e9ment <code>&lt;select&gt;<\/code> depuis notre JavaScript nous permet d&rsquo;ajouter notre comportement de changement de param\u00e8tres r\u00e9gionaux.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"07b2796c-fc57-4ec8-aa9a-398d7edf394f\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"11,12,14,16,17,21,22,23,24,26,28,29,30,31,34,35\">import Polyglot from \"node-polyglot\";\nconst defaultLocale = \"en\";\nconst polyglot = new Polyglot();\n\/\/ ...\n\/\/ Load translations for the given locale and translate\n\/\/ page elements for this locale\nasync function loadAndTranslate(locale) {\n  const translations = await loadTranslations(locale);\n  polyglot.replace(translations);\n  translatePage();\n}\n\/\/ Whenever the user switches the active locale, load\n\/\/ this locale's messages into the page\nfunction bindLocaleSwitcher(initialValue) {\n  const switcher = document.querySelector(\n    \"[data-i18n-switcher]\",\n  );\n  switcher.value = initialValue;\n  switcher.onchange = (e) =&gt; {\n    loadAndTranslate(e.target.value);\n  };\n}\n\/\/ Init\nloadAndTranslate(defaultLocale);\nbindLocaleSwitcher(defaultLocale);\n<\/pre>\n<p>Nous avons refactoris\u00e9 le code qui charge nos messages de traduction et traduit nos \u00e9l\u00e9ments de page en une fonction r\u00e9utilisable <code>loadAndTranslate()<\/code>. Une nouvelle fonction <code>bindLocaleSwitcher()<\/code> s&rsquo;int\u00e8gre dans le s\u00e9lecteur de langue <code>&lt;select&gt;<\/code>\u00a0; elle utilise <code>loadAndTranslate()<\/code> pour actualiser nos traductions en fonction des param\u00e8tres r\u00e9gionaux s\u00e9lectionn\u00e9s par l&rsquo;utilisateur.<\/p>\n<p>\u270b\ud83c\udffd <em>Avertissement \u00bb<\/em> <code>polyglot.extend()<\/code> va <em>ajouter<\/em> des messages de traduction \u00e0 ceux d\u00e9j\u00e0 charg\u00e9s, donc nous utilisons <code>polyglot.replace()<\/code> \u00e0 la place pour nous assurer que nous ne chargeons que les traductions du param\u00e8tre r\u00e9gional actif.<\/p>\n<p>Cela devrait faire fonctionner notre \u00e9l\u00e9gant s\u00e9lecteur de param\u00e8tres r\u00e9gionaux.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15822 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-lang-switcher.gif\" alt=\"Application de d\u00e9monstration avec Polyglot avec s\u00e9lecteur de param\u00e8tres r\u00e9gionaux fixe | Phrase\" width=\"600\" height=\"217\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"interpolation-2\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Notre texte principal inclut le nom de l&rsquo;utilisateur actuellement connect\u00e9 (fictif, bien s\u00fbr). Ce type de valeur interpol\u00e9e est g\u00e9r\u00e9 par Polyglot en utilisant une syntaxe sp\u00e9ciale <code>%{variable}<\/code> par d\u00e9faut.<\/p>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> Vous pouvez modifier les caract\u00e8res qui d\u00e9signent les valeurs interpol\u00e9es en utilisant l&rsquo;option <code>interpolation<\/code> <a href=\"https:\/\/airbnb.io\/polyglot.js\/#options-overview\">pass\u00e9e au constructeur Polyglot<\/a>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"daf6cafc-7812-46e9-bcd7-963798dbd65f\" data-enlighter-title=\"public\/index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Cadence\"}'&gt;\n      Welcome to my little spot on the interwebs, %{username}!\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"a78a94fe-dc3d-48c0-bf90-185531c0411d\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \/\/ ...\n  \"lead\": \"Welcome to my little spot on the interwebs, %{username}!\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"7868539a-4ab0-4bdc-823e-614ea96bbd06\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \/\/ ...\n  \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a \u064a\u0627 %{username}.\"\n}\n<\/pre>\n<p>Quelques lignes de code peuvent \u00eatre ajout\u00e9es \u00e0 notre fonction <code>translatePage()<\/code> pour prendre en charge les interpolations.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"5bd2109a-45be-407b-9277-b08354101d9c\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"13,14,15,16,17,18,20,21,22\">\/\/ ...\n\/\/ Translate all elements on the page that have a\n\/\/ data-i18n-key attribute\nfunction translatePage() {\n  const translatableElements = document.querySelectorAll(\n    \"[data-i18n-key]\",\n  );\n  translatableElements.forEach((el) =&gt; {\n    const key = el.getAttribute(\"data-i18n-key\");\n    \/\/ Extract interpolation key\/values from the HTML and\n    \/\/ parse them to JSON\n    const interpolations = el.getAttribute(\"data-i18n-opt\");\n    const parsedInterpolations = interpolations\n      ? JSON.parse(interpolations)\n      : {};\n    \/\/ Pass the parsed interpolations to polyglot.t(),\n    \/\/ which automatically handles substitutions\n    el.innerHTML = polyglot.t(key, parsedInterpolations);\n  });\n}\n\/\/ ...\n<\/pre>\n<p>Gr\u00e2ce au code ci-dessus, nous avons maintenant notre paragraphe d&rsquo;introduction interpol\u00e9 dans les param\u00e8tres r\u00e9gionaux actifs.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15824 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-en.png\" alt=\"Application de d\u00e9monstration des param\u00e8tres r\u00e9gionaux anglais avec Polyglot avec paragraphe d&apos;introduction correct | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15825 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-ar.png\" alt=\"Application de d\u00e9monstration des param\u00e8tres r\u00e9gionaux arabes avec Polyglot avec paragraphe d&apos;introduction correct | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-interpolation-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"formes-plurielles-2\"><\/span>Formes plurielles<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Imaginons que nous voulions indiquer \u00e0 l&rsquo;utilisateur actuellement connect\u00e9 combien de messages il ou elle a re\u00e7us\u00a0: Par exemple, \u00ab\u00a0Vous avez 1 nouveau message\u00a0\u00bb ou \u00ab\u00a0Vous avez 12 nouveaux messages\u00a0\u00bb. Polyglot g\u00e8re bien les pluriels de cette mani\u00e8re. Nous avons juste \u00e0 ajouter nos messages de traduction avec la valeur num\u00e9rique interpol\u00e9e sp\u00e9ciale <code>smart_count<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"dbe32626-2aef-4da8-9c76-db1941115eff\" data-enlighter-title=\"public\/index.html\">&lt;!-- ... --&gt;\n    &lt;p\n      data-i18n-key=\"new-messages\"\n      data-i18n-opt='{\"smart_count\": 12}'\n    &gt;\n      You have %{smart_count} new messages\"\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>Polyglot utilise <code>smart_count<\/code> pour s\u00e9lectionner la forme plurielle correcte d&rsquo;un message de traduction en fonction des param\u00e8tres r\u00e9gionaux actifs. Les formes plurielles sont s\u00e9par\u00e9es par quatre pipes <code>||||<\/code> dans nos messages. L&rsquo;anglais a <code>un<\/code> et <code>autre<\/code> formes plurielles, et nous devons les fournir dans l&rsquo;ordre :<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"effada7e-fa5e-4980-bade-8326330e9360\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \/\/ ...\n  \"new-messages\": \"You have %{smart_count} new message |||| You have %{smart_count} new messages\"\n}\n<\/pre>\n<p>L&rsquo;arabe a six formes plurielles, et nous les ajoutons \u00e0 nos messages de la m\u00eame mani\u00e8re.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"e551b929-010f-4505-857f-566dad4d8275\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \/\/ ...\n  \"new-messages\": \"\u0644\u0627 \u062a\u0648\u062c\u062f \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629 |||| \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629 |||| \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u062a\u0627\u0646 \u062c\u062f\u0627\u062f |||| \u0644\u062f\u064a\u0643 %{smart_count} \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629 |||| \u0644\u062f\u064a\u0643 %{smart_count} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629 |||| \u0644\u062f\u064a\u0643 %{smart_count} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\"\n}\n<\/pre>\n<p>\ud83d\uddd2 <em>Remarque\u00a0\u00bb<\/em> Consultez la section <a href=\"#Plurals\">Comment localiser une page web avec JavaScript\u00a0? \u279e Pluriels<\/a> pour plus de d\u00e9tails sur les formes plurielles.<\/p>\n<p>\u00c0 noter : par d\u00e9faut, Polyglot n&rsquo;est pas conscient des param\u00e8tres r\u00e9gionaux actifs, donc il ne conna\u00eetra pas les r\u00e8gles plurielles de ces param\u00e8tres \u00e0 moins que nous ne sp\u00e9cifiions explicitement les param\u00e8tres r\u00e9gionaux lors du chargement.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"4ffe4b2d-4324-4b53-a227-3d0fa25322c6\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"8\">\/\/ ...\n\/\/ Load translations for the given locale and translate\n\/\/ page elements for this locale\nasync function loadAndTranslate(locale) {\n  const translations = await loadTranslations(locale);\n  polyglot.locale(locale);\n  polyglot.replace(translations);\n  translatePage();\n}\n\/\/ ...\n<\/pre>\n<p>Voil\u00e0, c&rsquo;est termin\u00e9\u00a0! Notre application prend d\u00e9sormais en charge les pluriels de mani\u00e8re avanc\u00e9e.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15826 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-en.png\" alt=\"Application de d\u00e9monstration des param\u00e8tres r\u00e9gionaux anglais avec le comptage intelligent de Polyglot | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15827 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-ar.png\" alt=\"Application de d\u00e9monstration des param\u00e8tres r\u00e9gionaux arabes avec le comptage intelligent de Polyglot | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/polyglot-plurals-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> Obtenez tout le code de notre application Polyglot sur <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/polyglot\">notre d\u00e9p\u00f4t GitHub<\/a>.<\/p>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> La <a href=\"https:\/\/airbnb.io\/polyglot.js\/\">documentation officielle de Polyglot<\/a> est aussi concise que la biblioth\u00e8que elle-m\u00eame.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"comment-localiser-une-page-web-avec-i18next\"><\/span>Comment localiser une page web avec i18next\u00a0?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Au moment o\u00f9 j&rsquo;\u00e9cris ces lignes, <a href=\"https:\/\/www.i18next.com\/\">i18next<\/a> est l&rsquo;une des <a href=\"https:\/\/www.npmtrends.com\/globalize-vs-i18next-vs-node-polyglot-vs-react-intl\">biblioth\u00e8ques i18n JavaScript les plus populaires<\/a>. La biblioth\u00e8que <a href=\"https:\/\/www.i18next.com\/#learn-once-translate-everywhere\">learn once, [use] everywhere<\/a> fonctionne de mani\u00e8re autonome et avec une multitude de frameworks JavaScript. Un riche \u00e9cosyst\u00e8me de plug-ins signifie que vous \u00eates souvent \u00e0 une installation NPM de r\u00e9soudre un probl\u00e8me i18n courant. Nous vous recommandons donc d&rsquo;utiliser i18next.<br \/>\nBon, tr\u00eave de bavardages\u00a0! Nous allons reprendre notre petite d\u00e9monstration et la localiser en utilisant i18next.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"ebba1c2b-e62b-4423-8548-a8ccb4bcccdf\" data-enlighter-title=\"public\/index.html\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;ul class=\"navbar-list navbar-start\"&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"home\" class=\"navbar-link\"&gt;\n              Home\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"about\" class=\"navbar-link\"&gt;\n              About\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n        &lt;\/ul&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;With i18next&lt;\/h1&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Zelda\"}'&gt;\n      Welcome to my little spot on the interwebs, {{username}}!\n    &lt;\/p&gt;\n    &lt;p data-i18n-key=\"new-messages\" data-i18n-opt='{\"count\": 12}'&gt;\n      You have {{count}} new messages\n    &lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;script src=\".\/bundle.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Pas grand-chose de nouveau ici. Passons \u00e0 la localisation.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"installation-2\"><\/span>Installation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Nous allons utiliser <a href=\"https:\/\/nodejs.org\/en\/\">Node<\/a> et son gestionnaire de paquets <a href=\"https:\/\/www.npmjs.com\/\">NPM<\/a> pour installer i18next. Tout d&rsquo;abord, cr\u00e9ons un fichier <code>package.json<\/code> pour suivre les d\u00e9pendances du projet et les scripts NPM en ex\u00e9cutant ce qui suit depuis la ligne de commande.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm init -y\n<\/pre>\n<p>Le <a href=\"https:\/\/webpack.js.org\/\">Webpack<\/a> bundler nous permettra de regrouper i18next, ses plug-ins et notre JavaScript personnalis\u00e9 et de les servir dans un seul fichier au navigateur. Installons Webpack, ainsi que son serveur de d\u00e9veloppement, qui dispose d&rsquo;une fonctionnalit\u00e9 de rechargement \u00e0 chaud pratique qui facilite le d\u00e9veloppement :<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install --save-dev webpack webpack-cli webpack-dev-server\n<\/pre>\n<p>Nous ne pouvons pas oublier notre biblioth\u00e8que i18n, bien s\u00fbr :<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install i18next\n<\/pre>\n<p>Un script pratique <code>npm start<\/code> peut servir de raccourci pour lancer notre serveur de d\u00e9veloppement.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"ff750f62-93c9-495b-8b54-ffae0aa84a85\" data-enlighter-title=\"package.json\" data-enlighter-highlight=\"7\">{\n  \"name\": \"i18next-demo\",\n  \/\/...\n  \"scripts\": {\n    \"start\": \"webpack-dev-server --config webpack.config.js\"\n  },\n  \/\/ ...\n  \"devDependencies\": {\n    \"webpack\": \"^5.65.0\",\n    \"webpack-cli\": \"^4.9.1\",\n    \"webpack-dev-server\": \"^4.6.0\"\n  },\n  \"dependencies\": {\n    \"i18next\": \"^21.6.3\"\n  }\n}\n<\/pre>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> Nous utilisons un fichier <code>webpack.config.js<\/code> relativement simple pour regrouper notre application et configurer le serveur de d\u00e9veloppement. <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/blob\/main\/i18next\/webpack.config.js\">Consultez-le dans notre d\u00e9p\u00f4t Git sur GitHub<\/a>.<\/p>\n<p>Cr\u00e9ons un <code>index.js<\/code> point d&rsquo;entr\u00e9e pour notre application et testons i18next pour nous assurer qu&rsquo;il est install\u00e9 correctement.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"c346dcb9-2358-4eb7-b8a7-cbd6553cc9f1\" data-enlighter-title=\"src\/index.js\">import i18next from \"i18next\";\nconsole.log({ i18next });\n<\/pre>\n<p>Maintenant, lorsque nous ex\u00e9cutons <code>npm start<\/code> depuis la ligne de commande, nous devrions voir le serveur Webpack d\u00e9marrer et charger notre application dans notre navigateur automatiquement.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15828 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-post-install.png\" alt=\"Application de d\u00e9monstration avec le serveur de d\u00e9veloppement Webpack charg\u00e9 | Phrase\" width=\"1434\" height=\"1024\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-post-install.png 1434w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-post-install-300x214.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-post-install-1024x731.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-post-install-768x548.png 768w\" sizes=\"auto, (max-width: 1434px) 100vw, 1434px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"messages-de-base-de-traduction\"><\/span>Messages de base de traduction<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>i18next est flexible dans la fa\u00e7on dont il accepte les messages de traduction. Nous faisons la plupart de notre configuration lors de l&rsquo;initialisation de la biblioth\u00e8que avec <code>i18next.init(...)<\/code>\u00a0. La configuration la plus basique consiste \u00e0 inclure nos messages de traduction sous une option <code>resources<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"6b18cd08-103b-40a0-965c-4d0635e3b780\" data-enlighter-title=\"src\/index.js\">import i18next from \"i18next\";\ni18next.init({\n  \/\/ The active locale\n  lng: \"en\",\n  \/\/ Enabled useful console output when developing\n  debug: true,\n  \/\/ Translation messages, keyed by locale code\n  resources: {\n    en: {\n      \/\/ By default, i18next expects messages under the\n      \/\/ \"translation\" namespace\n      translation: {\n        \"app-title\": \"With Polyglot\",\n        home: \"Home\",\n        about: \"About\",\n      },\n    },\n    ar: {\n      translation: {\n        \"app-title\": \"\u0645\u0639 \u0628\u0648\u0644\u064a\u062c\u0644\u0648\u062a\",\n        home: \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n        about: \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\",\n      },\n    },\n  },\n});\n\/\/ Translate page elements\nconst translatableElements = document.querySelectorAll(\n  \"[data-i18n-key]\",\n);\ntranslatableElements.forEach((el) =&gt; {\n  const key = el.getAttribute(\"data-i18n-key\");\n  el.innerHTML = i18next.t(key);\n});\n<\/pre>\n<p>Avec cela, nous ne devrions voir aucun changement lorsque notre application se rechargera dans le navigateur. Cependant, si nous modifions <code>lng<\/code> en <code>\"ar\"<\/code>, nous voyons les traductions arabes suivantes.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15880 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-basic-translations-1.png\" alt=\"D\u00e9mo d&apos;application avec param\u00e8tres r\u00e9gionaux arabes et cl\u00e9 manquante | Phrase\" width=\"1434\" height=\"1270\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-basic-translations-1.png 1434w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-basic-translations-1-300x266.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-basic-translations-1-1024x907.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-basic-translations-1-768x680.png 768w\" sizes=\"auto, (max-width: 1434px) 100vw, 1434px\" \/><\/p>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> L&rsquo;option <code>debug: true<\/code> permet d&rsquo;activer des journaux de console tr\u00e8s pratiques dans le navigateur. Notez les messages cl\u00e9s manquants ci-dessus, par exemple.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"chargement-asynchrone-des-traductions-2\"><\/span>Chargement asynchrone des traductions<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>En tant que d\u00e9veloppeurs tourn\u00e9s vers l&rsquo;avenir, rendons notre application plus \u00e9volutive en divisant nos traductions en fichiers s\u00e9par\u00e9s, un par param\u00e8tres r\u00e9gionaux.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"c49398ad-5069-4bad-a35c-b2c6c85dd670\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \"app-title\": \"Avec i18next\",\n  \"home\": \"Home\",\n  \"about\": \"About\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"23ed2f14-52e9-41d3-979d-79e3fbd1c07d\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \"app-title\": \"\u0645\u0639 \u0622\u064a \u0623\u064a\u062a\u064a\u0646 \u0646\u064a\u0643\u0633\u062a\",\n  \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n  \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\"\n}\n<\/pre>\n<p>i18next est mature et couvre beaucoup de bases ; donc nous n&rsquo;avons pas besoin d&rsquo;\u00e9crire notre propre code pour charger des fichiers de traduction depuis le r\u00e9seau. Le <a href=\"https:\/\/github.com\/i18next\/i18next-http-backend\">backend HTTP officiel<\/a> s&rsquo;int\u00e8gre \u00e0 la biblioth\u00e8que et fait tout le travail pour nous. Nous allons l&rsquo;installer.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install i18next-http-backend\n<\/pre>\n<p>Nous pouvons maintenant int\u00e9grer le backend dans notre <code>index.js<\/code> et <code>utiliser()<\/code> lors de l&rsquo;initialisation d&rsquo;i18next.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"f7604a6a-683e-43b1-b53b-e043f26d4a97\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"2,4,5,6,7,8,9,10,14,16,17,19,20,21,22,23,24,26,27,40,41,42,43\">import i18next from \"i18next\";\nimport HttpApi from \"i18next-http-backend\";\n\/\/ We make the function async so we can await\n\/\/ the translation file as it pipes down the\n\/\/ network\nasync function initI18next() {\n  \/\/ We use() the backend and await it to load\n  \/\/ the translations from the network\n  await i18next.use(HttpApi).init({\n    lng: \"en\",\n    debug: true,\n    \/\/ Remove inlined `resources`\n    \/\/ Disable loading of dev locale\n    fallbackLng: false,\n    \/\/ Configure Http backend\n    backend: {\n      loadPath: \"\/lang\/{{lng}}.json\",\n    },\n  });\n}\n\/\/ Quick refactor of the page translation code\n\/\/ to a function\nfunction translatePageElements() {\n  const translatableElements = document.querySelectorAll(\n    \"[data-i18n-key]\",\n  );\n  translatableElements.forEach((el) =&gt; {\n    const key = el.getAttribute(\"data-i18n-key\");\n    el.innerHTML = i18next.t(key);\n  });\n}\n\/\/ Init\n(async function () {\n  await initI18next();\n  translatePageElements();\n})();\n<\/pre>\n<p>L&rsquo;option <code>backend.loadPath<\/code> remplace le chemin de fichier de traduction par d\u00e9faut du backend : Un espace r\u00e9serv\u00e9 sp\u00e9cial <code>{{lng}}<\/code> est remplac\u00e9 par les param\u00e8tres r\u00e9gionaux actifs. Par exemple, lorsque notre application se charge pour la premi\u00e8re fois, le backend cherchera <code>\/lang\/en.json<\/code>, puisque nous avons sp\u00e9cifi\u00e9 les param\u00e8tres r\u00e9gionaux par d\u00e9faut comme <code>en<\/code> plus t\u00f4t dans notre configuration.<br \/>\nC&rsquo;est presque termin\u00e9. Nos traductions se chargent maintenant depuis le r\u00e9seau au lieu d&rsquo;\u00eatre int\u00e9gr\u00e9es dans notre code.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"parametres-regionaux-pris-en-charge-et-solution-de-secours\"><\/span>Param\u00e8tres r\u00e9gionaux pris en charge et solution de secours<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Nous voulons souvent sp\u00e9cifier une liste de param\u00e8tres r\u00e9gionaux que notre application prend en charge et un param\u00e8tre r\u00e9gional de secours lorsqu&rsquo;une traduction est manquante. Nous pouvons utiliser les options de configuration <code>supportLngs<\/code> et <code>fallbackLng<\/code> d\u2019i18next, respectivement, pour y parvenir.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"80f41c54-7e75-43b6-902e-a1e086c14cf1\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"7,8\">\/\/ ...\nasync function initI18next() {\n  await i18next.use(HttpApi).init({\n    lng: \"en\",\n    debug: true,\n    supportedLngs: [\"en\", \"ar\"],\n    fallbackLng: \"en\",\n    backend: {\n      loadPath: \"\/lang\/{{lng}}.json\",\n    },\n  });\n}\n\/\/ ...\n<\/pre>\n<p>\u270b\ud83c\udffd <em>Avertissement \u00bb<\/em> Le param\u00e8tre linguistique de secours sera <em>toujours<\/em> charg\u00e9, peu importe le param\u00e8tre linguistique actif.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"detection-automatique-des-parametres-regionaux-de-lutilisateur\"><\/span>D\u00e9tection automatique des param\u00e8tres r\u00e9gionaux de l\u2019utilisateur<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Il est courant de vouloir d\u00e9tecter les param\u00e8tres du navigateur de l&rsquo;utilisateur et d&rsquo;utiliser ses param\u00e8tres r\u00e9gionaux si nous les prenons en charge. C&rsquo;est normalement un peu d\u00e9licat, mais encore une fois, i18next a un <a href=\"https:\/\/github.com\/i18next\/i18next-browser-languageDetector\">plug-in officiel<\/a> qui peut nous aider rapidement ici. Nous pouvons commencer par l&rsquo;installer.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install i18next-browser-languagedetector\n<\/pre>\n<p>Tout comme le backend HTTP, nous <code>importer<\/code> le plug-in de d\u00e9tection et <code>l'utiliser()<\/code> lors de l&rsquo;initialisation.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"8ffa7f23-6818-4876-8036-17aa252c47cf\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"3,8,14-16\" data-enlighter-linenumbers=\"false\">import i18next from \"i18next\";\nimport HttpApi from \"i18next-http-backend\";\nimport LanguageDetector from \"i18next-browser-languagedetector\";\nasync function initI18next() {\n  await i18next\n    .use(HttpApi)\n    .use(LanguageDetector)\n    .init({\n      debug: true,\n      supportedLngs: [\"en\", \"ar\"],\n      fallbackLng: \"en\",\n      \/\/ Allow \"en\" to be used for\n      \/\/ \"en-US\", \"en-CA\", etc.\n      nonExplicitSupportedLngs: true,\n      backend: {\n        loadPath: \"\/lang\/{{lng}}.json\",\n      },\n    });\n}\n\/\/ ...\n<\/pre>\n<p>Et c&rsquo;est tout ce qu&rsquo;il faut pour obtenir une d\u00e9tection automatique solide des param\u00e8tres r\u00e9gionaux avec i18next.<\/p>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> Vous vous demandez peut-\u00eatre quels crit\u00e8res le d\u00e9tecteur de param\u00e8tres r\u00e9gionaux utilise pour d\u00e9terminer les param\u00e8tres r\u00e9gionaux de l&rsquo;utilisateur. Nous couvrons cela en d\u00e9tail dans notre <a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-react-apps-with-i18next\/#Automatically_Detecting_the_Users_Language\">Guide de la localisation React avec i18next<\/a>.<\/p>\n<p>\u270b\ud83c\udffd <em>Avertissement \u00bb<\/em> Le d\u00e9tecteur de param\u00e8tres r\u00e9gionaux stockera les param\u00e8tres r\u00e9gionaux qu&rsquo;il d\u00e9tecte dans le stockage des param\u00e8tres r\u00e9gionaux du navigateur par d\u00e9faut, et utilisera <em>cette<\/em> valeur lorsque l&rsquo;utilisateur visitera notre site \u00e0 nouveau.<\/p>\n<p>\ud83e\udd3f <em>Pour approfondir \u00bb<\/em> Nous avons un guide d\u00e9di\u00e9 <a href=\"https:\/\/phrase.com\/fr\/blog\/posts\/detecting-a-users-locale\/\">Comment d\u00e9tecter la pr\u00e9f\u00e9rence de langue du navigateur avec JavaScript<\/a>, qui pourrait vous int\u00e9resser.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"selecteur-de-langue-2\"><\/span>S\u00e9lecteur de langue<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>La d\u00e9tection automatique des param\u00e8tres r\u00e9gionaux est utile, mais il nous faut souvent une interface utilisateur permettant \u00e0 nos utilisateurs de d\u00e9finir explicitement leur langue de pr\u00e9dilection. Nous avons d\u00e9j\u00e0 le balisage pour un s\u00e9lecteur de param\u00e8tres r\u00e9gionaux configur\u00e9.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"0ae136e3-6aab-4d56-8a59-449d5efe8768\" data-enlighter-title=\"public\/index.html\" data-enlighter-highlight=\"18,19,20,21\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n      &lt;!-- ... --&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/   &gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;script src=\".\/bundle.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Int\u00e9grons ce HTML et utilisons la fonction <code>i18next.changeLanguage()<\/code> pour d\u00e9finir le param\u00e8tre r\u00e9gional actif en fonction du choix de l&rsquo;utilisateur. Apr\u00e8s le chargement des messages des param\u00e8tres r\u00e9gionaux, nous pouvons encha\u00eener <code>translatePageElements<\/code> pour r\u00e9afficher la page avec les traductions mises \u00e0 jour.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"799f4601-6390-44a3-ba97-66e0fd7f1412\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"3,4,5,6,8,10,11,12,13,14,15,21\">\/\/ ...\nfunction bindLocaleSwitcher(initialValue) {\n  const switcher = document.querySelector(\n    \"[data-i18n-switcher]\",\n  );\n  switcher.value = initialValue;\n  switcher.onchange = (e) =&gt; {\n    i18next\n      .changeLanguage(e.target.value)\n      .then(translatePageElements);\n  };\n}\n\/\/ Init\n(async function () {\n  await initI18next();\n  translatePageElements();\n  bindLocaleSwitcher(i18next.resolvedLanguage);\n})();\n<\/pre>\n<p>\u270b\ud83c\udffd <em>Avertissement \u00bb<\/em> Le d\u00e9tecteur de param\u00e8tres r\u00e9gionaux pourrait avoir d\u00e9tect\u00e9 des param\u00e8tres r\u00e9gionaux que nous ne prenons pas en charge, et <em>cette<\/em> valeur existera dans <code>i18next.language<\/code>. Nous utilisons <code>i18next.resolvedLanguage<\/code> ci-dessus pour nous assurer que nous utilisons le param\u00e8tre r\u00e9gional actif, <em>pris en charge<\/em> lors de l&rsquo;initialisation de notre s\u00e9lecteur de param\u00e8tres r\u00e9gionaux.<\/p>\n<p>Et voil\u00e0 ! Une interface utilisateur de changement de langue :<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15830 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-locale-switcher.gif\" alt=\"Application de d\u00e9monstration Polyglot avec une interface utilisateur de changement de langue | Phrase\" width=\"600\" height=\"217\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"interpolation-3\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Par d\u00e9faut, i18next utilise une syntaxe <code>{{variable}}<\/code> pour d\u00e9signer les valeurs interpol\u00e9es dans les messages de traduction.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"5a3233fd-74a1-4c21-aec9-5a5dad9750a3\" data-enlighter-title=\"public\/index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Zelda\"}'&gt;\n      Welcome to my little spot on the interwebs, {{username}}!\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"9fedd78c-4683-4235-ba4e-4e91a3117bd9\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \/\/ ...\n  \"lead\": \"Welcome to my little spot on the interwebs, {{username}}!\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"7d7ce46a-079e-4239-91c3-3cf21ccf0310\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \/\/ ...\n  \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a \u064a\u0627 {{username}}.\"\n}\n<\/pre>\n<p>Nous pouvons tirer ces valeurs dynamiques de nos attributs HTML et les transmettre \u00e0 <code>i18next.t()<\/code>, qui g\u00e8re l&rsquo;interpolation pour nous.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"85eb09f1-0bcf-49d3-b44a-e803b326513f\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"11,12,13,14,16\">\/\/ ...\nfunction translatePageElements() {\n  const translatableElements = document.querySelectorAll(\n    \"[data-i18n-key]\",\n  );\n  translatableElements.forEach((el) =&gt; {\n    const key = el.getAttribute(\"data-i18n-key\");\n    const interpolations = el.getAttribute(\"data-i18n-opt\");\n    const parsedInterpolations = interpolations\n      ? JSON.parse(interpolations)\n      : {};\n    el.innerHTML = i18next.t(key, parsedInterpolations);\n  });\n}\n\/\/ ...\n<\/pre>\n<p>Avec cela en place, nos valeurs dynamiques sont remplac\u00e9es dans nos messages.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15831 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-en.png\" alt=\"Application de d\u00e9monstration avec Polyglot en param\u00e8tres r\u00e9gionaux anglais et paragraphe d&apos;introduction interpol\u00e9 | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15832 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-ar.png\" alt=\"Application de d\u00e9monstration avec Polyglot en param\u00e8tres r\u00e9gionaux arabes et paragraphe d&apos;introduction interpol\u00e9 | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-interpolation-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"formes-plurielles-3\"><\/span>Formes plurielles<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>En coulisses, i18next essaie d&rsquo;utiliser le standard <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/PluralRules\/PluralRules\">Intl.PluralRules<\/a> pour g\u00e9rer les pluriels. Une variable interpol\u00e9e sp\u00e9ciale <code>count<\/code> dirige le choix de la forme plurielle, en fonction du param\u00e8tre r\u00e9gional actif.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"6f1ac8ea-b04b-4ffa-97ff-0d5d961018fb\" data-enlighter-title=\"public\/index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n-key=\"new-messages\" data-i18n-opt='{\"count\": 12}'&gt;\n      You have {{count}} new messages\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>i18next utilise une convention <code>message_form<\/code> pour les cl\u00e9s de message pluriel. Par exemple, pour g\u00e9rer les formes <code>one<\/code> et <code>other<\/code> dans une traduction anglaise <code>new-messages<\/code>, nous pouvons sp\u00e9cifier ce qui suit.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"f5f7aa95-6e4d-4517-8245-ba0595110e31\" data-enlighter-title=\"public\/lang\/en.json\">{\n  \/\/ ...\n  \"new-messages_one\": \"You have {{count}} new message\",\n  \"new-messages_other\": \"You have {{count}} new messages\"\n}\n<\/pre>\n<p>L&rsquo;arabe a six formes plurielles, et nous pouvons les sp\u00e9cifier \u00e0 peu pr\u00e8s de la m\u00eame mani\u00e8re.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"a4211fe0-4e56-4b3c-a0de-83e9fb259bc9\" data-enlighter-title=\"public\/lang\/ar.json\">{\n  \/\/ ...\n  \"new-messages_zero\": \"\u0644\u0627 \u062a\u0648\u062c\u062f \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_one\": \"\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_two\": \"\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u062a\u0627\u0646 \u062c\u062f\u0627\u062f\",\n  \"new-messages_few\": \"\u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_many\": \"\u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_other\": \"\u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\"\n}\n<\/pre>\n<p>Avec peu d&rsquo;effort, notre application peut afficher des messages au pluriel.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15833 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-en.png\" alt=\"Application de d\u00e9monstration avec Polyglot en param\u00e8tres r\u00e9gionaux anglais et pluralisation | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15834 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-ar.png\" alt=\"Application de d\u00e9monstration avec Polyglot en param\u00e8tres r\u00e9gionaux arabes et pluralisation | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/i18next-plurals-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> Obtenez tout le code que nous avons couvert ci-dessus depuis notre <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/i18next\">d\u00e9p\u00f4t GitHub<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"comment-localiser-une-application-react-angular-ou-vue\"><\/span>Comment localiser une application React, Angular ou Vue ?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Ces derni\u00e8res ann\u00e9es, des frameworks d\u00e9claratifs comme <a href=\"https:\/\/reactjs.org\/\">React<\/a>, <a href=\"https:\/\/angular.io\/\">Angular<\/a>, <a href=\"https:\/\/vuejs.org\/\">Vue.js<\/a> et d&rsquo;autres ont pris d&rsquo;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&rsquo;autres frameworks d\u00e9claratifs, consultez les articles approfondis suivants.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"articles-sur-la-localisation-angular\"><\/span>Articles sur la localisation Angular<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/angular-localization-i18n\/\">Traduire les applications Angular avec le module I18n int\u00e9gr\u00e9<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/best-libraries-for-angular-i18n\/\">Quelle est la meilleure biblioth\u00e8que Angular pour l&rsquo;internationalisation\u00a0?<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/angular-l10n-with-i18next\/\">L10n Angular avec I18next<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/angular-10-tutorial-localization-transloco\/\">Tutoriel Angular 10 sur la localisation avec Transloco<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/full-stack-i18n-angular-net-core\/\">I18n Full-Stack avec Angular et .NET Core<\/a><\/li>\n<\/ul>\n<h3><span class=\"ez-toc-section\" id=\"articles-de-localisation-vuejs\"><\/span>Articles de localisation Vue.js<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/ultimate-guide-to-vue-localization-with-vue-i18n\/\">Le guide ultime de localisation Vue\u00a03<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/vue-translation-with-vue-i18next\/\">Plong\u00e9e approfondie\u00a0: Traduction Vue avec vue-i18next\u00a0<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/nuxt-js-tutorial-i18n\/\">Le seul tutoriel Nuxt.js sur l&rsquo;internationalisation dont vous aurez jamais besoin<\/a> (Bas\u00e9 sur Vue)<\/li>\n<\/ul>\n<h3><span class=\"ez-toc-section\" id=\"articles-de-localisation-pour-dautres-frameworks\"><\/span>Articles de localisation pour d&rsquo;autres frameworks<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Nous r\u00e9alisons que certains de nos lecteurs pourraient utiliser Svelte, Next ou un autre framework, donc nous \u00e9crivons toujours sur les nouveaut\u00e9s les plus r\u00e9centes. Voici une s\u00e9lection de guides pour localiser l&rsquo;application que vous construisez dans votre framework pr\u00e9f\u00e9r\u00e9 :<\/p>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/how-to-localize-a-svelte-app-with-svelte-i18n\/\">Comment localiser une application Svelte avec svelte-i18n<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/a-step-by-step-guide-to-svelte-localization-with-svelte-i18n-v3\/\">Un guide \u00c9tape par \u00e9tape pour la localisation Svelte avec svelte-i18n v3<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/\">Localiser les applications SolidJS avec I18next<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-mithril-applications\/\">Localisation des applications Mithril<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-aureliajs-applications\/\">Comment localiser des applications avec le Framework Aurelia<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-stimulusjs-i18next\/\">Localiser les applications StimulusJS avec I18next<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/full-stack-javascript-i18n\/\">JavaScript Full-Stack I18n \u00e9tape par \u00e9tape<\/a> (en utilisant Next.js et Sails.js)<\/li>\n<\/ul>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> Pour \u00eatre plus pr\u00e9cis\u00a0: Si vous allez travailler avec des num\u00e9ros de t\u00e9l\u00e9phone internationaux, assurez-vous de jeter un \u0153il \u00e0 la <a href=\"https:\/\/phrase.com\/blog\/posts\/libphonenumber-international-phone-numbers\/\">libphonenumber library<\/a>. C&rsquo;est ind\u00e9pendant du framework !<\/p>\n<h2><span class=\"ez-toc-section\" id=\"comment-localiser-une-application-react-avec-i18next\"><\/span>Comment localiser une application React avec i18next ?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Comme promis, nous allons passer en revue rapidement un guide pour localiser nos applications React avec la biblioth\u00e8que i18next. Nous avons d\u00e9j\u00e0 couvert i18next plus t\u00f4t dans cet article, donc nous allons nous concentrer sur son int\u00e9gration \u00e0 React ici.<\/p>\n<p>\ud83e\udd3f <em>Pour approfondir \u00bb<\/em> <a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-react-apps-with-i18next\/\">Un guide de la localisation React avec i18next<\/a> va plus loin et plus en profondeur que notre bref aper\u00e7u ici.<\/p>\n<p>Tout d&rsquo;abord, nous allons prendre notre pr\u00e9cieuse application de d\u00e9monstration et la diviser en composants React.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"08d116ad-cce1-45d7-b2aa-3e30e3c99bf5\" data-enlighter-title=\"src\/App.js\">import \".\/App.css\";\nimport Navbar from \".\/layout\/Navbar\";\nfunction App() {\n  return (\n    &lt;div className=\"container\"&gt;\n      &lt;Navbar \/&gt;\n      &lt;h1&gt;React i18n&lt;\/h1&gt;\n      &lt;p&gt;\n        Welcome to my little spot on the interwebs, user\n      &lt;\/p&gt;\n      &lt;p&gt;You have count new messages&lt;\/p&gt;\n    &lt;\/div&gt;\n  );\n}\nexport default App;\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"08acb43f-245a-4d89-b514-85c2056fd238\" data-enlighter-title=\"src\/layout\/Navbar.js\">import LocaleSwitcher from \"..\/features\/LocaleSwitcher\";\nfunction Navbar() {\n  return (\n    &lt;nav className=\"navbar\"&gt;\n      &lt;div className=\"container\"&gt;\n        &lt;ul className=\"navbar-list navbar-start\"&gt;\n          &lt;li className=\"navbar-item\"&gt;\n            &lt;a href=\"#\" className=\"navbar-link\"&gt;\n              Home\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n          &lt;li className=\"navbar-item\"&gt;\n            &lt;a href=\"#\" className=\"navbar-link\"&gt;\n              About\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n        &lt;\/ul&gt;\n        &lt;div className=\"navbar-end\"&gt;\n          &lt;LocaleSwitcher \/&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n  );\n}\nexport default Navbar;\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"ebef0bfc-8f59-4073-ba13-411c09225f66\" data-enlighter-title=\"src\/features\/LocaleSwitcher.js\">function LocaleSwitcher() {\n  return (\n    &lt;&gt;\n      &lt;img\n        alt=\"Translation icon\"\n        src=\"img\/translation-icon@2x.png\"\n        className=\"translation-icon\"\n      \/&gt;\n      &lt;select className=\"locale-switcher\"&gt;\n        &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n        &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n      &lt;\/select&gt;\n    &lt;\/&gt;\n  );\n}\nexport default LocaleSwitcher;\n<\/pre>\n<p>Le s\u00e9lecteur de param\u00e8tres r\u00e9gionaux fait peu de choses pour le moment, mais nous y rem\u00e9dierons bient\u00f4t. Pour l&rsquo;instant, nous avons un Starter solide \u00e0 localiser.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15836 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-starter.png\" alt=\"Application de d\u00e9monstration React en anglais avec la biblioth\u00e8que i18n | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-starter.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-starter-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-starter-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-starter-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"installation-de-la-bibliotheque\"><\/span>Installation de la biblioth\u00e8que<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>En plus d&rsquo;i18next, nous allons r\u00e9cup\u00e9rer le cadre d&rsquo;int\u00e9gration officiel <a href=\"https:\/\/react.i18next.com\/\">react-i18next <\/a>, qui facilite l&rsquo;utilisation d&rsquo;i18next avec React. Depuis la racine du projet, ex\u00e9cutons ce qui suit dans la ligne de commande pour installer les biblioth\u00e8ques.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install i18next react-i18next\n<\/pre>\n<p>Ensuite, initialisons i18next, en <code>utilisant()<\/code> l&rsquo;int\u00e9gration React comme nous le faisons. La mani\u00e8re la plus basique de fournir des traductions \u00e0 i18next est d&rsquo;utiliser l&rsquo;option <code>resources<\/code> lors de l&rsquo;initialisation.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"89a353c7-4c36-417b-a10f-68d97405c398\" data-enlighter-title=\"src\/services\/i18n.js\">import i18next from \"i18next\";\nimport { initReactI18next } from \"react-i18next\";\ni18next.use(initReactI18next).init({\n  resources: {\n    en: {\n      translation: {\n        \"app-title\": \"With React\",\n      },\n    },\n    ar: {\n      translation: {\n        \"app-title\": \"\u0645\u0639 \u0631\u064a\u0623\u0643\u062a\",\n      },\n    },\n  },\n  lng: \"en\",\n  debug: true,\n  interpolation: {\n    escapeValue: false,\n  },\n});\nexport default i18next;\n<\/pre>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> Nous avons d\u00e9fini <code>interpolation.escapeValue<\/code> sur <code>false<\/code> pour <a href=\"https:\/\/www.i18next.com\/translation-function\/interpolation#unescape\">d\u00e9sactiver l&rsquo;\u00e9chappement par d\u00e9faut que i18next effectue pour se prot\u00e9ger contre les attaques XSS<\/a>, puisque React le fait d\u00e9j\u00e0 pour nous.<\/p>\n<p>Importons notre module dans notre fichier racine <code>index.js<\/code> afin de pouvoir initialiser i18next lorsque notre application se charge.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"e0298a27-1532-4aa8-a85d-17ce5bf38526\" data-enlighter-title=\"src\/index.js\" data-enlighter-highlight=\"5\">import React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport \".\/lib\/skeleton\/normalize.css\";\nimport \".\/lib\/skeleton\/skeleton.css\";\nimport \".\/services\/i18n\";\nimport App from \".\/App\";\nimport reportWebVitals from \".\/reportWebVitals\";\nReactDOM.render(\n  &lt;React.StrictMode&gt;\n    &lt;App \/&gt;\n  &lt;\/React.StrictMode&gt;,\n  document.getElementById(\"root\"),\n);\n\/\/ ...\n<\/pre>\n<h3><span class=\"ez-toc-section\" id=\"traductions-de-base-2\"><\/span>Traductions de base<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>La fonction de traduction famili\u00e8re <code>i18next.t()<\/code> peut \u00eatre utilis\u00e9e dans nos composants React. Nous devons juste importer le hook React <code>useTranslation<\/code> afin de rendre <code>t<\/code> disponible.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-highlight=\"1,6,12\">import { useTranslation } from \"react-i18next\";\nimport Navbar from \".\/layout\/Navbar\";\nimport \".\/App.css\";\nfunction App() {\n  const { t } = useTranslation();\n  return (\n    &lt;div className=\"container\"&gt;\n      &lt;Navbar \/&gt;\n      &lt;h1&gt;{t(\"app-title\")}&lt;\/h1&gt;\n      \/\/ ...\n    &lt;\/div&gt;\n  );\n}\nexport default App;\n<\/pre>\n<p>Lorsque notre application se recharge, tout devrait \u00eatre identique. Cependant, si nous modifions la valeur <code>lng<\/code> en <code>\"ar\"<\/code> dans notre initialiseur <code>src\/services\/i18n.js<\/code>, nous devrions voir le titre de notre application localis\u00e9 en arabe.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15837 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-basic-ar.png\" alt=\"Application de d\u00e9monstration React avec biblioth\u00e8que i18n et titre en arabe | Phrase \" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-basic-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-basic-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-basic-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-basic-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"chargement-asynchrone-de-fichiers-de-traduction\"><\/span>Chargement asynchrone de fichiers de traduction<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Rendons notre application plus \u00e9volutive en divisant nos traductions en fichiers par param\u00e8tres r\u00e9gionaux. Le plug-in officiel <a href=\"https:\/\/github.com\/i18next\/i18next-http-backend\">i18next-http-backend<\/a>, pratique, fait de cela une t\u00e2che rapide pour nous. Nous allons l&rsquo;installer.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">npm install i18next-http-backend\n<\/pre>\n<p>Le backend cherchera des fichiers \u00e0 <code>\/locales\/{{lng}}\/{{ns}}.json<\/code> par d\u00e9faut, o\u00f9 <code>{{lng}}<\/code> correspond aux param\u00e8tres r\u00e9gionaux actifs, et <code>{{ns}}<\/code> correspond \u00e0 l&rsquo;espace de noms actif. Puisque l&rsquo;espace de noms par d\u00e9faut est <code>translation<\/code>, nous pouvons mettre nos messages de traduction en anglais dans <code>public\/locales\/en\/translation.json<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"22ff6381-59c4-4834-85c2-b2485183bb89\" data-enlighter-title=\"public\/locales\/en\/translation.json\">{\n  \"app-title\": \"With React\",\n  \"home\": \"Home\",\n  \"about\": \"About\"\n}\n<\/pre>\n<p>Notre fichier arabe suit la m\u00eame convention :<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"081af770-67c2-44e7-a080-d8082020f2ec\" data-enlighter-title=\"public\/locales\/ar\/translation.json\">{\n  \"app-title\": \"\u0645\u0639 \u0631\u064a\u0623\u0643\u062a\",\n  \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n  \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\"\n}\n<\/pre>\n<p>Nous devons maintenant juste importer le backend et <code>utiliser()<\/code> lors de l&rsquo;initialisation d&rsquo;i18next. Nous voudrons \u00e9galement retirer nos traductions en ligne sous la cl\u00e9 <code>resources<\/code>, puisque le plugin chargera maintenant nos messages de traduction depuis le r\u00e9seau.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"a8be33ed-ee58-4094-849b-8c996a1750fa\" data-enlighter-title=\"src\/services\/i18n.js\" data-enlighter-highlight=\"3,7,10\">import i18next from \"i18next\";\nimport { initReactI18next } from \"react-i18next\";\nimport HttpApi from \"i18next-http-backend\";\ni18next\n  .use(initReactI18next)\n  .use(HttpApi)\n  .init({\n    \/\/ Remove `resources`\n    lng: \"en\",\n    debug: true,\n    interpolation: {\n      escapeValue: false,\n    },\n  });\nexport default i18next;\n<\/pre>\n<p>Lorsque notre application se recharge, nous devrions voir exactement le m\u00eame rendu localis\u00e9. Bien s\u00fbr, les traductions des param\u00e8tres r\u00e9gionaux actifs sont maintenant transmises sur le r\u00e9seau, donc notre application est plus l\u00e9g\u00e8re au chargement, et plus facile \u00e0 mettre \u00e0 l&rsquo;\u00e9chelle et \u00e0 maintenir.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15838 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-async.png\" alt=\"Application de d\u00e9monstration React avec chargement de fichier de traduction asynchrone | Phrase\" width=\"1434\" height=\"1234\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-async.png 1434w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-async-300x258.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-async-1024x881.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-async-768x661.png 768w\" sizes=\"auto, (max-width: 1434px) 100vw, 1434px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"selecteur-de-langue-3\"><\/span>S\u00e9lecteur de langue<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Construire une interface utilisateur de changement de langue est un jeu d&rsquo;enfant avec React et i18next. Nous pouvons mettre \u00e0 jour notre <code>ComposantLocaleSwitcher<\/code>, contr\u00f4lant le <code>&lt;select&gt;<\/code> \u00e0 l&rsquo;int\u00e9rieur pour modifier les param\u00e8tres r\u00e9gionaux actifs selon le choix de l&rsquo;utilisateur.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"686af5ef-bb97-4677-b31f-3e6b0710d4f7\" data-enlighter-title=\"src\/features\/LocaleSwitcher.js\" data-enlighter-highlight=\"1,4,16,17,18,19\">import { useTranslation } from \"react-i18next\";\nfunction LocaleSwitcher() {\n  const { i18n } = useTranslation();\n  return (\n    &lt;&gt;\n      &lt;img\n        src=\"img\/translation-icon@2x.png\"\n        alt=\"Translation icon\"\n        className=\"translation-icon\"\n      \/&gt;\n      &lt;select\n        className=\"locale-switcher\"\n        value={i18n.language}\n        onChange={(e) =&gt;\n          i18n.changeLanguage(e.target.value)\n        }\n      &gt;\n        &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n        &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n      &lt;\/select&gt;\n    &lt;\/&gt;\n  );\n}\nexport default LocaleSwitcher;\n<\/pre>\n<p>L&rsquo;int\u00e9gration React d&rsquo;i18next garantit que les traductions sont re-rendues lorsque les param\u00e8tres r\u00e9gionaux actifs sont modifi\u00e9s.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15839 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/react-lang-switcher.gif\" alt=\"Application de d\u00e9monstration React avec s\u00e9lecteur de langue fonctionnel | Phrase\" width=\"600\" height=\"217\" \/><\/p>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> Obtenez le code pour tout ce que nous avons construit ci-dessus depuis <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/react\">notre d\u00e9p\u00f4t GitHub<\/a>.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"articles-de-localisation-react\"><\/span>Articles de localisation React<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Nous aimons \u00e9crire sur React dans notre blog, donc nous sommes heureux de vous donner une s\u00e9lection de nos analyses approfondies et de nos tutoriels bas\u00e9s sur React, tous centr\u00e9s sur la localisation :<\/p>\n<ul>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-react-apps-with-i18next\/\">Guide de localisation de React avec i18next<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-react-apps-with-i18next\/\">D\u00e9buter l&rsquo;internationalisation du contenu JavaScript avec i18next et Moment.js<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/react-redux-tutorial-internationalization-with-react-i18n-redux\/\">Tutoriel React Redux\u00a0: Internationalisation avec react-i18n-redux <\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/roll-your-own-i18n-solution-react-redux\/\">Cr\u00e9ez votre propre solution i18n avec React et Redux<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-javascript-react-apps-with-linguijs\/\">Localiser des applications JavaScript et React avec LinguiJS<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localized-server-side-rendering-with-react\/\">Rendu c\u00f4t\u00e9 serveur localis\u00e9 avec React<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-meteor-applications-react\/\">Localiser des applications Meteor propuls\u00e9es par React<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/react-native-i18n-with-expo-and-i18next-part-1\/\">Un guide complet de la localisation de React Native<\/a><\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/i18n-with-gatsby\/\">Tout ce que vous devez savoir sur i18n avec Gatsby<\/a> (bas\u00e9 sur React)<\/li>\n<li><a href=\"https:\/\/phrase.com\/blog\/posts\/full-stack-javascript-i18n\/\">I18n JavaScript Full-Stack \u00e9tape par \u00e9tape<\/a> (en utilisant Next.js, bas\u00e9 sur React)<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"comment-localiser-une-page-web-avec-jquery-et-i18next\"><\/span>Comment localiser une page web avec jQuery et i18next ?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Bien que ce ne soit plus aussi en vogue qu&rsquo;il y a quelques ann\u00e9es, <a href=\"https:\/\/jquery.com\/\">jQuery<\/a> reste l&rsquo;une des biblioth\u00e8ques JavaScript les plus populaires et est encore largement utilis\u00e9e aujourd&rsquo;hui. Vous constaterez que la localisation des applications jQuery est assez facile avec la biblioth\u00e8que <a href=\"https:\/\/www.i18next.com\/\">i18next<\/a> . Un <a href=\"https:\/\/github.com\/i18next\/jquery-i18next\">plugin jQuery i18next officiel<\/a> n\u00e9cessite tr\u00e8s peu de travail pour \u00eatre configur\u00e9, alors utilisons-le pour localiser notre fid\u00e8le application de d\u00e9monstration.<\/p>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> Nous avons couvert <a href=\"#How_do_I_localize_a_web_page_with_i18next\">i18next en d\u00e9tail<\/a> plus t\u00f4t dans cet article, donc nous allons nous concentrer sur l&rsquo;int\u00e9gration jQuery ici.<\/p>\n<p>Si vous avez suivi, le Starter suivant vous semblera familier. Elle sert de bonne base pour notre travail de localisation.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"93953e8f-9e89-4fa3-a120-0357ddc3d91b\" data-enlighter-title=\"index.html\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;ul class=\"navbar-list navbar-start\"&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n=\"home\" class=\"navbar-link\"&gt;\n              Home\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n=\"about\" class=\"navbar-link\"&gt;\n              About\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n        &lt;\/ul&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;h1 data-i18n=\"app-title\"&gt;jQuery i18n&lt;\/h1&gt;\n    &lt;p data-i18n=\"lead\" data-i18n-options='{\"username\": \"Jackie\"}'&gt;\n      Welcome to my little spot on the interwebs, {{username}}!\n    &lt;\/p&gt;\n    &lt;p data-i18n=\"new-messages\" data-i18n-options='{\"count\": 3}'&gt;\n      You have {{count}} new messages\n    &lt;\/p&gt;\n  &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> Par d\u00e9faut, le plug-in jQuery i18next utilise <code>data-i18n<\/code> (pas notre pr\u00e9c\u00e9dent <code>data-i18n-key<\/code>) pour ses cl\u00e9s de traduction. Ce <a href=\"https:\/\/github.com\/i18next\/jquery-i18next#initialize-the-plugin\">peut \u00eatre modifi\u00e9 dans les options du plugin<\/a>.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15883 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-start.png\" alt=\"D\u00e9mo jQuery | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-start.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-start-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-start-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-start-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<p>Il est temps de localiser ? Allons-y !<\/p>\n<h3><span class=\"ez-toc-section\" id=\"installation-3\"><\/span>Installation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>La mani\u00e8re la plus simple d&rsquo;installer i18next et son plug-in jQuery est de t\u00e9l\u00e9charger leurs fichiers de distribution minifi\u00e9s et de les inclure dans notre HTML. Vous pouvez r\u00e9cup\u00e9rer les fichiers aux emplacements suivants.<\/p>\n<ul>\n<li><a href=\"https:\/\/code.jquery.com\/jquery-3.6.0.min.js\">jQuery minifi\u00e9<\/a><\/li>\n<li><a href=\"https:\/\/unpkg.com\/i18next\/dist\/umd\/i18next.min.js\">i18next minifi\u00e9<\/a><\/li>\n<li><a href=\"https:\/\/raw.githubusercontent.com\/i18next\/jquery-i18next\/master\/jquery-i18next.min.js\">plug-in jQuery i18next minifi\u00e9<\/a><\/li>\n<\/ul>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> Consultez la <a href=\"https:\/\/github.com\/i18next\/jquery-i18next#introduction\">documentation officielle du plug-in jQuery i18next<\/a> sur GitHub.<\/p>\n<p>Apr\u00e8s avoir t\u00e9l\u00e9charg\u00e9 les fichiers ci-dessus, nous pouvons les placer dans un r\u00e9pertoire <code>js\/lib<\/code> dans notre projet et les int\u00e9grer dans notre page HTML principale.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"699ed787-7851-4f9d-a589-affb7b514112\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"13,14,15,17,18\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n   &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;script src=\".\/js\/lib\/jquery-3.6.0.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/lib\/i18next.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/lib\/jquery-i18next.min.js\"&gt;&lt;\/script&gt;\n  &lt;!-- Notre JavaScript personnalis\u00e9, va arriver dans un instant \u2026 --&gt;\n  &lt;script src=\".\/js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<h3><span class=\"ez-toc-section\" id=\"traductions-de-base-3\"><\/span>Traductions de base<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Avec nos biblioth\u00e8ques install\u00e9es, nous pouvons maintenant \u00e9crire un code de configuration pour faire fonctionner la localisation de base.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"e193e879-0e13-4ea2-916d-6237d777b668\" data-enlighter-title=\"js\/scripts.js\">\/\/ Initialiser i18next\ni18next.init({\n  lng: \"en\",     \/\/ Param\u00e8tre r\u00e9gional initial\n  debug: true,   \/\/ Fournit des messages utiles dans la console\n  resources: {   \/\/ Traductions\n    en: {\n      translation: {\n        \"app-title\": \"jQuery + i18next\",\n      },\n    },\n    ar: {\n      translation: {\n        \"app-title\": \"\u062c\u064a \u0643\u0648\u064a\u0631\u064a + \u0622\u064a \u0625\u064a\u062a\u064a\u0646 \u0646\u064a\u0643\u0633\u062a\",\n      },\n    },\n  },\n});\n\/\/ Initialiser le plug-in jQuery i18next\njqueryI18next.init(i18next, $);\n\/\/ Translate page elements\n$(\"body\").localize();\n<\/pre>\n<p>Nous avons couvert <code>i18next.init(...)<\/code> plus t\u00f4t dans cet article. Notez qu&rsquo;ici nous avons \u00e9galement un appel <code>jQueryI18next.init(...)<\/code>. Dans sa forme la plus basique, la fonction <code>init<\/code> du plug-in jQuery i18next prend l&rsquo;instance active <code>i18next<\/code> ainsi qu&rsquo;une r\u00e9f\u00e9rence \u00e0 l&rsquo;objet jQuery, <code>$<\/code>.<br \/>\nLorsque le plug-in est initialis\u00e9, il ajoute une fonction <code>localize()<\/code> \u00e0 jQuery. Appeler <code>$(selector).localize()<\/code> provoque la localisation de tous les \u00e9l\u00e9ments sous la hi\u00e9rarchie s\u00e9lectionn\u00e9e par le plug-in. Pour chaque \u00e9l\u00e9ment, si un attribut <code>data-i18n<\/code> est trouv\u00e9, sa traduction correspondante dans les param\u00e8tres r\u00e9gionaux actifs est \u00e9chang\u00e9e.<br \/>\nPar exemple\u00a0:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">\/\/ In our JavaScript\ni18next.init({\n  lng: \"en\",\n  resources: {\n    en: {\n      translation: {\n        \"app-title\": \"jQuery + i18next\",\n      },\n    },\n    ar: {\n      translation: {\n        \"app-title\": \"\u062c\u064a \u0643\u0648\u064a\u0631\u064a + \u0622\u064a \u0625\u064a\u062a\u064a\u0646 \u0646\u064a\u0643\u0633\u062a\",\n      },\n    },\n  },\n});\njqueryI18next.init(i18next, $);\n$(\"#main-title\").localize();\n\/\/ Dans notre HTML\n&lt;h1 id=\"main-title\" data-i18n=\"app-title\"&gt;&lt;\/h1&gt;\n\/\/ S'affiche comme:\n&lt;h1 id=\"main-title\" data-i18n=\"app-title\"&gt;jQuery + i18next&lt;\/h1&gt;\n<\/pre>\n<p>Simple et agr\u00e9able \ud83d\ude0a<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15884 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-en.png\" alt=\"application de d\u00e9monstration jQuery avec la biblioth\u00e8que i18next charg\u00e9e avec le param\u00e8tre linguistique anglais | Phrase\" width=\"1430\" height=\"1284\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-en.png 1430w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-en-300x269.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-en-1024x919.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-en-768x690.png 768w\" sizes=\"auto, (max-width: 1430px) 100vw, 1430px\" \/><\/div>\n<p>Et si nous modifions nos param\u00e8tres r\u00e9gionaux initiaux en arabe en changeant <code>lng<\/code> en <code>\"ar\"<\/code>, nous obtenons un titre en arabe \u00e0 la place.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15885 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-ar.png\" alt=\"application de d\u00e9monstration jQuery avec la biblioth\u00e8que i18next charg\u00e9e en param\u00e8tres r\u00e9gionaux arabes | Phrase\" width=\"1434\" height=\"1280\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-ar.png 1434w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-ar-300x268.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-ar-1024x914.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-install-ar-768x686.png 768w\" sizes=\"auto, (max-width: 1434px) 100vw, 1434px\" \/><\/div>\n<h3><span class=\"ez-toc-section\" id=\"chargement-asynchrone-de-fichiers-de-traduction-2\"><\/span>Chargement asynchrone de fichiers de traduction<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><em>Que diriez-vous de diviser nos fichiers de traduction en fichiers s\u00e9par\u00e9s, un par param\u00e8tres r\u00e9gionaux ?<\/em> J&rsquo;entends que vous vous le demandez. Pas de soucis, le <a href=\"https:\/\/github.com\/i18next\/i18next-http-backend\">plug-in HTTP officiel i18next<\/a> nous couvre.<br \/>\nPour installer le plug-in, nous pouvons <a href=\"https:\/\/raw.githubusercontent.com\/i18next\/i18next-http-backend\/master\/i18nextHttpBackend.min.js\">r\u00e9cup\u00e9rer le script de distribution minifi\u00e9 depuis GitHub<\/a> et le placer dans notre <code>r\u00e9pertoire js\/lib<\/code>. Bien s\u00fbr, nous voudrons aussi l&rsquo;int\u00e9grer dans notre&amp;nbsp;HTML.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"11a726ed-213c-4c67-a446-3420034397dd\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"15\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n   &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;script src=\".\/js\/lib\/jquery-3.6.0.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/lib\/i18next.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/lib\/i18nextHttpBackend.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/lib\/jquery-i18next.min.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/js\/scripts.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Maintenant, nous pouvons rendre notre application plus \u00e9volutive en d\u00e9pla\u00e7ant nos traductions dans des fichiers JSON s\u00e9par\u00e9s.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"39d7927a-c499-4d2a-b8db-34d65015996c\" data-enlighter-title=\"locales\/en\/translation.json\">{\n  \"app-title\": \"Avec jQuery + i18next\",\n  \"home\": \"Home\",\n  \"about\": \"About\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"37d46e00-a79c-4812-82e9-a89ba2ee6aa5\" data-enlighter-title=\"locales\/ar\/translation.json\">{\n  \"app-title\": \"\u0645\u0639 \u062c\u064a \u0643\u0648\u064a\u0631\u064a \u0648 \u0622\u064a \u0625\u064a\u062a\u064a\u0646 \u0646\u064a\u0643\u0633\u062a\",\n  \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n  \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\"\n}\n<\/pre>\n<p>Nous devrons retravailler notre code de configuration pour <code>utiliser()<\/code> le plugin HTTP lors de l&rsquo;initialisation d&rsquo;i18next. Nous voudrons \u00e9galement attendre que le fichier de traduction de nos param\u00e8tres r\u00e9gionaux initiaux soit t\u00e9l\u00e9charg\u00e9 avant d&rsquo;essayer de traduire nos \u00e9l\u00e9ments de page.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"19886823-97b7-4f73-8fe9-7f1d0af23104\" data-enlighter-title=\"js\/scripts.js\" data-enlighter-highlight=\"1,2,3,4,5,8,9,21,22,23,24\">\/\/ Wait for translations to come down the network\n\/\/ before initializing the jQuery plugin\nasync function initI18n() {\n  \/\/ Use Http backend plugin to download translations\n  await i18next.use(i18nextHttpBackend).init({\n    lng: \"en\",\n  \/\/ Remove `resources` option, since our translations\n  \/\/ are in JSON files now\n  });\n  jqueryI18next.init(i18next, $);\n}\n\/\/ Refactor to function\nfunction translatePage() {\n  $(\"body\").localize();\n}\n\/\/ Init\n(async function () {\n  \/\/ Wait for i18next to initialize before\n  \/\/ translating page elements\n  await initI18n();\n  translatePage();\n})();\n<\/pre>\n<p>Si nous rechargeons notre application, nous ne remarquons aucune diff\u00e9rence dans la sortie. Cependant, un examen plus attentif de l&rsquo;onglet R\u00e9seau de nos outils de d\u00e9veloppement r\u00e9v\u00e8le une solution de fichier de traduction plus maintenable \u00ab t\u00e9l\u00e9charger quand vous en avez besoin \u00bb.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15886 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-async.png\" alt=\"D\u00e9mo jQuery avec chargement asynchrone du fichier de traduction | Phrase\" width=\"1432\" height=\"1454\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-async.png 1432w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-async-295x300.png 295w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-async-1009x1024.png 1009w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-async-768x780.png 768w\" sizes=\"auto, (max-width: 1432px) 100vw, 1432px\" \/><\/div>\n<h3><span class=\"ez-toc-section\" id=\"selecteur-de-langue-4\"><\/span>S\u00e9lecteur de langue<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Vous avez peut-\u00eatre remarqu\u00e9 que nous avons du code HTML ressemblant \u00e0 un s\u00e9lecteur de langue dans notre d\u00e9mo.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"49b71021-1b5e-49ca-ac02-f2af3f97ab97\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"18,19,20,21\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;!-- ... --&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;!-- ... --&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Connectons l&rsquo;\u00e9l\u00e9ment <code>&lt;select&gt;<\/code> ci-dessus afin que notre application change ses traductions pour celles correspondant au param\u00e8tre linguistique s\u00e9lectionn\u00e9 par l&rsquo;utilisateur.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"5b4b7415-ce8e-4c83-b94f-a48864669b90\" data-enlighter-title=\"js\/scripts.js\" data-enlighter-highlight=\"3,4,6,7,9,11,12,13,14,15,16,17,18,23\">\/\/ ...\nfunction bindLocaleSwitcher() {\n  const $switcher = $(\"[data-i18n-switcher]\");\n  \/\/ Initial value\n  $switcher.val(i18next.language);\n  $switcher.on(\"change\", async function () {\n    \/\/ Changing the active locale will cause its\n    \/\/ translations to load from the network, so\n    \/\/ we wait for that load before refreshing\n    \/\/ page elements\n    await i18next.changeLanguage($switcher.val());\n    translatePage();\n  });\n}\n(async function () {\n  await initI18n();\n  translatePage();\n  bindLocaleSwitcher();\n})();\n<\/pre>\n<p>Et voil\u00e0, nous avons un s\u00e9lecteur de langue fonctionnel.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15887 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-locale-switcher.gif\" alt=\"application de d\u00e9monstration jQuery avec s\u00e9lecteur de langue | Phrase\" width=\"600\" height=\"217\" \/><\/div>\n<h3><span class=\"ez-toc-section\" id=\"interpolation-4\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>La gestion des valeurs dynamiques dans nos messages de traduction est pr\u00eate \u00e0 l&#8217;emploi avec i18next. Nous devons simplement fournir une carte JSON <code>data-i18n-options<\/code> dans l&rsquo;\u00e9l\u00e9ment pour lequel nous avons des interpolations.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"7d27ccfb-de5d-4985-86c1-a33c28264797\" data-enlighter-title=\"index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n=\"lead\" data-i18n-options='{\"username\": \"Jackie\"}'&gt;\n      Welcome to my little spot on the interwebs, {{username}}!\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>Le plug-in jQuery i18next ne cherche pas <code>data-i18n-options<\/code> par d\u00e9faut\u00a0; nous devons utiliser l\u2019<a href=\"https:\/\/github.com\/i18next\/jquery-i18next#initialize-the-plugin\">option de configuration<\/a> <code>useOptionsAttr<\/code> pour activer l&rsquo;interpolation automatique.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"1094953e-ed26-4d2c-b79f-afb14132c03c\" data-enlighter-title=\"js\/scripts.js\" data-enlighter-highlight=\"4\">async function initI18n() {\n  await i18next.use(i18nextHttpBackend).init({ lng: \"en\" });\n  jqueryI18next.init(i18next, $, { useOptionsAttr: true });\n}\n\/\/ ...\n<\/pre>\n<p>Bien s\u00fbr, nous voulons nous assurer que nous avons les <code>{{variable}}<\/code> placeholders que i18next attend dans nos messages de traduction.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"2fda0df5-e0a2-4e37-942c-b20d43bacf1a\" data-enlighter-title=\"locales\/en\/translation.json\" data-enlighter-highlight=\"5\">{\n  \"app-title\": \"With jQuery + i18next\",\n  \"home\": \"Home\",\n  \"about\": \"About\",\n  \"lead\": \"Welcome to my little spot on the interwebs, {{username}}!\"\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"8804283f-f7e0-4a9b-82a3-2e3b9d26fb58\" data-enlighter-title=\"locales\/ar\/translation.json\" data-enlighter-highlight=\"5\">{\n  \"app-title\": \"\u0645\u0639 \u062c\u064a \u0643\u0648\u064a\u0631\u064a \u0648 \u0622\u064a \u0625\u064a\u062a\u064a\u0646 \u0646\u064a\u0643\u0633\u062a\",\n  \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n  \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\",\n  \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a \u064a\u0627 {{username}}.\"\n}\n<\/pre>\n<p>Et voil\u00e0. Interpolations interpol\u00e9es.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15888 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-en.png\" alt=\"Paragraphe d&apos;introduction avec interpolation | Phrase\" width=\"824\" height=\"98\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-en.png 824w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-en-300x36.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-en-768x91.png 768w\" sizes=\"auto, (max-width: 824px) 100vw, 824px\" \/><\/div>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15889 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-ar.png\" alt=\"Paragraphe d&apos;introduction en arabe avec interpolation\u00a0| Phrase\" width=\"542\" height=\"102\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-ar.png 542w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-interpolation-ar-300x56.png 300w\" sizes=\"auto, (max-width: 542px) 100vw, 542px\" \/><\/div>\n<h3><span class=\"ez-toc-section\" id=\"formes-plurielles-4\"><\/span>Formes plurielles<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Nous utilisons le m\u00eame attribut <code>data-i18n-options<\/code> pour fournir une variable num\u00e9rique sp\u00e9ciale <code>count<\/code> lorsque nous avons des messages au pluriel.<\/p>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> Consultez la section <a href=\"#Plurals-3\">i18next \u279e Pluriels<\/a> ci-dessus pour plus de d\u00e9tails sur le fonctionnement de la biblioth\u00e8que avec le pluriel.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"9ba58e5c-c7d1-49b9-8baf-4acd07bade77\" data-enlighter-title=\"index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n=\"new-messages\" data-i18n-options='{\"count\": 3}'&gt;\n      You have {{count}} new messages\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>Nous utilisons une convention <code>key_form<\/code> lors de la sp\u00e9cification de nos messages au pluriel. L&rsquo;anglais a deux formes plurielles\u00a0: <code>one<\/code> et <code>other<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"77d50ab2-ce8e-44a9-b61e-40b6146e4c19\" data-enlighter-title=\"locales\/en\/translation.json\">{\n  \/\/ ...\n  \"new-messages_one\": \"You have {{count}} new message\",\n  \"new-messages_other\": \"You have {{count}} new messages\"\n}\n<\/pre>\n<p>L&rsquo;arabe a six formes plurielles, et elles sont g\u00e9r\u00e9es automatiquement par i18next.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"8cf54801-115c-44bd-b70d-4be380c413c6\" data-enlighter-title=\"locales\/ar\/translation.json\">{\n  \/\/ ...\n  \"new-messages_zero\": \"\u0644\u0627 \u062a\u0648\u062c\u062f \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_one\": \"\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_two\": \"\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u062a\u0627\u0646 \u062c\u062f\u0627\u062f\",\n  \"new-messages_few\": \"\u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_many\": \"\u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\",\n  \"new-messages_other\": \"\u0644\u062f\u064a\u0643 {{count}} \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629\"\n}\n<\/pre>\n<p>Ainsi, sans code suppl\u00e9mentaire, notre application g\u00e8re la pluralisation complexe.<\/p>\n<div><strong style=\"color: #ff6600\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15891 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-ar.png\" alt=\"Application de d\u00e9monstration jQuery en anglais avec param\u00e8tres r\u00e9gionaux et pluralisation | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/strong><\/div>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15890 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-en.png\" alt=\"Application de d\u00e9monstration jQuery en anglais avec param\u00e8tres r\u00e9gionaux et pluralisation | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/jquery-plurals-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> Obtenez le <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/jquery\">code complet fonctionnel de l&rsquo;application ci-dessus depuis notre r\u00e9f\u00e9rentiel GitHub<\/a>.<\/p>\n<p>\ud83d\udd17 <em>Ressources\u00a0\u00bb<\/em> Si vous cherchez une alternative \u00e0 i18next, consultez <a href=\"https:\/\/phrase.com\/blog\/posts\/jquery-i18n-the-advanced-guide\/\">le guide avanc\u00e9 de jQuery i18n<\/a> qui utilise la biblioth\u00e8que <a href=\"https:\/\/github.com\/wikimedia\/jquery.i18n\">jQuery.i18n<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"comment-localiser-une-page-web-avec-le-format-icu-en-utilisant-globalize\"><\/span>Comment localiser une page web avec le format ICU en utilisant Globalize ?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Si vous souhaitez une couverture de localisation presque exhaustive des <a href=\"https:\/\/icu.unicode.org\/home\">Composants Internationaux pour Unicode (ICU)<\/a> et du <a href=\"https:\/\/cldr.unicode.org\/\">R\u00e9f\u00e9rentiel de donn\u00e9es de param\u00e8tres r\u00e9gionaux communs (CLDR)<\/a> dans votre application JavaScript, la biblioth\u00e8que <a href=\"https:\/\/github.com\/globalizejs\/globalize\">Globalize<\/a> r\u00e9pondra certainement \u00e0 vos besoins. Passons en revue comment installer Globalize et localiser notre humble application de d\u00e9monstration.<\/p>\n<p>\ud83d\udd17 <em>Ressources\u00a0\u00bb<\/em> <a href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/\">Le guide manquant sur le format de message ICU<\/a> couvre ce que sont l&rsquo;ICU et le CLDR plus en d\u00e9tail.<\/p>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> Contrairement \u00e0 de nombreuses autres biblioth\u00e8ques que nous couvrons dans cet article, Globalize <em>prend en charge<\/em> Internet Explorer 9+.<\/p>\n<p><em>Quelle d\u00e9monstration ?<\/em>. Notre fid\u00e8le page unique, bien s\u00fbr.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"3120f7b6-18a4-4377-a423-5d0f2490a644\" data-enlighter-title=\"index.html\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;ul class=\"navbar-list navbar-start\"&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"home\" class=\"navbar-link\"&gt;\n              Home\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n          &lt;li class=\"navbar-item\"&gt;\n            &lt;a href=\"#\" data-i18n-key=\"about\" class=\"navbar-link\"&gt;\n              About\n            &lt;\/a&gt;\n          &lt;\/li&gt;\n        &lt;\/ul&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;h1 data-i18n-key=\"app-title\"&gt;With Globalize&lt;\/h1&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Stella\"}'&gt;\n      Welcome to my little spot on the interwebs, {username}!\n    &lt;\/p&gt;\n    &lt;p data-i18n-key=\"new-messages\" data-i18n-opt='{\"count\": 100}'&gt;\n      You have # new messages\n    &lt;\/p&gt;\n  &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15892 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-start.png\" alt=\"Application de d\u00e9monstration Globalize | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-start.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-start-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-start-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-start-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<p>Commen\u00e7ons la localisation.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"installation-4\"><\/span>Installation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Globalize est tr\u00e8s modulaire, donc il peut \u00eatre un peu encombrant \u00e0 installer avec du JavaScript pur. Cependant, c&rsquo;est une recette relativement simple.<\/p>\n<ul>\n<li>T\u00e9l\u00e9chargez la <a href=\"https:\/\/github.com\/globalizejs\/globalize\/releases\">derni\u00e8re version de Globalize<\/a><\/li>\n<li>T\u00e9l\u00e9chargez la <a href=\"https:\/\/github.com\/rxaviers\/cldrjs\/releases\">derni\u00e8re version du traverser CLDR (cldr.js)<\/a><\/li>\n<li>T\u00e9l\u00e9chargez la <a href=\"https:\/\/github.com\/unicode-org\/cldr-json\/releases\">derni\u00e8re version des donn\u00e9es JSON CLDR<\/a>\u2014assurez-vous de t\u00e9l\u00e9charger la variante <code>-full<\/code> de la liste des versions pour suivre ici<\/li>\n<\/ul>\n<p>Cela devrait nous donner trois fichiers ZIP. D\u00e9compressons-les, renommons leurs r\u00e9pertoires de premier niveau d\u00e9compress\u00e9s, et d\u00e9pla\u00e7ons-les dans notre r\u00e9pertoire de projet. Je les ai plac\u00e9s sous un r\u00e9pertoire <code>\/lib<\/code> dans mon projet, donc mon projet ressemble maintenant \u00e0 :<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-linenumbers=\"false\">.\n\u251c\u2500\u2500 css\/\n\u251c\u2500\u2500 img\/\n\u251c\u2500\u2500 lib\/\n\u2502   \u251c\u2500\u2500 cldr\/          &lt;&lt; renamed from cldr-x.x.x\n\u2502   \u251c\u2500\u2500 cldr-json\/     &lt;&lt; renamed from cldr-x.x.x-json-full\n\u2502   \u2514\u2500\u2500 globalize\/     &lt;&lt; renamed from globalize.x.x\n\u2514\u2500\u2500 index.html\n<\/pre>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> La documentation officielle passe en revue <a href=\"https:\/\/github.com\/globalizejs\/globalize#installation\">diff\u00e9rentes mani\u00e8res d&rsquo;installer Globalize<\/a>.<\/p>\n<h4>Utiliser l&rsquo;outil des exigences pour d\u00e9terminer les scripts requis<\/h4>\n<p>Nous avons toujours besoin de certaines parties de Globalize et cldr.js ; d&rsquo;autres d\u00e9pendront des fonctionnalit\u00e9s de localisation de notre application. Un outil pratique, <a href=\"https:\/\/johnnyreilly.github.io\/globalize-so-what-cha-want\/#\/?currency=true&amp;date=true&amp;message=true&amp;number=true&amp;plural=true&amp;relativeTime=true&amp;unit=true\">So What\u2019cha Want<\/a>, peut nous indiquer quels fichiers int\u00e9grer dans notre projet en fonction des fonctionnalit\u00e9s que nous avons s\u00e9lectionn\u00e9es. Nous pouvons commencer par d\u00e9s\u00e9lectionner toutes les fonctionnalit\u00e9s sauf <em>message<\/em>.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15893 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool.png\" alt=\"So What\u2019cha Want Globalize Capture d&apos;\u00e9cran | Phrase\" width=\"2856\" height=\"1262\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool.png 2856w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool-300x133.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool-1024x452.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool-768x339.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool-1536x679.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-reqs-tool-2048x905.png 2048w\" sizes=\"auto, (max-width: 2856px) 100vw, 2856px\" \/><\/div>\n<p>Notez les deux listes de fichiers en bas de la page. La liste \u00e0 gauche indique quels fichiers importer de Globalize et cldr.js ; nous pouvons utiliser les balises <code>&lt;script&gt;<\/code> pour cela.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"1079e969-94cb-435c-8773-0ad911048d64\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"13,14,15,16,17,18,20,21\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;!-- Globalize requirements --&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr\/event.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr\/supplemental.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/globalize\/dist\/globalize.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/globalize\/dist\/globalize\/message.js\"&gt;&lt;\/script&gt;\n  &lt;!-- Our app entry point --&gt;\n  &lt;script src=\".\/index.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>La liste en bas \u00e0 droite de <em>So What&rsquo;cha Want<\/em> contient des donn\u00e9es JSON CLDR dont nous avons besoin. Ce JSON, nous souhaitons le r\u00e9cup\u00e9rer dans notre code et le transmettre \u00e0 Globalize via sa fonction <code>load()<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"819ea5ad-0a88-42b0-9f9a-e3245b463959\" data-enlighter-title=\"index.js\">\/\/ We'll add more to this list as we go\nconst supplementals = [\"likelySubtags\"];\nasync function loadIntoGlobalize(featureUrls) {\n  await Promise.all(\n    featureUrls.map((url) =&gt; fetchJson(url))\n  ).then((downloaded) =&gt;\n    downloaded.forEach((feature) =&gt; Globalize.load(feature))\n  );\n}\nfunction supplementalUrlsFor(options) {\n  return options.map(\n    (feature) =&gt;\n      `\/lib\/cldr-json\/cldr-core\/supplemental\/${feature}.json`\n  );\n}\nasync function fetchJson(url) {\n  const response = await fetch(url);\n  return await response.json();\n}\n(async function () {\n  \/\/ Load supplemental requirements\n  await loadIntoGlobalize(\n    supplementalUrlsFor(supplementals)\n  );\n})();\n<\/pre>\n<p>C&rsquo;est \u00e0 peu pr\u00e8s tout pour la configuration. OK, ce n&rsquo;est pas la biblioth\u00e8que la plus facile \u00e0 installer, mais pour un projet qui n\u00e9cessite une localisation \u00e0 toute \u00e9preuve, cela en vaut la peine.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"traductions-de-base-4\"><\/span>Traductions de base<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Utiliser Globalize pour traduire les \u00e9l\u00e9ments de la page est un processus simple en trois \u00e9tapes.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"7f6d347f-e2aa-4e6a-b077-f960eea2e6fa\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"8,9,10,11,12,13,14,15,16,18,19,21,22,23,24\">\/\/ ...\n(async function () {\n  await loadIntoGlobalize(\n    supplementalUrlsFor(supplementals)\n  );\n  \/\/ 1. Load translation messages\n  Globalize.loadMessages({\n    en: {\n      \"app-title\": \"Hello Globalize!\",\n    },\n    ar: {\n      \"app-title\": \"\u0623\u0647\u0644\u0627\u064b \u062c\u0644\u0648\u0628\u0627\u0644\u0627\u064a\u0632\",\n    },\n  });\n  \/\/ 2. Set the default locale\n  Globalize.locale(\"en\");\n  \/\/ 3. Use formatMessage() to get translation by key\n  document.querySelector(\n    \"[data-i18n-key='app-title']\"\n  ).textContent = Globalize.formatMessage(\"app-title\");\n})();\n<\/pre>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> La documentation officielle de Globalize a une <a href=\"https:\/\/github.com\/globalizejs\/globalize#api\">section API pratique<\/a> qui pr\u00e9sente la liste des fonctions disponibles.<\/p>\n<p>Disco. Notre titre est affich\u00e9 en utilisant les traductions de notre param\u00e8tre r\u00e9gional par d\u00e9faut.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15894 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-en.png\" alt=\"Application de d\u00e9monstration Globalize version anglaise | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<p>Si nous modifions l&rsquo;appel ci-dessus, de sorte qu&rsquo;il se lise <code>Globalize.locale(\"ar\")<\/code>, nous obtenons notre titre d&rsquo;application en arabe.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15895 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-ar.png\" alt=\"Application de d\u00e9monstration Globalize traduction en arabe | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-basic-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<h3><span class=\"ez-toc-section\" id=\"gestion-des-erreurs-de-messages-manquants\"><\/span>Gestion des erreurs de messages manquants<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>G\u00e9n\u00e9ralisons le code qui traduit les \u00e9l\u00e9ments de la page en \u00e9crivant la fonction <code>translatePageElements()<\/code>. Contrairement \u00e0 d&rsquo;autres biblioth\u00e8ques i18n, Globalize lancera une erreur et s&rsquo;arr\u00eatera s&rsquo;il rencontre un message manquant pour une cl\u00e9 donn\u00e9e \u00e0 <code>formatMessage()<\/code>. Cependant, un simple <code>try\/catch<\/code> suffit \u00e0 att\u00e9nuer le probl\u00e8me.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"5ea79d29-3db0-486a-896a-e57c7423c91a\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"3,4,5,6,8,9,11,12,13,14,15,16,17,19,20,21,22,23,24,25,26,35\">\/\/ ...\nfunction translatePageElements() {\n  const elements = document.querySelectorAll(\n    \"[data-i18n-key]\"\n  );\n  elements.forEach((element) =&gt; {\n    const key = element.getAttribute(\"data-i18n-key\");\n    try {\n      element.innerHTML = Globalize.formatMessage(key);\n    } catch (error) {\n      if (error.code === \"E_MISSING_MESSAGE\") {\n        \/\/ Show console warnings on missing message\n        \/\/ instead of grinding to a halt.\n        console.warn(error.message);\n        \/\/ Show key value on page for missing message\n        element.innerHTML = key;\n      } else {\n        console.error(error);\n      }\n    }\n  });\n}\n(async function () {\n  \/\/ ...\n  Globalize.loadMessages(\/* ... *\/);\n  Globalize.locale(\"en\");\n  translatePageElements();\n})();\n<\/pre>\n<p>Moins \u00ab\u00a0plantage sur message manquant\u00a0\u00bb, plus \u00ab\u00a0afficher la valeur de la cl\u00e9 et avertir dans la console\u00a0\u00bb.<\/p>\n<h3><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15910 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-missing-messages.png\" alt=\"Application de d\u00e9monstration Globalize avec avertissement dans le code | Phrase\" width=\"1432\" height=\"888\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-missing-messages.png 1432w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-missing-messages-300x186.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-missing-messages-1024x635.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-missing-messages-768x476.png 768w\" sizes=\"auto, (max-width: 1432px) 100vw, 1432px\" \/><\/h3>\n<h3><span class=\"ez-toc-section\" id=\"chargement-asynchrone-de-fichiers-de-traduction-3\"><\/span>Chargement asynchrone de fichiers de traduction<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Comme nous l&rsquo;avons fait avec d&rsquo;autres solutions dans cet article, divisons nos messages de traduction en fichiers JSON par param\u00e8tres r\u00e9gionaux pour l&rsquo;\u00e9volutivit\u00e9 et la maintenabilit\u00e9.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"fb73dd42-e042-4d6a-9c1c-e96886cf5603\" data-enlighter-title=\"lang\/en.json\">{\n  \/\/ Globalize expects the locale code to be the top-level key\n  \"en\": {\n    \"app-title\": \"With Globalize\",\n    \"home\": \"Home\",\n    \"about\": \"About\"\n  }\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"a710d68f-9adb-4364-b4ef-437ec0c6ff35\" data-enlighter-title=\"lang\/ar.json\">{\n  \"ar\": {\n    \"app-title\": \"\u0645\u0639 \u062c\u0644\u0648\u0628\u0627\u0644\u0627\u064a\u0632\",\n    \"home\": \"\u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629\",\n    \"about\": \"\u0646\u0628\u0630\u0629 \u0639\u0646\u0627\"\n  }\n}\n<\/pre>\n<p>Une fonction r\u00e9utilisable <code>setLocale()<\/code> peut charger notre fichier de traduction, configurer Globalize et actualiser nos \u00e9l\u00e9ments de page.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"346671e3-919a-4566-a041-8086dea23f25\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"1,5,6,7,8,9,10,19\">const defaultLocale = \"en\";\n\/\/ ...\n async function setLocale(locale) {\n  const messages = await fetchJson(`\/lang\/${locale}.json`);\n  Globalize.loadMessages(messages);\n  Globalize.locale(locale);\n  translatePageElements();\n}\n\/\/ ...\n(async function () {\n  await loadIntoGlobalize(\n    supplementalUrlsFor(supplementals)\n  );\n  setLocale(defaultLocale);\n})();\n<\/pre>\n<p>Chargement de fichier de traduction asynchrone : c&rsquo;est fait.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"selecteur-de-langue-5\"><\/span>S\u00e9lecteur de langue<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Nous pouvons utiliser la fonction <code>setLocale()<\/code> que nous venons d&rsquo;\u00e9crire pour que notre interface de changement de langue fonctionne. Vous vous souvenez peut-\u00eatre que notre application de d\u00e9monstration a d\u00e9j\u00e0 un peu de HTML pour le s\u00e9lecteur\u00a0.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"15ce0f80-f807-4c90-9fbd-22d73a8287f6\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"18,19,20,21\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;nav class=\"navbar\"&gt;\n      &lt;div class=\"container\"&gt;\n        &lt;!-- ... --&gt;\n        &lt;div class=\"navbar-end\"&gt;\n          &lt;img src=\"img\/translation-icon@2x.png\" class=\"translation-icon\" \/&gt;\n          &lt;select data-i18n-switcher class=\"locale-switcher\"&gt;\n            &lt;option value=\"en\"&gt;English&lt;\/option&gt;\n            &lt;option value=\"ar\"&gt;Arabic (\u0627\u0644\u0639\u0631\u0628\u064a\u0629)&lt;\/option&gt;\n          &lt;\/select&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/nav&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;!-- ... --&gt;\n  &lt;script src=\".\/index.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Some JavaScripting will get the switcher switching.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"5a823850-e4e9-473e-9653-d9a5438b1a71\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"5,6,7,8,10,11,13,14,15,16,25\">const defaultLocale = \"en\";\n\/\/ ...\nfunction bindLocaleSwitcher() {\n  const switcher = document.querySelector(\n    \"[data-i18n-switcher]\"\n  );\n  \/\/ Globalize.locale() returns the active locale\n  switcher.value = Globalize.locale().locale;\n  switcher.onchange = (e) =&gt; {\n    setLocale(e.target.value);\n  };\n}\n(async function () {\n  await loadIntoGlobalize(\n    supplementalUrlsFor(supplementals)\n  );\n  await setLocale(defaultLocale);\n  bindLocaleSwitcher(defaultLocale);\n})();\n<\/pre>\n<p>Et avec cela, nos utilisateurs peuvent choisir la langue de leur choix.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15896 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-locale-switcher.gif\" alt=\"Application de d\u00e9monstration Globalize avec s\u00e9lecteur de langue | Phrase\" width=\"600\" height=\"217\" \/><\/div>\n<h4>Affichage des noms des param\u00e8tres r\u00e9gionaux dans les param\u00e8tres r\u00e9gionaux actifs<\/h4>\n<p>L&rsquo;une des fonctionnalit\u00e9s les plus puissantes de l&rsquo;utilisation d&rsquo;une biblioth\u00e8que ICU comme Globalize est l&rsquo;acc\u00e8s \u00e0 une vari\u00e9t\u00e9 immens\u00e9ment riche de donn\u00e9es de localisation CLDR. Par exemple, nous pouvons afficher les langues dans notre s\u00e9lecteur de param\u00e8tres r\u00e9gionaux <em>dans le param\u00e8tre r\u00e9gional actif<\/em>\u2014l&rsquo;anglais serait \u00ab\u0627\u0644\u0625\u0646\u062c\u0644\u064a\u0632\u064a\u0629\u00bb en arabe, par exemple. Nous devrions int\u00e9grer les donn\u00e9es CLDR principales et ensuite mettre \u00e0 jour le texte de notre <code>select &gt; option<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"eda6ed92-1c6d-4d7e-8d19-cbdab81843d6\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"2,3,4,11,16,19,20,21,22,23,24,25,26,27,28,29,30,32,33,34,35,36,37,38,40,41,43,44,45,46,48,49,51,52,53,54,55,56\">const defaultLocale = \"ar\";\nconst mains = {\n    localenames: [\"languages\"],\n};\nconst supplementals = \"likelySubtags\"];\nasync function setLocale(locale) {\n  const messages = await fetchJson(`\/lang\/${locale}.json`);\n  Globalize.loadMessages(messages);\n  await loadIntoGlobalize(mainUrlsFor(mains, locale));\n  Globalize.locale(locale);\n  translatePageElements();\n  setLocaleSwitcherDisplayNames();\n}\n\/\/ Given options = {\n\/\/   localenames: [\"languages\"],\n\/\/   dates: [\"ca-generic\", \"ca-gregorian\"],\n\/\/ }\n\/\/ and, locale = \"en\", returns an array like\n\/\/ [\n\/\/    \"\/lib\/cldr-json\/cldr-localenames-full\/main\/en\/languages.json\",\n\/\/    \"\/lib\/cldr-json\/cldr-dates-full\/main\/en\/ca-generic.json\",\n\/\/    \"\/lib\/cldr-json\/cldr-dates-full\/main\/en\/ca-gregorian.json\"\n\/\/ ]\nfunction mainUrlsFor(options, locale) {\n  const result = [];\n  Object.keys(options).forEach((key) =&gt; {\n    options[key].forEach((collection) =&gt; {\n      result.push(\n        `\/lib\/cldr-json\/cldr-${key}-full\/main\/${locale}\/${collection}.json`\n      );\n    });\n  });\n  return result;\n}\nfunction setLocaleSwitcherDisplayNames() {\n  const options = document.querySelectorAll(\n    \"[data-i18n-switcher] option\"\n  );\n  options.forEach((option) =&gt; {\n    const localeCode = option.value;\n    \/\/ Get CLDR main data by path\n    option.textContent = Globalize.cldr.main(\n      `localeDisplayNames\/languages\/${localeCode}`\n    );\n  });\n}\n\/\/ ...\n<\/pre>\n<p>Nous pouvons utiliser notre code principal de chargement JSON chaque fois que nous voulons utiliser les donn\u00e9es principales JSON CLDR. Sinon, un peu de manipulation du DOM lorsqu\u2019un nouveau param\u00e8tre r\u00e9gional est choisi nous donne des noms de param\u00e8tres r\u00e9gionaux localis\u00e9s (tellement m\u00e9ta !).<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15897 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-locale-display-names.gif\" alt=\"Application de d\u00e9monstration Globalize avec des noms de param\u00e8tres r\u00e9gionaux localis\u00e9s | Phrase\" width=\"600\" height=\"217\" \/><\/div>\n<p>\ud83d\udd17 <em>Ressources\u00a0\u00bb<\/em> La documentation officielle de cldr.js explique en d\u00e9tail <a href=\"https:\/\/github.com\/rxaviers\/cldrjs#get-item-given-its-path\">comment r\u00e9cup\u00e9rer les donn\u00e9es JSON CLDR<\/a>.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"interpolation-5\"><\/span>Interpolation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>La gestion des valeurs dynamiques dans notre message est g\u00e9r\u00e9e par le format message ICU via une syntaxe <code>{variable}<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"1c1af1b3-a6c4-4113-9def-94a483d7cc7a\" data-enlighter-title=\"lang\/en.json\">{\n  \"en\": {\n    \/\/ ...\n    \"lead\": \"Welcome to my little spot on the interwebs, {username}!\"\n  }\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"175d373e-1962-4588-80c4-f65b80eff5d2\" data-enlighter-title=\"lang\/ar\">{\n  \"ar\": {\n    \/\/ ...\n    \"lead\": \"\u0623\u0647\u0644\u0627\u064b \u0628\u0643 \u0641\u064a \u0645\u0643\u0627\u0646\u064a \u0627\u0644\u0635\u063a\u064a\u0631 \u0639\u0644\u0649 \u0627\u0644\u0646\u062a \u064a\u0627 {username}.\"\n  }\n}\n<\/pre>\n<p>Nous pouvons fournir des paires de substitution cl\u00e9\/valeur dans notre HTML.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"677e0228-8329-44a2-bcb6-63c6ee81b5c7\" data-enlighter-title=\"index.html\">&lt;!-- ... --&gt;\n    &lt;p data-i18n-key=\"lead\" data-i18n-opt='{\"username\": \"Stella\"}'&gt;\n      Welcome to my little spot on the interwebs, {username}!\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>Et maintenant, nous devons juste lire ces paires cl\u00e9\/valeur et les fournir \u00e0 <code>Globalize.formatMessage()<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"61ef9e83-97f1-453f-8037-ebf3044facce\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"11,12,13,14,15,20\">\/\/ ...\nfunction translatePageElements() {\n  const elements = document.querySelectorAll(\n    \"[data-i18n-key]\"\n  );\n  elements.forEach((element) =&gt; {\n    const key = element.getAttribute(\"data-i18n-key\");\n    const interpolations =\n      element.getAttribute(\"data-i18n-opt\");\n    const parsedInterpolations = interpolations\n      ? JSON.parse(interpolations)\n      : {};\n    try {\n      element.innerHTML = Globalize.formatMessage(\n        key,\n        parsedInterpolations\n      );\n    } catch (error) {\n      if (error.code === \"E_MISSING_MESSAGE\") {\n        console.warn(error.message);\n      } else {\n        console.error(error);\n      }\n    }\n  });\n}\n\/\/ ...\n<\/pre>\n<p>Pas de probl\u00e8me.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15898 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-en.png\" alt=\"Application de d\u00e9monstration Globalize avec interpolation dans le param\u00e8tre linguistique anglais | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15899 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-ar.png\" alt=\"Application de d\u00e9monstration Globalize avec interpolation dans les param\u00e8tres r\u00e9gionaux arabes | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-interpolation-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<div><\/div>\n<h3><span class=\"ez-toc-section\" id=\"formes-plurielles-5\"><\/span>Formes plurielles<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>La gestion des pluriels dans le format ICU est sans \u00e9gal et couvre des formes plurielles complexes, comme celles en russe ou en arabe. Nous devrons configurer la fonctionnalit\u00e9 des pluriels avant de pouvoir l&rsquo;utiliser.<\/p>\n<h4>Ajout des exigences relatives au pluriel<\/h4>\n<p>Allons sur <a href=\"https:\/\/johnnyreilly.github.io\/globalize-so-what-cha-want\/#\/?currency=true&amp;date=true&amp;message=true&amp;number=true&amp;plural=true&amp;relativeTime=true&amp;unit=true\">Alors, qu&rsquo;est-ce que vous voulez<\/a> pour d\u00e9terminer ce dont nous aurons besoin si nous devons activer <em>pluriel<\/em>.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15900 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs.png\" alt=\"Alors, qu&apos;est-ce que vous souhaitez pour la capture d&apos;\u00e9cran Globalize\u00a0| Phrase\" width=\"2876\" height=\"1270\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs.png 2876w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs-300x132.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs-1024x452.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs-768x339.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs-1536x678.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-reqs-2048x904.png 2048w\" sizes=\"auto, (max-width: 2876px) 100vw, 2876px\" \/><\/div>\n<p>Pas trop mala: d&rsquo;abord, nous devrons ajouter une balise <code>&lt;script&gt;<\/code> pour la fonctionnalit\u00e9.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"83186151-0cf9-432c-95cf-e4e7b1dbaf29\" data-enlighter-title=\"index.html\" data-enlighter-highlight=\"18\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n  &lt;!-- ... --&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n  &lt;div class=\"container\"&gt;\n    &lt;!-- ... --&gt;\n  &lt;\/div&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr\/event.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/cldr\/dist\/cldr\/supplemental.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/globalize\/dist\/globalize.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/globalize\/dist\/globalize\/message.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/lib\/globalize\/dist\/globalize\/plural.js\"&gt;&lt;\/script&gt;\n  &lt;script src=\".\/index.js\"&gt;&lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Nous aurons \u00e9galement besoin du JSON suppl\u00e9mentaire <code>pluriels<\/code>, et probablement du <code>ordinaux<\/code> si nous voulons couvrir le format comme \u00ab 3e \u00bb et \u00ab 4e \u00bb dans nos messages. Nous devrons simplement ajouter les exigences \u00e0 notre tableau de configuration <code>supplementals<\/code>. Notre application est d\u00e9j\u00e0 configur\u00e9e pour charger le JSON correspondant pour nous au d\u00e9marrage.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"1b2d9cb4-79c6-4aa9-960f-8d34d4764fc3\" data-enlighter-title=\"index.js\" data-enlighter-highlight=\"4,5\">const defaultLocale = \"ar\";\nconst supplementals = [\n  \"likelySubtags\",\n  \"plurals\",\n  \"ordinals\",\n];\n\/\/ ...\n<\/pre>\n<p>Maintenant, nous pouvons ajouter nos messages pluriels. La syntaxe plurielle ICU est largement intuitive : une variable <code>count<\/code> d\u00e9termine la forme plurielle choisie, et un symbole sp\u00e9cial <code>#<\/code> est remplac\u00e9 par la valeur <code>count<\/code> lors de l&rsquo;affichage.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\" data-enlighter-group=\"188a8655-10bf-427b-b559-8905194c51e5\" data-enlighter-title=\"lang\/en.json\">{\n  \"en\": {\n    \/\/ ...\n    \"new-messages\": [\n      \/\/ We can call `count` anything we want, as long as we\n      \/\/ match the key in our call to formatMessage()\n      \"You have {count, plural,\",\n      \"    one {# new message}\",\n      \"  other {# new messages}\",\n      \"}\"\n    ]\n  }\n}\n<\/pre>\n<p>\ud83d\uddd2 <em>Remarque\u00a0\u00bb<\/em> Globalize nous permet de diviser les messages multilignes en utilisant des tableaux.<\/p>\n<p>Les six formes plurielles en arabe sont g\u00e9r\u00e9es par le format ICU.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"8039927b-6183-45cb-822a-b52ccadd2653\" data-enlighter-title=\"lang\/ar.json\" data-enlighter-linenumbers=\"false\">{\n  \"ar\": {\n    \/\/ ...\n    \"new-messages\": [\n      \"{count, plural, \",\n      \"   zero {\u0644\u0627 \u062a\u0648\u062c\u062f \u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629}\",\n      \"    one {\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629}\",\n      \"    two {\u0644\u062f\u064a\u0643 \u0631\u0633\u0627\u0644\u062a\u0627\u0646 \u062c\u062f\u0627\u062f}\",\n      \"    few {\u0644\u062f\u064a\u0643 # \u0631\u0633\u0627\u0626\u0644 \u062c\u062f\u064a\u062f\u0629}\",\n      \"   many {\u0644\u062f\u064a\u0643 # \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629}\",\n      \"  other {\u0644\u062f\u064a\u0643 # \u0631\u0633\u0627\u0644\u0629 \u062c\u062f\u064a\u062f\u0629}\",\n      \"}\"\n    ]\n  }\n}\n<\/pre>\n<p>\ud83e\udd3f <em>Go deeper \u00bb<\/em> We cover ICU plurals in more detail in <a href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/#Plurals\">The Missing Guide to the ICU Message Format<\/a>.<\/p>\n<p>Of course, we need to make sure that <code>count<\/code> is supplied to <code>Globalize.formatMessage()<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"e9ab1b69-a2cd-44a1-95cf-06d075283840\" data-enlighter-title=\"index.html\" data-enlighter-linenumbers=\"false\">&lt;!-- ... --&gt;\n    &lt;p data-i18n-key=\"new-messages\" data-i18n-opt='{\"count\": 110}'&gt;\n      You have # new messages\n    &lt;\/p&gt;\n&lt;!-- ... --&gt;\n<\/pre>\n<p>Et c&rsquo;est \u00e0 peu pr\u00e8s tout pour les pluriels.<\/p>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15901 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-en.png\" alt=\"Application de d\u00e9monstration Globalize avec pluralisation en anglais | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-en.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-en-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-en-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-en-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-15902 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-ar.png\" alt=\"Application de d\u00e9monstration Globalize avec pluralisation en arabe | Phrase\" width=\"1330\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-ar.png 1330w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-ar-300x108.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-ar-1024x370.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/12\/globalize-plural-ar-768x277.png 768w\" sizes=\"auto, (max-width: 1330px) 100vw, 1330px\" \/><\/div>\n<div><\/div>\n<div>\ud83d\udd17 <em>Ressources\u00a0\u00bb<\/em> Obtenez tout le code de d\u00e9monstration Globalize de notre <a href=\"https:\/\/github.com\/PhraseApp-Blog\/javascript-l10n-ultimate-guide\/tree\/main\/globalize\">d\u00e9p\u00f4t GitHub<\/a>.<\/div>\n<p>\ud83d\uddd2 <em>Remarque \u00bb<\/em> L&rsquo;impl\u00e9mentation ICU de Globalize couvre le formatage complet des dates et des nombres. Jetez un \u0153il \u00e0 la <a href=\"https:\/\/github.com\/globalizejs\/globalize#api\">documentation officielle<\/a> pour plus d&rsquo;informations.<\/p>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> Nous couvrons plus d&rsquo;options d&rsquo;installation et de cas d&rsquo;utilisation de Globalize dans <a href=\"https:\/\/phrase.com\/blog\/posts\/js-i18n-with-globalizejs\/\">JS I18n avec Globalize.js<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"pour-conclure-notre-guide-de-localisation-javascript\"><\/span>Pour conclure notre guide de localisation JavaScript<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Et c&rsquo;est \u00e0 peu pr\u00e8s tout pour celui-ci. Nous esp\u00e9rons que vous avez appr\u00e9ci\u00e9 cette incursion dans certaines des solutions de localisation JavaScript les plus populaires.<\/p>\n<p>\ud83d\udd17 <em>Ressources \u00bb<\/em> Si vous travaillez avec Ruby on Rails, vous pourriez appr\u00e9cier <a href=\"https:\/\/phrase.com\/blog\/posts\/localizing-javascript-in-rails-apps\/\">La localisation de JavaScript dans les applications Rails<\/a>.<\/p>\n<p>Et si vous souhaitez faire passer votre processus de localisation au niveau sup\u00e9rieur, jetez un \u0153il \u00e0 Phrase. Phrase prend en charge le format ICU, tous les autres formats de traduction que nous avons couverts ici et bien d&rsquo;autres. Avec son CLI et la synchronisation avec Bitbucket, GitHub et GitLab, votre i18n peut \u00eatre en pilote automatique. La console web compl\u00e8te de Phrase, avec apprentissage automatique et suggestions intelligentes, est un plaisir \u00e0 utiliser pour les traducteurs. Une fois les traductions pr\u00eates, elles peuvent se synchroniser automatiquement avec votre projet. Vous le configurez et l&rsquo;oubliez, ce qui vous permet de vous concentrer sur le code que vous aimez.<\/p>\n<p>D\u00e9couvrez toutes les <a href=\"https:\/\/phrase.com\/fr\/roles\/developers\/\">fonctionnalit\u00e9s de Phrase pour les d\u00e9veloppeurs<\/a> et voyez par vous-m\u00eame comment cela peut rationaliser vos flux de travaux de localisation de logiciels.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>D\u00e9marrez la localisation JavaScript de votre navigateur avec ce guide complet et pr\u00e9parez votre application pour les utilisateurs internationaux.<\/p>\n","protected":false},"author":41,"featured_media":2612,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"post-refresh-updated","format":"standard","meta":{"_acf_changed":false,"_stopmodifiedupdate":false,"_modified_date":"","_searchwp_excluded":"","episode_type":"","audio_file":"","podmotor_file_id":"","podmotor_episode_id":"","cover_image":"","cover_image_id":"","duration":"","filesize":"","filesize_raw":"","date_recorded":"","explicit":"","block":"","itunes_episode_number":"","itunes_title":"","itunes_season_number":"","itunes_episode_type":"","footnotes":""},"categories":[2459],"class_list":["post-133371","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-localisation-de-logiciels-fr"],"acf":[],"_links":{"self":[{"href":"https:\/\/phrase.com\/fr\/wp-json\/wp\/v2\/posts\/133371","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/phrase.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/phrase.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/phrase.com\/fr\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/phrase.com\/fr\/wp-json\/wp\/v2\/comments?post=133371"}],"version-history":[{"count":3,"href":"https:\/\/phrase.com\/fr\/wp-json\/wp\/v2\/posts\/133371\/revisions"}],"predecessor-version":[{"id":136294,"href":"https:\/\/phrase.com\/fr\/wp-json\/wp\/v2\/posts\/133371\/revisions\/136294"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/phrase.com\/fr\/wp-json\/wp\/v2\/media\/2612"}],"wp:attachment":[{"href":"https:\/\/phrase.com\/fr\/wp-json\/wp\/v2\/media?parent=133371"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/phrase.com\/fr\/wp-json\/wp\/v2\/categories?post=133371"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}