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.
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.
First, install next-intl:
npm install next-intl
Define supported locales:
// src/i18n/routing.tsexport 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.
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.
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.
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 translationsBackend→ 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.