{"id":15220,"date":"2023-10-23T09:10:39","date_gmt":"2023-10-23T07:10:39","guid":{"rendered":"https:\/\/phrase.com\/blog\/?p=15220"},"modified":"2023-11-05T11:30:14","modified_gmt":"2023-11-05T10:30:14","slug":"solidjs-localization-i18next","status":"publish","type":"post","link":"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/","title":{"rendered":"How to Localize SolidJS Applications with i18next"},"content":{"rendered":"\n<div id=\"acf\/text-block_72554da4ca79904226aebce5fb460284\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>SolidJS is a declarative JavaScript library for building user interfaces. It&#8217;s fast, since it doesn\u2019t need a virtual DOM, and it only updates when needed. It also benefits from a relatively simple API, making it potentially faster to develop with than other libraries.<\/p>\n<p>When it comes to <a href=\"https:\/\/phrase.com\/blog\/posts\/step-step-guide-javascript-localization\/\">JavaScript localization<\/a> with SolidJS, we can use the solid-i18next library on top of the popular i18next library to get the job done effectively. This guide will walk you through localizing your SolidJS apps with solid-i18next.<\/p>\n<p>We will quickly build a weather app and localize it: translating simple messages, adding a language switcher, formatting dates, and more. Let&#8217;s start already!<\/p>\n<p><!-- notionvc: 6d9cf25e-9623-48fe-9878-1b3c6d8836c8 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n\n<div id=\"acf\/blog-cta-block_011ad36dd715d9cf8bc43149087eb320\" class=\"pxblock pxblock--blog-cta alignfull bg--yellow image--orientation-landscape\">\n\t<div class=\"block-container\">\n\t\t\t\t\t<div class=\"image image--align-middle\">\n\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"1260\" height=\"864\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/Translation_memory.png\" class=\"attachment-original size-original\" alt=\"Translation memory visual | Phrase\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/Translation_memory.png 1260w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/Translation_memory-300x206.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/Translation_memory-1024x702.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/Translation_memory-768x527.png 768w\" sizes=\"(max-width: 1260px) 100vw, 1260px\" \/>\t\t\t<\/div>\n\t\t\t\t<div class=\"content\">\n\t\t\t<p class=\"h6\">The right translation software for your needs<\/p>\n<p>Drive global growth by automating, managing, and translating all your content with the world\u2019s most powerful, connective, and customizable translation software.<\/p>\n<p><a class=\"btn btn--outline\" href=\"https:\/\/phrase.com\/platform\/\">Explore Suite<\/a><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n\n<div id=\"acf\/text-block_bdf707344e6738ca4b0e6bfe36f2489c\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_69_1 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\/blog\/posts\/solidjs-localization-i18next\/#our-demo-app-solidjs-weather\" title=\"Our demo app: SolidJS Weather\">Our demo app: SolidJS Weather<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#the-solid-i18next-library\" title=\"The solid-i18next library\">The solid-i18next library<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-set-up-solid-i18next\" title=\"How do I set up solid-i18next?\">How do I set up solid-i18next?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-setup-and-configure-i18next\" title=\"How do I setup and configure i18next?\">How do I setup and configure i18next?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-work-with-translation-files\" title=\"How do I work with translation files?\">How do I work with translation files?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-load-translation-files-asynchronosly\" title=\"How do I load translation files asynchronosly?\">How do I load translation files asynchronosly?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#a-note-on-the-dev-language-and-fallback\" title=\"A note on the dev language and fallback\">A note on the dev language and fallback<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-retrieve-the-active-locale\" title=\"How do I retrieve the active locale?\">How do I retrieve the active locale?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-work-with-basic-translation-messages\" title=\"How do I work with basic translation messages?\">How do I work with basic translation messages?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-add-a-language-switcher\" title=\"How do I add a language switcher?\">How do I add a language switcher?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-add-dynamic-values-in-translation-messages\" title=\"How do I add dynamic values in translation messages?\">How do I add dynamic values in translation messages?<\/a><\/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\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-work-with-plurals-in-translation-messages\" title=\"How do I work with plurals in translation messages?\">How do I work with plurals in translation messages?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#working-with-interval-plurals\" title=\"Working with interval plurals\">Working with interval plurals<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-localize-numbers\" title=\"How do I localize numbers?\">How do I localize numbers?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-localize-dates\" title=\"How do I localize dates?\">How do I localize dates?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-work-with-text-direction-ltr-rtl\" title=\"How do I work with text direction (LTR, RTL)?\">How do I work with text direction (LTR, RTL)?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#how-do-i-automatically-detect-the-users-language\" title=\"How do I automatically detect the user\u2019s language?\">How do I automatically detect the user\u2019s language?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/phrase.com\/blog\/posts\/solidjs-localization-i18next\/#step-up-your-game-in-javascript-localization\" title=\"Step up your game in JavaScript localization\">Step up your game in JavaScript localization<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"our-demo-app-solidjs-weather\"><\/span>Our demo app: SolidJS Weather<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Throughout this tutorial, we will make a weather app named \u201cSolidJS Weather\u201d.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-68456 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.06.07@2x-1024x631.jpg\" alt=\"Our app will show you a variety of information related to the weather | Phrase\" width=\"1024\" height=\"631\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.06.07@2x-1024x631.jpg 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.06.07@2x-300x185.jpg 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.06.07@2x-768x473.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.06.07@2x-1536x946.jpg 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.06.07@2x-2048x1261.jpg 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-68462 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.16.38.gif\" alt=\"Our weather app in action showing the weather data in different languages | Phrase\" width=\"2000\" height=\"568\" \/><\/p>\n<p>To build this weather app, we will need the following packages.<!-- notionvc: 8482eb2c-e0d5-421d-bd9e-4f2d98245c1c --><\/p>\n<table style=\"border-collapse: collapse; width: 100%; height: 300px;\">\n<thead>\n<tr style=\"height: 50px;\">\n<td style=\"width: 33.3333%; height: 50px;\">Library<\/td>\n<td style=\"width: 33.3333%; height: 50px;\">Version used<\/td>\n<td style=\"width: 33.3333%; height: 50px;\">Description<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr style=\"height: 50px;\">\n<td style=\"width: 33.3333%; height: 50px;\">SolidJS<\/td>\n<td style=\"width: 33.3333%; height: 50px;\">1.7.6<\/td>\n<td style=\"width: 33.3333%; height: 50px;\">Our main UI library.<!-- notionvc: 9bf5e2ac-f375-4666-9857-467f2f7ed301 --><\/td>\n<\/tr>\n<tr style=\"height: 50px;\">\n<td style=\"width: 33.3333%; height: 50px;\">i18next<!-- notionvc: 9aa2f072-34ba-41ef-8d53-8bfa55f2f202 --><\/td>\n<td style=\"width: 33.3333%; height: 50px;\">22.5.1<\/td>\n<td style=\"width: 33.3333%; height: 50px;\">Used to localize our app.<\/td>\n<\/tr>\n<tr style=\"height: 50px;\">\n<td style=\"width: 33.3333%; height: 50px;\">solid-i18next<\/td>\n<td style=\"width: 33.3333%; height: 50px;\">1.2.2<\/td>\n<td style=\"width: 33.3333%; height: 50px;\">Used to localize our app.<\/td>\n<\/tr>\n<tr style=\"height: 50px;\">\n<td style=\"width: 33.3333%; height: 50px;\">i18next-http-backend<\/td>\n<td style=\"width: 33.3333%; height: 50px;\">2.2.1<\/td>\n<td style=\"width: 33.3333%; height: 50px;\">Used to load translation files asynchronously.<\/td>\n<\/tr>\n<tr style=\"height: 50px;\">\n<td style=\"width: 33.3333%; height: 50px;\">i18next-browser-languagedetector<\/td>\n<td style=\"width: 33.3333%; height: 50px;\">7.1.0<\/td>\n<td style=\"width: 33.3333%; height: 50px;\">Used to automatically detect the user&#8217;s browser language.<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 33.3333%;\">tailwindcss<\/td>\n<td style=\"width: 33.3333%;\">3.3.2<\/td>\n<td style=\"width: 33.3333%;\">For styling; optional for our purposes.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Let&#8217;s use the SolidJS\u00a0<a class=\"notion-link-token notion-focusable-token notion-enable-hover\" tabindex=\"0\" href=\"https:\/\/vitejs.dev\/\" rel=\"noopener noreferrer\" data-token-index=\"1\"><span class=\"link-annotation-unknown-block-id-1178839340\">Vite<\/span><\/a>\u00a0template to kickstart a new project. From the command line:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">npx degit solidjs\/templates\/js weather-app<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_a594bf960ca6cd13eb94be9a66942a41\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Let&#8217;s quickly go over the components and starter code we need to have to get a working weather app. Our root <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">App.js<\/span> looks like this:<!-- notionvc: c9a42b39-101d-4236-9f7e-65aaeed846ef --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/app.js<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> WeatherForm <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/components\/WeatherForm\"<\/span>;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">App<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> (\n   {<span class=\"hljs-comment\">\/* CSS classes removed for brevity. *\/<\/span>}\n    &lt;div&gt;\n      <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>SolidJS Weather App<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span><\/span>\n      <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">WeatherForm<\/span> \/&gt;<\/span><\/span>\n    &lt;<span class=\"hljs-regexp\">\/div&gt;\n  );\n}\n\nexport default App;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_b78c8f80c0205657f65d55a7b26ab2a3\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>The <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">WeatherForm<\/span> component contains the following code:<!-- notionvc: 362f8f0b-1449-46e5-b4ac-8cfa0d7efe9c --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/components\/WeatherForm.jsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { createSignal } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"solid-js\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> mockWeatherData = &#91;\n  {\n    <span class=\"hljs-attr\">city<\/span>: <span class=\"hljs-string\">\"San Francisco\"<\/span>,\n    <span class=\"hljs-attr\">country<\/span>: <span class=\"hljs-string\">\"USA\"<\/span>,\n    <span class=\"hljs-attr\">temperatureCelcius<\/span>: <span class=\"hljs-number\">18<\/span>,\n    <span class=\"hljs-attr\">temperatureFahrenheit<\/span>: <span class=\"hljs-number\">64.4<\/span>,\n    <span class=\"hljs-attr\">feelsLike<\/span>: <span class=\"hljs-number\">15<\/span>,\n    <span class=\"hljs-attr\">humidity<\/span>: <span class=\"hljs-number\">0.7<\/span>,\n    <span class=\"hljs-attr\">date<\/span>: <span class=\"hljs-string\">\"10\/04\/2023\"<\/span>,\n    <span class=\"hljs-attr\">weatherStations<\/span>: <span class=\"hljs-number\">11<\/span>,\n  },\n  {\n    <span class=\"hljs-attr\">city<\/span>: <span class=\"hljs-string\">\"Paris\"<\/span>,\n    <span class=\"hljs-attr\">country<\/span>: <span class=\"hljs-string\">\"France\"<\/span>,\n    <span class=\"hljs-attr\">temperatureCelcius<\/span>: <span class=\"hljs-number\">5<\/span>,\n    <span class=\"hljs-attr\">temperatureFahrenheit<\/span>: <span class=\"hljs-number\">41<\/span>,\n    <span class=\"hljs-attr\">feelsLike<\/span>: <span class=\"hljs-number\">3<\/span>,\n    <span class=\"hljs-attr\">humidity<\/span>: <span class=\"hljs-number\">0.8<\/span>,\n    <span class=\"hljs-attr\">date<\/span>: <span class=\"hljs-string\">\"10\/04\/2023\"<\/span>,\n    <span class=\"hljs-attr\">weatherStations<\/span>: <span class=\"hljs-number\">10<\/span>,\n  },\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n];\n\n<span class=\"hljs-keyword\">const<\/span> WeatherForm = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;city, setCity] = createSignal(<span class=\"hljs-string\">\"\"<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;weatherData, setWeatherData] = createSignal(<span class=\"hljs-literal\">null<\/span>);\n\n  <span class=\"hljs-keyword\">const<\/span> handleSubmit = <span class=\"hljs-function\">(<span class=\"hljs-params\">event<\/span>) =&gt;<\/span> {\n    event.preventDefault();\n\n    <span class=\"hljs-keyword\">const<\/span> cityWeather = mockWeatherData.find(\n      <span class=\"hljs-function\">(<span class=\"hljs-params\">weather<\/span>) =&gt;<\/span> weather.city.toLowerCase() === city().toLowerCase()\n    );\n\n    <span class=\"hljs-keyword\">if<\/span> (cityWeather) {\n      setWeatherData(cityWeather);\n    } <span class=\"hljs-keyword\">else<\/span> {\n      setWeatherData({\n        <span class=\"hljs-attr\">error<\/span>: <span class=\"hljs-string\">`Could not find weather for <span class=\"hljs-subst\">${city()}<\/span>`<\/span>,\n      });\n    }\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">onSubmit<\/span>=<span class=\"hljs-string\">{handleSubmit}<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n          <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span>\n          <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Enter a city\"<\/span>\n          <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{city()}<\/span>\n          <span class=\"hljs-attr\">onInput<\/span>=<span class=\"hljs-string\">{(event)<\/span> =&gt;<\/span> setCity(event.target.value)}\n         \/&gt;\n\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span>&gt;<\/span>\n          Get Weather\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n\n      {weatherData() &amp;&amp; !weatherData().error &amp;&amp; (\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n            \ud83d\udccd {weatherData().city}, {weatherData().country}\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n              Temperature is: {weatherData().temperatureCelcius}\u00b0C\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">br<\/span> \/&gt;<\/span>\n              Or: {weatherData().temperatureFahrenheit}\u00b0F\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n              Feels like: {weatherData().feelsLike}\u00b0C\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Humidity:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{weatherData().humidity}%<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Data recorded on:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{weatherData().date}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Number of Weather Stations:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n              {weatherData().weatherStations}\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      )}\n      {weatherData() &amp;&amp; weatherData().error &amp;&amp; (\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{weatherData().error}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      )}\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> WeatherForm;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_63076828aed943e9c83d90240b720c35\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\" wp-image-68470 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-01-at-22.51.48@2x.png\" alt=\"Our weather component in the demo app | Phrase\" width=\"474\" height=\"174\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-01-at-22.51.48@2x.png 806w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-01-at-22.51.48@2x-300x110.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-01-at-22.51.48@2x-768x282.png 768w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/p>\n<p>We\u2019re mocking and hard-coding our weather data in the component, which uses the text <code>&lt;input&gt;<\/code> value to filter by city. If a city is found then all its weather is displayed, otherwise we show an error message.<\/p>\n<p>\ud83d\uddd2\ufe0f\u00a0<em>Note \u00bb\u00a0<\/em>You can <a href=\"https:\/\/github.com\/PhraseApp-Blog\/solidjs-i18next-starter\">find the\u00a0starter project\u00a0with all the app code before localization on GitHub<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"the-solid-i18next-library\"><\/span>The solid-i18next library<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>To add i18n functionality to our weather app, we will use the <a href=\"https:\/\/github.com\/mbarzda\/solid-i18next\">solid-i18next<\/a> library. solid-i18next provides utilities and components for integrating i18n functionality into SolidJS applications using the very popular <a href=\"https:\/\/www.i18next.com\/\">i18next<\/a> library. It\u2019s straightforward to integrate into your app, and supports various localization features like changing the language, interpolation, pluralization, and more.<\/p>\n<p>\ud83d\udce3 <em>Shout-out \u00bb<\/em> <a href=\"https:\/\/github.com\/mbarzda\">Martynas Barzda<\/a> created and continues to maintain solid-i18next.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-set-up-solid-i18next\"><\/span>How do I set up solid-i18next?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We will start by installing the required dependencies from the command line:<\/p>\n<p><!-- notionvc: e260a34f-4c90-433b-a602-eaec6526b7e5 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">npm<\/span> <span class=\"hljs-selector-tag\">install<\/span> <span class=\"hljs-keyword\">@mbarzda<\/span>\/solid-i18next i18next --save<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_33592633525180512673fb834e6de7e9\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Next, we need to wrap our app&#8217;s root component with solid-i18next\u2019s <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">&lt;TransProvider \/&gt;<\/span>:<!-- notionvc: e0d46e33-07e2-4a29-a11d-f0f750684143 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ src\/index.js<\/span>\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> App <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/App\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { TransProvider } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@mbarzda\/solid-i18next\"<\/span>;\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\nrender(\n  <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TransProvider<\/span> <span class=\"hljs-attr\">lng<\/span>=<span class=\"hljs-string\">\"en-US\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">App<\/span> \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">TransProvider<\/span>&gt;<\/span><\/span>\n  ),\n  root\n);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_17f45c8accd7b81df29a2247ffaf855e\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We incorporate the <code>&lt;TransProvider&gt;<\/code> component from solid-i18next which initializes i18next under the hood by executing <code>i18next.init()<\/code>. It creates a translation context in our SolidJS app and is responsible for providing the i18n functionality throughout our app components. We can also provide the <code>lng<\/code> prop to specify the default locale to use in the app.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-setup-and-configure-i18next\"><\/span>How do I setup and configure i18next?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We will support 4 languages in this tutorial: English (default), Spanish, Russian and Arabic.<\/p>\n<p>Let&#8217;s initialize i18next. Inside <code>App.jsx<\/code>, we create a new effect call to initialize i18next:<\/p>\n<p><!-- notionvc: 63b2d4a7-f56e-48fb-9c8f-014d69ae3ce0 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/App.jsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { createEffect } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"solid-js\"<\/span>;\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\ncreateEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    i18next\n      .init({\n\t\t\t\t<span class=\"hljs-comment\">\/\/ Default language.<\/span>\n        <span class=\"hljs-attr\">lng<\/span>: <span class=\"hljs-string\">\"en-US\"<\/span>,\n\t\t\t\t<span class=\"hljs-comment\">\/\/ Logging i18next information to the console.<\/span>\n        <span class=\"hljs-attr\">debug<\/span>: <span class=\"hljs-literal\">true<\/span>,\n\t\t\t\t<span class=\"hljs-comment\">\/\/ Interpolation option to escape passed in<\/span>\n        <span class=\"hljs-comment\">\/\/ values to avoid XSS injection.<\/span>\n        <span class=\"hljs-attr\">interpolation<\/span>: {\n          <span class=\"hljs-attr\">escapeValue<\/span>: <span class=\"hljs-literal\">true<\/span>,\n        },\n\t\t\t\t<span class=\"hljs-comment\">\/\/ Language to use if translations in the<\/span>\n        <span class=\"hljs-comment\">\/\/ active language are not available.<\/span>\n        <span class=\"hljs-attr\">fallbackLng<\/span>: <span class=\"hljs-literal\">false<\/span>,\n\t\t\t\t<span class=\"hljs-comment\">\/\/ Resources i18next will use to retrieve<\/span>\n        <span class=\"hljs-comment\">\/\/ the appropriate translations based on<\/span>\n        <span class=\"hljs-comment\">\/\/ the active locale.<\/span>\n\t\t\t\t<span class=\"hljs-attr\">resources<\/span>: {\n          <span class=\"hljs-string\">\"en-US\"<\/span>: {\n            <span class=\"hljs-attr\">translation<\/span>: {\n              <span class=\"hljs-attr\">app_name<\/span>: <span class=\"hljs-string\">\"SolidJS Weather App\"<\/span>,\n              <span class=\"hljs-attr\">get_weather_button_label<\/span>: <span class=\"hljs-string\">\"Get Weather\"<\/span>,\n              <span class=\"hljs-attr\">city_input_placeholder<\/span>: <span class=\"hljs-string\">\"Enter a city\"<\/span>,\n              <span class=\"hljs-comment\">\/\/ ...<\/span>\n            },\n          },\n          <span class=\"hljs-string\">\"es-ES\"<\/span>: {\n            <span class=\"hljs-attr\">translation<\/span>: {\n              <span class=\"hljs-attr\">app_name<\/span>: <span class=\"hljs-string\">\"Aplicaci\u00f3n meteorol\u00f3gica SolidJS\"<\/span>,\n              <span class=\"hljs-attr\">get_weather_button_label<\/span>: <span class=\"hljs-string\">\"Obtener el clima\"<\/span>,\n              <span class=\"hljs-attr\">city_input_placeholder<\/span>: <span class=\"hljs-string\">\"Introduce una ciudad\"<\/span>,\n              <span class=\"hljs-comment\">\/\/ ...<\/span>\n            },\n          },\n          <span class=\"hljs-string\">\"ru-RU\"<\/span>: {\n            <span class=\"hljs-attr\">translation<\/span>: {\n              <span class=\"hljs-attr\">app_name<\/span>: <span class=\"hljs-string\">\"\u041f\u043e\u0433\u043e\u0434\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 SolidJS\"<\/span>,\n              <span class=\"hljs-attr\">get_weather_button_label<\/span>: <span class=\"hljs-string\">\"\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u0433\u043e\u0434\u0443\"<\/span>,\n              <span class=\"hljs-attr\">city_input_placeholder<\/span>: <span class=\"hljs-string\">\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0433\u043e\u0440\u043e\u0434\"<\/span>,\n              <span class=\"hljs-comment\">\/\/ ...<\/span>\n            },\n          },\n          <span class=\"hljs-string\">\"ar-EG\"<\/span>: {\n            <span class=\"hljs-attr\">translation<\/span>: {\n              <span class=\"hljs-attr\">app_name<\/span>: <span class=\"hljs-string\">\"\u062a\u0637\u0628\u064a\u0642 \u0627\u0644\u0637\u0642\u0633 \u0633\u0648\u0644\u064e\u062f \u0686\u0627\u064a \u0625\u0633\"<\/span>,\n              <span class=\"hljs-attr\">get_weather_button_label<\/span>: <span class=\"hljs-string\">\"\u0627\u0644\u0637\u0642\u0633\"<\/span>,\n              <span class=\"hljs-attr\">city_input_placeholder<\/span>: <span class=\"hljs-string\">\"\u0623\u062f\u062e\u0644 \u0645\u062f\u064a\u0646\u0629\"<\/span>,\n              <span class=\"hljs-comment\">\/\/ ...<\/span>\n            },\n          },\n        },\n      })\n  });\n\n  <span class=\"hljs-keyword\">return<\/span> (\n      <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>SolidJS Weather App<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span><\/span>\n      <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">WeatherForm<\/span> \/&gt;<\/span><\/span>\n  );\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_f889d46eca271b876ce899226a889f63\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We are initializing an <code>i18next<\/code> instance inside the <code>createEffect()<\/code> to ensure we only initialize it <em>once<\/em>, instead of on every render.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-translation-files\"><\/span>How do I work with translation files?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Having all our translations inline in <code>resources<\/code> like we do above is not very scalable. Let\u2019s move our translations into separate files, one per locale, and only load in the one we need.<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> A <em>locale<\/em> defines linguistic conventions for data display, with a locale code identifying a specific locale and region e.g. <code>en-US<\/code> for English as spoken in the United States.<\/p>\n<p>Solid-i18next uses the i18next library and follows its format for adding translation files in folders that follow the naming convention, <code>locales\/{{lng}}\/{{ns}}.json<\/code>. <code>{{lng}}<\/code> is the locale and <code>{{ns}}<\/code> is the namespace (<code>translation<\/code> by default). For example, our Spanish translations would sit in <code>locales\/es-ES\/translation.json<\/code>.<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> Namespaces give us a bit of structure, so we can break up our translations into logical sections if need be (e.g. <code>admin<\/code>, <code>login<\/code>).<\/p>\n<p>Let\u2019s create a <code>locales<\/code> folder at the root of our project with locale folders inside e.g. <code>locales\/en-US<\/code>, <code>locales\/es-ES<\/code>.<\/p>\n<p>Next, we will add a <code>translations.json<\/code> file inside each locale folder.<\/p>\n<p><!-- notionvc: 2a03eb00-471f-405c-afe0-be5591d974b8 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/en-US\/translation.json<\/span>\n\n{\n  <span class=\"hljs-attr\">\"app_name\"<\/span>: <span class=\"hljs-string\">\"SolidJS Weather App\"<\/span>,\n  <span class=\"hljs-attr\">\"get_weather_button_label\"<\/span>: <span class=\"hljs-string\">\"Get Weather\"<\/span>,\n  <span class=\"hljs-attr\">\"city_input_placeholder\"<\/span>: <span class=\"hljs-string\">\"Enter a city\"<\/span>,\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/es-ES\/translation.json<\/span>\n\n{\n  <span class=\"hljs-attr\">\"app_name\"<\/span>: <span class=\"hljs-string\">\"Aplicaci\u00f3n meteorol\u00f3gica SolidJS\"<\/span>,\n  <span class=\"hljs-attr\">\"get_weather_button_label\"<\/span>: <span class=\"hljs-string\">\"Obtener el clima\"<\/span>,\n  <span class=\"hljs-attr\">\"city_input_placeholder\"<\/span>: <span class=\"hljs-string\">\"Introduce una ciudad\"<\/span>,\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/ru-RU\/translation.json<\/span>\n\n{\n <span class=\"hljs-attr\">\"app_name\"<\/span>: <span class=\"hljs-string\">\"\u041f\u043e\u0433\u043e\u0434\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 SolidJS\"<\/span>,\n <span class=\"hljs-attr\">\"get_weather_button_label\"<\/span>: <span class=\"hljs-string\">\"\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u0433\u043e\u0434\u0443\"<\/span>,\n <span class=\"hljs-attr\">\"city_input_placeholder\"<\/span>: <span class=\"hljs-string\">\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0433\u043e\u0440\u043e\u0434\"<\/span>,\n <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/ar-EG\/translation.json<\/span>\n\n{\n  <span class=\"hljs-attr\">\"app_name\"<\/span>: <span class=\"hljs-string\">\"\u062a\u0637\u0628\u064a\u0642 \u0627\u0644\u0637\u0642\u0633 \u0633\u0648\u0644\u064e\u062f \u0686\u0627\u064a \u0625\u0633\"<\/span>,\n  <span class=\"hljs-attr\">\"get_weather_button_label\"<\/span>: <span class=\"hljs-string\">\"\u0627\u0644\u0637\u0642\u0633\"<\/span>,\n  <span class=\"hljs-attr\">\"city_input_placeholder\"<\/span>: <span class=\"hljs-string\">\"\u0623\u062f\u062e\u0644 \u0645\u062f\u064a\u0646\u0629\"<\/span>,\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_9c6b5666860f5c9195a8d1ad4e3cb573\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>As we progress throughout the article, we will add new translation keys. But first, we need to load these translation files into i18next.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-load-translation-files-asynchronosly\"><\/span>How do I load translation files asynchronosly?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>i18next supports lazy loading of translations through the <a href=\"https:\/\/github.com\/i18next\/i18next-http-backend\">i18next-http-backend<\/a> plugin, which we will install in our app.<\/p>\n<p><!-- notionvc: 5de77634-0ae4-4d9c-82b6-5c81b21719c6 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">npm install i18next-http-backend<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_7b5b0ab08652ad25dc7ca13dce3870e3\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>And now, use it in our <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">App.jsx<\/span> so our modified initialization code becomes this:<!-- notionvc: 69b9d40a-142d-427f-94a0-70b1b98c6646 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/App.jsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { Show, createEffect, createSignal } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"solid-js\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> Backend <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"i18next-http-backend\"<\/span>;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">App<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\t<span class=\"hljs-keyword\">const<\/span> &#91;isReady, setIsReady] = createSignal(<span class=\"hljs-literal\">false<\/span>);\n\t\n\tcreateEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\t\ti18next\n\t\t  .use(Backend)\n\t\t  .init({\n\t\t    <span class=\"hljs-attr\">lng<\/span>: <span class=\"hljs-string\">\"en-US\"<\/span>,\n\t\t    <span class=\"hljs-attr\">debug<\/span>: <span class=\"hljs-literal\">true<\/span>,\n\t\t    <span class=\"hljs-attr\">interpolation<\/span>: {\n\t\t      <span class=\"hljs-attr\">escapeValue<\/span>: <span class=\"hljs-literal\">true<\/span>\n\t\t    },\n\t\t    <span class=\"hljs-attr\">fallbackLng<\/span>: <span class=\"hljs-literal\">false<\/span>,\n\t\t    <span class=\"hljs-comment\">\/\/ The default namespace to load when none<\/span>\n        <span class=\"hljs-comment\">\/\/ are specified explicitly.<\/span>\n        <span class=\"hljs-comment\">\/\/ \"translation\" is the default value here,<\/span>\n        <span class=\"hljs-comment\">\/\/ so we can can remove the `ns` option here<\/span>\n        <span class=\"hljs-comment\">\/\/ entirely if we wanted.<\/span>\n\t\t    <span class=\"hljs-attr\">ns<\/span>: <span class=\"hljs-string\">\"translation\"<\/span>,\n\t\t    <span class=\"hljs-attr\">backend<\/span>: {\n\t\t      <span class=\"hljs-attr\">loadPath<\/span>: <span class=\"hljs-string\">\"..\/locales\/{{lng}}\/{{ns}}.json\"<\/span>\n\t\t    }\n\t\t  })\n\t\t  .then(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> setIsReady(<span class=\"hljs-literal\">true<\/span>))\n\t\t  .catch(<span class=\"hljs-function\">(<span class=\"hljs-params\">err<\/span>) =&gt;<\/span> <span class=\"hljs-built_in\">console<\/span>.error(err));\n\t});\n\n<span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Show<\/span> <span class=\"hljs-attr\">when<\/span>=<span class=\"hljs-string\">{isReady()}<\/span> <span class=\"hljs-attr\">fallback<\/span>=<span class=\"hljs-string\">{<\/span>&lt;<span class=\"hljs-attr\">div<\/span>&gt;<\/span>Loading...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>}&gt;\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>SolidJS Weather App<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">WeatherForm<\/span> \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Show<\/span>&gt;<\/span><\/span>\n  );\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_53091898f868eb792df6d4b77a652409\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Since we\u2019re loading our translation file asynchronously, we use a SolidJS <code>&lt;Show&gt;<\/code> component to display a loading message.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"a-note-on-the-dev-language-and-fallback\"><\/span>A note on the dev language and fallback<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>You might get an error like the following in your browser console:<\/p>\n<p><!-- notionvc: fcd8a5da-8a6d-4b37-985b-0eae7bf96caf --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"Diff\" data-shcb-language-slug=\"diff\"><span><code class=\"hljs language-diff\">GET http:\/\/localhost:3000\/locales\/dev\/translation.json\nnet::ERR ABORTED 404 (Not Found).<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Diff<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">diff<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_fead73a788e6daf34d77f24a4ca79a26\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>This is normal, and just indicates that i18next is falling back to the developer language, <code>dev<\/code>. You can set <code>fallbackLng<\/code> to <code>false<\/code> during development to disable the error if you like, although <a href=\"https:\/\/www.i18next.com\/principles\/fallback#fallback-to-different-languages\">according to the official docs<\/a> the <code>dev<\/code> language does have its uses.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-retrieve-the-active-locale\"><\/span>How do I retrieve the active locale?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We can use i18next&#8217;s <a href=\"https:\/\/www.i18next.com\/overview\/api#language\">language<\/a> property to get the currently active locale.<\/p>\n<p><!-- notionvc: f2b821e8-c6b9-4f80-805d-6b3d51fefc6f --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> i18next <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"i18next\"<\/span>;\n\n<span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">\"Active locale:\"<\/span>, i18next.language);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_a336550095e75232ddf04906f3b27c82\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-basic-translation-messages\"><\/span>How do I work with basic translation messages?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Now that we set up our translations in the supported locales, we can start using solid-i18next to translate our app. The library provides a <code>useTransContext()<\/code> hook, which provides us with a translation <code>t<\/code> function. Given a translation key, <code>t<\/code> returns the associated translation in the active locale.<\/p>\n<p><!-- notionvc: d771260f-5150-4445-9e5e-b5efa489ead6 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/App.jsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { useTransContext } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@mbarzda\/solid-i18next\"<\/span>;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">App<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> &#91;t] = useTransContext();\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Show<\/span> <span class=\"hljs-attr\">when<\/span>=<span class=\"hljs-string\">{isReady()}<\/span> <span class=\"hljs-attr\">fallback<\/span>=<span class=\"hljs-string\">{<\/span>&lt;<span class=\"hljs-attr\">div<\/span>&gt;<\/span>Loading...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>}&gt;\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>{t(\"app_name\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Show<\/span>&gt;<\/span><\/span>\n  );\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> App;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_fde7996072e3050c9502048db805b1d3\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Try changing the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">lng<\/span> value in the i18next config from <span class=\"discussion-id-6df0ca2e-72e1-4a5b-8b50-ddb300731cda notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">en-US<\/span> to <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"5\">es-ES<\/span>; you will see the heading now translated into Spanish. The same goes for the other locales, <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"7\">ru-RU<\/span> and <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"9\">ar-EG<\/span>.<!-- notionvc: d9865b20-027b-43a9-b5d1-4682440d375c --><img loading=\"lazy\" decoding=\"async\" class=\" wp-image-68479 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-13.37.52@2x-1024x143.png\" alt=\"Our app name translated to the es-ES locale | Phrase\" width=\"530\" height=\"74\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-13.37.52@2x-1024x143.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-13.37.52@2x-300x42.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-13.37.52@2x-768x108.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-13.37.52@2x.png 1042w\" sizes=\"(max-width: 530px) 100vw, 530px\" \/><\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> solid-i18next library also has a <code>&lt;Trans&gt;<\/code> component for translating messages. You can <a href=\"https:\/\/github.com\/mbarzda\/solid-i18next#simple-example\">Read more about &lt;Trans&gt; the official docs<\/a>.<\/p>\n<p>Now let&#8217;s switch our attention to the weather form component and translate its strings:<\/p>\n<p><!-- notionvc: f376bbf3-223d-4240-ad94-66fd7d6bca9f --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/components\/WeatherForm.jsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { useTransContext } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@mbarzda\/solid-i18next\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> WeatherForm = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;t] = useTransContext();\n  \n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span>\n        <span class=\"hljs-attr\">onSubmit<\/span>=<span class=\"hljs-string\">{handleSubmit}<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n          <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span>\n          <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">{t(<\/span>\"<span class=\"hljs-attr\">placeholder<\/span>\")}\n          <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{city()}<\/span>\n          <span class=\"hljs-attr\">onInput<\/span>=<span class=\"hljs-string\">{(event)<\/span> =&gt;<\/span> setCity(event.target.value)} \/&gt;\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span>&gt;<\/span>\n          {t(\"button_label\")}\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n\n      {weatherData() &amp;&amp; !weatherData().error &amp;&amp; (\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          \/\/ ...\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{t(\"humidity\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{weatherData().humidity}%<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{t(\"date_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{weatherData().date}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{t(\"weather_stations_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n              {weatherData().weatherStations}\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      )}\n      \/\/ ...\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> WeatherForm;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_14eaafb75da56565fa551491c67702f1\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>But what if a translation value is missing from your translation files? In that case, the translation key will act as a fallback. For example, if we do this:<!-- notionvc: 6d6e6982-d8ae-4326-acd8-828a2c56886f --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">&lt;p&gt;{t(<span class=\"hljs-string\">\"section_heading\"<\/span>)}&lt;<span class=\"hljs-regexp\">\/p&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_24e8a60214a02d134feba25b9d50a23c\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>And if you haven&#8217;t defined <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">section_heading<\/span> in your translation files, <span class=\"discussion-id-df2b12e1-3099-419b-b095-a8e6dfb81aa6 notion-enable-hover\" data-token-index=\"3\">you will get &#8220;section_heading&#8221; in our browser<\/span>.<!-- notionvc: 0083790d-ffa2-43bf-b1c4-dde7d516e05e --><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" wp-image-68485 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-13.39.49@2x.png\" alt=\"Our weather app with the fallback translation key | Phrase\" width=\"495\" height=\"259\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-13.39.49@2x.png 722w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-13.39.49@2x-300x157.png 300w\" sizes=\"(max-width: 495px) 100vw, 495px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-add-a-language-switcher\"><\/span>How do I add a language switcher?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let\u2019s give our users a nice little drop-down to select their language of choice. We will create a new component for this: it will comprise a basic <code>&lt;select&gt;<\/code> element for choosing from the supported languages in our app:<\/p>\n<p><!-- notionvc: 18951aa6-d473-477b-b373-3afcddc26624 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/components\/LanguageSwitcher.jsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { useTransContext } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@mbarzda\/solid-i18next\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> i18next <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"i18next\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> LanguageSwitcher = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;t, { changeLanguage }] = useTransContext();\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handleLanguageChange<\/span>(<span class=\"hljs-params\">event<\/span>) <\/span>{\n    changeLanguage(event.target.value);\n  }\n\n  <span class=\"hljs-keyword\">return<\/span> (\n   <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n       <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">select<\/span>\n\t\t\t\t\t<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{i18next.language}<\/span> \n\t\t\t\t\t<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{handleLanguageChange}<\/span>\n       &gt;<\/span>\n         <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"en-US\"<\/span>&gt;<\/span>{t(\"english_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n         <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"es-ES\"<\/span>&gt;<\/span>{t(\"spanish_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n         <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"ru-RU\"<\/span>&gt;<\/span>{t(\"russian_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n         <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"ar-EG\"<\/span>&gt;<\/span>{t(\"arabic_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n       <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">select<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> LanguageSwitcher;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_cccd13b2418890a2417df9fa1ab59b05\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>i18next provides a <code>changeLanguage<\/code> function which updates the active locale in the i18next instance. When we use the <code>changeLanguage<\/code> to update the active locale, solid-i18next ensures that all of our <code>t()<\/code> calls are re-rendered.<\/p>\n<p><!-- notionvc: 5c4dad3d-d1f0-43bc-ab2d-60cbb2e87c45 --><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" wp-image-68491 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-14.00.44-1023x370.gif\" alt=\"Our app\u2019s language switcher in action | Phrase\" width=\"589\" height=\"213\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-14.00.44-1023x370.gif 1023w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-14.00.44-299x108.gif 299w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-05-at-14.00.44-768x278.gif 768w\" sizes=\"(max-width: 589px) 100vw, 589px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-add-dynamic-values-in-translation-messages\"><\/span>How do I add dynamic values in translation messages?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Sometimes we need to include variables in our translation messages at runtime. In our app, we show an error message to the user when they search for a city we don\u2019t have weather data for.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-68499 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.20.47@2x.png\" alt=\"Our app\u2019s error message when there\u2019s no weather data for a particular city | Phrase\" width=\"560\" height=\"353\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.20.47@2x.png 742w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.20.47@2x-300x189.png 300w\" sizes=\"(max-width: 560px) 100vw, 560px\" \/><\/p>\n<p>When translating this message, we\u2019d like to interpolate the city name at runtime. We can do this using i18next\u2019s <code>{{variable}}<\/code> format.<!-- notionvc: 1e80da68-ef70-4910-832e-898c3398a0ac --><\/p>\n<p><!-- notionvc: 9164d3c3-74a6-4f8a-bacf-9f72e16d1ea2 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/en-US\/translation.json<\/span>\n\n{\n  <span class=\"hljs-attr\">\"error_message\"<\/span>: <span class=\"hljs-string\">\"Could not find weather for {{city}}\"<\/span>,\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/es-ES\/translation.json<\/span>\n\n{\n  <span class=\"hljs-attr\">\"error_message\"<\/span>: <span class=\"hljs-string\">\"No se pudo encontrar el clima para {{city}}\"<\/span>,\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/ru-RU\/translation.json<\/span>\n\n{\n  <span class=\"hljs-attr\">\"error_message\"<\/span>: <span class=\"hljs-string\">\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u043f\u043e\u0433\u043e\u0434\u0443 \u0434\u043b\u044f {{city}}\"<\/span>,\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/ar-EG\/translation.json<\/span>\n\n{\n   <span class=\"hljs-attr\">\"error_message\"<\/span>: <span class=\"hljs-string\">\"\u0644\u0627 \u064a\u0645\u0643\u0646 \u0627\u0644\u0639\u062b\u0648\u0631 \u0639\u0644\u0649 \u0627\u0644\u0637\u0642\u0633 \u0644{{city}}\"<\/span>,\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_8c898aaa3c7cb7b3b348a259039612f2\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Inside the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">WeatherComponent<\/span>, instead of hardcoding the error string, we can replace it with the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">t<\/span> function, passing in the correct key and passing <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"5\">city<\/span> as the second parameter:<!-- notionvc: 2ede5a7c-65b8-4199-826e-1192da34d946 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/components\/WeatherForm.jsx<\/span>\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\n\t\t<span class=\"hljs-keyword\">if<\/span> (cityWeather) {\n\t\t\tsetWeatherData(cityWeather);\n\t\t} <span class=\"hljs-keyword\">else<\/span> {\n\t\t\tsetWeatherData({\n\t\t\t  <span class=\"hljs-attr\">error<\/span>: t(<span class=\"hljs-string\">\"error_message\"<\/span>, {\n\t\t\t    <span class=\"hljs-attr\">city<\/span>: city(),\n\t\t\t  }),\n\t\t\t});\n\t\t}\n\n<span class=\"hljs-comment\">\/\/ ...<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_a844124f50f389dd7428408f3ed16ab4\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-68507 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-22.44.26@2x.png\" alt=\"Our app\u2019s error message when there\u2019s no weather data for a particular city in es-ES locale | Phrase\" width=\"756\" height=\"335\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-22.44.26@2x.png 1016w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-22.44.26@2x-300x133.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-22.44.26@2x-768x340.png 768w\" sizes=\"(max-width: 756px) 100vw, 756px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-plurals-in-translation-messages\"><\/span>How do I work with plurals in translation messages?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The languages our app supports have different plural forms. English has 2 forms (<em>one<\/em>\u00a0and\u00a0<em>other<\/em>), Spanish has 3 (<em>one<\/em>,\u00a0<em>many<\/em>\u00a0and\u00a0<em>other<\/em>), Russian has 4 (<em>one<\/em>,\u00a0<em>few<\/em>,\u00a0<em>many<\/em>\u00a0and\u00a0<em>other<\/em>), and Arabic has 6 (<em>zero<\/em>,\u00a0<em>one<\/em>,\u00a0<em>two<\/em>,\u00a0<em>few<\/em>,\u00a0<em>many<\/em>\u00a0and\u00a0<em>other<\/em>).<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> i18next uses the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/PluralRules\">Intl.PluralRules<\/a> API under the hood to determine which plural form to use for a message in the active locale.<\/p>\n<p>In our app, we need the number of weather stations for a city, a pluralized message:<\/p>\n<p><!-- notionvc: e8338757-4d67-40df-aa8e-680524592877 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/components\/WeatherForm.jsx<\/span>\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\n<span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      \/\/ ...\n      {weatherData() &amp;&amp; !weatherData().error &amp;&amp; (\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          \/\/ ...\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{t(\"weather_stations_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n              {t(\"station\", { count: weatherData().weatherStations })}\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      )}\n      \/\/ ...\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_86e2d2c09d5df210d94394013a6c1147\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Note the <code>count<\/code> variable here: this will be used by i18next to select the appropriate plural form.<\/p>\n<p>\u270b <em>Heads up \u00bb<\/em> There will be no fallback for a plural message if <code>count<\/code> is not provided.<\/p>\n<p>Now let&#8217;s add the corresponding translations. For plurals, we use separate keys to mark the plural forms of a message, adding a plural form suffix to each. For example, the <code>station<\/code> message plural forms would have keys <code>station_one<\/code>, <code>station_many<\/code>, etc.<\/p>\n<p>\ud83d\udd17 <em>Resource \u00bb<\/em> The <a href=\"https:\/\/www.unicode.org\/cldr\/charts\/42\/supplemental\/language_plural_rules.html\">CLDR Language Plural Rules chart<\/a> is a canonical source for languages\u2019 plural forms.<\/p>\n<p>To differentiate between normal and plural form keys, i18next relies on the <code>count<\/code> option passed to <code>t()<\/code>. If it sees the <code>count<\/code>, it will look for plural form keys.<\/p>\n<p><!-- notionvc: 3a1ede8e-a5d9-43df-8da3-6f6ab0bb3123 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/en-US\/translation.json<\/span>\n\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"station_one\"<\/span>: <span class=\"hljs-string\">\"{{count}} weather station\"<\/span>,\n  <span class=\"hljs-attr\">\"station_other\"<\/span>: <span class=\"hljs-string\">\"{{count}} weather stations\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/es-ES\/translation.json<\/span>\n\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"station_one\"<\/span>: <span class=\"hljs-string\">\"{{count}} estaci\u00f3n\"<\/span>,\n  <span class=\"hljs-attr\">\"station_many\"<\/span>: <span class=\"hljs-string\">\"{{count}} estaciones\"<\/span>,\n  <span class=\"hljs-attr\">\"station_other\"<\/span>: <span class=\"hljs-string\">\"{{count}} estaciones\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/ru-RU\/translation.json<\/span>\n\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"station_one\"<\/span>: <span class=\"hljs-string\">\"{{count}} \u043c\u0435\u0442\u0435\u043e\u0441\u0442\u0430\u043d\u0446\u0438\u044f\"<\/span>,\n  <span class=\"hljs-attr\">\"station_few\"<\/span>: <span class=\"hljs-string\">\"{{count}} \u043c\u0435\u0442\u0435\u043e\u0441\u0442\u0430\u043d\u0446\u0438\u0438\"<\/span>,\n  <span class=\"hljs-attr\">\"station_many\"<\/span>: <span class=\"hljs-string\">\"{{count}} \u041c\u043d\u043e\u0433\u043e \u0441\u0442\u0430\u043d\u0446\u0438\u0439\"<\/span>,\n  <span class=\"hljs-attr\">\"station_other\"<\/span>: <span class=\"hljs-string\">\"{{count}} \u0441\u0442\u0430\u043d\u0446\u0438\u0439\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/ar-EG\/translation.json<\/span>\n\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"station_one\"<\/span>: <span class=\"hljs-string\">\"\u0645\u062d\u0637\u0629 {{count}}\"<\/span>,\n  <span class=\"hljs-attr\">\"station_two\"<\/span>: <span class=\"hljs-string\">\"\u0645\u062d\u0637\u062a\u0627\u0646 {{count}}\"<\/span>,\n  <span class=\"hljs-attr\">\"station_few\"<\/span>: <span class=\"hljs-string\">\"{{count}} \u0645\u062d\u0637\u0627\u062a\"<\/span>,\n  <span class=\"hljs-attr\">\"station_many\"<\/span>: <span class=\"hljs-string\">\"{{count}} \u0645\u062d\u0637\u0629\"<\/span>,\n  <span class=\"hljs-attr\">\"station_other\"<\/span>: <span class=\"hljs-string\">\"{{count}} \u0645\u062d\u0637\u0629\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_cbc7dbb49e1e97c29e2fa37a7f3fd965\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-68513 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.22.01@2x-1024x286.png\" alt=\"Number of weather stations based on the count value in en-US locale | Phrase\" width=\"1024\" height=\"286\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.22.01@2x-1024x286.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.22.01@2x-300x84.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.22.01@2x-768x215.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.22.01@2x-1536x429.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.22.01@2x-2048x572.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-68519 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.02.14@2x-1024x267.png\" alt=\"Number of weather stations based on the count value in ru-RU locale | Phrase\" width=\"1024\" height=\"267\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.02.14@2x-1024x267.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.02.14@2x-300x78.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.02.14@2x-768x200.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.02.14@2x-1536x400.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.02.14@2x-2048x533.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p>\ud83d\udd17 <em>Resource \u00bb<\/em> See the <a href=\"https:\/\/www.i18next.com\/translation-function\/plurals\">official i18next documenation on Plurals<\/a> for more information.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"working-with-interval-plurals\"><\/span>Working with interval plurals<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Interval plurals help to define phrases expressing the number of items that lie within a range. If you want to use\u00a0<a href=\"https:\/\/www.i18next.com\/translation-function\/plurals#interval-plurals\">interval plurals<\/a>\u00a0in your application where, for instance, you want to pluralize the number of likes of a post, then you will need to use a post-processor called <a href=\"https:\/\/github.com\/i18next\/i18next-intervalplural-postprocessor\">i18next-intervalplural-postprocessor<\/a>.<\/p>\n<p>We first install it in our app:<\/p>\n<p><!-- notionvc: f3ec857a-b50a-47f2-a261-5b30cbe7ec6c --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">npm install i18next-intervalplural-postprocessor<\/code><\/span><\/pre>\n\n\n<div id=\"acf\/text-block_d54a908b99f8eee47e10f328ce058f5e\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Then add it to our <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">i18next<\/span> initialization code:<!-- notionvc: 61daf370-7d9c-494f-a9de-15ccb52a914f --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/App.jsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> i18next <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'i18next'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> intervalPlural <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'i18next-intervalplural-postprocessor'<\/span>;\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\ni18next\n .use(intervalPlural)\n .init({\n   <span class=\"hljs-comment\">\/\/ ...<\/span>\n })\n<span class=\"hljs-comment\">\/\/ ...<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_947a85a0915d4bad4cc74f363bc5b218\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Interval plurals have a special syntax in i18next translation messages:<!-- notionvc: 80c3879a-0f2f-4ce9-b40e-feb392c1e449 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-30\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/es-ES\/translation.json<\/span>\n\n<span class=\"hljs-string\">\"likes_interval\"<\/span>: <span class=\"hljs-string\">\"(0)&#91;No me gusta];(1-100)&#91;1+ gustos];(100-999)&#91;100+ gustos];(1000-9999)&#91;1k+ gustos];(10000-99999)&#91;10k+ gustos];\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-30\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_17f982314ffb08a6158daed2b13da5f0\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>With this, we can use these keys in our app like this:<!-- notionvc: 4e3acf4f-a940-4436-ba12-0927841a92f5 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-31\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/App.jsx<\/span>\n\n&lt;div&gt;\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Number of likes in this post: \n      {t(\"likes_interval\", {\n        postProcess: \"interval\",\n        count: weatherData().likes,\n      })}\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/span>\n  &lt;<span class=\"hljs-regexp\">\/div&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-31\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_5f16e3fc27a96cb01062c9db70d84eee\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-68529\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.25.13@2x.png\" alt=\"Interval plurals rendering number of likes in a post in en-US locale | Phrase\" width=\"816\" height=\"162\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.25.13@2x.png 816w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.25.13@2x-300x60.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.25.13@2x-768x152.png 768w\" sizes=\"(max-width: 816px) 100vw, 816px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-68535\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.13.19@2x-1024x137.png\" alt=\"Interval plurals rendering number of likes in a post in es-ES locale I Phrase\" width=\"1024\" height=\"137\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.13.19@2x-1024x137.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.13.19@2x-300x40.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.13.19@2x-768x103.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.13.19@2x.png 1032w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-localize-numbers\"><\/span>How do I localize numbers?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We need to format the numbers we see for the temperature, \u201cfeels like\u201d, and humidity values. Unlike English, Spanish and Russian, Arabic doesn&#8217;t use Western Arabic numerals (1, 2, 3). Instead, it uses Eastern Arabic numerals (\u0661,\u0662,\u0663). Each locale can also have its own large number separation rules (e.g. 1,000,000), percentage symbol, currency formatting, and more.<\/p>\n<p>\ud83e\udd3f <em>Go deeper \u00bb<\/em> Our <a href=\"https:\/\/phrase.com\/blog\/posts\/number-localization\/\">Concise Guide to Number Localization<\/a> covers the topic in much more detail.<\/p>\n<p>So we need a way to localize our numbers. We can format numbers in our translation messages using the <code>{{val, number}}<\/code> syntax. The <code>number<\/code> keyword here is essential: <a href=\"https:\/\/www.i18next.com\/translation-function\/formatting#number\">It tells i18next to format the value as a number<\/a> in the active locale.<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> i18next uses JavaScript\u2019s built-in <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/NumberFormat\">Intl.NumberFormat<\/a> ****object for number formatting.<\/p>\n<p>Let&#8217;s update all the instances of number formatting in our translation files so we get the following:<\/p>\n<p><!-- notionvc: 03dc5141-7498-4bc6-a4c8-551ea99700f0 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-32\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/en-US\/translation.json<\/span>\n\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"temperature_celcius\"<\/span>: <span class=\"hljs-string\">\"Temperature is: {{val, number}}\"<\/span>,\n  <span class=\"hljs-attr\">\"temperature_fahrenheit\"<\/span>: <span class=\"hljs-string\">\"Or: {{val, number}}\"<\/span>,\n  <span class=\"hljs-attr\">\"feels_like\"<\/span>: <span class=\"hljs-string\">\"Feels like: {{val, number}}\"<\/span>,\n  <span class=\"hljs-attr\">\"humidity_number\"<\/span>: <span class=\"hljs-string\">\"{{val, number}}\"<\/span>,\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-32\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-33\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/es-ES\/translation.json<\/span>\n\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"temperature_celcius\"<\/span>: <span class=\"hljs-string\">\"La temperatura es: {{val, number}}\"<\/span>,\n  <span class=\"hljs-attr\">\"temperature_fahrenheit\"<\/span>: <span class=\"hljs-string\">\"O: {{val, number}}\"<\/span>,\n  <span class=\"hljs-attr\">\"feels_like\"<\/span>: <span class=\"hljs-string\">\"Se siente como: {{val, number}}\"<\/span>,\n  <span class=\"hljs-attr\">\"humidity_number\"<\/span>: <span class=\"hljs-string\">\"{{val, number}}\"<\/span>,\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-33\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-34\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/ru-RU\/translation.json<\/span>\n\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"temperature_celcius\"<\/span>: <span class=\"hljs-string\">\"\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430: {{val, number}}\"<\/span>,\n  <span class=\"hljs-attr\">\"temperature_fahrenheit\"<\/span>: <span class=\"hljs-string\">\"\u0418\u043b\u0438: {{val, number}}\"<\/span>,\n  <span class=\"hljs-attr\">\"feels_like\"<\/span>: <span class=\"hljs-string\">\"\u041a\u0430\u043a \u0431\u0443\u0434\u0442\u043e: {{val, number}}\"<\/span>,\n  <span class=\"hljs-attr\">\"humidity_number\"<\/span>: <span class=\"hljs-string\">\"{{val, number}}\"<\/span>,\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-34\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-35\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/locales\/ar-EG\/translation.json<\/span>\n\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"temperature_celcius\"<\/span>: <span class=\"hljs-string\">\"\u062f\u0631\u062c\u0629 \u0627\u0644\u062d\u0631\u0627\u0631\u0629: {{val, number}} \u062f\u0631\u062c\u0629 \u0645\u0626\u0648\u064a\u0629\"<\/span>,\n  <span class=\"hljs-attr\">\"temperature_fahrenheit\"<\/span>: <span class=\"hljs-string\">\"\u0623\u0648: {{val, number}} \u062f\u0631\u062c\u0629 \u0641\u0647\u0631\u0646\u0647\u0627\u064a\u062a\"<\/span>,\n  <span class=\"hljs-attr\">\"feels_like\"<\/span>: <span class=\"hljs-string\">\"\u062a\u0628\u062f\u0648 \u0645\u062b\u0644: {{val, number}} \u062f\u0631\u062c\u0629 \u0645\u0626\u0648\u064a\u0629\"<\/span>,\n  <span class=\"hljs-attr\">\"humidity_number\"<\/span>: <span class=\"hljs-string\">\"{{val, number}}\"<\/span>,\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-35\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_86ebbdc56eac2cc694f344b06780c110\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>In our <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">WeatherForm<\/span> component, we pass the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">val<\/span> property in the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"5\">t<\/span> function. This property&#8217;s value should be the corresponding weather data we need:<!-- notionvc: 4726a661-fd5e-4f01-a024-0bbca3948cd0 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-36\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ \/src\/components\/WeatherForm.jsx<\/span>\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n{weatherData() &amp;&amp; !weatherData().error &amp;&amp; (&lt;div&gt;\n  &lt;h2&gt;\n    \ud83d\udccd {\n    weatherData().city\n  }, {\n    weatherData().country\n  } &lt;\/h2&gt;\n  &lt;div&gt;\n    &lt;p&gt; {\n      t(<span class=\"hljs-attr\">\"temperature_celcius\"<\/span>, {\n        val: weatherData().temperatureCelcius,\n        formatParams: {\n          val: {\n            style: <span class=\"hljs-string\">\"unit\"<\/span>,\n            unit: <span class=\"hljs-string\">\"celsius\"<\/span>\n          }\n        }\n      })\n    }\n      &lt;br\/&gt; {\n      t(<span class=\"hljs-attr\">\"temperature_fahrenheit\"<\/span>, {\n        val: weatherData().temperatureFahrenheit,\n        formatParams: {\n          val: {\n            style: <span class=\"hljs-string\">\"unit\"<\/span>,\n            unit: <span class=\"hljs-string\">\"fahrenheit\"<\/span>\n          }\n        }\n      })\n    } &lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;div&gt;\n    &lt;p&gt; {\n      t(<span class=\"hljs-attr\">\"feels_like\"<\/span>, {\n        val: weatherData().feelsLike,\n        formatParams: {\n          val: {\n            style: <span class=\"hljs-string\">\"unit\"<\/span>,\n            unit: <span class=\"hljs-string\">\"celsius\"<\/span>\n          }\n        }\n      })\n    } &lt;\/p&gt;\n  &lt;\/div&gt;\n  &lt;div&gt;\n    &lt;p&gt; {\n      t(<span class=\"hljs-attr\">\"humidity\"<\/span>)\n    }&lt;\/p&gt;\n    &lt;p&gt; {\n      t(<span class=\"hljs-attr\">\"humidity_number\"<\/span>, {\n        val: weatherData().humidity,\n        formatParams: {\n          val: {\n            style: <span class=\"hljs-string\">\"percent\"<\/span>\n          }\n        }\n      })\n    } &lt;\/p&gt;\n  &lt;\/div&gt;\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n&lt;\/div&gt;)\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-36\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_9f17735402a8528642d5488778bfa90e\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Note how we are also passing additional formatting options with the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">formatParams<\/span> property to the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">t<\/span> function. We add the nested <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"5\">val<\/span> property along with two sub <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"7\">style<\/span> and <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"9\">unit<\/span> properties so that our unit of measurement for temperature values shows as expected.<!-- notionvc: 8c5af714-6c51-4d9f-b6ef-778c8a37cf79 --><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-68543 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.27.05@2x-1024x268.png\" alt=\"Weather app showing localized numbers in en-US locale | Phrase\" width=\"1024\" height=\"268\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.27.05@2x-1024x268.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.27.05@2x-300x78.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.27.05@2x-768x201.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.27.05@2x-1536x401.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.27.05@2x-2048x535.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-68549 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.18.12@2x-1024x246.png\" alt=\"Weather app showing localized numbers in ru-RU locale | Phrase\" width=\"1024\" height=\"246\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.18.12@2x-1024x246.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.18.12@2x-300x72.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.18.12@2x-768x185.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.18.12@2x-1536x369.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.18.12@2x-2048x493.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-localize-dates\"><\/span>How do I localize dates?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We\u2019re being transparent and showing our users the date our weather data was recorded. Let\u2019s localize this date. Similar to number formatting, for date formatting, we need to provide the following syntax in our translation value: <code>{{val, datetime}}<\/code>. This indicates to <code>i18next<\/code> to format the value as a DateTime.<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> Under the hood i18next uses the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Intl\/DateTimeFormat\">Intl.DateTimeFormat<\/a> object to format localized dates.<\/p>\n<p>Let\u2019s add this DateTime to our translation files:<\/p>\n<p><!-- notionvc: c3062f1d-3064-48ba-a15a-76771c1e9eba --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-37\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"data_recorded_on_date\"<\/span>: <span class=\"hljs-string\">\"{{val, datetime}}\"<\/span>,\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-37\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_02764788aa3f5715494f388798779127\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>And then pass on the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">date<\/span> property from our data in the weather component:<!-- notionvc: f4381f7e-c89d-4fb4-825b-a9a2839fc955 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-38\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/components\/WeatherForm.jsx<\/span>\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n&lt;div&gt;\n  <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{t(\"date_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/span>\n  <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{t(\"data_recorded_on_date\", { val: weatherData().date })}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/span>\n&lt;<span class=\"hljs-regexp\">\/div&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-38\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_dbe8e112582e6159cdfe93ee6d92462a\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-68555 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.28.05@2x-1024x271.png\" alt=\"Weather app showing the localized date in en-US locale | Phrase\" width=\"1024\" height=\"271\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.28.05@2x-1024x271.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.28.05@2x-300x79.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.28.05@2x-768x203.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.28.05@2x-1536x407.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.28.05@2x-2048x543.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-68561 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.21.15@2x-1024x256.png\" alt=\"Localized date in ru-RU locale | Phrase\" width=\"1024\" height=\"256\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.21.15@2x-1024x256.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.21.15@2x-300x75.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.21.15@2x-768x192.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.21.15@2x-1536x383.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-19-at-23.21.15@2x-2048x511.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p>If you need to modify the default date format, use the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">formatParams<\/span> property of <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">t<\/span> function and pass the relevant values:<!-- notionvc: 912480b2-174b-4ae9-9c5a-89cc806643b0 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-39\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">{t(<span class=\"hljs-string\">\"data_recorded_on_date\"<\/span>, {\n\t<span class=\"hljs-attr\">val<\/span>: weatherData().date,\n  <span class=\"hljs-attr\">formatParams<\/span>: {\n\t  <span class=\"hljs-attr\">val<\/span>: {\n\t    <span class=\"hljs-attr\">year<\/span>: <span class=\"hljs-string\">\"2-digit\"<\/span>,\n      <span class=\"hljs-attr\">month<\/span>: <span class=\"hljs-string\">\"short\"<\/span>,\n      <span class=\"hljs-attr\">day<\/span>: <span class=\"hljs-string\">\"numeric\"<\/span>,\n    },\n  },\n})}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-39\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_73319df575692e075390b3331dae742c\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>This results in a date that is a number, but the month name is shortened and the year is of 2 digits:<!-- notionvc: a449f3f0-6a10-4668-a5da-172ec939fbbe --><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-68567 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.30.18@2x-1024x268.png\" alt=\"Localized date with custom formatting in en-US locale | Phrase\" width=\"1024\" height=\"268\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.30.18@2x-1024x268.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.30.18@2x-300x78.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.30.18@2x-768x201.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.30.18@2x-1536x402.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.30.18@2x-2048x536.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-68573\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.31.07@2x-1024x213.png\" alt=\"Localized date with custom formatting in es-ES locale | Phrase\" width=\"1024\" height=\"213\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.31.07@2x-1024x213.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.31.07@2x-300x62.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.31.07@2x-768x160.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.31.07@2x-1536x320.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-09-20-at-19.31.07@2x-2048x426.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p>\ud83d\udd17 Resource \u00bb Learn all the date formatting options i18next prodivides in <a href=\"https:\/\/www.i18next.com\/translation-function\/formatting#datetime\">the official docs<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-text-direction-ltr-rtl\"><\/span>How do I work with text direction (LTR, RTL)?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Arabic, among other languages, is laid out right-to-left. To accommodate this, we need to switch the <code>&lt;html dir&gt;<\/code> attribute so that it becomes <code>&lt;html dir=\"rtl\"&gt;<\/code> when the active locale is Arabic.<\/p>\n<p><!-- notionvc: a2f24b70-578c-4e07-a9d2-cd27831c79bb --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-40\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/App.jsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> {createSignal, createEffect} <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"solid-js\"<\/span>;\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">App<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\t<span class=\"hljs-comment\">\/\/...<\/span>\n  <span class=\"hljs-keyword\">const<\/span> &#91;locale, setLocale] = createSignal(<span class=\"hljs-string\">'en-US'<\/span>);\n\n  <span class=\"hljs-comment\">\/\/...<\/span>\n\n  <span class=\"hljs-keyword\">const<\/span> handleLocaleChange = <span class=\"hljs-function\">(<span class=\"hljs-params\">newLocale<\/span>) =&gt;<\/span> {\n    setLocale(newLocale);\n  };\n\n  <span class=\"hljs-comment\">\/\/ Update the document text direction when <\/span>\n  <span class=\"hljs-comment\">\/\/ the locale is changed.<\/span>\n  createEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    <span class=\"hljs-keyword\">if<\/span> (locale().startsWith(<span class=\"hljs-string\">'ar-EG'<\/span>)) {\n      <span class=\"hljs-built_in\">document<\/span>.documentElement.setAttribute(<span class=\"hljs-string\">'dir'<\/span>, <span class=\"hljs-string\">'rtl'<\/span>);\n    } <span class=\"hljs-keyword\">else<\/span> {\n      <span class=\"hljs-built_in\">document<\/span>.documentElement.setAttribute(<span class=\"hljs-string\">'dir'<\/span>, <span class=\"hljs-string\">'ltr'<\/span>);\n    }\n  });\n\n  <span class=\"hljs-comment\">\/\/...<\/span>\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> App;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-40\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_99a5d7f6ecd925859219952c16a2ecb0\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Finally, we need to modify the language switcher drop-down to incorporate the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">locale()<\/span> and <span class=\"discussion-id-e4fc0796-f9b0-4967-9269-78feedf33865 notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">onLocaleChange<\/span> params:<!-- notionvc: f617fb72-0730-4063-bb21-14aec7ecdd1d --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-41\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/components\/LanguageSwitcher.jsx<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> LanguageSwitcher = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ locale, onLocaleChange }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;t, { changeLanguage }] = useTransContext();\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handleLanguageChange<\/span>(<span class=\"hljs-params\">event<\/span>) <\/span>{\n    <span class=\"hljs-keyword\">const<\/span> newLocale = event.target.value;\n    changeLanguage(newLocale);\n    onLocaleChange(newLocale);\n  }\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">select<\/span>\n        <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{locale}<\/span>\n        <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{handleLanguageChange}<\/span>\n      &gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"en-US\"<\/span>&gt;<\/span>{t(\"english_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"es-ES\"<\/span>&gt;<\/span>{t(\"spanish_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"ru-RU\"<\/span>&gt;<\/span>{t(\"russian_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"ar-EG\"<\/span>&gt;<\/span>{t(\"arabic_label\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">select<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> LanguageSwitcher;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-41\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_bcaf0623d2cc1164a38c4b1f74bc478a\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Then make sure we also add these props to our <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">App<\/span> component as well:<!-- notionvc: 28570f70-1ad7-490a-b7dd-be22fbd73422 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-42\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/App.jsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> LanguageSwitcher <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/components\/LanguageSwitcher\"<\/span>;\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">App<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Show<\/span> <span class=\"hljs-attr\">when<\/span>=<span class=\"hljs-string\">{isReady()}<\/span> <span class=\"hljs-attr\">fallback<\/span>=<span class=\"hljs-string\">{<\/span>&lt;<span class=\"hljs-attr\">div<\/span>&gt;<\/span>Loading...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>}&gt;\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>{t(\"app_name\")}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">LanguageSwitcher<\/span>\n          <span class=\"hljs-attr\">locale<\/span>=<span class=\"hljs-string\">{locale()}<\/span>\n          <span class=\"hljs-attr\">onLocaleChange<\/span>=<span class=\"hljs-string\">{handleLocaleChange}<\/span>\n        \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">WeatherForm<\/span> \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Show<\/span>&gt;<\/span><\/span>\n  );\n}\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> App;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-42\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_237ecae60a944e99f68c4810cfc0e07a\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>With these changes, your language switcher should work normally, and we now get our document in the RTL direction when in the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">ar-EG<\/span> locale:<!-- notionvc: 7cb1a557-baae-4949-88c1-29bd21afa8f8 --><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-68581 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.35.22-1-1024x291.gif\" alt=\"RTL direction when in the ar-EG locale | Phrase\" width=\"1024\" height=\"291\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.35.22-1-1024x291.gif 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.35.22-1-299x85.gif 299w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.35.22-1-768x218.gif 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/CleanShot-2023-08-21-at-19.35.22-1-1535x436.gif 1535w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-automatically-detect-the-users-language\"><\/span>How do I automatically detect the user\u2019s language?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>To let i18next automatically detect locale in our app based on the user&#8217;s preferred language, we can use the <a href=\"https:\/\/github.com\/i18next\/i18next-browser-languageDetector\">i18next-browser-languagedetector<\/a> plugin. It detects the language in the following order as stated in its <a href=\"https:\/\/github.com\/i18next\/i18next-browser-languageDetector#detector-options\">docs<\/a>:<\/p>\n<p><!-- notionvc: 83b18bcc-ad5e-4bf0-8d04-6e4205864010 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-43\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">order: &#91;<span class=\"hljs-string\">'querystring'<\/span>, <span class=\"hljs-string\">'cookie'<\/span>, <span class=\"hljs-string\">'localStorage'<\/span>, <span class=\"hljs-string\">'sessionStorage'<\/span>, <span class=\"hljs-string\">'navigator'<\/span>, <span class=\"hljs-string\">'htmlTag'<\/span>, <span class=\"hljs-string\">'path'<\/span>, <span class=\"hljs-string\">'subdomain'<\/span>],<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-43\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_cfe54be9fb133a64fdc3b70c72116b83\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Let&#8217;s install it:<!-- notionvc: 403a92a8-c237-486f-8ec7-53892b5ddbc4 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-44\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">npm install i18next-browser-languagedetector<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-44\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_11b03cb6a6a5a12a7426bd1bda5c767d\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Then we can <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">use()<\/span> it in our <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">i18next<\/span> code:<!-- notionvc: caefdac4-a90b-466b-86e2-3b504fe159de --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-45\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ \/src\/App.jsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> LanguageDetector <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"i18next-browser-languagedetector\"<\/span>;\n\ni18next\n .use(LanguageDetector)\n .init({\n   <span class=\"hljs-comment\">\/\/ ...<\/span>\n })\n .then(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> setIsReady(<span class=\"hljs-literal\">true<\/span>))\n .catch(<span class=\"hljs-function\">(<span class=\"hljs-params\">err<\/span>) =&gt;<\/span> <span class=\"hljs-built_in\">console<\/span>.error(err));<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-45\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e6c54aad46fccddbb9d4a11161b22dac\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>If a user&#8217;s browser is set to a preferred language, say <code>fr-FR<\/code>, and our app only supports <code>fr-CA<\/code>, i18next will still match the language part (<code>fr<\/code>) and use <code>fr-CA<\/code> for translations.<\/p>\n<p>In cases where none of the user&#8217;s preferred locales match our app&#8217;s locales, i18next will resort to the <code>fallbackLng<\/code> set during the library&#8217;s configuration.<\/p>\n<p>But how does automatic language detection interplay with our manual <code>LanuageSwitcher<\/code>? Here, if the <a href=\"https:\/\/www.i18next.com\/overview\/api#changelanguage\">changeLanguage<\/a> method of i18next is called without any arguments, it defaults to what the i18next-browser-languagedetector plugin determines. However, if a user decides to manually select a language via the <code>LanguageSwitcher<\/code>, this will override the automatic language detection.<\/p>\n<p>\ud83d\uddd2\ufe0f\u00a0<em>Note \u00bb<\/em>\u00a0You can <a href=\"https:\/\/github.com\/PhraseApp-Blog\/solidjs-i18next-final\">find the final project<\/a> with all the component code on GitHub.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"step-up-your-game-in-javascript-localization\"><\/span>Step up your game in JavaScript localization<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>In this tutorial, we&#8217;ve delved into internationalizing SolidJS applications with the powerful solid-i18next and i18next libraries. We&#8217;ve covered translating messages, implementing a language switcher, and mastering interpolation and plurals with number and date formatting.<\/p>\n<p>Now that your application is ready to go global, it&#8217;s time to make the translation process simpler. Enter <a href=\"https:\/\/phrase.com\/platform\/strings\/\">Phrase Strings<\/a>, our dedicated software localization solution, designed to streamline string translation management from start to finish.<\/p>\n<p>With its robust API for automating translation workflows and seamless integrations with platforms like GitHub, GitLab, and Bitbucket, Phrase Strings takes care of the heavy lifting, giving you back time to focus on your code.<\/p>\n<p><!-- notionvc: a6b00d86-d54b-4c10-a7ca-f61bd94e9c17 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n\n<div id=\"acf\/blog-cta-block_171c1ee2efe36a3ac6183347a1b81e39\" class=\"pxblock pxblock--blog-cta bg--green image--orientation-landscape\">\n\t<div class=\"block-container\">\n\t\t\t\t\t<div class=\"image image--align-middle\">\n\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"1260\" height=\"992\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero.png\" class=\"attachment-original size-original\" alt=\"String Management UI visual | Phrase\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero.png 1260w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero-300x236.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero-1024x806.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero-768x605.png 768w\" sizes=\"(max-width: 1260px) 100vw, 1260px\" \/>\t\t\t<\/div>\n\t\t\t\t<div class=\"content\">\n\t\t\t<p class=\"subhead\">Phrase Strings<\/p>\n<p class=\"secondary h6\">Take your web or mobile app global without any hassle<\/p>\n<div class=\"text--copy\">\n<p class=\"small\">Adapt your software, website, or video game for global audiences with the leanest and most realiable software localization platform.<\/p>\n<p><a class=\"btn btn--outline\" href=\"https:\/\/phrase.com\/platform\/strings\/\">Explore Phrase Strings<\/a><\/p>\n<\/div>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n\n<div id=\"acf\/text-block_7590301f4ed8a4984f743dee0f9d09c3\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Phrase Strings offers a comprehensive strings editor that empowers translators to pick up content for translation with ease. Once they&#8217;ve completed their work, you can automatically integrate the translated content back into your project in the preferred file format.<\/p>\n<p>Check out all\u00a0<a href=\"https:\/\/phrase.com\/roles\/developers\/\">Phrase features for developers<\/a> and see for yourself how they can streamline your software localization workflows right from the start.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Master the art of tailoring SolidJS applications for international users by leveraging the powerful i18next library.<\/p>\n","protected":false},"author":41,"featured_media":2612,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_stopmodifiedupdate":true,"_modified_date":"","_searchwp_excluded":"","footnotes":""},"categories":[40],"class_list":["post-15220","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-localization"],"acf":[],"_links":{"self":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/15220"}],"collection":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/comments?post=15220"}],"version-history":[{"count":11,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/15220\/revisions"}],"predecessor-version":[{"id":83776,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/15220\/revisions\/83776"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media\/2612"}],"wp:attachment":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media?parent=15220"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/categories?post=15220"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}