Next.js Internationalization (i18n) with next-intl

Full Stack Engineer
August 14, 2024
Updated on March 23, 2025
0 MIN READ
#performance#tailwind#cicd#next.js#internationalization

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:

  1. Simplified Setup: Abstracts away much of the complexity of Next.js i18n routing
  2. Type Safety: Provides excellent TypeScript support out of the box
  3. React Integration: Offers React hooks and components for easy usage in your components
  4. Message Formatting: Supports advanced formatting including plurals, numbers, dates, and times
  5. 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:

  1. Organize Your Translations: Group related messages into logical namespaces
  2. Use Meaningful Keys: Avoid generic keys like message1 in favor of descriptive ones like homepage.welcome
  3. Handle Plurals Properly: Use next-intl's pluralization support
  4. Consider Right-to-Left (RTL): Add support for RTL languages if needed
  5. Localize SEO Elements: Translate metadata and alt texts
  6. Test Edge Cases: Verify with long translations and special characters
  7. 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.

Share this article