<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alan Pazetto</title>
    <description>The latest articles on DEV Community by Alan Pazetto (@alancpazetto).</description>
    <link>https://dev.to/alancpazetto</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1667839%2Fd4ec2dc4-ca4b-455e-be98-0e4cdddc5497.jpg</url>
      <title>DEV Community: Alan Pazetto</title>
      <link>https://dev.to/alancpazetto</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alancpazetto"/>
    <language>en</language>
    <item>
      <title>Astro + Nx + Paraglide - Creating i18n module</title>
      <dc:creator>Alan Pazetto</dc:creator>
      <pubDate>Wed, 21 Aug 2024 12:23:40 +0000</pubDate>
      <link>https://dev.to/alancpazetto/astro-nx-paraglide-creating-i18n-module-llg</link>
      <guid>https://dev.to/alancpazetto/astro-nx-paraglide-creating-i18n-module-llg</guid>
      <description>&lt;p&gt;As I told in this &lt;a href="https://dev.to/alancpazetto/creating-dynamic-route-language-for-i18n-in-astro-build-2iim"&gt;my other article&lt;/a&gt;, I'm learning &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro.build&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And one of things that I don't like in the integration with Astro and &lt;a href="https://inlang.com/m/gerre34r/library-inlang-paraglideJs" rel="noopener noreferrer"&gt;Paraglide&lt;/a&gt; is keep all things in &lt;code&gt;/src&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;In case that you have a large codebase, it could be a problem to manage and keep code clean in future. Okay, I know that Astro can manage very well large codebase in final bundle, but the developer experience isn't very well to put all files together.&lt;/p&gt;

&lt;p&gt;I'm very familiar with monorepos and specially with &lt;a href="https://nx.dev/getting-started/intro" rel="noopener noreferrer"&gt;Nx&lt;/a&gt;, I've working in another projects, small and very large projects, and Nx really helps to organize code by splitting in modules/libs.&lt;/p&gt;

&lt;p&gt;The idea in this article is share how to integrate Astro with Nx and create an i18n module to centralize all messages and &lt;em&gt;paraglide things&lt;/em&gt; in one module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating repo
&lt;/h2&gt;

&lt;p&gt;First of all we need to create our start repository.&lt;/p&gt;

&lt;p&gt;Just to skip Astro and Paraglide setup, I will start from my last article repository: &lt;a href="https://github.com/alancpazetto/astro-i18n-dynamic" rel="noopener noreferrer"&gt;https://github.com/alancpazetto/astro-i18n-dynamic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, to use it, just clone repository, run install and start project:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

git clone https://github.com/alancpazetto/astro-i18n-dynamic
&lt;span class="nb"&gt;cd &lt;/span&gt;astro-i18n-dynamic
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm start


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you want to start from scratch, you can follow these nice articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.astro.build/en/getting-started/" rel="noopener noreferrer"&gt;Creating Astro app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://inlang.com/m/iljlwzfs/paraglide-astro-i18n" rel="noopener noreferrer"&gt;Adding Paraglide in Astro&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Adding Nx
&lt;/h2&gt;

&lt;p&gt;Before we continue to 18n module, we need to setup Nx.&lt;/p&gt;

&lt;p&gt;This is simple, Nx has the &lt;code&gt;init&lt;/code&gt; command that helps to init Nx workspace in an existing code, so just run this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx nx@latest init


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When Nx command ask for cacheable script, you can select &lt;code&gt;build&lt;/code&gt; and setup folder to &lt;code&gt;./dist&lt;/code&gt; (it could be changed in future)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qr2c9u3hbejn1gjjssx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qr2c9u3hbejn1gjjssx.png" alt="Nx Init console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to select any other option that command show, this won't impact in this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding i18n module
&lt;/h2&gt;

&lt;p&gt;If you already uses Nx, you are familiar with Nx Plugins, but if not I will introduce you.&lt;/p&gt;

&lt;p&gt;Nx uses a plugin architecture, which you can add or remove a plugin to add or remove features in your workspace.&lt;/p&gt;

&lt;p&gt;These plugins can add &lt;a href="https://nx.dev/features/generate-code" rel="noopener noreferrer"&gt;generators&lt;/a&gt;, &lt;a href="https://nx.dev/concepts/executors-and-configurations" rel="noopener noreferrer"&gt;executors&lt;/a&gt; or any other Nx feature.&lt;/p&gt;

