Next.js Internationalization (i18n) with next-intl
Introduction
In today's global digital landscape, building applications that cater to multiple languages and regions is no longer optional—it's essential. Next.js, the popular React framework, provides excellent support for internationalization (i18n), and when combined with the next-intl
library, it becomes a powerful solution for creating multilingual applications.
This guide will walk you through implementing internationalization in Next.js using next-intl
, covering setup, configuration, dynamic routing, and best practices. Whether you're building a simple blog or a complex enterprise application, these techniques will help you deliver a seamless multilingual experience.
Why Choose next-intl for Next.js Internationalization?
While Next.js has built-in i18n capabilities, next-intl
offers several advantages that make it particularly compelling:
- Simplified Setup: Abstracts away much of the complexity of Next.js i18n routing
- Type Safety: Provides excellent TypeScript support out of the box
- React Integration: Offers React hooks and components for easy usage in your components
- Message Formatting: Supports advanced formatting including plurals, numbers, dates, and times
- SSR/SSG Support: Works seamlessly with both server-side rendering and static generation
Compared to alternatives like next-i18next
or React's native Intl
APIs, next-intl
provides a more Next.js-optimized solution with better developer experience.
Setting Up next-intl in Your Next.js Project
Let's start with the basic setup process. First, you'll need to install the package:
npm install next-intl
Next, create a folder structure for your translations. A common approach is:
public/
locales/
en/
common.json
es/
common.json
fr/
common.json
Now, configure Next.js to use next-intl
. Create or modify your next.config.js
:
const withNextIntl = require('next-intl/plugin')(); module.exports = withNextIntl({ // Other Next.js config options here });
Create a i18n.ts
(or .js
) configuration file:
import {notFound} from 'next/navigation'; import {getRequestConfig} from 'next-intl/server'; const locales = ['en', 'es', 'fr']; export default getRequestConfig(async ({locale}) => { if (!locales.includes(locale)) notFound(); return { messages: (await import(`../public/locales/${locale}/common.json`)).default }; });
Finally, wrap your app with the NextIntlClientProvider
in app/layout.tsx
:
import {NextIntlClientProvider} from 'next-intl'; import {notFound} from 'next/navigation'; export default async function LocaleLayout({children, params: {locale}}) { let messages; try { messages = (await import(`../../public/locales/${locale}/common.json`)).default; } catch (error) { notFound(); } return ( <html lang={locale}> <body> <NextIntlClientProvider locale={locale} messages={messages}> {children} </NextIntlClientProvider> </body> </html> ); }
Implementing Translations in Components
With the setup complete, let's explore how to use translations in your components. next-intl
provides several ways to work with translations:
Using the useTranslations
Hook
For client components, use the useTranslations
hook:
'use client'; import {useTranslations} from 'next-intl'; function Greeting() { const t = useTranslations('Common'); return ( <div> <h1>{t('welcome')}</h1> <p>{t('description')}</p> </div> ); }
Server Components with getTranslations
For server components, use getTranslations
:
import {getTranslations} from 'next-intl/server'; async function ServerGreeting({params: {locale}}) { const t = await getTranslations({locale, namespace: 'Common'}); return ( <div> <h1>{t('welcome')}</h1> <p>{t('description')}</p> </div> ); }
Formatting Dates and Numbers
next-intl
provides excellent formatting utilities:
import {useFormatter} from 'next-intl'; function DateDisplay() { const format = useFormatter(); const date = new Date(); return ( <div> <p>{format.dateTime(date, {dateStyle: 'full'})}</p> <p>{format.number(1234.56, {style: 'currency', currency: 'USD'})}</p> </div> ); }
Advanced Routing and Language Switching
Implementing language switching requires careful handling of routes. Here's how to set it up:
First, create a language switcher component:
'use client'; import {useRouter} from 'next-intl/client'; import {usePathname} from 'next-intl/client'; function LanguageSwitcher() { const router = useRouter(); const pathname = usePathname(); function onLanguageChange(locale) { router.push(pathname, {locale}); } return ( <select onChange={(e) => onLanguageChange(e.target.value)}> <option value="en">English</option> <option value="es">Español</option> <option value="fr">Français</option> </select> ); }
For static generation, you'll need to generate paths for all locales. In your page:
export async function generateStaticParams() { return [{locale: 'en'}, {locale: 'es'}, {locale: 'fr'}]; }
Best Practices for Next.js Internationalization
To ensure a smooth internationalization process:
- Organize Your Translations: Group related messages into logical namespaces
- Use Meaningful Keys: Avoid generic keys like
message1
in favor of descriptive ones likehomepage.welcome
- Handle Plurals Properly: Use
next-intl
's pluralization support - Consider Right-to-Left (RTL): Add support for RTL languages if needed
- Localize SEO Elements: Translate metadata and alt texts
- Test Edge Cases: Verify with long translations and special characters
- Implement Fallbacks: Handle missing translations gracefully
Here's an example of handling plurals:
// In your translation file { "messages": { "one": "You have one message", "other": "You have {count} messages" } } // In your component const t = useTranslations('Messages'); const count = 5; t('messages', {count}); // Returns "You have 5 messages"
Conclusion
Implementing internationalization in Next.js with next-intl
provides a robust solution for creating multilingual applications. From the initial setup to advanced routing and formatting, next-intl
simplifies the process while maintaining flexibility and performance.
By following the patterns outlined in this guide, you can build applications that seamlessly adapt to different languages and regions, providing a better experience for your global audience. Remember to structure your translations thoughtfully, leverage TypeScript for type safety, and test your implementation across all supported locales.
As your application grows, consider adding translation management tools and automating the extraction of translation keys. With proper planning and next-intl
's powerful features, internationalization can become a strength rather than a challenge in your Next.js projects.