Implementing Internationalization

1. Installing i18n Package

Example: Install

npm i i18next i18next-fs-backend i18next-http-middleware accept-language-parser

2. Configuring i18n Middleware

Example: i18next setup

import i18next from "i18next";
import Backend from "i18next-fs-backend";
import middleware from "i18next-http-middleware";

await i18next.use(Backend).use(middleware.LanguageDetector).init({
  fallbackLng: "en",
  preload: ["en","es","fr","ja"],
  backend: { loadPath: "./locales/{{lng}}/{{ns}}.json" }
});
app.use(middleware.handle(i18next));

3. Setting Supported Locales

SettingEffect
supportedLngsAllowlist locales
fallbackLngDefault if detection fails
preloadLoad up front
nonExplicitSupportedLngsTreat en-US as en

4. Detecting User Locale

Example: Manual detection

import { pick } from "accept-language-parser";
const supported = ["en","es","fr","ja"];
app.use((req, res, next) => {
  req.locale = pick(supported, req.get("Accept-Language") || "") || "en";
  next();
});

5. Creating Translation Files

Example: locales/en/translation.json

{
  "welcome": "Welcome, {{name}}!",
  "items": "{{count}} item",
  "items_other": "{{count}} items"
}

6. Using Translation Functions

Example: req.t()

app.get("/welcome", (req, res) => {
  res.json({ message: req.t("welcome", { name: req.user?.name || "guest" }) });
});

7. Pluralization Handling

Example: Plural via count

req.t("items", { count: 1 });   // "1 item"
req.t("items", { count: 5 });   // "5 items"

8. Setting Default Locale

Example: fallbackLng

i18next.init({ fallbackLng: "en", supportedLngs: ["en","es","fr","ja"] });

9. Using URL-Based Locale

Example: /:locale prefix

app.use("/:locale", (req, res, next) => {
  if (supported.includes(req.params.locale)) {
    req.i18n.changeLanguage(req.params.locale);
  }
  next();
});

Example: Persist preference

app.post("/locale", (req, res) => {
  if (!supported.includes(req.body.locale)) return res.status(400).end();
  res.cookie("lng", req.body.locale, { maxAge: 365 * 86400_000, httpOnly: true, sameSite: "lax" });
  res.sendStatus(204);
});