&lt;p&gt;In our case, we need to create a JS library module, so we will need to &lt;em&gt;plugin&lt;/em&gt; &lt;code&gt;JS plugin&lt;/code&gt;, called &lt;code&gt;@nx/js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can find all Nx Plugins here: &lt;a href="https://nx.dev/plugin-registry" rel="noopener noreferrer"&gt;https://nx.dev/plugin-registry&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, lets install JS Plugin, by running below command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; @nx/js


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As installed we can generate our i18n module.&lt;/p&gt;

&lt;p&gt;Here I would like to make a recommendation, I really like to use command line tools, however Nx has a nice &lt;a href="https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console" rel="noopener noreferrer"&gt;VSCode Extension&lt;/a&gt; that make all generators visually (there are other feature too). So I highly recommend to install this.&lt;/p&gt;

&lt;p&gt;But if you doesn't want to use extension or don't use VSCode, no problem, we can run this in terminal:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx nx generate @nx/js:library &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;i18n &lt;span class="nt"&gt;--bundler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;swc &lt;span class="nt"&gt;--directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;libs/i18n &lt;span class="nt"&gt;--importPath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@astro-nx-paraglide/i18n &lt;span class="nt"&gt;--projectNameAndRootFormat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;as-provided &lt;span class="nt"&gt;--unitTestRunner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;none &lt;span class="nt"&gt;--no-interactive&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will create a module as JS Library using JS plugin, inside &lt;code&gt;libs/i18n&lt;/code&gt; path with import path &lt;code&gt;@astro-nx-paraglide/i18n&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can change to your configs here.&lt;/p&gt;

&lt;p&gt;If you want to use VSCode extension, open command palette, search for &lt;code&gt;Nx generate (ui)&lt;/code&gt; and select &lt;code&gt;@nx/js:library&lt;/code&gt;, add these information in new window:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbgx0go61jg1r69ayzeh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbgx0go61jg1r69ayzeh.png" alt="Nx generate ui"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Meeting i18n module
&lt;/h2&gt;

&lt;p&gt;New module will be created inside &lt;code&gt;libs/i18n&lt;/code&gt;, and basically it's a JS library, with &lt;code&gt;index.ts&lt;/code&gt; as entrypoint and &lt;code&gt;lib&lt;/code&gt; folder that could add all library code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpondy9wpi5be7myucxjt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpondy9wpi5be7myucxjt.png" alt="i18n module fodler structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Paraglide to i18n module
&lt;/h2&gt;

&lt;p&gt;To configure Paraglide in our i18n module, we need to change some things in Paraglide config.&lt;/p&gt;

&lt;p&gt;First of all, open your Astro config (like &lt;code&gt;astro.config.mjs&lt;/code&gt;) and change paraglide integration &lt;code&gt;outdir&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astro/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;paraglide&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@inlang/paraglide-astro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// Use astro's i18n routing for deciding which language to use&lt;/span&gt;
  &lt;span class="na"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pt-br&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;defaultLocale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;integrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;paraglide&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// recommended settings&lt;/span&gt;
      &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./project.inlang&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;outdir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./libs/i18n/src/paraglide&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;=== HERE&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It will setup Astro + Paraglide to looks inside this folder (in code we will import in other way).&lt;/p&gt;

&lt;p&gt;We need to setup &lt;code&gt;package.json&lt;/code&gt; scripts changing paraglide output dir in build time (&lt;code&gt;build&lt;/code&gt; and &lt;code&gt;postinstall&lt;/code&gt; script):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"astro dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"astro dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"paraglide-js compile --project ./project.inlang --outdir ./libs/i18n/src/paraglide &amp;amp;&amp;amp; astro check &amp;amp;&amp;amp; astro build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preview"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"astro preview"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"astro"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"astro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postinstall"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"paraglide-js compile --project ./project.inlang --outdir ./libs/i18n/src/paraglide"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we can rerun &lt;code&gt;postinstall&lt;/code&gt; script to regenerate paraglide folder: &lt;code&gt;npm run postinstall&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After all we have this folder strucuture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnawczqcyepbi4cu6tcmp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnawczqcyepbi4cu6tcmp.png" alt="new module structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to export to all code generated paragrlide files.&lt;/p&gt;

