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
Terminalnpm 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:
JSON1{ 2 "nav": { 3 "home": "Home", 4 "about": "About" 5 }, 6 "footer": "© 2026 My App" 7}
public/locales/en/home.json:
JSON1{ 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:
JavaScript1module.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:
JavaScript1const { i18n } = require('./next-i18next.config'); 2 3module.exports = { 4 i18n, 5 reactStrictMode: true, 6};
Step 5: Wrap Your App
pages/_app.tsx:
TSX1import 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:
TSX1import { 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:
TSX1import { 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:
TSX1export const getStaticProps: GetStaticProps = async ({ locale }) => ({ 2 props: { 3 ...(await serverSideTranslations(locale ?? 'en', ['common', 'home'])), 4 }, 5});
Pluralization
translations:
JSON1{ 2 "message_one": "You have {{count}} message", 3 "message_other": "You have {{count}} messages" 4}
usage:
TSXt('message', { count: 5 }); // "You have 5 messages"
TypeScript Support
types/i18next.d.ts:
TypeScript1import '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
Terminal1npx @intlpullhq/cli init 2npx @intlpullhq/cli extract --format i18next 3npx @intlpullhq/cli translate --all 4npx @intlpullhq/cli download --output ./public/locales
