Build a multilingual NextJS app using the new app directory

Learn how to build a multilingual NextJS app using the new app directory and i18next library

Wednesday, August 9, 2023



Check the demo here

Check the source code here


Internationalization, or i18n for short, is the process of designing and developing software applications that can be adapted to different languages and cultures. It is an important consideration for any application that is intended for a global audience. Next.js is a popular framework for building web applications that simplifies the process of implementing i18n. In this article, we will explore how to handle i18n in Next.js using the app directory and the i18next library. We will also cover some of the key translation functions and techniques that you can use to make your application more accessible to users around the world.

With the introduction of app directory, my previous i18n blog is not applicable anymore since next-i18next is not necessary.


The easiest way to follow this guide is to degit a Nextjs boilerplate.

I will be using TailwindCSS and TypeScript due to personal preference, but you can use plain CSS and JavaScript if you want.

Install dependencies

Remove unused files

Delete everything under the app and components folders

Project Structure

Our i18n strategy

In this blog post, we will use a path-based strategy to determine the locale of our web application. Implementing this strategy in Next.js is easy because the framework follows a convention for creating paths. We will create a [locale] folder and place all our pages inside it. This means our folder structure should look like this:

Since we're utilizing a path-based i18n approach, we can effortlessly obtain the locale from the params object that our page will receive.

Create our home page to display the locale

Create another page to test navigation

NextJS has a feature that automatically creates a layout component if we don't provide one. However, I prefer to create my own layout component because I need to basic customization.

Testing our pages

We'll get a 404 page if we try to access the root URL because every page is now under the [locale] folder. We'll handle this later.

An image of a blog post

To see our page, we need to add the "locale" of our choice to the URL. For example, if we want to see the English version of our page, we need to add /en to the URL.

An image of a blog post
An image of a blog post

Locale switcher and navigation links

To simplify our demo, we'll create a component that will handle the locale switcher and navigation links.

Implement a new layout component to manage the organization of page content

We can put everything in the root layout if you prefer.

Create the Header component

This component will include the reusable navigation and locale switcher component

Create the locale switcher component

Now it's easier to test around what language and page we want to see.

An image of a blog post

Actual translation

We're done with the project setup. It's time to actually do some internationalization

Create translation files

Unless our users use translation plugins like Google Translate, there is no way for our content to be magically translated. Therefore, we need to determine how our pages will be translated based on the user's locale.

Here is what our translation files' structure will look like.

NOTE: It does not matter where you put the translation files as long as you can import them correctly.

English translations

Chinese translations

Swedish translation

Install required dependencies

There are various libraries available for handling translations, but I find libraries from i18next very easy to use.

i18next-resources-to-backend is a very small utility, so you can just copy the implementation if you don't want an additional dependency.

Create a reusable settings file

Let's create a utility file for both the server and the client-side translations

To learn more about the options, check out the i18next documentation.

Server Components translation

Create a utility for translations happening in the server/backend

Translate the Home page

Translate the About page

With the codes above, we can now see the content translated.

An image of a blog post

Client-side translation

One issue in the previous example is that the links in the header do not update accordingly to the selected language.

Create a reusable hook to use for translations

Install i18next-browser-languagedetector to simplify language detection in the frontend

The code below might be lengthy because we need to support both server rendering and client rendering. Don't confuse SSR with Server Component rendering.

Create the translations for the navigation links

We'll put the translations in the common namespace as they are shared common pages.

Translate the Header component

Now the links will update accordingly to the selected language

An image of a blog post

Handle default locale

This may vary based on the user's requirements, but I prefer not to include the default locale in the URL. I want localhost:3000 to be equivalent to localhost:3000/en, and when I visit localhost:3000/en, I want the /en in the URL to be automatically removed.

To achieve this, we need to do some URL rewriting and redirecting.

NextJS is truly magical in making things just work

An image of a blog post


Nested translation keys and default translation

We are not limited to a flat JSON structure.

We can omit some translation keys if we want it to use the default locale value(en in our case).

Create the component

Let's create a component that uses the translations above. We'll make this a server component to demonstrate one way of passing the locale.

(OPTIONAL) Add styles to form fields

Render the form on the home page

And now, we have this!

An image of a blog post

Built-in Formatting

It is very easy to format most of our data since i18next comes with a lot of utilities we can use.

Let's use the translation files below to showcase the formatting features.

Create the component

Let's create a component that will use the previous translations. We'll make it a client component just for fun.

Don't forget to render the component on the home page.

The more you look, the more you'll be amazed

An image of a blog post

Other translation functions to check


Internationalization is a complex requirement simplified in Nextjs due to the way applications are built using the framework. With the introduction of app directory, we need a different approach to handle i18n. Because of i18next it makes the task even simpler.