Home
Next.js
Next.js i18n with next-intl: A Practical Setup
September 12, 2026
1 min

Table Of Contents

01
1. What is next-intl?
02
2. Define Locales and Translations
03
3. Use One Page for All Locales
04
4. Handle Navigation and Language Switching
05
5. Dynamic Content Comes from the Backend

Supporting multiple languages in a Next.js application doesn’t mean creating separate pages for each language. With next-intl, we can use the same components and pages while using the locale as part of the URL.

1. What is next-intl?

next-intl is an internationalization library for Next.js that provides locale-based routing, translations, and locale-aware navigation.

For example:

/vi/plants
/en/plants

Both URLs can use the same page:

app/[locale]/plants/page.tsx

The locale comes directly from the URL, so /vi means Vietnamese and /en means English.


2. Define Locales and Translations

First, install next-intl:

npm install next-intl

Define supported locales:

// src/i18n/routing.ts
export const routing = {
locales: ['vi', 'en'],
defaultLocale: 'vi',
};

Then create translation files:

messages/
├── vi.json
└── en.json

For example:

// vi.json
{
"common": {
"addToCart": "Thêm vào giỏ"
}
}
// en.json
{
"common": {
"addToCart": "Add to cart"
}
}

These files should contain UI translations, not dynamic product or blog content.


3. Use One Page for All Locales

Move storefront routes under a dynamic [locale] segment:

app/
└── [locale]/
└── (store)/
├── plants/
├── cart/
├── checkout/
└── blog/

Now:

/vi/plants
/en/plants

both render:

app/[locale]/(store)/plants/page.tsx

Inside a component, translations can be accessed with:

const t = useTranslations('common');
t('addToCart');

The same component automatically displays the correct language based on the current locale.


4. Handle Navigation and Language Switching

Use next-intl’s locale-aware navigation instead of manually building URLs.

import {Link} from '@/i18n/navigation';
<Link href="/plants">
Shop
</Link>

The language switcher can then change:

/vi/plants
/en/plants

while preserving dynamic routes such as:

/vi/blog/monstera
/en/blog/monstera

There is no need to use window.location, localStorage, or hard-coded /vi and /en paths.


5. Dynamic Content Comes from the Backend

next-intl should handle UI translations. Products, categories, and blog posts should remain backend data.

The current locale can be passed to the API:

/vi/plants
GET /plants?locale=vi

or:

/en/plants
GET /plants?locale=en

This creates a clean separation:

next-intl
→ UI translations
Backend
→ Products, categories, blog content

As a result, adding another language only requires adding a new locale and its UI translation file. Pages and components do not need to be duplicated.


Tags

#NextJS

Share

Related Posts

NextJS
NextJS - Proxy
October 10, 2023
1 min
© 2026, All Rights Reserved.
Powered By

Social Media

githublinkedinyoutube