i18next / i18next-http-backend Goto Github PK
View Code? Open in Web Editor NEWi18next-http-backend is a backend layer for i18next using in Node.js, in the browser and for Deno.
License: MIT License
i18next-http-backend is a backend layer for i18next using in Node.js, in the browser and for Deno.
License: MIT License
A clear and concise description of what the bug is.
A codesandbox example or similar
or at least steps to reproduce the behavior:
import i18n from 'i18next';
import Backend from 'i18next-http-backend';
i18n
.use(Backend)
.init({
fallbackLng: 'ko-KR',
preload: ['ko-KR'],
backend: {
loadPath: `http://localhost/api/translation-resource/{{lng}}`,
},
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
});
A clear and concise description of what you expected to happen.
only one request, or Is this normal ?
I'm not sure if I understand exactly the parse
option. I suppose is a way "modify" the json file with the translations before loading it.
I have a JSON like this:
{
"yes": {
"message": "Si",
"description": "General Yes message to be used across the application"
},
"no": {
"message": "No",
"description": "General No message to be used across the application"
},
...
And I want to transform it like this:
{
"yes": "Si",
"no": "No",
...
I have modified the parse
function to accomplish that:
parse(data) {
// Remove the .message of each string
const jsonData = JSON.parse(data);
Object.entries(jsonData).forEach(([key, value]) => {
jsonData[key] = value.message;
});
return JSON.stringify(jsonData);
}
The console log show the correct json, but when I do a i18next.t('yes')
it does not found the message.
I have tried the most simple, like this:
parse(data) {
return data;
}
But the i18next.t('yes.message')
does not work neither to my surprise.
But if I remove the parse
function totally, then it works perfectly, the i18next.t('yes.message')
returns Si
.
Is there something that I have not understood of this function?
Add an empty parse function to the options and test it:
parse(data) {
return data;
}
To find the keys.
The plugin overrides the backend type for i18next. This breaks existing code where developers use i18next-chained-backend
Worked up to version: 1.1.1
Stopped working in version: 1.2.0
Steps to reproduce the behavior:
Follow the guide at https://github.com/i18next/i18next-chained-backend
To work as before
Hi,
I'm using i18next-http-backend to initialise my languages and i would like to know if the parameter 'loadPAth 'can point to a folder is in the 'src' sub-directory like the exemple :
loadPath: 'src/languages/{{lng}}/{{ns}}.json'
Think you for your help.
When I use it with Node 13.6.0.
My code like this:
const httpApi = require('i18next-http-backend');
Here is error:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/thaunguyen/Documents/Projects/front-end/node_modules/i18next-http-backend/lib/index.js require() of ES modules is not supported. require() of /Users/thaunguyen/Documents/Projects/front-end/node_modules/i18next-http-backend/lib/index.js from /Users/thaunguyen/Documents/Projects/front-end/server/i18n.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules. Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/thaunguyen/Documents/Projects/front-end/node_modules/i18next-http-backend/package.json.
But it works fine with node 12.x.x. Can I use require
on Node 13.x.x?
I installed this library to download the translation files as needed (lazy loading), inside my project. If the file is not in the server or the server is offline for some reason, I want to get whatever error happened and show it nicely to the user.
The main problem happens when the file is not in the server or the server is offline for some reason. This library simply throw some warnings on the console.log and change the language anyway (even if the file was not downloaded), like so:
As a default behavior, I'd expect the i18n.changeLanguage Promise to throw an error so I can treat it inside a try/catch block function. Or maybe an extra configuration key called "ifFileNotDownloadedChangeLanguageAnyway" so we can prevent the language to be changed if the download fails.
The installation:
npx create-react-app pig --template typescript
cd pig
Then install the libraries:
npm i react-i18next i18next i18next-http-backend @material-ui/core
My i18n.ts
file:
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import Backend from 'i18next-http-backend'
i18n
.use(initReactI18next)
.use(Backend)
.init({
backend:{
loadPath: '/translations/{{lng}}.json'
},
react:{useSuspense:false},
debug: true,
lng: "en",
keySeparator: false,
interpolation: {
escapeValue: false
}
});
export default i18n;
My index.tsx
:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './i18n';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<App />,
document.getElementById('root')
);
reportWebVitals();
My huge App.tsx
:
import { Backdrop, CircularProgress, Button } from "@material-ui/core"
import { makeStyles } from '@material-ui/core/styles';
import './App.css';
import { useTranslation } from 'react-i18next';
import { useState } from "react"
const useStyles = makeStyles((theme) => ({
backdrop: {
zIndex: theme.zIndex.drawer + 1,
color: '#fff',
},
}));
function App() {
const [lang, setLang] = useState("en");
const [block, setBlock] = useState(false);
const { t, i18n, ready } = useTranslation();
const handle = async () => {
try{
setBlock(true)
const newLang = lang==="en"?"pt":"en"
await i18n.changeLanguage(newLang)
setLang(newLang)
setBlock(false)
}catch(e){
console.log("this is never called", e)
}
}
const classes = useStyles();
return (
<div className="App">
<Backdrop className={classes.backdrop} open={block || !ready}>
<CircularProgress/>
</Backdrop>
{ready && (
<>
<h1 onClick={handle}>{t("pig")}</h1>
<Button onClick={handle} >Click me</Button>
</>
)}
</div>
);
}
export default App;
My folder structure/translation files:
Then you can simply exclude the pt.json
file or block the requests to this file in the chrome dev tools > Network tab and click the "Click me" button.
Hi,
In a browser context, seem's that options are not taken.
var boptions = {
queryStringParams: { v: '1.2.3' },
};
var backend = new i18nextHttpBackend(boptions);
You can see in the network console that the query string is never passed.
After some investigation i found that.
Actual (not working, chrome browser):
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
Expected (working)
var options = arguments.length > 1 && arguments[0] !== undefined ? arguments[0] : {};
Cheers
i18next-http backend, trying to get ns "translation" even though the default name is "common". And I get 404 error on console.
My code working well but I don't want get console error.
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import backend from "i18next-http-backend";
import languageDetector from "i18next-browser-languagedetector";
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.use(backend) // for /public/locales
.use(languageDetector)
.init({
fallbackLng: "en",
supportedLngs: ["en", "tr"],
defaultNS: "common",
keySeparator: false, // we do not use keys in form messages.welcome
interpolation: {
escapeValue: false, // react already safes from xss
},
});
export default i18n;
request.js:60 GET https://tweet-maker-react.netlify.app/locales/en/translation.json 404
public
-- locales
---- en
------ common.json
---- tr
------ common.json
json translation files appear to be fetched, but i18n cannot load the data.
It appears that the data is not correctly downloaded from Google cloud storage bucket, but no error is returned and the fetch appears to be successful. Not sure how to debug that one.
I noticed that my fetches have a sec-fetch-dest: empty
request header, but according to the MDN doc, the header seems to be set automatically by the browser and it's unclear if some requestOptions could fix the issue.
Here's my config:
i18n.ts
import Backend from "i18next-chained-backend";
import HttpApi from "i18next-http-backend"; // fallback http load
import LanguageDetector from "i18next-browser-languagedetector";
import LocalStorageBackend from "i18next-localstorage-backend"; // primary use cache
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import environment from "./environment";
const { debug, localesPath } = environment;
const backendOptions = [
{
debug,
// prefix for stored languages
prefix: "i18next_res_",
// expiration
expirationTime: 7 * 24 * 60 * 60 * 1000,
// Version applied to all languages, can be overriden using the option `versions`
defaultVersion: "",
// language versions
versions: {},
// can be either window.localStorage or window.sessionStorage. Default: window.localStorage
store: window.localStorage,
/* below options */
},
{
loadPath: `${localesPath}{{lng}}/{{ns}}.json`, // xhr load path for my own fallback
// allow cross domain requests
crossDomain: false,
requestOptions: {
// used for fetch, can also be a function (payload) => ({ method: 'GET' })
mode: "no-cors", // Same as webpack files, request header 'sec-fetch-mode: no-cors'
credentials: "same-origin",
cache: "default",
},
},
];
i18n
// load translations from http
.use(Backend)
// detect user language
.use(LanguageDetector)
// pass i18n down to react-i18next
.use(initReactI18next)
.init({
debug: true,
backend: {
backends: [
LocalStorageBackend, // primary
HttpApi, // fallback
],
backendOptions,
},
ns: ["common", "translation", "glossary", "auth"], // NOTE: keep synced with i18nextWebpackPlugin options
defaultNS: "common",
supportedLngs: ["en", "ja"],
fallbackLng: "ja",
keySeparator: ".", // we use keys in form messages.welcome
interpolation: {
escapeValue: false, // react already safes from xss
},
});
if (process.env.NODE_ENV !== "production") {
const { applyClientHMR } = require("i18next-hmr"); // eslint-disable-line
applyClientHMR(i18n);
}
export default i18n;
SwitchLanguage.tsx
import i18n from "i18next";
import React, { SyntheticEvent, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
const SwitchLanguage: React.FC = () => {
const [lang, setLang] = useState("ja");
const { t } = useTranslation();
const isFirstRender = useRef(true);
useEffect(() => {
// Skip the effect on the first render
if (isFirstRender.current) {
isFirstRender.current = false;
} else {
i18n.changeLanguage(lang);
}
}, [lang]);
const handleChange = ({ target }: SyntheticEvent): void =>
setLang((target as HTMLSelectElement).value);
return (
<select onChange={handleChange} value={lang}>
<option value="ja">{t("lang.ja")}</option>
<option value="en">{t("lang.en")}</option>
</select>
);
};
export default SwitchLanguage;
Checking from the console Network tab, the files are fetched with status code 200
i18next backend connector fails to load the data:
Keys are shown instead of translations in the page:
The data can not be examined from the browser console:
Here a screenshots of request headers sent:
Accessing the file directly works fine
I should be able to see the json code from the browser console and i18n should load the data correctly.
Works in the dev environment with webpack devServer
After installing this plugin, an error occurs in IE/Edge.
I connect this plugin like this:
`import i18next from 'i18next';
import HttpApi from 'i18next-http-backend';
i18next
.use(HttpApi)
.init({
lng: 'ru',
fallbackLng: 'ru',
backend: {
loadPath: staticUrl + '/locales/{{lng}}/{{ns}}.json'
}
});`
I use webpack and babel:
module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ "@babel/preset-env", "@babel/preset-react", ], plugins: [ "@babel/plugin-proposal-function-bind", "@babel/plugin-proposal-class-properties", "@babel/plugin-transform-spread", "@babel/plugin-proposal-object-rest-spread" ], cacheDirectory: true } } ] },
The request is to have customHeaders that can change over calls, in particular Authorization one.
Already implemented in i18next-xhr-backend but now it is deprecated, link to commit here.
The necessity is basically the same reported i18next-xhr-backend here.
Initialize i18next without any token as Authorization header, login to your app and need to set the retrieved token into i18next backend plugin, for example to add missing labels.
Vite is a blazingly fast bundler and is being increasingly used amongst devs. I've recently gotten the product team at our company to use it more because webpack-dev-server can get extremely slow for large projects.
In production mode, Vite uses rollup internally to bundle code. The problem is, it seems that this library specifically is causing builds to fail with runtime errors on initial load.
To save you some time, I've already done a little bit of research and figured out the root of this problem. node-fetch
is somehow being bundled into the browser in rollup. Thus the runtime errors are from trying to access non-existent node libs in the browser.
Pull from this test repo: https://github.com/diracs-delta/i18next-http-backend-rollup-demo
And then run
yarn
yarn vite build && yarn vite preview
and navigate to localhost:5000
.
i18next
plugins should be compatible with the latest bundlers and not sometimes import node-fetch
on accident.
With the resolution of #45 a bug was introduced when posting missing resources, due to the fact that the create
function may be called without a callback and that would cause the backend to crash.
create
without a callback (backend.create(languages, namespace, key, fallbackValue)
)import i18next from 'i18next'
const backend = new Http(
{
interpolator: i18next.services.interpolator
}
)
// Produces "callback is not a function" error
backend.create('en', 'test', 'key', 'value')
The create
function should work correctly without a callback.
This would allow to provide a function for addPath
, just as it is done from loadPath
.
const addPath = (lng, ns) => {
// Decide what path to return according to ns/lng
}
This would basically account for a certain use case, using different paths based on namespace and language to post the missing resources, we have that ability when getting resources using loadPath
as a function, but the same is not extended to the addPath property.
We use the namespace to fetch data from two different backends, which we can perfectly do using loadPath
as a function:
const loadPath = (lngs, namespaces) => {
if (namespaces[0] === "ns1") {
return `/first/path/${lngs[0]}/${namespaces[0]}`
}
return `/second/path/${lngs[0]}/${namespaces[0]}`
}
We would also need to post missing resources to those different backends, something like:
const addPath = (lng, namespace) => {
if (namespace === "ns1") {
return `/first/path/add/${lng}/${namespace}`
}
return `/second/path/add/${lng}/${namespace}`
}
Goodmorning,
i have a little problem when i try to load translation from my api.
Code:
import i18n from 'i18next'
import XHR from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next'
i18n.use(XHR)
.use(initReactI18next)
.init({
backend: {
loadPath : 'urlexample/{{lng}}/{{ns}}/',
allowMultiLoading: false,
crossDomain : false
},
lng: locale.split("-")[0],
ns: locale.split("-")[1],
defaultNS: locale.split("-")[1],
react: {
useSuspense: false
},
fallbackLng: false,
debug: true,
react: {
wait: true
}
}, (error, t) => {
if(error)
console.error(error);
});
Backend i18next log is this:
And the translation doesn't work.
what is my problem ?
Thanks in advance.
P.S.
When i use static hardcoded json like this work well:
import i18n from 'i18next'
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector'
import itit from './translations/it-it.json'
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
lng: locale.split("-")[0],
react: {
useSuspense: false
}
fallbackLng: 'en',
debug: true,
resources: { en, itit, eses, ruru, roro, vivn, envn, zaza, enza, frfr, huhu, plpl, uaua, enid, idid, usus, enus, nlnl, ensg, elgr, frma },
interpolation: {
escapeValue: false,
},
})
},
(error) => {
console.log(error, "i18n error loading");
});
Firefox 34 got syntax error, the file compiled by Babel are not use older syntax.
SyntaxError: missing ; before statement
Compiled file:
const arr = [];
const each = arr.forEach;
const slice = arr.slice;
function defaults(obj) {
each.call(slice.call(arguments, 1), source => {
if (source) {
for (var prop in source) {
if (obj[prop] === undefined) obj[prop] = source[prop];
}
}
});
return obj;
}
i18next-xhr-backend
for the first time with i18n.js
import i18n from 'i18next';
import Backend from 'i18next-xhr-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init();
i18next-http-backend
import i18n from 'i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init();
Use both i18next-xhr-backend
and i18next-http-backend
should be work fine.
package.json
{
"dependencies": {
"@babel/runtime": "^7.7.6",
"core-js": "^3.6.4",
"i18next": "^19.0.1",
"i18next-browser-languagedetector": "^4.0.1",
"i18next-chained-backend": "^2.0.0",
"i18next-http-backend": "^1.0.15",
"i18next-localstorage-backend": "^3.0.0",
"i18next-xhr-backend": "^3.2.2",
"react": "^16.12.0",
"react-i18next": "^11.2.5"
},
"devDependencies": {
"@babel/core": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.4",
"@babel/preset-react": "^7.7.4",
"babel-loader": "^8.0.6",
"babel-plugin-module-resolver": "^3.2.0"
}
}
.babelrc
{
"presets": [
["@babel/preset-env", { "useBuiltIns": "entry", "corejs": "3" }],
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-transform-runtime"
]
}
.browserslistrc
Firefox 34
Hi again,
I lost 2 hours because of a missing coma in my json files (server side)
I suggest to add something (at least a message in the console) or something to handle bad json files ;)
Cheers
Hi. I'm trying to load translation file from public URL but I get (failed)net::ERR_FAILED
. URL is correct and I can open it from my browser.
Here is my config file:
{
debug: true,
lng: 'en',
fallbackLng: 'en',
defaultNS: 'common',
interpolation: {
escapeValue: false,
format: (value, format: string | undefined) => {
if (format === 'uppercase') {
return value.toUpperCase();
}
return value;
},
},
backend: {
crossDomain: true,
loadPath() {
return '<S3_BUCKET>.eu-central-1.amazonaws.com/{{lng}}/{{ns}}.json';
},
},
}
Should load translation file
When 'fetch' is not available in the browser the request fallback to node-fetch instead of sending an "XMLHttpRequest" or "ActiveXObject" request.
//Code snippet from `lib/request.js`
if (fetchApi) {
// use fetch api
return requestWithFetch(options, url, payload, callback)
}
if (typeof XMLHttpRequest === 'function' || typeof ActiveXObject === 'function') {
// use xml http request
return requestWithXmlHttpRequest(options, url, payload, callback)
}
This causes an issue as node-fetch sets its own user-agent
if (!headers.has('user-agent')) {
headers.set('user-agent', 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)');
}
and the issue is: Refused to set unsafe header "user-agent"
Just disable fetch.
Personally I face this issue meanwhile running Cypress test which currently doesn't support fetch.
I expect the node-fetch fallback only happens when we are on node/deno environment but not in the browser.
Cypress test environment
I'm using i18next-http-backend
with typescript. I use the request
function to load the message resources from a backend and receive the messages as JSON-Object. So if I want to call the RequestCallback
I have to convert the JSON-Object to an JSON String via JSON#stringify
to fullfill the typing of RequestCallback
because the second param is of type RequestResponse
. And there data
should be of type string
.
If I look to the code (https://github.com/i18next/i18next-http-backend/blob/master/lib/index.js#L72) I see that data doen't have to be of type string.
So is it possible to adjust the typing to something like this:
interface RequestResponse {
status: number,
data: string | Record<string, unknown>
}
Or why only a string is supported ?
Backend pulls "en" in addition to other languages even when another fallbackLng is specified.
loadPath: '/api/translations?lang={{lng}}',
I am using the i18n
module in my react app which is hosted as an app (which I will refer to as live app) on S3
and has cloudfront
sitting in front of it.
I want to store the s3 url in a config file as to avoid having it hardcoded in my app so that I can work against translation files stored locally in the public/locales
folder when I'm developing.
Initially I had my backend options set-up so that it would always try and look up the files in the locales
path. And this worked locally but stopped working on S3 although the locales folder was present in the S3 bucket. I also noticed that no request was being sent from the app to retrieve the translation files.
backend: {
loadPath: 'locales'
}
I then decided to upload the translation files to another S3 bucket and host them there to see if this would fix the issue.
I changed my config to hardcode the s3 bucket path. This worked both locally and on the live app. But this means that I can't use my config file to determine the loadPath option.
backend: {
loadPath: '<myhardcoded-s3-bucket-url>/{{lng}}/translation.json',
crossDomain: true
}
I then thought I could construct the url in place as so:
/*global AWS_CONFIG */
/*eslint no-undef: "error"*/
...
...
...
backend: {
loadPath: `${AWS_CONFIG.aws_app_translations_path}/{{lng}}/translation.json`,
crossDomain: true
}
Strangely again this worked locally when AWS_CONFIG.aws_app_translations_path
was both http://localhost:3000/locales
and <myhardcoded-s3-bucket-url>
.
However once I pushed it live, it failed again. This time making a request to https://<my-apps-base-path>/undefined/en-GB/translation.json
for example. So it's trying to use the app path and append why I have defined in loadPath
.
I then saw that I could have loadPath
as a function to construct my url. This didn't work for the live app either, but again worked locally.
backend: {
loadPath: function (lng) {
return `${AWS_CONFIG.aws_app_translations_path}/${lng}/translation.json`
},
crossDomain: true
}
This is my entire i18n
file
/*global AWS_CONFIG */
/*eslint no-undef: "error"*/
import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import Backend from "i18next-http-backend";
import { initReactI18next } from "react-i18next";
const options = {
order: ['navigator', 'localStorage'],
caches: ['localStorage'],
fallbackLng: "en-GB",
debug: true,
interpolation: {
escapeValue: false // not needed for react as it escapes by default
},
backend: {
loadPath: function (lng) {
return `${AWS_CONFIG.aws_app_translations_path}/${lng}/translation.json`
},
crossDomain: true
}
}
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init(options);
export default i18n;
What could explain this odd behaviour? Am I missing configuration here?
After loading the web application, an http request goes out to get localization data from *loadPath
.
If you change the current language, then the corresponding files will come from the backend - this behavior is quite logical,because I only get files that will use.
Folders structure
- public/
--- locales/
----- ru
------- translation.json
------- filters.json
----- ua
------- translation.json
------- filters.json
In i18n options I also use LanguageDetector
, which helps me to save the current state of the language in local storage (init file below)
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
initImmediate: false,
fallbackLng: 'ru',
debug: isDebugMode,
ns: ['filters'],
defaultNS: 'translation',
interpolation: {
escapeValue: false,
formatSeparator: ','
},
react: {
wait: true,
useSuspense: true
},
backend: {
loadPath: './locales/{{lng}}/{{ns}}.json' // * where i store my locales
},
detection: {
order: ['localStorage'],
caches: ['localStorage', 'cookie'],
cookieMinutes: 80
}
})
Further, after switching the language and reloading the page, I will receive a json
that corresponds to the current language. But there will also be extra requests to get localization files that should not be used
*red dots mark unnecessary requests
In other words.
I ran the page for the first time and got the files corresponding to 'fallbackLng' - from locales/ru/*
Then I changed the language to ua, the changes were saved in localStorage, and after reboot, I hope to only get the files from locales/ua/*
.
But in the end I get files from both directories
After changing the language and reloading the page, I want to receive those files that correspond to the current language, avoiding unnecessary requests
Hello,
I am reaching out because we are using your library along with i18next
and react-i18next
. Recently there was a string check added to your codebase that stopped our common translation files from getting loaded.
i18next-http-backend/lib/index.js
Line 72 in ce9de75
The way I am reading the code is if a response comes back that is not a string it will stop there and not attempt to process any additional languages or namespaces. Because of the way our environment is set up, we sometimes receive HTML responses when a file is not found or in the case of a 403. I was able to validate this issue locally by building and linking.
Is it possible for the string check to be removed or add some kind of escape hatch for unique scenarios like ours?
To solve for the issue in the meantime, I rolled back our package file to 1.0.15 but would love to be able to use your latest releases moving forward. Any help or guidance would be appreciated.
Hello,
I am trying to config my i18n to have local static json file: "backupMessages.json",
and also to fetch other messages file from CDN, by using i18next-http-backend.
I came to this configuration but it only loads the local static file "backupMessages.json",
without the files from the CDN:
couldn't find a way to make it work.
any help?
thanks :)
It seems that loading translations of a single language JSON file that has multiple namespaces within it doesn't seem to work as expected. To be clear, this isn't a single language that is split up into multiple JSON files per namespace, but rather a single JSON file that has multiple namespaces within it.
{
"es-ES": {
"common": {
"date": "La fecha de hoy es: {{date, LL}}",
"english": "InglΓ©s",
"german" : "AlemΓ‘n",
"spanish": "EspaΓ±ol",
"logout": "Cerrar sesiΓ³n",
"help": "Ayuda",
"changeLanguage": "Cambiar idioma",
"nav": "Nav",
"home": "Casa",
"list": "Lista",
"app": "Solicitud"
},
"content": {
"greeting": "Bienvenido a la aplicaciΓ³n de ejemplo"
}
}
}
i18n
.use(LanguageDetector)
.use(HttpApi)
.use(initReactI18next)
.init({
lng: 'en',
interpolation: {
escapeValue: false
},
react: {
useSuspense: false
},
defaultNs: 'common',
ns: ['common', 'content'],
partialBundledLanguages: true,
nonExplicitSupportedLngs: true,
backend: {
loadPath: `/api/project/1/Version/1/Language/{{lng}}`,
customHeaders: {
'Content-Type': 'application/json; charset=utf-8',
},
parse: (data) => {
const json = JSON.parse(data) // returns [es-ES: {common: {β¦}, content: {β¦}}]
return json[0].fileContent['es-ES'] // Getting a specific language for example purposes
}
}
})
// es-ES is loaded from the BE using this plugin
console.log(i18n.getResourceBundle('es-ES')) // returns {common: {β¦}, content: {β¦}}
// EN is a static resource hosted in the codebase.
console.log(i18n.getResourceBundle('en')) // returns {date: "Today's date is: {{date, LL}}", english: "English", german: "German", spanish: "Spanish", Β β¦}
Returning an object with objects inside should be mapped as namespaces.
i18n
.use(LanguageDetector)
.use(HttpApi)
.use(initReactI18next)
.init({
lng: 'en',
interpolation: {
escapeValue: false
},
react: {
useSuspense: false
},
defaultNs: 'common',
ns: ['common', 'content'],
partialBundledLanguages: true,
nonExplicitSupportedLngs: true,
backend: {
loadPath: `/api/project/1/Version/1/Language/{{lng}}`,
customHeaders: {
'Content-Type': 'application/json; charset=utf-8',
},
parse: (data) => {
const json = JSON.parse(data) // returns [es-ES: {common: {β¦}, content: {β¦}}]
return json[0].fileContent['es-ES']
}
}
})
// es-ES is loaded from the BE using this plugin
console.log(i18n.getResourceBundle('es-ES')) // returns {date: "La fecha de hoy es: {{date, LL}}", english: "InglΓ©s", german: "AlemΓ‘n", spanish: "EspaΓ±ol", Β β¦}
// EN is a static resource hosted in the codebase.
console.log(i18n.getResourceBundle('en')) // returns {date: "Today's date is: {{date, LL}}", english: "English", german: "German", spanish: "Spanish", Β β¦}
I also attempted to use this plugin in combination with i18next-multiload-backend-adapter to solve this issue, but that just seemed to cause issues of its own.
I can also get ONE namespace to work if I access the namespace directly in the JSON object in the parse
function. Example:
parse: (data) => {
const json = JSON.parse(data) // returns [es-ES: {common: {β¦}, content: {β¦}}]
return json[0].fileContent['es-ES'].common // common namespace will work now! But I lose content....
}
I'm trying to use the backend to fetch from a backend I don't control. The endpoint contains the translations of ALL languages. I'm trying to use the parse
callback to translate the data returned from the backend into something this plugin understands.
What is the expected format of the return value of parse
?
I had to step through the invocation of callback
from my application code to figure out that data
property indicated within the res
object should be stringified JSON rather than an Object
instance. It also is worth noting that there was no indication of error in this case until attempting to look up the translation for a key (e.g. "i18next::translator: missingKey ..."
).
Perhaps it would help to provide an example of overriding request
in the examples/
within the repository.
The documentation in question (from the README):
// define a custom request function
// can be used to support XDomainRequest in IE 8 and 9
//
// '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 the key:value translation pairs for the
// requested language and namespace, or null in case of an error.
request: function (options, url, payload, callback) {},
Override request
option with something like the following:
request: async (
_options: object,
_url: string,
_payload: object,
callback: (err?: object, res?: object) => never,
): Promise<never> => {
const en = await import('./locales/en.json');
callback(undefined, { data: en.default });
// callback(undefined, { data: JSON.stringify(en.default) }); // "Correct" code
};
}
Bare minimum: update the documentation on the README:
// define a custom request function
// can be used to support XDomainRequest in IE 8 and 9
//
// '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 containing the key:value translation pairs for the
// requested language and namespace, or null in case of an error.
request: function (options, url, payload, callback) {},
Stretch goal:
Also ensure an error is thrown and/or logged when parsing fails via the default behavior (with only request
overriden). I don't believe this callback invocation results in consuming application code from being notified of an issue (unless I have something misconfigured).
i18next-http-backend/lib/index.js
Line 75 in ead886f
Hi there, does anyone know what i'm doing wrong with backend config? seems to me this error is related to i18next-http-backend
Build fails with error i18next::backendConnector: loading namespace translations for language en failed TypeError: Only absolute URLs are supported
tried different backend libs with no success, this error appears only with i18next-http-backend
**i18n.js**
import i18n from 'i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
backend: { loadPath: `/locales/{{lng}}/{{ns}}.json` },
fallbackLng: 'en',
// have a common namespace used around the full app
ns: ['translations'],
defaultNS: 'translations',
debug: true,
initImmediate: false,
react: {
wait: true
}
});
export default i18n;
**gatsby-ssr.js**
import React from 'react';
import { I18nextProvider } from 'react-i18next';
import { GlobalState } from './src/components/GlobalState/GlobalState';
import i18n from './src/utils/i18n';
import 'slick-carousel/slick/slick.css';
export const wrapRootElement = ({ element }) => (
<I18nextProvider i18n={i18n}>{element}</I18nextProvider>
);
export const wrapPageElement = ({ element, props }) => (
<GlobalState {...props}>{element}</GlobalState>
);
export const replaceRenderer = ({ bodyComponent, replaceBodyHTMLString }) => {
i18n.loadNamespaces(['translations'], () => {
replaceBodyHTMLString(bodyComponent);
});
};
**gatsby-node.js**
exports.onPostBuild = () => {
fs.copySync(`./src/locales`, `./public/locales`);
};
Build project without errors
HttpApi doesn't work server side (I can't see translations on first page load)
https://codesandbox.io/s/smoosh-thunder-o0fuw
just add plugin i18next-http-backend specify url to download translation
const NextI18Next = require('next-i18next').default;
import HttpApi from 'i18next-http-backend';
const { localeSubpaths } = require('next/config').default().publicRuntimeConfig;
const path = require('path');
module.exports = new NextI18Next({
use: [HttpApi],
backend: {
loadPath: `https://pobeda37.io/api/gettranslate?lng={{lng}}&ns={{ns}}`,
crossDomain: true
},
otherLanguages: ['de'],
localeSubpaths,
localePath: path.resolve('./public/static/locales')
});
I expected translations to be loaded in the same way on the server side
There seems to be an inconsistent usage of the request function when provided in options, in loadUrl
it provides 3 parameters, where create
calls it with the documented 4
For some strange reason, using the default fetch won't return any data from the S3 bucket where my translation files are. Here are my configs:
{
lng: getLocales()[0].languageCode,
fallbackLng: 'en',
debug: false,
interpolation: {escapeValue: false},
keySeparator: false,
nsSeparator: false,
load: 'currentOnly',
defaultNS: 'main',
ns: 'main',
backend: {
crossDomain: true,
loadPath:
'https:/bucket.s3.eu-west-2.amazonaws.com/app/{{lng}}/{{ns}}.json',
},
}
The request returns a 200, but with no data:
This is a problem with fetch, I'm not sure why. Then I moved to axios:
{
lng: getLocales()[0].languageCode,
fallbackLng: 'en',
debug: false,
interpolation: {escapeValue: false},
keySeparator: false,
nsSeparator: false,
load: 'currentOnly',
defaultNS: 'main',
ns: 'main',
backend: {
crossDomain: true,
async loadPath(lng, ns) {
const translations = await axios.get(
`https://bucket.s3.eu-west-2.amazonaws.com/app/${lng[0]}/${ns[0]}.json`,
);
return translations.data;
},
},
}
This works fine, but it tries to make a strange request as well:
How should I configure the backend to use axios?
Given the json file:
{
"ns": {
"example": "Example"
}
}
You can access example via t
like so: t("ns:example")
by passing in resources
option to init.
When using i18next-http-backend to load the resource, i.e. loadPath: "/translations/{{lng}}.json"
, it seems the namespace is lost and you must access it via dotty notation t("ns.example")
.
Accessing keys should be consistent between the two packages.
Chrome latest
"i18next": "^19.6.3",
"i18next-http-backend": "^1.0.18",
MacOS
Add option to retry requests in case of error.
Currently I'm working on an app that has all translations inside the bundle. To optimize this and reduce bundle size, we are moving translations to json files. I have found that a lot of users can get network errors, and I have not been able to find any api to re-do requests if some fails because of a network error, also this plugin is not doing any retry. This is a big real world problem in production environment for apps that receive thousands of visits every day, so the proposal is to add configuration api to allow retries.
Sorry if this feature is already available, then we can change this issue description to update docs.
Proposal
{
retry: {
attempts: 5,
interval: 500 /*in ms*/
}
}
I know this is not a place for questions but I am really confused about what this library really does and I can't understand even after going through the documentation. I am not using nodejs in the backend. Do I still need it ?
When used in a cordova app with a local translation file the url is something like file:///android_asset/www/locale/es-US.json
The fetch API does not support the file schema.
Load a locale json file from a file://
url.
The following output is logged: Fetch API cannot load file:///android_asset/www/locale/es-US.json. URL scheme "file" is not supported
The json loads
"Mozilla/5.0 (Linux; Android 7.1.1; Moto E (4) Plus Build/NDRS26.58-33-9-16; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/84.0.4147.125 Mobile Safari/537.36"
Within a cordova app (android webview)
The description shows the request options being a function that takes (options, url, payload, callback) however the 3rd argument slot is callback when loading, and is the payload when saving missing. Anyway the argument list could be consistent?
BackendOptions
TypeScript interface is missing reloadInterval
(at a minimum).
https://github.com/i18next/i18next-http-backend/blob/master/index.d.ts#L7
const httpApiOptions: BackendOptions = {
loadPath: '/locales/{{lng}}/{{ns}}.json',
allowMultiLoading: false,
crossDomain: false,
reloadInterval: false, // flagged by TypeScript
};
No TypeScript errors.
Babel 7.9.0 fails to compile request.js
.
This is the complete error that is given by npm start
Failed to compile.
./node_modules/i18next-http-backend/lib/request.js
Module parse failed: Unexpected token (47:20)
You may need an appropriate loader to handle this file type.
| url = addQueryString(url, options.queryStringParams)
| }
| const headers = { ...(options.customHeaders || {}) }
| if (payload) headers['Content-Type'] = 'application/json'
| fetchApi(url, {
Line 47 is const headers = { ...(options.customHeaders || {}) }
.
The requestOptions
property in BackendOptions
cannot be used in ts files due to its type missing in index.d.ts
type RequestType = BackendOptions['request'];
const getTranslations: RequestType = (
options,
url,
payload,
callback,
) => {
const {requestOptions} = options; // TypeScript error: Property 'requestOptions' does not exist on type 'BackendOptions'.
};
No error
Using the package in Internet Explorer throws a SyntaxError due to the use of non-transpiled ES6 syntax in the code.
Use the package in a IE11 context.
I know there is debate on whether dependencies should be shipped already transpiled or not, but even other packages of this project (i.e. i18next
, react-i18next
) are transpiled, so it would make sense for this one to be as well.
For those facing the same issue, I modified my Webpack config and made babel-loader
"un-ignore" the package like this:
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules\/(?!i18next-http-backend)/
}
Language interpolation in customHeader.
For a project, weβd like to fetch our translations from our backend. To indicate which translation for which backend should be requested, weβd like to use the βAccept-Languageβ-Header instead provide the information in the URL itself.
We thought about something like:
...
customHeaders: {
βAccept-Languageβ: β{{lng}}β
}
...
in the backend config. Is that possible already and Iβm just incapable of making it work?
Thank you very much for your help!
loadPath
seem not to be taken into account, and whether the path is valid or not the behavior is the same.
Run:
const i18next = require('i18next')
const backend = require('i18next-fs-backend')
const options = {
lng: 'en',
fallbackLng: 'en',
loadPath: '/path/is/wrong/translation.json',
}
i18next.use(backend).init(options)
// sleep time expects milliseconds
function sleep (time : number) {
return new Promise((resolve) => setTimeout(resolve, time));
}
sleep(300).then(() => {
console.log(i18next.languages)
console.log(i18next.language)
console.log(i18next.dir())
console.log(i18next.exists('mykey.mymessage'))
});
The code executes well and returns exit code 0, output:
[ 'en' ]
en
ltr
false
Additionally, setting loadPath
to the real path, e.g. locales/en/translation.json
, still compiles well but the output is the same, even if my key mykey.mymessage
does exist in my translation.json
file:
{
"mykey": {
"mymessage": "hehe",
"description": "haha"
}
}
Then, this is very hard to debug because you don't know whether the problem is that your json file is not loaded, of if it is something else.
I would expect having an error because the path I give is wrong.
There is a possibility to define a custom request function for getting the translations but looks like there is no such param for adding a missing translation. A feature would define a custom add missing functions that would be called when a missing translation found, just like a custom request,
My project uses typed api helpers generated from open API spec, so it makes sense to use API methods provided rather than configuring request options by hand.
request: (
payload,
callback: RequestCallback,
) :void => {
const {lng, ns, key} = payload;
const params: addMissingParameters = {
lng,
ns,
key,
};
addMissing(params).then(({ status, data }) => {
const response: RequestResponse = {
status,
data: JSON.stringify(data?.[0]?.namespaces?.[0]?.values),
};
callback(null, response);
}).catch((error: AxiosError) => callback(error, { status: error?.response?.status || 500, data: '' }));
},
Hi, I have similar question to #1 but specifically pertaining to react app with custom webpack configuration i.e not create-react-app. The public folder is located somewhere different than where it would be in a create-react-app. How do I set the i18n config to the location of the public folder? So far I tried
Thanks in advance!
A project setup with Typescript (targeting es5) and Webpack seems to be causing i18next-http-backend
to be loaded via require
which is configured to load the library from the cjs
distribution.
If I chage my import to import i18nextHttpBackend from "i18next-http-backend/esm";
it works as expected, but then TypeScript cannot find the type declaration.
Sandbox: https://codesandbox.io/s/re1sn
(run npm start
if not already running)
https://re1sn.sse.codesandbox.io/
(should show something like defaultBackend is undefined
and esmBackend is function
)
import i18nextHttpBackend from "i18next-http-backend";
to result in i18nextHttpBackend !== undefined
.
[email protected] includes type definition changes which cause TypeScript errors when used with i18next-http-backend.
import i18next from 'i18next';
import HttpApi from 'i18next-http-backend';
i18next.use(HttpApi).init(i18nextOptions);
node_modules/i18next-http-backend/index.d.ts:69:3 - error TS2416: Property 'readMulti' in type 'I18NextHttpBackend' is not assignable to the same property in base type 'BackendModule<BackendOptions>'.
Type '(languages: string[], namespaces: string[], callback: ReadCallback) => void' is not assignable to type '(languages: string[], namespaces: string[], callback: MultiReadCallback) => void'.
Types of parameters 'callback' and 'callback' are incompatible.
Types of parameters 'data' and 'data' are incompatible.
Type 'boolean | ResourceKey | null | undefined' is not assignable to type 'Resource | null | undefined'.
Type 'string' is not assignable to type 'Resource | null | undefined'.
69 readMulti(languages: string[], namespaces: string[], callback: ReadCallback): void;
~~~~~~~~~
No TypeScript errors when importing i18next-http-backend.
Running on server side, I have an init configuration that, when reading an environment variable that it is "true" allows me to load translations from and endpoint, and when the envvar is false, I add them from a js object. In this case, I would like to avoid reloading as well.
Currently I am passing down that option but it is ignored, reload happens every hour. When setting debug: true, the i18next: initialized event shows that reloadInterval is indeed false.
Here's the portion of init config that enables this behavior, other parameters are not listed to simplify reading.
CONFIG.ENABLE_TRANSLATIONS is the env var that I mentioned, which is set to false,
so reloadInterval is false and request throws an error.
backend: {
reloadInterval: CONFIG.ENABLE_TRANSLATIONS && CONFIG.TRANSLATIONS_SERVER_RELOAD_INTERVAL,
loadPath: (lngs, namespace) =>
`${CONFIG.API_BASE_URL}${API_ROUTES.LOCALIZATION}/${lngs}_${namespace}`,
request: async function(options, url, payload, callback) {
try {
if (!CONFIG.ENABLE_TRANSLATIONS)
throw new Error('Backoffice translations disabled - Loading static keys')
//REQUEST TO BACKEND
return callback(null, {
status: result.status,
data: result.data && result.data.translations
})
} catch (error) {
return callback(
{
status: error.response && error.response.status,
data: (error.response && error.response.data) || error.message
},
null
)
}
}
}
Here's is the portion where I load the js object in case of error or envvar being disabled:
i18n
.use(Backend)
.use(i18nextMiddleware.LanguageDetector)
.use(i18nCustomLogger)
.init(serverOptions, i18nError => {
if (i18nError) {
logError('I18n Backend Connector error - Loading static keys', '', i18nError)
i18n.addResourceBundle(LANGUAGE_TAGS.DEFAULT, NAMESPACES.COMMON, staticKeys)
}
// some other code
I would like to avoid reloading when I set reloadInterval to false.
I would like to use I18next from my own library but loadPath searching in main app (where I init-ed I18Next first time).
In my lib I would like to add a new namespace and some translation files. How can I trigger my lib dir?
My lib ts:
ngOnInit(): void {
i18next.init({
ns: 'dialog',
backend: {
loadPath: '/assets/locales/{{ns}}-{{lng}}.json'
}
});
}
zone-evergreen.js:1068 GET http://localhost:4200/assets/locales/dialog-hu.json 404 (Not Found)
Attempting to upgrade from the deprecated i18n-xhr-backend
to i18next-http-backend
with the same configuration approach means the translations keys are no longer loaded. They are downloaded from the server successfully, but it seems that the json response is not parsed by i18next-http-backend
.
Moving from this config in i18n-xhr-backend
:
ajax: async (url, options, callback) => {
try {
const response = await customFetch(url);
const body = await response.text();
callback(body, response);
} catch (e) {
console.info('Error fetching translations', e);
callback(null, e);
}
}
to this one in i18n-http-backend
:
request: async (options, url, payload, callback) => {
try {
const response = await customFetch(url);
const body = await response.text();
callback(body, response);
} catch (e) {
console.info('Error fetching translations', e);
callback(null, e);
}
}
breaks the translations, and results in a bunch of "missing keys" debug messages in the console.
(customFetch
is just adding a number of headers and tokens to the fetch
request)
The translations should be displayed in my application.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.