🌐 Next.js Internationalization Using i18n

Mukesh BishnoiMukesh Bishnoi
4 min read

As digital products aim to reach a global audience, internationalization (i18n) emerges as an essential feature in contemporary web applications. Next.js, a robust React framework, provides comprehensive built-in support for internationalized routing. Coupled with tools like next-i18next, managing translations becomes both elegant and efficient.

In this blog post, we'll dive deep into how to implement internationalization in a Next.js appβ€”from setup to best practicesβ€”with a focus on SEO, routing, translation management, and language switching.

🧭 What is Internationalization (i18n)?

Internationalization, abbreviated as i18n (because there are 18 letters between "i" and "n"), is the process of designing software that can be easily adapted to various languages and locales.

Examples of localized elements:

  • Static text (e.g., headings, buttons)

  • Dates and times

  • Currency formats

  • Number formats

  • Right-to-left (RTL) layout support for languages like Arabic or Hebrew

πŸ“¦ Why Use Next.js for i18n?

Next.js supports i18n out of the box with:

  • Localized routing (/fr/about vs /en/about)

  • Automatic locale detection

  • Support for Static Site Generation (SSG), Server-Side Rendering (SSR), and Client-Side Rendering (CSR)

With this built-in support combined with a library like next-i18next, you get:

  • Namespaced translation files

  • Translation fallback support

  • TypeScript compatibility

  • Server and client translation sync

βš™οΈ Step 1: Next.js Configuration for i18n

Start by enabling internationalized routing in your Next.js config.

// next.config.js
const { i18n } = require('./next-i18next.config');

module.exports = {
  i18n,
  reactStrictMode: true,
};
// next-i18next.config.js
module.exports = {
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'fr', 'de'],
    localeDetection: true,
  },
  reloadOnPrerender: process.env.NODE_ENV === 'development',
};

πŸ“ Step 2: Project Folder Structure

Organize your translations in the public/locales directory.

public/
└── locales/
    β”œβ”€β”€ en/
    β”‚   └── common.json
    β”œβ”€β”€ fr/
    β”‚   └── common.json
    └── de/
        └── common.json

Example common.json:

{
  "title": "Welcome to our i18n!",
  "subtitle": "Experience internationalization with i18n in Next.js",
  "button_label": "Hit me"
}

πŸ› οΈ Step 3: Install Required Dependencies

npm install next-i18next react-i18next i18next
# For TypeScript users:
npm install -D @types/react-i18next

🧩 Step 4: Using Translations in Components

// pages/index.tsx
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';

export default function HomePage() {
  const { t } = useTranslation('common');

  return (
    <main>
      <h1>{t('title')}</h1>
      <p>{t('subtitle')}</p>
      <button>{t('button_label')}</button>
    </main>
  );
}

export async function getStaticProps({ locale }) {
  return {
    props: {
      ...(await serverSideTranslations(locale, ['common'])),
    },
  };
}

πŸ”€ Step 5: Language Switcher Component

import { useRouter } from 'next/router';

export default function LanguageSwitcher() {
  const router = useRouter();
  const { locale, locales, pathname, query, asPath } = router;

  const switchTo = (lng: string) => {
    router.push({ pathname, query }, asPath, { locale: lng });
  };

  return (
    <div>
      {locales?.map((lng) => (
        <button key={lng} onClick={() => switchTo(lng)} disabled={lng === locale}>
          {lng.toUpperCase()}
        </button>
      ))}
    </div>
  );
}

🌍 Handling Dates, Currency & Numbers

const formattedDate = new Intl.DateTimeFormat(locale, {
  year: 'numeric', month: 'long', day: 'numeric'
}).format(new Date());

const formattedCurrency = new Intl.NumberFormat(locale, {
  style: 'currency', currency: 'EUR'
}).format(499.99);

πŸ§ͺ Testing & Debugging

  • Use browser dev tools to test Accept-Language headers

  • Log useRouter().locale to verify current language

  • Try toggling between SSR, SSG, and CSR to see how translations load

⚠️ Common Pitfalls

IssueSolution
Translations not loadingCheck namespace names in getStaticProps
SSR hydration mismatchAvoid hard-coded text outside t()
Language switch causes full reloadUse router.push() with locale correctly
Missing fallback stringsSet fallbackLng in i18next config

πŸš€ Deployment Considerations

  • Ensure the public/locales folder is included in your build.

  • Use CDN caching headers that vary by language (e.g., Vary: Accept-Language).

  • If using middleware or edge functions, ensure locale headers are respected.

🧠 Best Practices

  • Separate translation files by feature or route (e.g., home.json, auth.json)

  • Use TypeScript + react-i18next for safer translation key usage

  • Keep UI text and translations decoupled

  • Automate translation string extraction and management using tools like locize or phrase.com

βœ… Conclusion

With built-in support for localized routing and seamless integration with translation libraries, Next.js makes internationalization straightforward and powerful. Whether you're building a multilingual marketing site or a global SaaS dashboard, i18n in Next.js ensures scalability and user-centric experiences.

πŸ”— Resources

0
Subscribe to my newsletter

Read articles from Mukesh Bishnoi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mukesh Bishnoi
Mukesh Bishnoi

Passionate engineer who loves building, breaking and learning stuff. Looking for opportunities to work in a fast paced learning & growth environment. Currently focus on Frontend development,