Internationalization (i18n) in Next.js: Building Multi-Language Applications | SoniNow Blog

Limited TimeLearn More

i18ninternationalizationnext.jslocalizationmulti-language

Internationalization (i18n) in Next.js: Building Multi-Language Applications

Published

2026-06-23

Read Time

4 mins

Internationalization (i18n) in Next.js: Building Multi-Language Applications

Building a multi-language web application in Next.js involves more than translating strings—it requires routing infrastructure, locale detection, formatting abstractions, and search engine optimization for every language version. Next.js provides first-class support for internationalization through the App Router's built-in i18n features, but choosing the right strategy for routing, translation management, and locale persistence makes the difference between a maintainable system and a brittle one.

Routing Strategies for Locales

Next.js supports two routing approaches for internationalization: sub-path routing (/en/products, /fr/produits) and domain routing (en.example.com, fr.example.com). Sub-path routing is simpler and preferred for most applications:

// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  i18n: false, // App Router handles i18n without this config
}

// middleware.ts — handle locale routing
import { NextResponse } from 'next/server'
import { match } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'

const locales = ['en', 'fr', 'de', 'es', 'ja']
const defaultLocale = 'en'

function getLocale(request: Request): string {
  const negotiator = new Negotiator({ headers: { 'accept-language': request.headers.get('accept-language') || '' } })
  const languages = negotiator.languages()
  return match(languages, locales, defaultLocale)
}

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl
  const pathnameHasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  )

  if (pathnameHasLocale) return

  const locale = getLocale(request)
  request.nextUrl.pathname = `/${locale}${pathname}`
  return NextResponse.redirect(request.nextUrl)
}

export const config = {
  matcher: ['/((?!api|_next|_vercel|static|favicon.ico).*)'],
}

This middleware detects the user's preferred language from the Accept-Language header and redirects to the correct locale path. Users can override their locale by navigating to a different prefix, and the cookie-based locale selection persists across sessions.

Translation Management with Files

A robust i18n system separates translation data from application code. Use JSON files organized by locale and namespace:

// messages/en/common.json
{
  "nav": {
    "products": "Products",
    "pricing": "Pricing",
    "contact": "Contact"
  },
  "home": {
    "hero": "Build better software",
    "cta": "Get started"
  }
}
// messages/fr/common.json
{
  "nav": {
    "products": "Produits",
    "pricing": "Tarifs",
    "contact": "Contact"
  },
  "home": {
    "hero": "Créez de meilleurs logiciels",
    "cta": "Commencer"
  }
}

Integrate with next-intl for runtime translation resolution:

// app/[locale]/page.tsx
import { useTranslations } from 'next-intl'

export default function HomePage() {
  const t = useTranslations('home')
  return (
    <section>
      <h1>{t('hero')}</h1>
      <button>{t('cta')}</button>
    </section>
  )
}

Using namespaced JSON files instead of a single monolithic translations object keeps bundles small—each page only loads the namespaces it needs.

Date, Number, and Currency Formatting

Numbers, dates, and currencies require locale-aware formatting that goes beyond simple string replacement. The Intl APIs handle this correctly across all locales:

// Formatting utility with fallback
export function formatPrice(amount: number, locale: string, currency: string) {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency,
  }).format(amount)
}

export function formatDate(date: Date, locale: string) {
  return new Intl.DateTimeFormat(locale, {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  }).format(date)
}

export function formatRelativeTime(ms: number, locale: string) {
  const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' })
  const seconds = Math.floor((Date.now() - ms) / 1000)
  if (seconds < 60) return rtf.format(-seconds, 'second')
  const minutes = Math.floor(seconds / 60)
  if (minutes < 60) return rtf.format(-minutes, 'minute')
  // ...extend for hours, days
}

Intl.RelativeTimeFormat handles "2 hours ago" or "yesterday" in any locale without third-party libraries. Combined with Intl.ListFormat for lists and Intl.PluralRules for pluralization, the native Intl API covers most formatting needs.

SEO for Multi-Language Sites

Search engines rely on hreflang tags to serve the correct language version of a page. Next.js's generateMetadata can inject these tags dynamically:

import { getTranslations } from 'next-intl/server'

export async function generateMetadata({ params: { locale } }) {
  const t = await getTranslations({ locale, namespace: 'products' })

  return {
    title: t('meta.title'),
    description: t('meta.description'),
    alternates: {
      canonical: `/${locale}/products`,
      languages: {
        'en': '/en/products',
        'fr': '/fr/produits',
        'de': '/de/produkte',
        'x-default': '/en/products',
      },
    },
  }
}

The x-default language tag tells search engines which page to show when no language match is found—typically the English version. Each language variant should have its own URL, unique metadata, and properly translated content for maximum SEO impact.

Internationalization is an investment in user experience and market reach. Next.js's App Router, combined with next-intl and native Intl APIs, provides a production-grade foundation for multi-language applications that scale.

Planning to launch in multiple markets? <a href="/services/web-development">Our web development team</a> can architect your i18n strategy and handle the implementation. Get in touch to discuss your requirements.