expressi18nextexpress-handlebarsnext-i18next

Why is i18next-http-middleware defaulting back to fallback language


I am setting up i18next middleware in my express handlebars app, i18next works for any translation I put as the fallbackLng, the problem is none of the i18next middleware features are working like for example getting the lng param from url

Here is my i18next init setup

import { filterNodesByGroups, getNodeLabel } from "@ory/integrations/ui"
import express, { Request, Response } from "express"
import handlebars from "express-handlebars"
import * as fs from "fs"
import * as https from "https"

import { middleware as middlewareLogger } from "./pkg/logger"
import { toUiNodePartial } from "./pkg/ui"
import {
  register404Route,
  register500Route,
  registerErrorRoute,
  registerHealthRoute,
  registerLoginRoute,
  registerRecoveryRoute,
  registerRegistrationRoute,
  registerSettingsRoute,
  registerStaticRoutes,
  registerVerificationRoute,
  registerWelcomeRoute,
} from "./routes"

const i18next = require('i18next')
const i18nextMiddleware = require('i18next-http-middleware')
const Backend = require('i18next-fs-backend')
const HandlebarsI18n = require("handlebars-i18n");

const app = express()

console.log("were here")
i18next
  .use(Backend)
  // .use(languageDetector)
  .use(i18nextMiddleware.LanguageDetector)
  .init({
    debug: true,
    backend: {
      // eslint-disable-next-line no-path-concat
      loadPath: __dirname + '/i18n/{{lng}}/{{ns}}.json'
    },
    fallbackLng: 'en',
    preload: ['en', 'fr'],
    // nonExplicitSupportedLngs: true,
    // supportedLngs: ['en', 'fr'],
    load: 'languageOnly'
  })

app.use(middlewareLogger)
app.set("view engine", "hbs")
app.use(i18nextMiddleware.handle(i18next))

HandlebarsI18n.init();

app.engine(
  "hbs",
  handlebars({
    extname: "hbs",
    layoutsDir: `${__dirname}/../views/layouts/`,
    partialsDir: `${__dirname}/../views/partials/`,
    defaultLayout: "main",
    helpers: {
      ...require("handlebars-helpers")(),
      jsonPretty: (context: any) => JSON.stringify(context, null, 2),
      onlyNodes: filterNodesByGroups,
      toUiNodePartial,
      getNodeLabel: getNodeLabel,
    },
  }),
)

registerStaticRoutes(app)
registerHealthRoute(app)
registerLoginRoute(app)
registerRecoveryRoute(app)
registerRegistrationRoute(app)
registerSettingsRoute(app)
registerVerificationRoute(app)
registerWelcomeRoute(app)
registerErrorRoute(app)

app.get("/", (req: Request, res: Response) => {
  res.redirect("welcome", 303)
})

register404Route(app)
register500Route(app)

const port = Number(process.env.PORT) || 3000

let listener = (proto: "http" | "https") => () => {
  console.log(`Listening on ${proto}://0.0.0.0:${port}`)
}

if (process.env.TLS_CERT_PATH?.length && process.env.TLS_KEY_PATH?.length) {
  const options = {
    cert: fs.readFileSync(process.env.TLS_CERT_PATH),
    key: fs.readFileSync(process.env.TLS_KEY_PATH),
  }

  https.createServer(options, app).listen(port, listener("https"))
} else {
  app.listen(port, listener("http"))
}

Here is the new package example

The deprecated package has an example that is close to the new package here

What could be wrong with my initialization of i18next-http-middleware?

Edit: If I change the language in the url header from en to fr in the debug output I get several language changes between requests for example:

i18next: languageChanged en
i18next: languageChanged fr
{"level":"info","message":"HTTP GET ... "}
i18next: languageChanged en
i18next: languageChanged en-US
{"level":"info","message":"HTTP GET ... "}
i18next: languageChanged en
i18next: languageChanged en-US
{"level":"info","message":"HTTP GET ... "}
i18next: languageChanged en
i18next: languageChanged en-US
i18next: languageChanged en
i18next: languageChanged en-US
i18next: languageChanged en
i18next: languageChanged en-US
i18next: languageChanged en
i18next: languageChanged en-US

Solution

  • I think this is a problem of integration between https://github.com/Aller-Couleur/handlebars-i18n/ and https://github.com/i18next/i18next-http-middleware

    i18next-http-middleware sets the language on a clone of i18next instance that is reported on request (req.i18n), while handlebars-i18n use the i18next global instance, and I don't see anyway it can be changed.

    I opened an issue on handlebars-i18n: https://github.com/Aller-Couleur/handlebars-i18n/issues/43 as it could be an easy fix on their side.