I implement Next-Intl into my Portfolio (Next JS & App Router)
Tags: nextjs, frontend, beginners, webdev
Published: 2024-06-11 06:12:18

Before I dive into implementing the new feature (Next-Intl) I added to my portfolio, you might want to check it out first at [my portfolio](https://bram-portfolio-website.vercel.app/en).

Internationalization (often abbreviated as i18n) is an important aspect of web development, especially for websites that cater to a global audience. Here are some key features and benefits of using internationalization on your website:

1. **Language Localization**: Internationalization allows your website to be translated into different languages. This makes your content accessible to users around the world, regardless of their native language.
2. **Improved User Experience**: By providing content in a user’s native language and adhering to their cultural norms, you enhance their experience and engagement with your website.
3. **SEO Benefits**: Search engines favor websites that cater to a global audience. By having your website content available in multiple languages, you can improve your search engine rankings.

Because I'm using Next JS as my tech to build my portfolio, I use the **Next-Intl** so it will be compatible with my website. To implement **Next-Intl** as I did in my portfolio you can follow this step(I will demonstrate this with my web code):

1. **Add Next-Intl into your application** 
   ```
   npm i next-intl
   ``` 
2. **Create this folder structure** 
   ```
   ├── locales (1)
   │   ├── en.json
   │   └── ... // your others locales file (e.g. id.json)
   ├── next.config.js (2)
   ├── i18n.ts (3)
   ├── middleware.ts (4)
   └── app
       └── [locale]
           ├── layout.tsx (5)
           └── page.tsx (6)
   ``` 
   You can follow my folder structure example
   ![Folder Structure Example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/linsuortgg06034trb0o.png) 
3. **Setup the files** 
   1. Add JSON files in your project based on locales(e.g. en.json, id.json)

      `en.json`
      ```
      "Project": {
         "label": "My Projects"
      },
      ``` 
   2. Add this code to your `next.config.js`
 
      `next.config.js`
      ```
      // @ts-check
      const createNextIntlPlugin = require('next-intl/plugin')

      const withNextIntl = createNextIntlPlugin()

      /** @type {import('next').NextConfig} */

      const config = {}

      module.exports = withNextIntl(config)
      ```
   3. Add this code to your `i18n.ts`

      `i18n.ts`
      ```
      import { notFound } from 'next/navigation'
      import { getRequestConfig } from 'next-intl/server'

      const locales = ['en', ...] // Add your other locales here

      export default getRequestConfig(async ({ locale }) => { 
         // Validate that the incoming `locale` parameter is valid 
         if (!locales.includes(locale as any)) notFound()

         return {
            messages: (await import(`./locales/${locale}.json`)).default,
         }
      })

      ``` 
   4. Add this code to your `middleware.ts`

      `middleware.ts`
       ```
       import createMiddleware from 'next-intl/middleware'

       export default createMiddleware({
	   // A list of all locales that are supported
	   locales: ['en', ...], // Add your other locales

	   // Used when no locale matches
	   defaultLocale: 'en',
       })

       export const config = {
	   // Match only internationalized pathnames
	   matcher: ['/', '/(id|en)/:path*'],
       }
       ``` 
    5. Add this code to your `app/[locale]/layout.tsx`

       `app/[locale]/layout.tsx`
        ```
        import { NextIntlClientProvider } from 'next-intl'
        import { getMessages } from 'next-intl/server'

        type Props = {
	       children: ReactNode
	       params: { locale: string }
        }

        export default async function RootLayout({
	      children,
	      params: { locale },
        }: Props) {
            const messages = await getMessages()
            return (
               <html lang={locale}>
                 <body>
                   <NextIntlClientProvider messages={messages}
                       {children}
                   </NextIntlClientProvider>
                 </body>
               </html>
            )
        }
        ```
     6. Call your translation using `useTranslation` in your page components or anywhere else!

        `e.g. Projects.tsx`
        ```
        import { useTranslations } from 'next-intl'

        export default function Projects() {
           const t = useTranslations('Project')

           return (<p>{t('label')}</p>)
        }
        ```
You can make a language toggler button to switch language as you wish, but before that you guys need to make a routing using **Next-Intl** so follow this step: 

1. Create `config.ts` on your root folder and add this code

   `config.ts`
   ```
   export const locales = ['en', 'id'] as const

   // Use the default: `always`
   export const localePrefix = undefined
   ``` 
2. Create `navigation.ts` on your root folder and add this code

   `navigation.ts`
    ```
    import { createSharedPathnamesNavigation } from 'next-intl/navigation'
    import { locales, localePrefix } from './config'

    export const { Link, redirect, usePathname, useRouter } =
       createSharedPathnamesNavigation({
          locales,
          localePrefix,
       })
    ```
3. If you done two of the previous step you can make the language button toggler

    `LangToggler.tsx`
    ```
    'use client'

    import { useTransition } from 'react'
    import { useRouter, usePathname } from '../navigation'
    import { useLocale } from 'next-intl'

    import inaIcon from '@/public/ina-icon.svg'
    import enIcon from '@/public/en-icon.svg'
    import Image from 'next/image'

    export default function LangToggler() {
        const router = useRouter()
        const [isPending, startTransition] = useTransition()
        const pathname = usePathname()
        const locale = useLocale()

        const nextLocale = locale === 'en' ? 'id' : 'en'

        function onButtonClick() {
            startTransition(() => {
                 router.replace(pathname, { locale: nextLocale })
            })
        }

        return (
           <button
               disabled={isPending}
               onClick={onButtonClick}
           >
               <Image
                  src={locale === 'en' ? inaIcon : enIcon}
                  alt='language flag icon'
                  className='w-4'
               />
           </button>
        )
    }

    ```

Voila, you have done adding Next-Intl to your application. This is an example of my portfolio website

![My Web with English Language](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lb5jsogq4ud1x9drngmx.png)

![My Web with Indonesian Language](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/odiln5v7dl69zrxyhnvu.png)

Happy coding guys, Thankyou!