IntlPull
Tutorial
14 min read

Next.js Pages Router with next-i18next: Complete i18n Tutorial 2026

Complete tutorial for setting up next-i18next in Next.js Pages Router. Learn configuration, serverSideTranslations, language detection, and namespace management.

IntlPull Team
IntlPull Team
03 Feb 2026, 11:44 AM [PST]
On this page
Summary

Complete tutorial for setting up next-i18next in Next.js Pages Router. Learn configuration, serverSideTranslations, language detection, and namespace management.

Quick Answer

To use next-i18next with Next.js Pages Router: Install next-i18next, create next-i18next.config.js with your locales, add i18n config to next.config.js, wrap _app.tsx with appWithTranslation, use serverSideTranslations in getStaticProps/getServerSideProps, and call useTranslation hook in components.


Prerequisites

  • Next.js with Pages Router (not App Router)
  • Node.js 18+
  • Basic React knowledge

Project Setup

Step 1: Install next-i18next

Terminal
npm install next-i18next react-i18next i18next

Directory Structure

├── public/
│   └── locales/
│       ├── en/
│       │   ├── common.json
│       │   └── home.json
│       └── es/
│           ├── common.json
│           └── home.json
├── pages/
│   ├── _app.tsx
│   └── index.tsx
├── next.config.js
└── next-i18next.config.js

Step 2: Create Translation Files

public/locales/en/common.json:

JSON
1{
2  "nav": {
3    "home": "Home",
4    "about": "About"
5  },
6  "footer": "© 2026 My App"
7}

public/locales/en/home.json:

JSON
1{
2  "title": "Welcome to Next.js",
3  "greeting": "Hello, {{name}}!",
4  "items_one": "{{count}} item",
5  "items_other": "{{count}} items"
6}

Step 3: Create next-i18next Configuration

next-i18next.config.js:

JavaScript
1module.exports = {
2  i18n: {
3    defaultLocale: 'en',
4    locales: ['en', 'es', 'fr'],
5  },
6  localePath:
7    typeof window === 'undefined'
8      ? require('path').resolve('./public/locales')
9      : '/locales',
10  reloadOnPrerender: process.env.NODE_ENV === 'development',
11};

Step 4: Update Next.js Configuration

next.config.js:

JavaScript
1const { i18n } = require('./next-i18next.config');
2
3module.exports = {
4  i18n,
5  reactStrictMode: true,
6};

Step 5: Wrap Your App

pages/_app.tsx:

TSX
1import type { AppProps } from 'next/app';
2import { appWithTranslation } from 'next-i18next';
3
4function MyApp({ Component, pageProps }: AppProps) {
5  return <Component {...pageProps} />;
6}
7
8export default appWithTranslation(MyApp);

Step 6: Create Pages with Translations

pages/index.tsx:

TSX
1import { GetStaticProps } from 'next';
2import { useTranslation } from 'next-i18next';
3import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
4import Link from 'next/link';
5import LanguageSwitcher from '../components/LanguageSwitcher';
6
7export default function Home() {
8  const { t } = useTranslation('home');
9  const { t: tCommon } = useTranslation('common');
10
11  return (
12    <main>
13      <nav>
14        <Link href="/">{tCommon('nav.home')}</Link>
15        <Link href="/about">{tCommon('nav.about')}</Link>
16      </nav>
17
18      <h1>{t('title')}</h1>
19      <p>{t('greeting', { name: 'World' })}</p>
20      <p>{t('items', { count: 5 })}</p>
21
22      <LanguageSwitcher />
23
24      <footer>{tCommon('footer')}</footer>
25    </main>
26  );
27}
28
29export const getStaticProps: GetStaticProps = async ({ locale }) => {
30  return {
31    props: {
32      ...(await serverSideTranslations(locale ?? 'en', ['common', 'home'])),
33    },
34  };
35};

Step 7: Create Language Switcher

components/LanguageSwitcher.tsx:

TSX
1import { useRouter } from 'next/router';
2import Link from 'next/link';
3
4const languageNames = {
5  en: 'English',
6  es: 'Español',
7  fr: 'Français',
8};
9
10export default function LanguageSwitcher() {
11  const router = useRouter();
12  const { locales, locale: currentLocale, asPath } = router;
13
14  return (
15    <div>
16      {locales?.map((locale) => (
17        <Link
18          key={locale}
19          href={asPath}
20          locale={locale}
21          style={{
22            marginRight: '0.5rem',
23            fontWeight: currentLocale === locale ? 'bold' : 'normal',
24          }}
25        >
26          {languageNames[locale]}
27        </Link>
28      ))}
29    </div>
30  );
31}

Using Namespaces

Organize translations by feature:

public/locales/en/
├── common.json      # Shared UI
├── home.json        # Home page
├── auth.json        # Login/signup
└── dashboard.json   # Dashboard

Load only what you need:

TSX
1export const getStaticProps: GetStaticProps = async ({ locale }) => ({
2  props: {
3    ...(await serverSideTranslations(locale ?? 'en', ['common', 'home'])),
4  },
5});

Pluralization

translations:

JSON
1{
2  "message_one": "You have {{count}} message",
3  "message_other": "You have {{count}} messages"
4}

usage:

TSX
t('message', { count: 5 }); // "You have 5 messages"

TypeScript Support

types/i18next.d.ts:

TypeScript
1import 'i18next';
2import common from '../public/locales/en/common.json';
3import home from '../public/locales/en/home.json';
4
5declare module 'i18next' {
6  interface CustomTypeOptions {
7    defaultNS: 'common';
8    resources: {
9      common: typeof common;
10      home: typeof home;
11    };
12  }
13}

Scaling with IntlPull

Terminal
1npx @intlpullhq/cli init
2npx @intlpullhq/cli extract --format i18next
3npx @intlpullhq/cli translate --all
4npx @intlpullhq/cli download --output ./public/locales

Tags
next.js
next-i18next
pages-router
i18n
tutorial
ssr
2026
IntlPull Team
IntlPull Team
Engineering

Building tools to help teams ship products globally. Follow us for more insights on localization and i18n.