Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
i18next-http-middleware
Advanced tools
i18next-http-middleware is a middleware to be used with Node.js web frameworks like express or Fastify and also for Deno.
The i18next-http-middleware package is a middleware for i18next, a popular internationalization framework. It allows you to add localization and internationalization capabilities to your Node.js HTTP servers, such as Express. This middleware helps in detecting user language, loading translations, and handling language changes dynamically.
Language Detection
This feature allows the middleware to detect the user's language based on various factors such as query parameters, cookies, headers, and more. The detected language is then used to serve the appropriate translations.
const express = require('express');
const i18next = require('i18next');
const i18nextMiddleware = require('i18next-http-middleware');
i18next.use(i18nextMiddleware.LanguageDetector).init({
// i18next configuration
});
const app = express();
app.use(i18nextMiddleware.handle(i18next));
app.get('/', (req, res) => {
res.send(req.t('welcome')); // 'welcome' key from translation files
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Translation Handling
This feature allows the middleware to handle translations by loading them from specified backend sources, such as file systems or databases. It ensures that the correct translations are served based on the detected language.
const express = require('express');
const i18next = require('i18next');
const i18nextMiddleware = require('i18next-http-middleware');
const Backend = require('i18next-fs-backend');
i18next
.use(Backend)
.use(i18nextMiddleware.LanguageDetector)
.init({
backend: {
loadPath: './locales/{{lng}}/{{ns}}.json'
},
fallbackLng: 'en',
preload: ['en', 'de']
});
const app = express();
app.use(i18nextMiddleware.handle(i18next));
app.get('/', (req, res) => {
res.send(req.t('welcome')); // 'welcome' key from translation files
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Middleware Integration
This feature demonstrates how to integrate the i18next middleware into an Express application. It shows the basic setup required to use i18next for handling translations in your HTTP server.
const express = require('express');
const i18next = require('i18next');
const i18nextMiddleware = require('i18next-http-middleware');
i18next.init({
// i18next configuration
});
const app = express();
app.use(i18nextMiddleware.handle(i18next));
app.get('/', (req, res) => {
res.send(req.t('welcome')); // 'welcome' key from translation files
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
express-i18n is another middleware for internationalization in Express applications. It provides similar functionalities such as language detection and translation handling. However, it is less feature-rich compared to i18next and its ecosystem.
Polyglot is a lightweight internationalization library that provides a simple way to manage translations in JavaScript applications. While it is not a middleware, it can be used in conjunction with Express to handle translations. It is simpler but less powerful than i18next.
node-polyglot is the Node.js version of Polyglot.js. It offers basic internationalization features and can be used in server-side applications. It is more lightweight and less complex compared to i18next.
This is a middleware to be used with Node.js web frameworks like express or Fastify and also for Deno.
It's based on the deprecated i18next-express-middleware and can be used as a drop-in replacement. It's not bound to a specific http framework anymore.
To get started with server side internationalization, you may also have a look at this blog post also using using i18next-http-middleware.
# npm package
$ npm install i18next-http-middleware
var i18next = require('i18next')
var middleware = require('i18next-http-middleware')
var express = require('express')
i18next.use(middleware.LanguageDetector).init({
preload: ['en', 'de', 'it'],
...otherOptions
})
var app = express()
app.use(
middleware.handle(i18next, {
ignoreRoutes: ['/foo'], // or function(req, res, options, i18next) { /* return true to ignore */ }
removeLngFromUrl: false // removes the language from the url when language detected in path
})
)
// in your request handler
app.get('myRoute', (req, res) => {
var lng = req.language // 'de-CH'
var lngs = req.languages // ['de-CH', 'de', 'en']
req.i18n.changeLanguage('en') // will not load that!!! assert it was preloaded
var exists = req.i18n.exists('myKey')
var translation = req.t('myKey')
})
// in your views, eg. in pug (ex. jade)
div = t('myKey')
var i18next = require('i18next')
var middleware = require('i18next-http-middleware')
var fastify = require('fastify')
i18next.use(middleware.LanguageDetector).init({
preload: ['en', 'de', 'it'],
...otherOptions
})
var app = fastify()
app.register(i18nextMiddleware.plugin, {
i18next,
ignoreRoutes: ['/foo'] // or function(req, res, options, i18next) { /* return true to ignore */ }
})
// or
// app.addHook('preHandler', i18nextMiddleware.handle(i18next, {
// ignoreRoutes: ['/foo'] // or function(req, res, options, i18next) { /* return true to ignore */ }
// }))
// in your request handler
app.get('myRoute', (request, reply) => {
var lng = request.language // 'de-CH'
var lngs = v.languages // ['de-CH', 'de', 'en']
request.i18n.changeLanguage('en') // will not load that!!! assert it was preloaded
var exists = request.i18n.exists('myKey')
var translation = request.t('myKey')
})
const i18next = require('i18next')
const middleware = require('i18next-http-middleware')
const Hapi = require('@hapi/hapi')
i18next.use(middleware.LanguageDetector).init({
preload: ['en', 'de', 'it'],
...otherOptions
})
const server = Hapi.server({
port: port,
host: '0.0.0.0',
await server.register({
plugin: i18nextMiddleware.hapiPlugin,
options: {
i18next,
ignoreRoutes: ['/foo'] // or function(req, res, options, i18next) { /* return true to ignore
}
})
// in your request handler
server.route({
method: 'GET',
path: '/myRoute',
handler: (request, h) => {
var lng = request.language // 'de-CH'
var lngs = v.languages // ['de-CH', 'de', 'en']
request.i18n.changeLanguage('en') // will not load that!!! assert it was preloaded
var exists = request.i18n.exists('myKey')
var translation = request.t('myKey')
}
})
var i18next = require('i18next')
var middleware = require('i18next-http-middleware')
const Koa = require('koa')
const router = require('@koa/router')()
i18next.use(middleware.LanguageDetector).init({
preload: ['en', 'de', 'it'],
...otherOptions
})
var app = new Koa()
app.use(i18nextMiddleware.koaPlugin(i18next, {
ignoreRoutes: ['/foo'] // or function(req, res, options, i18next) { /* return true to ignore */ }
}))
// in your request handler
router.get('/myRoute', ctx => {
ctx.body = JSON.stringify({
'ctx.language': ctx.language,
'ctx.i18n.language': ctx.i18n.language,
'ctx.i18n.languages': ctx.i18n.languages,
'ctx.i18n.languages[0]': ctx.i18n.languages[0],
'ctx.t("home.title")': ctx.t('home.title')
}, null, 2)
})
import i18next from 'https://deno.land/x/i18next/index.js'
import Backend from 'https://cdn.jsdelivr.net/gh/i18next/i18next-fs-backend/index.js'
import i18nextMiddleware from 'https://deno.land/x/i18next_http_middleware/index.js'
import { Application } from 'https://deno.land/x/abc/mod.ts'
import { config } from 'https://deno.land/x/dotenv/dotenv.ts'
i18next
.use(Backend)
.use(i18nextMiddleware.LanguageDetector)
.init({
// debug: true,
backend: {
// eslint-disable-next-line no-path-concat
loadPath: 'locales/{{lng}}/{{ns}}.json',
// eslint-disable-next-line no-path-concat
addPath: 'locales/{{lng}}/{{ns}}.missing.json'
},
fallbackLng: 'en',
preload: ['en', 'de']
})
const port = config.PORT || 8080
const app = new Application()
const handle = i18nextMiddleware.handle(i18next)
app.use((next) => (c) => {
handle(c.request, c.response, () => {})
return next(c)
})
app.get('/', (c) => c.request.t('home.title'))
await app.start({ port })
import i18next from 'https://deno.land/x/i18next/index.js'
import Backend from 'https://cdn.jsdelivr.net/gh/i18next/i18next-fs-backend/index.js'
import i18nextMiddleware from 'https://deno.land/x/i18next_http_middleware/index.js'
import { createApp } from 'https://servestjs.org/@v1.0.0-rc2/mod.ts'
import { config } from 'https://deno.land/x/dotenv/dotenv.ts'
i18next
.use(Backend)
.use(i18nextMiddleware.LanguageDetector)
.init({
// debug: true,
backend: {
// eslint-disable-next-line no-path-concat
loadPath: 'locales/{{lng}}/{{ns}}.json',
// eslint-disable-next-line no-path-concat
addPath: 'locales/{{lng}}/{{ns}}.missing.json'
},
fallbackLng: 'en',
preload: ['en', 'de']
})
const port = config.PORT || 8080
const app = createApp()
app.use(i18nextMiddleware.handle(i18next))
app.get('/', async (req) => {
await req.respond({
status: 200,
headers: new Headers({
'content-type': 'text/plain'
}),
body: req.t('home.title')
})
})
await app.listen({ port })
// missing keys make sure the body is parsed (i.e. with [body-parser](https://github.com/expressjs/body-parser#bodyparserjsonoptions))
app.post('/locales/add/:lng/:ns', middleware.missingKeyHandler(i18next))
// addPath for client: http://localhost:8080/locales/add/{{lng}}/{{ns}}
// multiload backend route
app.get('/locales/resources.json', middleware.getResourcesHandler(i18next))
// can be used like:
// GET /locales/resources.json
// GET /locales/resources.json?lng=en
// GET /locales/resources.json?lng=en&ns=translation
// serve translations:
app.use('/locales', express.static('locales'))
// GET /locales/en/translation.json
// loadPath for client: http://localhost:8080/locales/{{lng}}/{{ns}}.json
// or instead of static
app.get('/locales/:lng/:ns', middleware.getResourcesHandler(i18next))
// GET /locales/en/translation
// loadPath for client: http://localhost:8080/locales/{{lng}}/{{ns}}
app.get('/locales/:lng/:ns', middleware.getResourcesHandler(i18next, {
maxAge: 60 * 60 * 24 * 30, // adds appropriate cache header if cache option is passed or NODE_ENV === 'production', defaults to 30 days
cache: true // defaults to false
}))
You can add your routes directly to the express app
var express = require('express'),
app = express(),
i18next = require('i18next'),
FilesystemBackend = require('i18next-fs-backend'),
i18nextMiddleware = require('i18next-http-middleware'),
port = 3000
i18next
.use(i18nextMiddleware.LanguageDetector)
.use(FilesystemBackend)
.init({ preload: ['en', 'de', 'it'], ...otherOptions }, () => {
i18nextMiddleware.addRoute(
i18next,
'/:lng/key-to-translate',
['en', 'de', 'it'],
app,
'get',
(req, res) => {
//endpoint function
}
)
})
app.use(i18nextMiddleware.handle(i18next))
app.listen(port, () => {
console.log('Server listening on port', port)
})
or to an express router
var express = require('express'),
app = express(),
i18next = require('i18next'),
FilesystemBackend = require('i18next-fs-backend'),
i18nextMiddleware = require('i18next-http-middleware'),
router = require('express').Router(),
port = 3000
i18next
.use(i18nextMiddleware.LanguageDetector)
.use(FilesystemBackend)
.init({ preload: ['en', 'de', 'it'], ...otherOptions }, () => {
i18nextMiddleware.addRoute(
i18next,
'/:lng/key-to-translate',
['en', 'de', 'it'],
router,
'get',
(req, res) => {
//endpoint function
}
)
app.use('/', router)
})
app.use(i18nextMiddleware.handle(i18next))
app.listen(port, () => {
console.log('Server listening on port', port)
})
Define your own functions to handle your custom request or response
middleware.handle(i18next, {
getPath: (req) => req.path,
getUrl: (req) => req.url,
setUrl: (req, url) => (req.url = url),
getQuery: (req) => req.query,
getParams: (req) => req.params,
getBody: (req) => req.body,
setHeader: (res, name, value) => res.setHeader(name, value),
setContentType: (res, type) => res.contentType(type),
setStatus: (res, code) => res.status(code),
send: (res, body) => res.send(body)
})
Detects user language from current request. Comes with support for:
Based on the i18next language detection handling: https://www.i18next.com/misc/creating-own-plugins#languagedetector
Wiring up:
var i18next = require('i18next')
var middleware = require('i18next-http-middleware')
i18next.use(middleware.LanguageDetector).init(i18nextOptions)
As with all modules you can either pass the constructor function (class) to the i18next.use or a concrete instance.
{
// order and from where user language should be detected
order: [/*'path', 'session', */ 'querystring', 'cookie', 'header'],
// keys or params to lookup language from
lookupQuerystring: 'lng',
lookupCookie: 'i18next',
lookupHeader: 'accept-language',
lookupHeaderRegex: /(([a-z]{2})-?([A-Z]{2})?)\s*;?\s*(q=([0-9.]+))?/gi,
lookupSession: 'lng',
lookupPath: 'lng',
lookupFromPathIndex: 0,
// cache user language, you can define if an how the detected language should be "saved" => 'cookie' and/or 'session'
caches: false, // ['cookie']
ignoreCase: true, // ignore case of detected language
// optional expire and domain for set cookie
cookieExpirationDate: new Date(),
cookieDomain: 'myDomain',
cookiePath: '/my/path',
cookieSecure: true, // if need secure cookie
cookieSameSite: 'strict', // 'strict', 'lax' or 'none'
// optional conversion function used to modify the detected language code
convertDetectedLanguage: 'Iso15897',
convertDetectedLanguage: (lng) => lng.replace('-', '_')
}
Options can be passed in:
preferred - by setting options.detection in i18next.init:
var i18next = require('i18next')
var middleware = require('i18next-http-middleware')
i18next.use(middleware.LanguageDetector).init({
detection: options
})
on construction:
var middleware = require('i18next-http-middleware')
var lngDetector = new middleware.LanguageDetector(null, options)
via calling init:
var middleware = require('i18next-http-middleware')
var lngDetector = new middleware.LanguageDetector()
lngDetector.init(options)
module.exports = {
name: 'myDetectorsName',
lookup: function (req, res, options) {
// options -> are passed in options
return 'en'
},
cacheUserLanguage: function (req, res, lng, options) {
// options -> are passed in options
// lng -> current language, will be called after init and on changeLanguage
// store it
}
}
var i18next = require('i18next')
var middleware = require('i18next-http-middleware')
var lngDetector = new middleware.LanguageDetector()
lngDetector.addDetector(myDetector)
i18next.use(lngDetector).init({
detection: options
})
Don't forget: You have to add the name of your detector (myDetectorsName
in this case) to the order
array in your options
object. Without that, your detector won't be used. See the Detector Options section for more.
FAQs
i18next-http-middleware is a middleware to be used with Node.js web frameworks like express or Fastify and also for Deno.
The npm package i18next-http-middleware receives a total of 113,618 weekly downloads. As such, i18next-http-middleware popularity was classified as popular.
We found that i18next-http-middleware demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.