
Guide for Using i18n in NextJS
Background
My current project is using NextJS App Router and SSG/ISR for internationalization.
This guide explains how to set up and use i18next in this environment.
Installation Requirements
Install the following packages in your project:
- i18next: Core internationalization framework
- react-i18next: React bindings
- i18next-browser-languagedetector: For detecting browser language
- i18next-resources-to-backend: For dynamically loading translation resources
- react-cookie: For managing language preference cookies
Installation command:
npm install i18next react-i18next i18next-browser-languagedetector i18next-resources-to-backend react-cookie
Configuration Steps
1. Path and Folder Setup
Set up a dynamic route folder [lng] in the app folder. All other paths (folders) will be stored under this folder.
app
βββ [lng]
β βββ All path folders
βββ i18n
βββ locales
βββ client.ts // Configuration and useTranslation function for client components
βββ index.ts // Configuration and useTranslation function for server components
βββ setting.ts
βββ lang.ts // Get current language from URL2. Create i18n Configuration Files
Create configuration files for client-side and server-side i18n setup. (The content of these files is omitted for brevity, but includes setup for i18next, language detection, and custom useTranslation hooks.)
3. Create Translation Files
Create translation files for each language under the app/i18n/locales/ directory, for example:
- app/i18n/locales/en/common.json
- app/i18n/locales/zh/common.json
4. Using Translations
In Client Components:
"use client";
import { useTranslation } from "../i18n/client";
import { CurrentLang } from "../i18n/lang";
export default function ClientComponent() {
const lang = CurrentLang(); // Get current language
const { t } = useTranslation(lang, "common");
return <h1>{t('welcome')}</h1>; // Use translation
}In Server Components:
import { useTranslation } from "../i18n";
export default async function ServerComponent({ params: { lng } }) {
const { t } = await useTranslation(lng, "common");
return <h1>{t('welcome')}</h1>; // Use translation
}Notes
- Ensure i18n settings are correctly configured in next.config.js.
- For dynamic routes, make sure to handle language settings correctly in the app/[lng] folder.
- To get the current language setting: It can be passed down from params, or use the CurrentLang() function if it can't be passed from params.
- Use different useTranslation import methods in Client Components and Server Components.
- The i18next configurations for client-side and server-side are slightly different, pay attention to distinguish their usage.
Code Snippets
client.ts
"use client";
import { useEffect, useState } from "react";
import i18next from "i18next";
import {
initReactI18next,
useTranslation as useTranslationOrg,
} from "react-i18next";
import { useCookies } from "react-cookie";
import resourcesToBackend from "i18next-resources-to-backend";
import LanguageDetector from "i18next-browser-languagedetector";
import { getOptions, languages, cookieName } from "./setting";
// Check if the code is running on the server side
const runsOnServerSide = typeof window === "undefined";
// Initialize i18next
i18next
.use(initReactI18next) // Passes i18n down to react-i18next
.use(LanguageDetector) // Use language detector
.use(
resourcesToBackend(
(lng: string, ns: string) => import(`./locales/${lng}/${ns}.json`)
)
) // Load translations dynamically
.init({
...getOptions(),
lng: undefined, // Let detect the language on client side
detection: {
order: ["path", "htmlTag", "cookie", "navigator"],
},
preload: runsOnServerSide ? languages : [], // Preload all languages on server side
});
// Custom useTranslation hook
export function useTranslation(lng?: string, ns?: string, options?: {}) {
const [cookies, setCookie] = useCookies([cookieName]);
const ret = useTranslationOrg(ns, options);
const { i18n } = ret;
if (runsOnServerSide && lng && i18n.resolvedLanguage !== lng) {
i18n.changeLanguage(lng);
} else {
// Client-side language handling
const [activeLng, setActiveLng] = useState(i18n.resolvedLanguage);
// Update active language when resolved language changes
useEffect(() => {
if (activeLng === i18n.resolvedLanguage) return;
setActiveLng(i18n.resolvedLanguage);
}, [activeLng, i18n.resolvedLanguage]);
// Change language when lng prop changes
useEffect(() => {
if (!lng || i18n.resolvedLanguage === lng) return;
i18n.changeLanguage(lng);
}, [lng, i18n]);
// Update cookie when language changes
useEffect(() => {
if (cookies.i18next === lng) return;
setCookie(cookieName, lng, { path: "/" });
}, [lng, cookies.i18next, setCookie]);
}
return { ...ret };
}index.ts
import { createInstance } from "i18next";
import resourcesToBackend from "i18next-resources-to-backend";
import { initReactI18next } from "react-i18next/initReactI18next";
import { getOptions } from "./setting"
// Initialize i18next instance
const initI18next = async (lng: string, ns: string) => {
const i18nInstance = createInstance();
await i18nInstance
.use(initReactI18next)
.use(
resourcesToBackend(
(lng: string, ns: string) => import(`./locales/${lng}/${ns}.json`)
)
)
.init(getOptions(lng, ns));
return i18nInstance;
};
// useTranslation hook for server components
export async function useTranslation(lng: string, ns: string, options = {}) {
const i18nextInstance = await initI18next(lng, ns);
return {
t: i18nextInstance.getFixedT(lng, Array.isArray(ns) ? ns[0] : ns),
i18n: i18nextInstance,
};
}setting.js
// Default language
export const fallbackLng = "en";
// Supported languages
export const languages = [fallbackLng, "zh-Hant"];
// Cookie name for storing language preference
export const cookieName = 'i18next'
// Default namespace
export const defaultNS = "translation";
// Function to get i18next options
export function getOptions(lng = fallbackLng, ns = defaultNS) {
return {
debug: true,
supportedLngs: languages,
fallbackLng,
lng,
fallbackNS: defaultNS,
defaultNS,
ns,
};
}Code Explanation
client.ts: This file sets up i18next for client-side use. It initializes i18next with various plugins, including language detection and dynamic resource loading. The custom useTranslation hook manages language changes and syncs the language preference with cookies.
index.ts: This file provides a server-side implementation of i18next. It creates a new i18next instance for each request, ensuring that server-side rendering uses the correct language.
setting.js: This file contains configuration settings for i18next, including supported languages, default language, and options for i18next initialization. These settings are used by both client-side and server-side implementations.