&lt;p&gt;Paraglide exports 2 folders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;messages.js&lt;/code&gt;: that contains all translated message functions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;runtime.js&lt;/code&gt;: that contains all runtime function, like which language is setted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we need to edit library entrypoint (&lt;code&gt;index.ts&lt;/code&gt;) to export these files:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./paraglide/messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./paraglide/runtime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: By default Nx setup project &lt;code&gt;"verbatimModuleSyntax": true&lt;/code&gt; in &lt;code&gt;tsconfig.json&lt;/code&gt; and it cause an erro in i18n module, but you can configure your library &lt;code&gt;tsconfig.json&lt;/code&gt; to disable this by editing &lt;code&gt;libs/i18n/tsconfig.json&lt;/code&gt; adding &lt;code&gt;"verbatimModuleSyntax": false&lt;/code&gt; inside &lt;code&gt;compilerOptions&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By now, we don't need &lt;code&gt;libs/i18n/src/lib&lt;/code&gt; folder anymore, just delete it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration Astro app with i18n module
&lt;/h2&gt;

&lt;p&gt;Now it's time to integrate all our code.&lt;/p&gt;

&lt;p&gt;If you check &lt;code&gt;./tsconfig.json&lt;/code&gt;, a new &lt;code&gt;compilerOptions.path&lt;/code&gt; was added by Nx with importPath informed previously appoint to our library entrypoint.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: if you get an import error here, you need to setup &lt;code&gt;compilerOptions.baseUrl&lt;/code&gt; to &lt;code&gt;./&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So to integrate our code with module we'll use this import path in our code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@astro-nx-paraglide/i18n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In our application files, inside &lt;code&gt;src&lt;/code&gt; we need to change all imports from &lt;code&gt;../paraglide/messages&lt;/code&gt; (or runtime) to new import path.&lt;/p&gt;

&lt;p&gt;For example in &lt;code&gt;src/components/LanguagePicker.astro&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="p"&gt;---
import * as m from '../paraglide/messages';
&lt;/span&gt;&lt;span class="gd"&gt;- import { languageTag } from '../paraglide/runtime';
&lt;/span&gt;&lt;span class="gi"&gt;+ import { runtime } from '@astro-nx-paraglide/i18n';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- const makeUrlForLanguage = (lang: string) =&amp;gt; `/${lang}${Astro.url.pathname.replace(`/${languageTag()}`, '')}`;
&lt;/span&gt;&lt;span class="gi"&gt;+ const makeUrlForLanguage = (lang: string) =&amp;gt; `/${lang}${Astro.url.pathname.replace(`/${runtime.languageTag()}`, '')}`;
&lt;/span&gt;&lt;span class="p"&gt;---
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And inside our pages, like &lt;code&gt;src/pages/index.astro&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="p"&gt;---
import Layout from '../layouts/Layout.astro';
&lt;/span&gt;&lt;span class="gd"&gt;- import * as m from '../paraglide/messages';
- import { languageTag } from '../paraglide/runtime';
&lt;/span&gt;&lt;span class="gi"&gt;+ import { messages as m, runtime } from '@astro-nx-paraglide/i18n';
&lt;/span&gt;&lt;span class="p"&gt;---
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&amp;lt;Layout title={m.homePageTitle()}&amp;gt;
  &amp;lt;h1&amp;gt;{m.homePageHeading()}&amp;lt;/h1&amp;gt;
&lt;span class="gd"&gt;-  &amp;lt;p&amp;gt;{m.homePageContent({ languageTag: languageTag() })}&amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+  &amp;lt;p&amp;gt;{m.homePageContent({ languageTag: runtime.languageTag() })}&amp;lt;/p&amp;gt;
&lt;/span&gt;&amp;lt;/Layout&amp;gt;
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Cleanning src folder
&lt;/h2&gt;

&lt;p&gt;As module was setted and all imports changed, we can delete the &lt;code&gt;src/paraglide&lt;/code&gt; folder, as we don't use it anymore.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example repository
&lt;/h2&gt;

&lt;p&gt;Here is the example repository: &lt;a href="https://github.com/alancpazetto/astro-nx-paraglide" rel="noopener noreferrer"&gt;https://github.com/alancpazetto/astro-nx-paraglide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just clone repository, run install and start project:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

