Skip to main content

Next.js Integration

Integrate LocaleLens with Next.js App Router or Pages Router

Next.js App Router — Minimal i18n Example
Recommended for new projects

This repository shows a minimal, server-first i18n setup for Next.js using LocaleLens. Translations are fetched server-side at runtime via a simple API call — no JSON files, no frameworks, no locale routing.

→ View the example on GitHub

Translations are fetched server-side using a LocaleLens API key. The key is never bundled into client-side code.

Why this approach?

  • Server-first by default
  • Works with caching and ISR
  • No client-side translation state
  • LocaleLens is the source of truth

Using the Pages Router? The guide below covers Pages Router integration with next-i18next.

Installation (Pages Router)

This guide uses next-i18next, the commonly used i18n library for Pages Router applications.

npm install next-i18next react-i18next i18next
Setup

1. Create next-i18next.config.js

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

2. Update next.config.js

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

module.exports = {
  i18n,
  // ... other config
}

3. Create API Route for Translations

Create pages/api/translations/[locale].ts:

// pages/api/translations/[locale].ts
import type { NextApiRequest, NextApiResponse } from 'next'

const API_URL = 'https://localelens.ai/api/v1'
const PROJECT_ID = process.env.LOCALELENS_PROJECT_ID
const API_KEY = process.env.LOCALELENS_API_KEY

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { locale } = req.query

  try {
    const response = await fetch(
      `${API_URL}/projects/${PROJECT_ID}/translations/${locale}`,
      {
        headers: {
          'Authorization': `Bearer ${API_KEY}`,
        },
      }
    )

    if (!response.ok) {
      throw new Error('Failed to fetch translations')
    }

    const translations = await response.json()
    res.setHeader('Cache-Control', 'public, s-maxage=3600, stale-while-revalidate=86400')
    res.status(200).json(translations)
  } catch (error) {
    res.status(500).json({ error: 'Failed to load translations' })
  }
}

⚠️ Security Warning: Never expose LOCALELENS_API_KEY client-side. This API route must remain server-only. The environment variable is only accessible in API routes and will not be bundled with your client code.

4. Create Custom Backend for i18next

Create lib/localelens-backend.ts:

// lib/localelens-backend.ts
import { BackendModule, ReadCallback } from 'i18next'

const LocaleLensBackend: BackendModule = {
  type: 'backend',
  init() {},
  read(language: string, namespace: string, callback: ReadCallback) {
    fetch(`/api/translations/${language}`)
      .then((res) => res.json())
      .then((data) => callback(null, data))
      .catch((error) => callback(error, null))
  },
}

export default LocaleLensBackend

5. Update _app.tsx

// pages/_app.tsx
import type { AppProps } from 'next/app'
import { appWithTranslation } from 'next-i18next'
import nextI18NextConfig from '../next-i18next.config'

function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}

export default appWithTranslation(MyApp, nextI18NextConfig)

6. Environment Variables

Add to .env.local:

LOCALELENS_PROJECT_ID=your_project_id
LOCALELENS_API_KEY=ll_your_api_key
Usage

Server-Side Props

// pages/index.tsx
import { GetStaticProps } from 'next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { useTranslation } from 'next-i18next'
import nextI18NextConfig from '../next-i18next.config'

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

  return (
    <div>
      <h1>{t('welcome')}</h1>
      <button>{t('save')}</button>
    </div>
  )
}

export const getStaticProps: GetStaticProps = async ({ locale }) => {
  return {
    props: {
      ...(await serverSideTranslations(
        locale ?? 'en',
        ['common'],
        nextI18NextConfig
      )),
    },
  }
}

Language Switching

import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'

export function LanguageSwitcher() {
  const router = useRouter()
  const { i18n } = useTranslation()

  const changeLanguage = (locale: string) => {
    router.push(router.pathname, router.asPath, { locale })
  }

  return (
    <select
      value={i18n.language}
      onChange={(e) => changeLanguage(e.target.value)}
    >
      <option value="en">English</option>
      <option value="es">Spanish</option>
      <option value="fr">French</option>
    </select>
  )
}
Best Practices
  • Keep API keys server-side only — see Production Guide for patterns
  • Use getStaticProps or getServerSideProps to load translations server-side
  • Use flat structure for simpler key access (common.save)
  • Enable caching headers in your API route for better performance
  • Use ISR (Incremental Static Regeneration) with revalidate for updated translations