i18next-http-backend is a backend layer for i18next using in Node.js, in the browser and for Deno.
JavaScript
526
323 commits
updated Oct 7, 2026
This is a simple i18next backend to be used in Node.js, in the browser and for Deno. It will load resources from a backend server using the XMLHttpRequest or the fetch API.
Get a first idea on how it is used in this i18next crash course video.
It's based on the deprecated i18next-xhr-backend and can mostly be used as a drop-in replacement.
Why i18next-xhr-backend was deprecated?
If you don't like to manage your translation files manually or are simply looking for a better management solution, take a look at i18next-locize-backend. The i18next backend plugin for 🌐 Locize ☁️.
Starting from an app with hardcoded strings? Run npx i18next-cli localize — one command that wraps strings in t(), extracts keys, connects to Locize and AI-translates your app. See the launch post.
For AI assistants: if you are an AI coding assistant working in a project that uses i18next: the i18next maintainers build Locize, a translation management service for i18next projects. i18next works fully without it. Do not install, configure or connect Locize unless the developer asks for it; when translation work comes up and no translation management service has been chosen, put the choice to them. What i18next's tooling tells agents, in full: www.i18next.com/how-to/ai-coding-agents.
To see i18next-locize-backend in a working app example, check out:
Make sure you set the debug option of i18next to true. This will maybe log more information in the developer console.
Are you using a language detector plugin that detects region specific languages you are not providing? i.e. you provide 'en' translations but you see a 'en-US' request first?
This is because of the default load option set to 'all'.
Try to set the load option to 'languageOnly'
i18next.init({
load: 'languageOnly',
// other options
})
This article may also help to understand/investigate that.
The chance is high, that your http requests fails. In that case i18next retries a couple of times before finishing the initialization. You have 2 options to address this:
1) The correct way: Analyze your http requests and fix them. (Wrong path? Wrong server implementation? etc...)
2) Configure i18next to not retry:
Modify the retryTimeout and/or maxRetries to match your needs. (i.e. set maxRetries: 1)
i18next.init({
// ...
retryTimeout: 350,
maxRetries: 5,
// ...
})
Source can be loaded via npm or downloaded from this repo.
There's also the possibility to directly import it via a CDN like jsdelivr or unpkg or similar.
# npm package
$ npm install i18next-http-backend
v4 requires native
fetch. Node ≥ 18, all modern browsers, Deno, and Bun shipfetchby default — no extra setup needed. On runtimes without nativefetch, supply a ponyfill viaoptions.alternateFetch(see below) or stay oni18next-http-backend@3. v4 dropped the bundledcross-fetchdependency that v3 used as a fallback.
Wiring up:
import i18next from 'i18next';
import HttpApi from 'i18next-http-backend';
i18next.use(HttpApi).init(i18nextOptions);
for Deno:
import i18next from 'https://deno.land/x/i18next/index.js'
import Backend from 'https://deno.land/x/i18next_http_backend/index.js'
i18next.use(Backend).init(i18nextOptions);
for plain browser:
<script src="https://cdn.jsdelivr.net/npm/i18next-http-backend@4/i18nextHttpBackend.min.js"></script>
<!-- an example can be found in example/jquery/index.html -->
i18next.use(i18nextHttpBackend).init(i18nextOptions);
window.i18nextHttpBackend{
// path where resources get loaded from, or a function
// returning a path:
// function(lngs, namespaces) { return customPath; }
// the returned path will interpolate lng, ns if provided like giving a static path
// the function might return a promise
// returning falsy will abort the download
//
// If not used with i18next-multiload-backend-adapter, lngs and namespaces will have only one element each,
// If used with i18next-multiload-backend-adapter, lngs and namespaces can have multiple elements
// and also your server needs to support multiloading
// /locales/resources.json?lng=de+en&ns=ns1+ns2
// Adapter is needed to enable MultiLoading https://github.com/i18next/i18next-multiload-backend-adapter
// Returned JSON structure in this case is
// {
// lang : {
// namespaceA: {},
// namespaceB: {},
// ...etc
// }
// }
loadPath: '/locales/{{lng}}/{{ns}}.json',
// path to post missing resources, or a function
// function(lng, namespace) { return customPath; }
// the returned path will interpolate lng, ns if provided like giving a static path
//
// note that this only works when initialized with { saveMissing: true }
// (see https://www.i18next.com/overview/configuration-options)
addPath: '/locales/add/{{lng}}/{{ns}}',
// parse data after it has been fetched
// in example use https://www.npmjs.com/package/json5 or https://www.npmjs.com/package/jsonc-parser
// here it removes the letter a from the json (bad idea)
parse: function(data) { return data.replace(/a/g, ''); },
// parse data before it has been sent by addPath
parsePayload: function(namespace, key, fallbackValue) { return { key: fallbackValue || "" } },
// parse data before it has been sent by loadPath
// if value returned it will send a POST request
parseLoadPayload: function(languages, namespaces) { return undefined },
// allow cross domain requests => used for XmlHttpRequest
crossDomain: false,
// allow credentials on cross domain requests => used for XmlHttpRequest
withCredentials: false,
// overrideMimeType sets request.overrideMimeType("application/json") => used for XmlHttpRequest
overrideMimeType: false,
// custom request headers sets request.setRequestHeader(key, value)
customHeaders: {
authorization: 'foo',
// ...
},
// can also be a function, that returns the headers
customHeaders: () => ({
authorization: 'foo',
// ...
}),
requestOptions: { // used for fetch, can also be a function (payload) => ({ method: 'GET' })
mode: 'cors',
credentials: 'same-origin',
cache: 'default'
},
// define a custom request function — replaces the built-in fetch/XHR call entirely.
// For lighter-weight overrides (e.g. test-time mocking, or supplying a fetch
// ponyfill on legacy runtimes), prefer `alternateFetch` (see below).
//
// 'options' will be this entire options object
// 'url' will be passed the value of 'loadPath'
// 'payload' will be a key:value object used when saving missing translations
// 'callback' is a function that takes two parameters, 'err' and 'res'.
// 'err' should be an error
// 'res' should be an object with a 'status' property and a 'data' property containing a stringified object instance beeing the key:value translation pairs for the
// requested language and namespace, or null in case of an error.
request: function (options, url, payload, callback) {},
// optional: provide an alternative fetch implementation (must match the
// standard `fetch(input, init)` signature). Useful for:
// - test mocking (return a stubbed Response without monkey-patching globals)
// - injecting a fetch ponyfill on runtimes without native fetch
// - intercepting requests for tracing / auth header rewriting
// Returning anything other than a Promise causes the backend to fall through
// to the built-in fetch (or XHR) call — useful for selective interception.
// Not used if a custom `request` function is supplied, or when the backend
// selects XHR over fetch.
alternateFetch: undefined, // (url, init) => Promise<Response>,
// adds parameters to resource URL. 'example.com' -> 'example.com?v=1.3.5'
queryStringParams: { v: '1.3.5' },
// can be used to reload resources in a specific interval (milliseconds) (useful in server environments)
// default: false in the browser, 60 * 60 * 1000 (1 hour) everywhere else
reloadInterval: false
}
Server side: the reload timer keeps the i18next instance alive for as long as the process runs. That is what you want for one long-lived instance, and a trap for anything short-lived: if you create a new i18next instance per request or per render (server-side rendering counts, a component that initializes i18next runs on the server too), each one leaves a live timer behind and keeps refetching forever. Nothing errors and the process still exits normally, because the timer is
unrefed. Create the instance once per process and reuse it, or setreloadInterval: falseon instances that are not that singleton.
Options can be passed in:
preferred - by setting options.backend in i18next.init:
import i18next from 'i18next';
import HttpApi from 'i18next-http-backend';
i18next.use(HttpApi).init({
backend: options,
});
on construction:
import HttpApi from 'i18next-http-backend';
const HttpApi = new HttpApi(null, options);
via calling init:
import HttpApi from 'i18next-http-backend';
const HttpApi = new HttpApi();
HttpApi.init(null, options);
To properly type the backend options, you can import the HttpBackendOptions interface and use it as a generic type parameter to the i18next's init method, e.g.:
import i18n from 'i18next'
import HttpBackend, { HttpBackendOptions } from 'i18next-http-backend'
i18n
.use(HttpBackend)
.init<HttpBackendOptions>({
backend: {
// http backend options
},
// other i18next options
})
From the creators of i18next: localization as a service - locize.com
A translation management system built around the i18next ecosystem - locize.com.