git clone https://github.com/alancpazetto/astro-nx-paraglide
&lt;span class="nb"&gt;cd &lt;/span&gt;astro-nx-paraglide
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm start


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Thanks to read and don't forget to give a heat in this article if you like and leave a comment.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>astro</category>
      <category>programming</category>
    </item>
    <item>
      <title>Criando rotas dinâmicas para internacionalização (i18n) com Astro Build</title>
      <dc:creator>Alan Pazetto</dc:creator>
      <pubDate>Sat, 17 Aug 2024 14:42:40 +0000</pubDate>
      <link>https://dev.to/alancpazetto/criando-rotas-dinamicas-para-internacionalizacao-i18n-com-astro-build-1o75</link>
      <guid>https://dev.to/alancpazetto/criando-rotas-dinamicas-para-internacionalizacao-i18n-com-astro-build-1o75</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;If you want read this article in English &lt;a href="https://dev.to/alancpazetto/creating-dynamic-route-language-for-i18n-in-astro-build-2iim"&gt;go here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Recentemente, comecei a aprender &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; para criar um projeto no estilo de dashboard.&lt;/p&gt;

&lt;p&gt;Eu realmente quero implementar a internacionalização (i18n) neste projeto—o objetivo é que qualquer pessoa possa usá-lo, independentemente do idioma.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problema
&lt;/h2&gt;

&lt;p&gt;O suporte a i18n no Astro é muito bom. Ele funciona de maneira semelhante ao Next.js ou qualquer outro framework com roteamento baseado na estrutura de arquivos/pastas.&lt;/p&gt;

&lt;p&gt;Então, se quisermos ter uma página em inglês e a mesma página em português, podemos organizar nossos arquivos assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
└── src/
    └── pages/
        ├── en/
        │   ├── login.astro
        │   └── dashboard.astro
        └── pt-br/
            ├── login.astro
            └── dashboard.astro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E cada página tem suas próprias strings i18n—legal!&lt;/p&gt;

&lt;p&gt;Mas é aqui que meu problema começa: eu não quero clonar todas as minhas páginas; eu só quero mudar as strings nessas páginas.&lt;/p&gt;

&lt;p&gt;Eu preciso de algo como &lt;code&gt;/[qualquer-bandeira-de-idioma]/todas-as-minhas-rotas&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Você pode perguntar: "Por que não usar algo como react-intl?" Minha resposta é que eu quero aproveitar ao máximo o mecanismo do Astro, especialmente para SSG/SSR, e evitar quaisquer componentes do lado do cliente. Geralmente, esses frameworks usam React Context, que é renderizado apenas no lado do cliente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tentativas e Fracassos
&lt;/h2&gt;

&lt;p&gt;Primeiro de tudo, eu li a &lt;a href="https://docs.astro.build/en/recipes/i18n/" rel="noopener noreferrer"&gt;receita do Astro sobre i18n&lt;/a&gt; e conferi algumas bibliotecas da comunidade para resolver esse problema.&lt;/p&gt;

&lt;p&gt;A primeira biblioteca que testei foi a &lt;a href="https://github.com/yassinedoghri/astro-i18next" rel="noopener noreferrer"&gt;astro-i18next&lt;/a&gt;, e parecia exatamente o que eu precisava!&lt;/p&gt;

&lt;p&gt;Com base em um array no arquivo de configuração, o &lt;code&gt;astro-i18next&lt;/code&gt; gera minhas páginas i18n no momento da build, então eu só preciso codificar uma vez e não me preocupar em clonar páginas.&lt;/p&gt;

&lt;p&gt;O problema é que o &lt;code&gt;astro-i18next&lt;/code&gt; parece estar arquivado ou não ser mais mantido. Há muitos problemas e o último commit foi há mais de um ano.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solução
&lt;/h2&gt;

&lt;p&gt;Depois de tentar outras bibliotecas (menção honrosa para &lt;a href="https://github.com/alexandre-fernandez/astro-i18n" rel="noopener noreferrer"&gt;astro-i18n&lt;/a&gt;), encontrei o &lt;a href="https://inlang.com/c/astro" rel="noopener noreferrer"&gt;Paraglide&lt;/a&gt;, e ele foi um divisor de águas para o meu projeto.&lt;/p&gt;

