<?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: Martin Ratinaud</title>
    <description>The latest articles on DEV Community by Martin Ratinaud (@martinratinaud).</description>
    <link>https://dev.to/martinratinaud</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%2F247401%2F884af2f4-8818-4958-b678-9cf4e1f17760.png</url>
      <title>DEV Community: Martin Ratinaud</title>
      <link>https://dev.to/martinratinaud</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/martinratinaud"/>
    <language>en</language>
    <item>
      <title>Send from your business email address</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Thu, 20 Mar 2025 04:47:05 +0000</pubDate>
      <link>https://dev.to/martinratinaud/send-from-your-business-email-address-3me9</link>
      <guid>https://dev.to/martinratinaud/send-from-your-business-email-address-3me9</guid>
      <description>&lt;p&gt;Ok so now that we have a ready startup business email address, it's time to be able to send email from it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the link you can send to your coworkers who need to setup their business email address&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure Google App Passwords
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://myaccount.google.com/apppasswords" rel="noopener noreferrer"&gt;App Password&lt;/a&gt; in your Google Account &amp;gt; Security&lt;/li&gt;
&lt;li&gt;Create a new app password called &lt;code&gt;Cloudflare&lt;/code&gt; (or anything you want)&lt;/li&gt;
&lt;li&gt;Note the password provided to you (something like &lt;code&gt;tksd padv shty ojiz&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configure Gmail
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Go to Gmail Settings &amp;gt; Accounts and imports &amp;gt; Send mail as &amp;gt; Add another email address&lt;/li&gt;
&lt;li&gt;Enter the email address created on Cloudflare (&lt;a href="mailto:martin@headlinker.com"&gt;martin@headlinker.com&lt;/a&gt; for instance) and uncheck "Treat as an alias"&lt;/li&gt;
&lt;li&gt;Then use &lt;code&gt;smtp.gmail.com&lt;/code&gt;, &lt;code&gt;youraddress+headlinker@gmail.com&lt;/code&gt; and the password you got before &lt;code&gt;tksd padv shty ojiz&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click on the email you just received&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And VOILA! (as we say in french)&lt;/p&gt;

&lt;h2&gt;
  
  
  PRO Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Filtering
&lt;/h3&gt;

&lt;p&gt;As you may have noticed, I have used the &lt;code&gt;+&lt;/code&gt; tweak of gmail in order to be able to categorize the email received. I often do that as it will help me automatically filter and organize my inbox. This way, I can easily set up rules to label, archive, or prioritize emails based on the specific address used. Let me know if you need me to use a different email format!&lt;/p&gt;

&lt;h3&gt;
  
  
  Redirection alias
&lt;/h3&gt;

&lt;p&gt;There is one missing thing though when using Cloudflare + Gmail and it's the redirection aliases.&lt;br&gt;
For instance, I want to redirect &lt;code&gt;hello@headlinker.com&lt;/code&gt; to &lt;code&gt;martin@...&lt;/code&gt; and &lt;code&gt;dorothee@...&lt;/code&gt; but it's not possible yet.&lt;br&gt;
So I created a custom filter in Gmail which does the forwarding for me&lt;/p&gt;

</description>
      <category>startup</category>
      <category>protip</category>
    </item>
    <item>
      <title>Unlimited free business email addresses for your startup</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Thu, 20 Mar 2025 04:45:39 +0000</pubDate>
      <link>https://dev.to/martinratinaud/unlimited-free-business-email-addresses-for-your-startup-7bf</link>
      <guid>https://dev.to/martinratinaud/unlimited-free-business-email-addresses-for-your-startup-7bf</guid>
      <description>&lt;p&gt;This is something I've been wanting for years and that I found very difficult to get. I mean, without paying per email address of course :-)&lt;/p&gt;

&lt;p&gt;First, for my projects &lt;a href="https://headlinker.com/fr" rel="noopener noreferrer"&gt;Headlinker&lt;/a&gt;, &lt;a href="https://stakingcrypto.io" rel="noopener noreferrer"&gt;Staking Crypto&lt;/a&gt; and &lt;a href="https://en.baste-sportive.com" rel="noopener noreferrer"&gt;Baste Sportive&lt;/a&gt; I was using Zoho: one email, another inbox, no redirection. I found that very annoying and in order to make it simpler for me, I wanted to be able to receive, classify and reply from within my gmail account.&lt;/p&gt;

&lt;p&gt;After extensive research, I find the perfect way using Cloudflare.&lt;/p&gt;

&lt;p&gt;Let me tell you how to do so&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Business email address
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Enable email handling in Cloudflare
&lt;/h3&gt;

&lt;p&gt;First of all, you need to &lt;a href="https://developers.cloudflare.com/registrar/get-started/transfer-domain-to-cloudflare/" rel="noopener noreferrer"&gt;have your DNS handled by Cloudflare&lt;/a&gt;. If you're reluctant about that here are all the benefits I see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance Boost&lt;/strong&gt; – Cloudflare’s globally distributed network ensures faster DNS resolution, reducing latency for your users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Enhancements&lt;/strong&gt; – It protects against DDoS attacks, domain hijacking, and other threats with built-in security measures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy DNS Management&lt;/strong&gt; – Cloudflare offers a user-friendly dashboard with quick propagation times for DNS changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy import&lt;/strong&gt; - migration process will copy all your DNS data and you will only have to change the name servers in your original domain name provider&lt;/li&gt;
&lt;li&gt;and &lt;strong&gt;many more&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once done&lt;/p&gt;

&lt;h3&gt;
  
  
  Create email address
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Enable &lt;strong&gt;Email Routing&lt;/strong&gt;
&lt;img src="https://media2.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%2Faku6ba4p2poyzltxy3c1.png" alt=" " width="800" height="500"&gt;
&lt;/li&gt;
&lt;li&gt;Create a &lt;strong&gt;custom address&lt;/strong&gt; and select "Send to an email"
&lt;img src="https://media2.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%2Fh4kugsxlq2x9mdukyabl.png" alt=" " width="800" height="560"&gt;
&lt;/li&gt;
&lt;li&gt;Click on the email you received&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VOILA, you will now receive all emails to this address into your inbox.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create as many addresses as you need
&lt;/h3&gt;

&lt;p&gt;Repeat the process for all email addresses you want to have and be able to send emails from: info, support, hello etc...&lt;/p&gt;

&lt;p&gt;For others you can configure the Catch-all address to redirect to one of your emails (I redirected it to &lt;a href="mailto:hello@headlinker.com"&gt;hello@headlinker.com&lt;/a&gt; which was previously created)&lt;/p&gt;

&lt;h2&gt;
  
  
  Send email from your business email address
&lt;/h2&gt;

&lt;p&gt;Now let's configure how you can configure your inbox to be able to &lt;a href="https://dev.to/martinratinaud/send-from-your-business-email-address-3me9"&gt;send from this email address&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>startup</category>
      <category>emailing</category>
      <category>dns</category>
      <category>protip</category>
    </item>
    <item>
      <title>Centering flex items with overflow</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Thu, 02 Nov 2023 04:59:55 +0000</pubDate>
      <link>https://dev.to/martinratinaud/centering-flex-items-with-overflow-40j0</link>
      <guid>https://dev.to/martinratinaud/centering-flex-items-with-overflow-40j0</guid>
      <description>&lt;p&gt;Have you ever tried scrolling horizontally a list of flex items that are &lt;code&gt;justify-center&lt;/code&gt; and end up with the first item not shown or partially hidden.&lt;/p&gt;

&lt;p&gt;Well, it just happened to me while working on &lt;a href="https://stakingcrypto.io" rel="noopener noreferrer"&gt;staking crypto&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fa392pl076n62q3x3qvr6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fa392pl076n62q3x3qvr6.gif" alt="Visual description of problem and solution" width="1104" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well this is very annoying, but fortunately enough, here is the solution to this problem in 3 simple steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  For Tailwind
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Remove &lt;code&gt;justify-center&lt;/code&gt; from the parent element&lt;/li&gt;
&lt;li&gt;Apply &lt;code&gt;ml-auto&lt;/code&gt; to the first element&lt;/li&gt;
&lt;li&gt;Apply &lt;code&gt;mr-auto&lt;/code&gt; to the last element&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tailwind having great utility classes, this can just be done in one step&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;justify-center&lt;/code&gt; by &lt;code&gt;[&amp;amp;&amp;gt;*:first-child]:ml-auto [&amp;amp;&amp;gt;*:last-child]:mr-auto&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a &lt;a href="https://play.tailwindcss.com/2U9HRJkZJH" rel="noopener noreferrer"&gt;Playground&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  For CSS
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Remove &lt;code&gt;justify-content:center&lt;/code&gt; from the parent element&lt;/li&gt;
&lt;li&gt;Apply &lt;code&gt;margin-left: auto&lt;/code&gt; to the first element&lt;/li&gt;
&lt;li&gt;Apply &lt;code&gt;margin-right: auto&lt;/code&gt; to the last element&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Who am I?
&lt;/h2&gt;

&lt;p&gt;My name is &lt;a href="https://www.linkedin.com/in/martinratinaud" rel="noopener noreferrer"&gt;Martin Ratinaud&lt;/a&gt; and I’m a freelance senior software engineer and remote enthusiast, contagiously happy and curious.&lt;br&gt;
I create websites like this one for &lt;a href="https://www.stakingcrypto.io/" rel="noopener noreferrer"&gt;staking crypto&lt;/a&gt; and &lt;a href="https://github.com/martinratinaud/work-with-me#side-projects" rel="noopener noreferrer"&gt;many more...&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, I'm always looking for new challenges or MVP to create. Hire me !&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>protip</category>
      <category>css</category>
      <category>flex</category>
    </item>
    <item>
      <title>Retrieve vanity url and headline from LinkedIn with NextAuth (using r_basicprofile)</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Mon, 23 Oct 2023 07:48:11 +0000</pubDate>
      <link>https://dev.to/martinratinaud/retrieve-vanity-url-and-headline-from-linkedin-with-nextauth-using-rbasicprofile-26o3</link>
      <guid>https://dev.to/martinratinaud/retrieve-vanity-url-and-headline-from-linkedin-with-nextauth-using-rbasicprofile-26o3</guid>
      <description>&lt;p&gt;Hi all&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://headlinker.com/fr" rel="noopener noreferrer"&gt;Headlinker&lt;/a&gt; the community for indie recruiters i'm building right now, I set up a LinkedIn login capability, as all of my users do have LinkedIn.&lt;/p&gt;

&lt;p&gt;It's very easy to setup to get the &lt;strong&gt;email&lt;/strong&gt;, &lt;strong&gt;name&lt;/strong&gt; and &lt;strong&gt;profile image&lt;/strong&gt; but not quite easy to get the &lt;strong&gt;vanity url&lt;/strong&gt; and &lt;strong&gt;headline&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So here is how to do it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You have &lt;a href="https://next-auth.js.org/providers/linkedin" rel="noopener noreferrer"&gt;NextAuth&lt;/a&gt; already setup in your app with basic &lt;a href="https://next-auth.js.org/providers/linkedin" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Permissions
&lt;/h2&gt;

&lt;p&gt;By default, when asking for login permissions on LinkedIn, you get &lt;code&gt;r_liteprofile&lt;/code&gt; and &lt;code&gt;r_emailaddress&lt;/code&gt; which gives you basically first name, last name, image and email.&lt;/p&gt;

&lt;p&gt;We need to get access to &lt;code&gt;r_basicprofile&lt;/code&gt; to get back the &lt;strong&gt;vanity URL&lt;/strong&gt; and the &lt;strong&gt;headline&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For this &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to your &lt;a href="https://www.linkedin.com/developers/apps" rel="noopener noreferrer"&gt;LinkedIn Developer apps page&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Select you app&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Products&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ask for permissions on the &lt;code&gt;Advertising API&lt;/code&gt; (Yes it's weird but it's in there)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fbfcc6jqf9iw17hehury4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fbfcc6jqf9iw17hehury4.png" alt="Advertising API" width="776" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answer the questions and be as specific as possible on why you need access to these informations&lt;/li&gt;
&lt;li&gt;Wait&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It can take several hours to get access or you can even get refused.&lt;br&gt;
If you manage though, you can go to next step.&lt;/p&gt;
&lt;h2&gt;
  
  
  Configure Next-Auth
&lt;/h2&gt;

&lt;p&gt;No that you have access to those new fields, you need to update next-auth to actually retrieve those fields and save them in your database.&lt;/p&gt;

&lt;p&gt;For this, you will need to replace&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nc"&gt;LinkedInProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_LINKEDIN_CLIENT_ID&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_LINKEDIN_CLIENT_SECRET&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;string&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;by&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nc"&gt;LinkedInProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_LINKEDIN_CLIENT_ID&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_LINKEDIN_CLIENT_SECRET&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.linkedin.com/oauth/v2/authorization&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;r_liteprofile r_basicprofile r_emailaddress&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="nx"&gt;userinfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.linkedin.com/v2/me&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`(id,localizedFirstName,localizedLastName,vanityName,localizedHeadline,profilePicture(displayImage~digitalmediaAsset:playableStreams))`&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;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.linkedin.com/v2/emailAddress?q=members&amp;amp;projection=(elements*(handle~))&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;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;emailResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://www.linkedin.com/in/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vanityName&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="na"&gt;name&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;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localizedFirstName&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;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localizedLastName&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="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;emailData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handle~&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;emailAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localizedHeadline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profilePicture&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;displayImage~&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;identifiers&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;identifier&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;span class="nx"&gt;allowDangerousEmailAccountLinking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, change the scope and add &lt;code&gt;r_basicprofile&lt;/code&gt;, this will give access to the corresponding fields&lt;/li&gt;
&lt;li&gt;Then, change the projection in &lt;strong&gt;userinfo&lt;/strong&gt; so that it includes the &lt;strong&gt;vanityName&lt;/strong&gt; (martinratinaud) which we will use to build the &lt;a href="https://www.linkedin.com/in/martinratinaud" rel="noopener noreferrer"&gt;vanity URL&lt;/a&gt; and the &lt;strong&gt;localizedHeadline&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Finally, map the profile to where you want to store it in your database -&amp;gt; for me, &lt;strong&gt;url&lt;/strong&gt; and &lt;strong&gt;title&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Optionally, set &lt;code&gt;allowDangerousEmailAccountLinking&lt;/code&gt; if you already have LinkedIn account in the database. If it's the case and you don't do it, users will get an error as they can't signin with different permissions. See &lt;a href="https://dev.toofficial%20doc"&gt;https://next-auth.js.org/configuration/providers/oauth#allowdangerousemailaccountlinking-option&lt;/a&gt; regarding this matter.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;In order to find all the fields available you just need to empty  &lt;code&gt;userinfo.params.projection&lt;/code&gt; and &lt;em&gt;console.log&lt;/em&gt; in &lt;code&gt;async profile&lt;/code&gt; function.&lt;/p&gt;

</description>
      <category>linkedin</category>
      <category>nextjs</category>
      <category>nextauth</category>
    </item>
    <item>
      <title>Prisma "null or does not exists"</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Mon, 28 Aug 2023 13:04:27 +0000</pubDate>
      <link>https://dev.to/martinratinaud/prisma-null-or-does-not-exists-443e</link>
      <guid>https://dev.to/martinratinaud/prisma-null-or-does-not-exists-443e</guid>
      <description>&lt;p&gt;I've been using Prisma on my latest project &lt;a href="https://karaoketools.com" rel="noopener noreferrer"&gt;Karaoke Tools&lt;/a&gt; and as crazy as it may seem, I did not find any easy way (like a plugin) to handle soft delete behaviour.&lt;/p&gt;

&lt;p&gt;So I had to tweak it myself manually and ending up in a case where&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;deletedAt&lt;/code&gt; can be null&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deletedAt&lt;/code&gt; can NOT exist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is because I'm using mongo and no default value for &lt;code&gt;deletedAt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This filtering in Prisma is not handled by default (like &lt;code&gt;deletedAt: null&lt;/code&gt; so I had to do it with an &lt;code&gt;OR&lt;/code&gt; request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;OR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;deletedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;isSet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;span class="na"&gt;deletedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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;As it took me almost an hour to find this (chatGPT not being helpful), I wrote this article.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
      <category>prisma</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Use Next.js &lt;Link&gt; component in posts with Markdown</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Fri, 09 Jun 2023 05:28:16 +0000</pubDate>
      <link>https://dev.to/martinratinaud/use-nextjs-component-in-posts-with-markdown-483m</link>
      <guid>https://dev.to/martinratinaud/use-nextjs-component-in-posts-with-markdown-483m</guid>
      <description>&lt;p&gt;For &lt;a href="https://stakingcrypto.io" rel="noopener noreferrer"&gt;stakingcrypto.io&lt;/a&gt; I'm trying to store all content that is meant for blog, coin and exchange details in markdown components because I find it really easier to add and update data quickly.&lt;/p&gt;

&lt;p&gt;A pain has always been to be able to insert &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; nextjs components as I always ending up with markdown files like this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fmh0vhf8hexnkdm8eg1bz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fmh0vhf8hexnkdm8eg1bz.png" alt="Bad MDX formatting" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, it does not look like markdown anymore and thus I can't ask not technical people to fill them up for me ☹️.&lt;/p&gt;

&lt;p&gt;That's when I stumbled on &lt;a href="https://scastiel.dev/nextjs-image-in-markdown" rel="noopener noreferrer"&gt;this excellent post of Sebastien Castiel&lt;/a&gt; which fixed the &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt; problem in a very neat way. And I ended up with this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fcmcm5r26e73822im95q6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fcmcm5r26e73822im95q6.png" alt="Better MDX formatting with images" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A lot better 😀, and I thus realized it would be very easy to replace those ugly links (&lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt;) in the same way with one easy trick!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;         &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"prose lg:prose-xl"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mdxContent&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MDXRemote&lt;/span&gt;
                &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;any&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;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="p"&gt;(&lt;/span&gt;
                      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"noreferrer"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;mdxContent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;for an internal link, &lt;code&gt;[Stake Ethereum](/stake/ETH/ethereum)&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;for a _blank page, &lt;code&gt;[Stake Ethereum](https://stakingcrypto.io/stake/ETH/ethereum)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So easy to write and a lot neater&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fcw35rpdhz42hjmjnb47l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fcw35rpdhz42hjmjnb47l.png" alt="Better MDX formatting with images and links" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I now can also then add some nice CSS tweaks to add a small icon next to those external links.&lt;/p&gt;

&lt;p&gt;Splendid! 🤩&lt;/p&gt;

&lt;p&gt;Thanks for reading and &lt;a href="https://twitter.com/martinratinaud" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt; for more&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Internationalize NextJs URLs with next-translate (Part 2)</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Fri, 17 Mar 2023 14:10:55 +0000</pubDate>
      <link>https://dev.to/martinratinaud/internationalize-nextjs-urls-with-next-translate-part-2-3cf5</link>
      <guid>https://dev.to/martinratinaud/internationalize-nextjs-urls-with-next-translate-part-2-3cf5</guid>
      <description>&lt;p&gt;Now that it's finally possible to &lt;a href="https://dev.to/martinratinaud/internationalize-nextjs-urls-with-next-translate-49d8"&gt;translate URLs with NextJs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What would be really awesome would be to add directly &lt;code&gt;permalinks&lt;/code&gt; within the javascript page.&lt;/p&gt;

&lt;p&gt;Something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;import { LanguageSwitcher, useTranslation } from 'modules/I18n';
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="gi"&gt;+ export const permalinks = {
+  en: '/',
+  fr: '/accueil',
+ };
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;const HomePage = () =&amp;gt; {
&lt;/span&gt;  const { t } = useTranslation();
&lt;span class="err"&gt;
&lt;/span&gt;  return (
    &amp;lt;section&amp;gt;
      &amp;lt;LanguageSwitcher /&amp;gt;
      &amp;lt;br /&amp;gt;
      &amp;lt;br /&amp;gt;
      &amp;lt;div&amp;gt;{t('common:variable-example', { count: 42 })}&amp;lt;/div&amp;gt;
    &amp;lt;/section&amp;gt;
  );
};
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export default HomePage;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, let's do this then&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;p&gt;Repository with all source code can be found &lt;strong&gt;&lt;a href="https://github.com/martinratinaud/next-translate-i18n-routes/tree/urls_in_page_const" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;&lt;/strong&gt; (branch &lt;code&gt;urls_in_page_const&lt;/code&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Follow the &lt;a href="https://dev.to/martinratinaud/internationalize-nextjs-urls-with-next-translate-49d8"&gt;part 1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What will be done
&lt;/h2&gt;

&lt;p&gt;Instead of having all permalinks stored in a &lt;code&gt;permalinks.json&lt;/code&gt; file, simply remove this file and export a constant directly from the page itself. &lt;/p&gt;

&lt;p&gt;It will be a lot clearer for developers and is the approach commonly used in static blogs sites (usually a &lt;code&gt;permalinks&lt;/code&gt; entry in a &lt;code&gt;frontmatter&lt;/code&gt; variable)&lt;/p&gt;

&lt;h3&gt;
  
  
  Procedure
&lt;/h3&gt;

&lt;p&gt;Idea here is to loop over all page files and extract the corresponding permalinks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: See commit &lt;a href="https://github.com/martinratinaud/next-translate-i18n-routes/commit/f554eea443bfefb9d12316d74c332f7d33c20339" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Add some utils
&lt;/h4&gt;

&lt;p&gt;For listing all pages and read the exported const, let's use the fact that all pages are listed in the &lt;code&gt;pages&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Simply deep scan them and read their content.&lt;/p&gt;

&lt;p&gt;Here, I used a very "not perfect" hack that does the job.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;As the codebase is in typescript but &lt;code&gt;next.config.js&lt;/code&gt; is a &lt;code&gt;js&lt;/code&gt; file, it is not possible to simply import the files and read the &lt;code&gt;permalinks&lt;/code&gt; object.&lt;br&gt;
I thus used ChatGPT to come up with the perfect regexp to do so 🤩. &lt;br&gt;
When NextJS will support &lt;code&gt;next.config.ts&lt;/code&gt;, this can be easily improved but it does the work for now.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;modules/I18n/utils/index.js&lt;/code&gt; file which will extract the permalinks from the pages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listFilesRecursively&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dir&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filesAndFolders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;withFileTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;filesAndFolders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entry&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entryPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&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;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isDirectory&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;listFilesRecursively&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entryPath&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="nx"&gt;entryPath&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseJavascriptJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsonString&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validJsonString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonString&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;[\{\}&lt;/span&gt;&lt;span class="sr"&gt;,&lt;/span&gt;&lt;span class="se"&gt;]\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)(\w&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)(\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;:/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$1"$2"$3:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Add quotes around keys&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/'/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Replace single quotes with double quotes&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/,&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*}/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Remove trailing commas&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsonObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validJsonString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jsonObj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error parsing JSON:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getPagePermalinks&lt;/span&gt; &lt;span class="o"&gt;=&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pagePermalinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;containingFolder&lt;/span&gt; &lt;span class="o"&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;src&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;pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filenames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listFilesRecursively&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;containingFolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;filenames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;filepath&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Read the file and extract the permalinks object&lt;/span&gt;
    &lt;span class="c1"&gt;// This could be done with a require, but it would not work with typescript&lt;/span&gt;
    &lt;span class="c1"&gt;// files&lt;/span&gt;
    &lt;span class="c1"&gt;// This is definitely a quick working hack 😎&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(?&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;=const permalinks&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;=&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*= &lt;/span&gt;&lt;span class="se"&gt;)\{[\s\S]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?\}&lt;/span&gt;&lt;span class="sr"&gt;;/gm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsonString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;;&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="p"&gt;);&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;jsonString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relativeFilePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;containingFolder&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="dl"&gt;''&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;relativeFilePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&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;parsedUri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;
        &lt;span class="nx"&gt;parsedUri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index&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="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parsedUri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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="c1"&gt;// Depending on the eslint rules, javascript object may not be straightly convertible in JSON, so parse it&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseJavascriptJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsonString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nx"&gt;pagePermalinks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;routes&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;pagePermalinks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getPagePermalinks&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, use this function instead of the &lt;code&gt;permalinks.json&lt;/code&gt; file in &lt;code&gt;next.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- const permalinks = require('./permalinks.json');
&lt;/span&gt;&lt;span class="gi"&gt;+ const { getPagePermalinks } = require('./utils');
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ const permalinks = getPagePermalinks();
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, remove the &lt;code&gt;permalinks.json&lt;/code&gt; file and add the export to the &lt;code&gt;pages/index.tsx&lt;/code&gt; page&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;import { LanguageSwitcher, useTranslation, Link } from 'modules/I18n';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ export const permalinks: { [key: string]: string } = {
+   en: '/',
+   fr: '/accueil',
+ };
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;const HomePage = () =&amp;gt; {
&lt;/span&gt;...
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉 Voila! (again)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fq00jizzw1d0hlkx79etj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fq00jizzw1d0hlkx79etj.gif" alt=" " width="516" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now enjoy handling permalinks directly from your pages components and I find it awesome.&lt;/p&gt;

&lt;p&gt;What about you?&lt;/p&gt;

&lt;h2&gt;
  
  
  Who am I?
&lt;/h2&gt;

&lt;p&gt;My name is Martin Ratinaud and I’m a senior software engineer and remote enthusiast, contagiously happy and curious.&lt;br&gt;
I create websites like this one for &lt;a href="https://www.stakingcrypto.io/" rel="noopener noreferrer"&gt;staking crypto&lt;/a&gt; and &lt;a href="https://www.indiehackers.com/martinratinaud" rel="noopener noreferrer"&gt;many more...&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check my &lt;a href="https://fr.linkedin.com/in/martinratinaud" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and get in touch!&lt;/p&gt;

&lt;p&gt;Also, I'm looking for a remote job. Hire me !&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Internationalize NextJs URLs with next-translate (Part 1)</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Fri, 17 Mar 2023 12:19:04 +0000</pubDate>
      <link>https://dev.to/martinratinaud/internationalize-nextjs-urls-with-next-translate-49d8</link>
      <guid>https://dev.to/martinratinaud/internationalize-nextjs-urls-with-next-translate-49d8</guid>
      <description>&lt;p&gt;I18n URLs have been available in all major static sites generator but is somehow lacking in NextJs and this is definitely &lt;strong&gt;a pity 😔&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'm french and always create &lt;a href="http://indiehackers.com/martinratinaud/history" rel="noopener noreferrer"&gt;my websites&lt;/a&gt; in at least french and english.&lt;/p&gt;

&lt;p&gt;Here is a detailed explanation on how any developer can achieve this in &lt;strong&gt;less than 10 min&lt;/strong&gt; and finally be able to handle urls such as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;/&lt;/li&gt;
&lt;li&gt;/fr/accueil&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;p&gt;Repository with all source code can be found &lt;strong&gt;&lt;a href="https://github.com/martinratinaud/next-translate-i18n-routes" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This tutorial is using the excellent &lt;a href="https://github.com/aralroca/next-translate" rel="noopener noreferrer"&gt;next-translate&lt;/a&gt; library but can be adapted for next-i18next.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;having next-translate already setup in your project&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What will be done
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Generate &lt;code&gt;rewrites&lt;/code&gt; rules that will make the URLs adapt accordingly to the language selected by the user&lt;/li&gt;
&lt;li&gt;Supercharge the &lt;code&gt;nextTranslate&lt;/code&gt; function to use those &lt;code&gt;rewrites&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Write a custom &lt;code&gt;Link&lt;/code&gt; component that will use those &lt;code&gt;rewrites&lt;/code&gt; to generate corresponding i18n slugs and use them for navigation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Procedure
&lt;/h2&gt;

&lt;p&gt;In all the following steps, files and functions created will be put in a &lt;code&gt;modules/I18n&lt;/code&gt; folder.&lt;br&gt;
This is a practice I came up with after many years of programming and that helps a lot for separating parts of applications (In this case, all I18n related logic).&lt;br&gt;
I will soon write a blog post about it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Specify permalinks
&lt;/h3&gt;

&lt;p&gt;First thing to be done is to specify the permalinks we want to use.&lt;br&gt;
Let's create a &lt;code&gt;modules/I18n/permalinks.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&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;span class="nl"&gt;"fr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/accueil"&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;&lt;strong&gt;NOTE&lt;/strong&gt;: this is not an ideal solution for me as it separates the actual page (the jsx file) from the definition of its permalinks and it would be better to have an &lt;code&gt;export const permalinks&lt;/code&gt; from within the page. This problem ias addressed in the &lt;a href="https://dev.to/martinratinaud/internationalize-nextjs-urls-with-next-translate-part-2-3cf5"&gt;part 2&lt;/a&gt; of this article (and you can contact me also if you want more info).&lt;/p&gt;

&lt;h3&gt;
  
  
  Supercharge &lt;code&gt;nextTranslate&lt;/code&gt; function
&lt;/h3&gt;

&lt;p&gt;The goal here is to transform the permalinks we created into rewrite rules so that NextJS can rewrite correctly the URL depending on the language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; See commit &lt;a href="https://github.com/martinratinaud/next-translate-i18n-routes/commit/c03e2d74ad8c19e052e1ebb5b23e9babe65492d1" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;modules/I18n/next.config.js&lt;/code&gt; with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextTranslate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-translate-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;permalinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./permalinks.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * 
 * Transforms
{
  "/": {
    "fr": "/accueil"
  }
}
 * into
[
  {
    source: '/fr/accueil',
    destination: '/fr',
    locale: false
  }
]
*/&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;permalinksToRewriteRules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;permalinks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;permalinks&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;originalSlug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;permalinks&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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;permalinks&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i18nSlug&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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;acc2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;source&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;locale&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;i18nSlug&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="na"&gt;destination&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;locale&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;originalSlug&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="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;span class="p"&gt;[]&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;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextConfig&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextTranslateConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;nextTranslate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;nextTranslateConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;rewrites&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingRewrites&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextTranslateConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rewrites&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;nextTranslateConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rewrites&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nf"&gt;permalinksToRewriteRules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;permalinks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;existingRewrites&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;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and replace the call to the function in &lt;code&gt;next.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- const nextTranslate = require('next-translate-plugin')
&lt;/span&gt;&lt;span class="gi"&gt;+ const nextTranslate = require('./src/modules/I18n/next.config');
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome, now, if you reload your server, you will be able to access&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://localhost:3000/fr/accueil" rel="noopener noreferrer"&gt;http://localhost:3000/fr/accueil&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, let's adapt the Link component to take this new URL into account&lt;/p&gt;

&lt;h3&gt;
  
  
  Adapt Link component
&lt;/h3&gt;

&lt;p&gt;Goal is to be able to navigate directly to the nice URLs defined earlier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: See commit &lt;a href="https://github.com/martinratinaud/next-translate-i18n-routes/commit/24da9244238f65af9c9ab3077f74edc558b698b7" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new &lt;code&gt;modules/I18n&lt;/code&gt; component called &lt;code&gt;Link&lt;/code&gt; has to be created and all imports of &lt;code&gt;next/link&lt;/code&gt; has to be modified.&lt;/p&gt;

&lt;p&gt;Yes, that's really a pain point, I admit but i could not find a way to do other wise.&lt;/p&gt;

&lt;p&gt;This is in fact not a big problem as a simple "search and replace" will work&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- import Link from 'next/link';
&lt;/span&gt;&lt;span class="gi"&gt;+ import { Link } from 'modules/I18n';
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, the &lt;code&gt;permalinks&lt;/code&gt; variable has to be exposed to the frontend in order to be used by the &lt;code&gt;Link&lt;/code&gt; component that will be created.&lt;/p&gt;

&lt;p&gt;In nextJs, this is done with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  return {
    ...nextTranslateConfig,
&lt;span class="gi"&gt;+    publicRuntimeConfig: {
+      ...nextTranslateConfig.publicRuntimeConfig,
+      permalinks, // add it to publicRuntimeConfig so it can be used by the Link component
+    },
&lt;/span&gt;    async rewrites() {
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How the nextJS built in &lt;code&gt;Link&lt;/code&gt; component works is it will build up the URL from the &lt;code&gt;href&lt;/code&gt; and the passed (or existing) &lt;code&gt;locale&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This means a link to &lt;code&gt;/&lt;/code&gt; in &lt;code&gt;fr&lt;/code&gt; will lead to &lt;code&gt;/fr&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This component will create a map of URLs to route directly to the corresponding correct URL &lt;code&gt;/fr/accueil&lt;/code&gt;&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="nx"&gt;React&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;react&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;Link&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;next/link&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;useRouter&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;next/router&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;getConfig&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;next/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;publicRuntimeConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;permalinks&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;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;publicRuntimeConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permalinks&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Formats permalinks
{
  "/": {
    "fr": "/accueil"
  }
}
 * into
{
  "/fr/": "/fr/accueil",
  "/en/accueil": "/"
} 
 */&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;i18nFallbackUrls&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;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;permalinks&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;originalSlug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;permalinks&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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;permalinks&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;permalink&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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;acc2&lt;/span&gt;&lt;span class="p"&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="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;originalSlug&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="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;permalink&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="s2"&gt;`/en&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;permalink&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;originalSlug&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;span class="p"&gt;),&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;I18nLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="kr"&gt;any&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wantedLocale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;i18nProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&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;i18nFallbackUrls&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;wantedLocale&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;href&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="nx"&gt;i18nProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i18nFallbackUrls&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;wantedLocale&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;href&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="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;i18nProps&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&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="nx"&gt;I18nLink&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;strong&gt;voila!&lt;/strong&gt;. It's all done and here is how it looks like&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Flxja36rhdydohaswfhdm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Flxja36rhdydohaswfhdm.gif" alt=" " width="948" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;Check out the &lt;a href="https://github.com/martinratinaud/next-translate-i18n-routes" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Who am I?
&lt;/h2&gt;

&lt;p&gt;My name is Martin Ratinaud and I’m a senior software engineer and remote enthusiast, contagiously happy and curious.&lt;br&gt;
I create websites like this one for &lt;a href="https://www.stakingcrypto.io/" rel="noopener noreferrer"&gt;staking crypto&lt;/a&gt; and &lt;a href="http://indiehackers.com/martinratinaud/history" rel="noopener noreferrer"&gt;many more...&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check my &lt;a href="https://fr.linkedin.com/in/martinratinaud" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and get in touch!&lt;/p&gt;

&lt;p&gt;Also, I'm looking for a remote job. Hire me !&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>i18n</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Fetch several branches locally in GitHub actions</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Thu, 27 Oct 2022 11:39:51 +0000</pubDate>
      <link>https://dev.to/martinratinaud/checkout-several-branches-in-github-actions-3nmc</link>
      <guid>https://dev.to/martinratinaud/checkout-several-branches-in-github-actions-3nmc</guid>
      <description>&lt;p&gt;&lt;em&gt;Edited&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Working on &lt;a href="https://www.opentermsarchive.org" rel="noopener noreferrer"&gt;Open Terms Archive&lt;/a&gt;, I recently needed to fetch both the &lt;code&gt;main&lt;/code&gt; branch and my current branch &lt;strong&gt;locally&lt;/strong&gt; in order to make a programmatic &lt;code&gt;git diff&lt;/code&gt; on a file.&lt;/p&gt;

&lt;p&gt;Unfortunately, GitHub Actions only allows the local fetch of one branch at a time, and even though such a syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# fetch all history for all branches and tags&lt;/span&gt;
  &lt;span class="na"&gt;fetchLocal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;would have been great, it does not exist.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE: &lt;code&gt;fetch-depth: 0&lt;/code&gt; will fetch all the branches and will make them available on &lt;code&gt;remotes/origin/&amp;lt;branch&amp;gt;&lt;/code&gt; but my script used the local branch &lt;code&gt;main&lt;/code&gt; so I had to map it locally&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So here is how fetching 2 branches locally can be done:&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Thanks to &lt;a href="https://mattischneider.fr/" rel="noopener noreferrer"&gt;Matti&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# fetch all history for all branches and tags&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git branch main remotes/origin/main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other workaround
&lt;/h2&gt;

&lt;p&gt;I keep it here as it shows how to retrieve the current branch name on both &lt;code&gt;push&lt;/code&gt; and &lt;code&gt;pull_request&lt;/code&gt; events (which I did not see anywhere).&lt;br&gt;
But you should definitely use the above solution&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get branch name&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;if [ -z $GITHUB_HEAD_REF ]; then BRANCH=$GITHUB_REF_NAME; else BRANCH=$GITHUB_HEAD_REF; fi&lt;/span&gt;
    &lt;span class="s"&gt;echo "BRANCH=$BRANCH" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
    &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# fetch all history for all branches and tags&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git checkout --force -B $BRANCH refs/remotes/origin/$BRANCH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This way, both branches are available locally for the diff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who am I?
&lt;/h2&gt;

&lt;p&gt;My name is Martin Ratinaud and I’m a senior software engineer and remote enthusiast, contagiously happy and curious.&lt;br&gt;
I create websites like this one for &lt;a href="https://www.stakingcrypto.io/" rel="noopener noreferrer"&gt;staking crypto&lt;/a&gt; and &lt;a href="https://www.indiehackers.com/martinratinaud" rel="noopener noreferrer"&gt;many more...&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check my &lt;a href="https://fr.linkedin.com/in/martinratinaud" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; and get in touch!&lt;/p&gt;

</description>
      <category>github</category>
      <category>workflow</category>
    </item>
    <item>
      <title>Recreate mysql database from frm files and idb</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Fri, 22 Jul 2022 12:20:00 +0000</pubDate>
      <link>https://dev.to/martinratinaud/recreate-mysql-database-from-frm-files-and-idb-5b04</link>
      <guid>https://dev.to/martinratinaud/recreate-mysql-database-from-frm-files-and-idb-5b04</guid>
      <description>&lt;p&gt;Recently, I made a huge mistake on my server and ended up with a failed install and no backups.&lt;br&gt;
I basically had plesk with several wordpress sites and by chance, I still had access to my data, even though it was not useable anymore.&lt;/p&gt;

&lt;p&gt;So here is how I did to recover my files and be able to get back a .sql file with structure and data.&lt;/p&gt;
&lt;h2&gt;
  
  
  First, retrieve data
&lt;/h2&gt;

&lt;p&gt;So all my mysql data is lying on my server in &lt;code&gt;/var/lib/mysql&lt;/code&gt; and it's organized in&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ibdata1&lt;/code&gt; (one important file to keep)&lt;/li&gt;
&lt;li&gt;one folder per database name&lt;/li&gt;
&lt;li&gt;inside each of this folder, &lt;code&gt;.frm&lt;/code&gt; and &lt;code&gt;.idb&lt;/code&gt; files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So let's rappatriate everything on my computer using &lt;code&gt;scp&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir mysql
scp -r root@&amp;lt;ip&amp;gt;:/var/lib/mysql/* .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Then, retrieve mysql version
&lt;/h2&gt;

&lt;p&gt;In order to be able to recreate the databases, we need to use the same version of mysql/mariadb&lt;/p&gt;

&lt;p&gt;For this, launch &lt;code&gt;cat /var/lib/mysql/mysql_upgrade_info&lt;/code&gt; on the server.&lt;/p&gt;

&lt;p&gt;I got something like &lt;code&gt;10.1.48-MariaDB&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launch mysql/mariadb on local
&lt;/h2&gt;

&lt;p&gt;As we know the exact version we need, we will now use &lt;code&gt;docker&lt;/code&gt; to launch a mysql server of exactly this version.&lt;br&gt;
So on your local, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker pull mariadb:10.1.48
docker run -p 127.0.0.1:3307:3306 --name mariadbtest -e MYSQL_ROOT_PASSWORD=Password123! -d mariadb:10.1.48
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this, we launch docker on port 3307 just in case another mysql server is already running.&lt;/p&gt;

&lt;p&gt;Now, we can login to the database by using our favorite mySQL explorer, for me, it's &lt;a href="https://www.sequelpro.com/" rel="noopener noreferrer"&gt;Sequel Pro&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can connect to the database using &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Host: 127.0.0.1&lt;/li&gt;
&lt;li&gt;username: root&lt;/li&gt;
&lt;li&gt;password: Password123!&lt;/li&gt;
&lt;li&gt;port: 3307&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🎉 you got now the same version of mysql you used to have on your server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copy your old databases to the docker machine
&lt;/h2&gt;

&lt;p&gt;So now, let's move each database inside docker to be able to access it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker cp dbname/ mariadbtest:/var/lib/mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dbname&lt;/code&gt; begin the folder holding the &lt;code&gt;.frm&lt;/code&gt; and &lt;code&gt;.idb&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;From there, if you refresh your databases in Sequel Pro, you should see your database but an error occurs when you try to open any table.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Table 'dbname.mod844_icl_string_status' doesn't exist&lt;/code&gt; for example&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix errors
&lt;/h2&gt;

&lt;p&gt;In order to fix this, you need to copy the previous file we backed up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker cp ibdata1 mariadbtest:/var/lib/mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, restart your docker machine and you should be able to access your data using Sequel Pro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export your data
&lt;/h2&gt;

&lt;p&gt;Now you can safely export your data into a &lt;code&gt;.sql&lt;/code&gt; file and think about always setting up a backup system from now on!&lt;/p&gt;

&lt;h2&gt;
  
  
  Cheers
&lt;/h2&gt;

&lt;p&gt;My name is Martin Ratinaud and I am a full stack developer&lt;/p&gt;

&lt;p&gt;I run a &lt;a href="https://stakingcrypto.io" rel="noopener noreferrer"&gt;staking crypto comparator&lt;/a&gt; to help you find the best rates for any crypto.&lt;/p&gt;

&lt;p&gt;And as a remote worker since 2016, I also want you to discover &lt;a href="https://remote-family.com" rel="noopener noreferrer"&gt;your sweet spot&lt;/a&gt; based on your hobbies, so that you can expatriate and live your best life.&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>mariadb</category>
      <category>devops</category>
    </item>
    <item>
      <title>Generate dynamic social images the easy way</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Fri, 22 Jul 2022 05:26:00 +0000</pubDate>
      <link>https://dev.to/martinratinaud/generate-dynamic-social-images-the-easy-way-jla</link>
      <guid>https://dev.to/martinratinaud/generate-dynamic-social-images-the-easy-way-jla</guid>
      <description>&lt;p&gt;At &lt;a href="https://stakingcrypto.io" rel="noopener noreferrer"&gt;staking crypto&lt;/a&gt;, we are aiming at comparing the best exchanges for getting the best possible rate on the market for your favorite crypto.&lt;/p&gt;

&lt;p&gt;For this, if you follow our &lt;a href="https://twitter.com/stakingcryptoio" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;, you will always be alerted as soon as a rate increases, or a new coin to stake is discovered.&lt;/p&gt;

&lt;p&gt;I'm always sharing a direct link to the coin detail page such as &lt;a href="https://stakingcrypto.io/stake/FTM/fantom" rel="noopener noreferrer"&gt;staking FTM&lt;/a&gt; but the social image was only the logo of the crypto, in 64x64, which was not really appealing.&lt;/p&gt;

&lt;p&gt;Fordtunately I discovered &lt;a href="https://github.com/tuhinpal/dynamic-image" rel="noopener noreferrer"&gt;This Github repo&lt;/a&gt; and forked it to create my own social images, generated on the fly everytime I share a link of my website.&lt;/p&gt;

&lt;p&gt;I still remember when I had to use imagemagick to do that and can't believe how easy it is now... As easy as creating an HTML page!&lt;/p&gt;

&lt;p&gt;So here is how it looks like now !&lt;br&gt;
A lot better right!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fywlhafme2nbgteujtp02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fywlhafme2nbgteujtp02.png" alt=" " width="601" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

</description>
    </item>
    <item>
      <title>NextJs on vercel - missing files after build when interpolating file names</title>
      <dc:creator>Martin Ratinaud</dc:creator>
      <pubDate>Fri, 22 Jul 2022 05:15:27 +0000</pubDate>
      <link>https://dev.to/martinratinaud/nextjs-on-vercel-missing-files-after-build-when-interpolating-file-names-40bl</link>
      <guid>https://dev.to/martinratinaud/nextjs-on-vercel-missing-files-after-build-when-interpolating-file-names-40bl</guid>
      <description>&lt;p&gt;While working on my free comparator for &lt;a href="https://stakingcrypto.io" rel="noopener noreferrer"&gt;staking crypto&lt;/a&gt; - the perfect tool to find the best exchange to stake or lend your cryptos and earn the best rewards - I came across a difficult problem to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Site is in Next / typescript&lt;br&gt;
It is deployed on Vercel&lt;br&gt;
It loads mdx through next-mdx-remote in getStaticProps&lt;/p&gt;
&lt;h2&gt;
  
  
  Build
&lt;/h2&gt;

&lt;p&gt;Build works fine and resulting static files contains the content of an mdx file I'm loading in the page.&lt;/p&gt;

&lt;p&gt;But after revalidation, content is not there anymore.&lt;/p&gt;
&lt;h2&gt;
  
  
  ERROR: Could not find /var/task/content/static/en/home.mdx
&lt;/h2&gt;

&lt;p&gt;Weirdly enough, the &lt;code&gt;content&lt;/code&gt; folder exists during the build, thus creating correctly the static files&lt;/p&gt;

&lt;p&gt;But after deploy, this &lt;code&gt;content&lt;/code&gt; folder was not here anymore, thus revalidating page with empty content&lt;/p&gt;
&lt;h2&gt;
  
  
  Reason
&lt;/h2&gt;

&lt;p&gt;This is because nextjs uses &lt;a href="https://github.com/vercel/nft" rel="noopener noreferrer"&gt;@vercel/nft&lt;/a&gt; to determine exactly which files (including node_modules) are necessary for the application runtime.&lt;/p&gt;

&lt;p&gt;So when you're using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const folder = "toto"
fs.readFileSync(path.join(process.cwd(), folder))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this library can not guess what it should include in the build and thus do not include the files&lt;/p&gt;

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

&lt;p&gt;You need to specify exactly the path of the file to be included, and not a variable containing that path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fs.readFileSync(path.join(process.cwd(),'toto'))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