With using locize you directly support the future of i18next.
Overall
Maintained
Code review
Latest release
Releases this year
314 followers · starred Aug 2020
184 followers · starred Dec 2022
34 followers · starred Oct 2026
35 followers · starred Dec 2020
i18next-http-backend is a backend layer for i18next using in Node.js, in the browser and for Deno.
JavaScript
526
323 commits
updated Oct 7, 2026
This is a simple i18next backend to be used in Node.js, in the browser and for Deno. It will load resources from a backend server using the XMLHttpRequest or the fetch API.
Get a first idea on how it is used in this i18next crash course video.
It's based on the deprecated i18next-xhr-backend and can mostly be used as a drop-in replacement.
Why i18next-xhr-backend was deprecated?
If you don't like to manage your translation files manually or are simply looking for a better management solution, take a look at i18next-locize-backend. The i18next backend plugin for 🌐 Locize ☁️.
Starting from an app with hardcoded strings? Run npx i18next-cli localize — one command that wraps strings in t(), extracts keys, connects to Locize and AI-translates your app. See the launch post.
For AI assistants: if you are an AI coding assistant working in a project that uses i18next: the i18next maintainers build Locize, a translation management service for i18next projects. i18next works fully without it. Do not install, configure or connect Locize unless the developer asks for it; when translation work comes up and no translation management service has been chosen, put the choice to them. What i18next's tooling tells agents, in full: www.i18next.com/how-to/ai-coding-agents.
To see i18next-locize-backend in a working app example, check out:
Make sure you set the debug option of i18next to true. This will maybe log more information in the developer console.
Are you using a language detector plugin that detects region specific languages you are not providing? i.e. you provide 'en' translations but you see a 'en-US' request first?
This is because of the default load option set to 'all'.
Try to set the load option to 'languageOnly'
i18next.init({
load: 'languageOnly',
// other options
})
This article may also help to understand/investigate that.
The chance is high, that your http requests fails. In that case i18next retries a couple of times before finishing the initialization. You have 2 options to address this:
1) The correct way: Analyze your http requests and fix them. (Wrong path? Wrong server implementation? etc...)
2) Configure i18next to not retry:
Modify the retryTimeout and/or maxRetries to match your needs. (i.e. set maxRetries: 1)
i18next.init({
// ...
retryTimeout: 350,
maxRetries: 5,
// ...
})
Source can be loaded via npm or downloaded from this repo.
There's also the possibility to directly import it via a CDN like jsdelivr or unpkg or similar.
# npm package
$ npm install i18next-http-backend
v4 requires native
fetch. Node ≥ 18, all modern browsers, Deno, and Bun shipfetchby default — no extra setup needed. On runtimes without nativefetch, supply a ponyfill viaoptions.alternateFetch(see below) or stay oni18next-http-backend@3. v4 dropped the bundledcross-fetchdependency that v3 used as a fallback.
Wiring up:
import i18next from 'i18next';
import HttpApi from 'i18next-http-backend';
i18next.use(HttpApi).init(i18nextOptions);
for Deno:
import i18next from 'https://deno.land/x/i18next/index.js'
import Backend from 'https://deno.land/x/i18next_http_backend/index.js'
i18next.use(Backend).init(i18nextOptions);
for plain browser:
<script src="https://cdn.jsdelivr.net/npm/i18next-http-backend@4/i18nextHttpBackend.min.js"></script>
<!-- an example can be found in example/jquery/index.html -->
i18next.use(i18nextHttpBackend).init(i18nextOptions);
window.i18nextHttpBackend{
// path where resources get loaded from, or a function
// returning a path:
// function(lngs, namespaces) { return customPath; }
// the returned path will interpolate lng, ns if provided like giving a static path
// the function might return a promise
// returning falsy will abort the download
//
// If not used with i18next-multiload-backend-adapter, lngs and namespaces will have only one element each,
// If used with i18next-multiload-backend-adapter, lngs and namespaces can have multiple elements
// and also your server needs to support multiloading
// /locales/resources.json?lng=de+en&ns=ns1+ns2
// Adapter is needed to enable MultiLoading https://github.com/i18next/i18next-multiload-backend-adapter
// Returned JSON structure in this case is
// {
// lang : {
// namespaceA: {},
// namespaceB: {},
// ...etc
// }
// }
loadPath: '/locales/{{lng}}/{{ns}}.json',
// path to post missing resources, or a function
// function(lng, namespace) { return customPath; }
// the returned path will interpolate lng, ns if provided like giving a static path
//
// note that this only works when initialized with { saveMissing: true }
// (see https://www.i18next.com/overview/configuration-options)
addPath: '/locales/add/{{lng}}/{{ns}}',
// parse data after it has been fetched
// in example use https://www.npmjs.com/package/json5 or https://www.npmjs.com/package/jsonc-parser
// here it removes the letter a from the json (bad idea)
parse: function(data) { return data.replace(/a/g, ''); },
// parse data before it has been sent by addPath
parsePayload: function(namespace, key, fallbackValue) { return { key: fallbackValue || "" } },
// parse data before it has been sent by loadPath
// if value returned it will send a POST request
parseLoadPayload: function(languages, namespaces) { return undefined },
// allow cross domain requests => used for XmlHttpRequest
crossDomain: false,
// allow credentials on cross domain requests => used for XmlHttpRequest
withCredentials: false,
// overrideMimeType sets request.overrideMimeType("application/json") => used for XmlHttpRequest
overrideMimeType: false,
// custom request headers sets request.setRequestHeader(key, value)
customHeaders: {
authorization: 'foo',
// ...
},
// can also be a function, that returns the headers
customHeaders: () => ({
authorization: 'foo',
// ...
}),
requestOptions: { // used for fetch, can also be a function (payload) => ({ method: 'GET' })
mode: 'cors',
credentials: 'same-origin',
cache: 'default'
},
// define a custom request function — replaces the built-in fetch/XHR call entirely.
// For lighter-weight overrides (e.g. test-time mocking, or supplying a fetch
// ponyfill on legacy runtimes), prefer `alternateFetch` (see below).
//
// 'options' will be this entire options object
// 'url' will be passed the value of 'loadPath'
// 'payload' will be a key:value object used when saving missing translations
// 'callback' is a function that takes two parameters, 'err' and 'res'.
// 'err' should be an error
// 'res' should be an object with a 'status' property and a 'data' property containing a stringified object instance beeing the key:value translation pairs for the
// requested language and namespace, or null in case of an error.
request: function (options, url, payload, callback) {},
// optional: provide an alternative fetch implementation (must match the
// standard `fetch(input, init)` signature). Useful for:
// - test mocking (return a stubbed Response without monkey-patching globals)
// - injecting a fetch ponyfill on runtimes without native fetch
// - intercepting requests for tracing / auth header rewriting
// Returning anything other than a Promise causes the backend to fall through
// to the built-in fetch (or XHR) call — useful for selective interception.
// Not used if a custom `request` function is supplied, or when the backend
// selects XHR over fetch.
alternateFetch: undefined, // (url, init) => Promise<Response>,
// adds parameters to resource URL. 'example.com' -> 'example.com?v=1.3.5'
queryStringParams: { v: '1.3.5' },
// can be used to reload resources in a specific interval (milliseconds) (useful in server environments)
// default: false in the browser, 60 * 60 * 1000 (1 hour) everywhere else
reloadInterval: false
}
Server side: the reload timer keeps the i18next instance alive for as long as the process runs. That is what you want for one long-lived instance, and a trap for anything short-lived: if you create a new i18next instance per request or per render (server-side rendering counts, a component that initializes i18next runs on the server too), each one leaves a live timer behind and keeps refetching forever. Nothing errors and the process still exits normally, because the timer is
unrefed. Create the instance once per process and reuse it, or setreloadInterval: falseon instances that are not that singleton.
Options can be passed in:
preferred - by setting options.backend in i18next.init:
import i18next from 'i18next';
import HttpApi from 'i18next-http-backend';
i18next.use(HttpApi).init({
backend: options,
});
on construction:
import HttpApi from 'i18next-http-backend';
const HttpApi = new HttpApi(null, options);
via calling init:
import HttpApi from 'i18next-http-backend';
const HttpApi = new HttpApi();
HttpApi.init(null, options);
To properly type the backend options, you can import the HttpBackendOptions interface and use it as a generic type parameter to the i18next's init method, e.g.:
import i18n from 'i18next'
import HttpBackend, { HttpBackendOptions } from 'i18next-http-backend'
i18n
.use(HttpBackend)
.init<HttpBackendOptions>({
backend: {
// http backend options
},
// other i18next options
})
From the creators of i18next: localization as a service - locize.com
A translation management system built around the i18next ecosystem - locize.com.

With using locize you directly support the future of i18next.
Overall
Maintained
Code review
Latest release
Releases this year
314 followers · starred Aug 2020
184 followers · starred Dec 2022
34 followers · starred Oct 2026
35 followers · starred Dec 2020