&lt;p&gt;Escolhi o Paraglide porque:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ele é seguro por tipo, então eu posso usá-lo com TypeScript e aproveitar o autocomplete.&lt;/li&gt;
&lt;li&gt;Ele converte strings i18n em funções, então se uma chave de string mudar, minha build falhará, capturando erros cedo.&lt;/li&gt;
&lt;li&gt;Usar funções i18n permite uma melhor tree shaking, removendo funções não utilizadas.&lt;/li&gt;
&lt;li&gt;Existe uma extensão do VS Code que melhora a experiência de desenvolvimento.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Nota: Você pode usar o Paraglide em um projeto JS também, e ele também suporta Next.js.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Após a instalação e configuração, usei minhas mensagens assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import * as m from "../paraglide/messages.js";
---

&amp;lt;h1&amp;gt;{m.hello({ name: "Alan" })}&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No entanto, isso não resolveu meu problema de roteamento—eu ainda estava clonando minhas páginas para cada idioma que queria adicionar.&lt;/p&gt;

&lt;p&gt;Para resolver isso, mudei meu projeto para usar rotas dinâmicas na rota raiz, então todas as minhas rotas agora começam com a bandeira do idioma.&lt;/p&gt;

&lt;p&gt;Minha estrutura de pastas ficou assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
└── src/
    └── pages/
        └── [lang]/
            ├── login.astro
            └── dashboard.astro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois dessa mudança, o Paraglide pode obter automaticamente o idioma do parâmetro da rota:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://localhost:4321/en/login&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://localhost:4321/pt-br/login&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agora, posso adicionar um novo idioma apenas configurando-o em &lt;code&gt;astro.config.ts&lt;/code&gt; e traduzindo meu arquivo de strings.&lt;/p&gt;

&lt;p&gt;Mas ainda tenho dois problemas para resolver:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quando o usuário acessa &lt;code&gt;http://localhost:4321/&lt;/code&gt; pela primeira vez sem uma bandeira de idioma.&lt;/li&gt;
&lt;li&gt;Se o usuário mudar o idioma em uma rota específica, preciso mantê-lo na mesma rota (por exemplo, &lt;code&gt;/en/create-account&lt;/code&gt; deve redirecionar para &lt;code&gt;/pt-br/create-account&lt;/code&gt; ou &lt;code&gt;/pt-br/criar-conta&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Middleware para Redirecionamento de Idioma
&lt;/h3&gt;

&lt;p&gt;Para resolver o primeiro problema de redirecionamento de idioma, usei middlewares do Astro.&lt;/p&gt;

&lt;p&gt;Em &lt;code&gt;src/middleware/index.ts&lt;/code&gt;, adicionei este código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astro:middleware&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;languageTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;setLanguageTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AvailableLanguageTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../paraglide/runtime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineMiddleware&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Obter o idioma do parâmetro da URL&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Se mudou&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nf"&gt;languageTag&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLanguageTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;AvailableLanguageTag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Redirecionar para o idioma alterado ou padrão (en)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Seletor de Idioma com Rota Atual
&lt;/h3&gt;

&lt;p&gt;Para manter o usuário na mesma rota ao mudar de idioma, adicionei este componente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import { languageTag } from '../paraglide/runtime';

const pathName = Astro.url.pathname.replace(`/${languageTag()}/`, '');
---

&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a href={`/pt-br/${pathName}`}&amp;gt;Ir para Português&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a href={`/en/${pathName}`}&amp;gt;Go to English&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Além disso, podemos traduzir essas mensagens também, usando o segundo parâmetro na função de mensagens do Paraglide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a href={`/pt-br/${pathName}`}&amp;gt;{m.goToLanguage(undefined, { languageTag: 'pt-br' })}&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a href={`/en/${pathName}`}&amp;gt;{m.goToLanguage(undefined, { languageTag: 'en' })}&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Repositório de Exemplo
&lt;/h2&gt;

&lt;p&gt;Aqui esta o repositório com as técnicas aplicadas no artigo &lt;a href="https://github.com/alancpazetto/astro-i18n-dynamic" rel="noopener noreferrer"&gt;https://github.com/alancpazetto/astro-i18n-dynamic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Só fazer o clone do repositório, instalar as dependências e iniciar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/alancpazetto/astro-i18n-dynamic
&lt;span class="nb"&gt;cd &lt;/span&gt;astro-i18n-dynamic
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois de iniciado, basta abrir essas URLs no navegador:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://localhost:4321/" rel="noopener noreferrer"&gt;http://localhost:4321/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:4321/en/" rel="noopener noreferrer"&gt;http://localhost:4321/en/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:4321/en/login" rel="noopener noreferrer"&gt;http://localhost:4321/en/login&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:4321/pt-br/" rel="noopener noreferrer"&gt;http://localhost:4321/pt-br/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:4321/pt-br/login" rel="noopener noreferrer"&gt;http://localhost:4321/pt-br/login&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Considerações
&lt;/h2&gt;

&lt;p&gt;Não considero minha solução a melhor, especialmente porque ainda estou aprendendo Astro, então pode haver outras soluções. Se você souber de alguma, por favor comente, e eu tentarei :)&lt;/p&gt;

&lt;p&gt;Obrigado por ler este artigo! Se você tiver alguma dúvida, comente, ficarei feliz em responder.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>astro</category>
      <category>learning</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Creating dynamic route language for i18n in Astro Build</title>
      <dc:creator>Alan Pazetto</dc:creator>
      <pubDate>Sat, 17 Aug 2024 14:35:47 +0000</pubDate>
      <link>https://dev.to/alancpazetto/creating-dynamic-route-language-for-i18n-in-astro-build-2iim</link>
      <guid>https://dev.to/alancpazetto/creating-dynamic-route-language-for-i18n-in-astro-build-2iim</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Se quiser ler esse artigo em Portugês &lt;a href="https://dev.to/alancpazetto/criando-rotas-dinamicas-para-internacionalizacao-i18n-com-astro-build-1o75"&gt;clique aqui&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Recently, I started learning &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; to create a dashboard-like project.&lt;/p&gt;

&lt;p&gt;I really want to implement internationalization (i18n) in this project—the idea is that everyone should be able to use it, regardless of their language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Astro’s i18n support is quite good. It works similarly to Next.js or any other framework with routing based on file/folder structure.&lt;/p&gt;

&lt;p&gt;So, if we want to have a page in English and the same page in Portuguese, we can organize our files like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
└── src/
    └── pages/
        ├── en/
        │   ├── login.astro
        │   └── dashboard.astro
        └── pt-br/
            ├── login.astro
            └── dashboard.astro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And each page has its own i18n strings—nice!&lt;/p&gt;

&lt;p&gt;But here’s where my problem starts: I don't want to clone all my pages; I only want to change the strings on those pages.&lt;/p&gt;

&lt;p&gt;I need something like &lt;code&gt;/[any-language-flag]/all-my-routes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You might ask: "Why not use something like react-intl?" My answer is that I want to fully leverage Astro's engine, especially for SSG/SSR, and avoid any client components. Generally, these frameworks use React Context, which is only rendered on the client side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying and Failing
&lt;/h2&gt;

&lt;p&gt;First of all, I read the &lt;a href="https://docs.astro.build/en/recipes/i18n/" rel="noopener noreferrer"&gt;Astro recipe about i18n&lt;/a&gt; and checked out some community libraries to solve this problem.&lt;/p&gt;

&lt;p&gt;The first library I tested was &lt;a href="https://github.com/yassinedoghri/astro-i18next" rel="noopener noreferrer"&gt;astro-i18next&lt;/a&gt;, and it looked like exactly what I needed!&lt;/p&gt;

&lt;p&gt;Based on an array in the config file, &lt;code&gt;astro-i18next&lt;/code&gt; generates my i18n pages at build time, so I only need to code once and don't have to worry about cloning pages.&lt;/p&gt;

&lt;p&gt;The problem is that &lt;code&gt;astro-i18next&lt;/code&gt; seems to be archived or no longer maintained. There are a lot of issues, and the last commit was over a year ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;After trying other libraries (honorable mention to &lt;a href="https://github.com/alexandre-fernandez/astro-i18n" rel="noopener noreferrer"&gt;astro-i18n&lt;/a&gt;), I found &lt;a href="https://inlang.com/c/astro" rel="noopener noreferrer"&gt;Paraglide&lt;/a&gt;, and it was a game-changer for my project.&lt;/p&gt;

&lt;p&gt;I chose Paraglide because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s type-safe, so I can use it with TypeScript and benefit from autocomplete.&lt;/li&gt;
&lt;li&gt;It converts i18n strings into functions, so if a string key changes, my build will fail, catching errors early.&lt;/li&gt;
&lt;li&gt;Using i18n functions allows for better tree shaking, removing unused functions.&lt;/li&gt;
&lt;li&gt;There’s a VS Code extension that enhances the development experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: You can use Paraglide in a JS project too, and it also supports Next.js.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After installation and configuration, I used my messages like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import * as m from "../paraglide/messages.js";
---

&amp;lt;h1&amp;gt;{m.hello({ name: "Alan" })}&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this didn't solve my routing problem—I was still cloning my pages for each language I wanted to add.&lt;/p&gt;

&lt;p&gt;To solve this, I changed my project to use dynamic routes in the root route, so all my routes now start with the language flag.&lt;/p&gt;

&lt;p&gt;My folder structure turned into this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
└── src/
    └── pages/
        └── [lang]/
            ├── login.astro
            └── dashboard.astro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this change, Paraglide can automatically get the language from the route parameter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://localhost:4321/en/login&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://localhost:4321/pt-br/login&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, I can add a new language just by setting it in &lt;code&gt;astro.config.ts&lt;/code&gt; and translating my string file.&lt;/p&gt;

&lt;p&gt;But I still have two more issues to solve:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When the user first accesses &lt;code&gt;http://localhost:4321/&lt;/code&gt; without a language flag.&lt;/li&gt;
&lt;li&gt;If the user changes the language on a specific route, I need to keep them on the same route (e.g., &lt;code&gt;/en/create-account&lt;/code&gt; should redirect to &lt;code&gt;/pt-br/create-account&lt;/code&gt; or &lt;code&gt;/pt-br/criar-conta&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Middleware for Language Redirect
&lt;/h3&gt;

&lt;p&gt;To solve the first issue of language redirect, I used Astro middlewares.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;src/middleware/index.ts&lt;/code&gt;, I added this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astro:middleware&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;languageTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;setLanguageTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AvailableLanguageTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../paraglide/runtime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineMiddleware&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Get lang from url param&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// If changed&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nf"&gt;languageTag&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLanguageTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;AvailableLanguageTag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Redirect to lang changed or default (en)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Language Picker with Current Route
&lt;/h3&gt;

&lt;p&gt;To keep the user on the same route when they switch languages, I added this component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import { languageTag } from '../paraglide/runtime';

const pathName = Astro.url.pathname.replace(`/${languageTag()}/`, '');
---

&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a href={`/pt-br/${pathName}`}&amp;gt;Ir para Português&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a href={`/en/${pathName}`}&amp;gt;Go to English&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, we could translate these messages too, using the second parameter in the Paraglide messages function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a href={`/pt-br/${pathName}`}&amp;gt;{m.goToLanguage(undefined, { languageTag: 'pt-br' })}&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;a href={`/en/${pathName}`}&amp;gt;{m.goToLanguage(undefined, { languageTag: 'en' })}&amp;lt;/a&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example Repository
&lt;/h2&gt;

&lt;p&gt;Here is the example repository with these techniches: &lt;a href="https://github.com/alancpazetto/astro-i18n-dynamic" rel="noopener noreferrer"&gt;https://github.com/alancpazetto/astro-i18n-dynamic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just clone repository, run install and start project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/alancpazetto/astro-i18n-dynamic
&lt;span class="nb"&gt;cd &lt;/span&gt;astro-i18n-dynamic
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open follow URLs to test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://localhost:4321/" rel="noopener noreferrer"&gt;http://localhost:4321/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:4321/en/" rel="noopener noreferrer"&gt;http://localhost:4321/en/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:4321/en/login" rel="noopener noreferrer"&gt;http://localhost:4321/en/login&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:4321/pt-br/" rel="noopener noreferrer"&gt;http://localhost:4321/pt-br/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://localhost:4321/pt-br/login" rel="noopener noreferrer"&gt;http://localhost:4321/pt-br/login&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for &lt;a class="mentioned-user" href="https://dev.to/gaundergod"&gt;@gaundergod&lt;/a&gt; for the first comment in this article asking for code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Considerations
&lt;/h2&gt;

&lt;p&gt;I don't consider my solution to be the best, especially since I'm still learning Astro, so there might be other solutions. If you know of any, please comment, and I'll give them a try :)&lt;/p&gt;

&lt;p&gt;Thanks for reading this article! If you have any questions, please comment, I’d be happy to reply.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>astro</category>
      <category>learning</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
