i18next load json from api using i18next-xhr-backend - javascript

I try to load a translation JSON file from API in React Native and parse it but it seems that load does not work because the parse method never invokes.
function initI18Next(onInit) {
return (
i18n
.use(initReactI18next)
.use(XHR)
.init({
...initOptions,
backend : backendOptions
}).then(onInit())
)}
initOptions :
export const initOptions = {
fallbackLng : 'es',
defaultNS : 'translation',
lng : 'es',
debug: true,
keySeparator : false,
initImmediate: false,
preload : ['en', 'es'],
react: {
wait: true,
useSuspense: false
},
interpolation: {
escapeValue: true // not needed for react as it escapes by default
},
resources : i18nResources
}
backend options :
export const backendOptions = {
loadPath: 'http://www.mocky.io/v2/5ecd0fe73200006400236655.json',
parse(data) {
console.log('data', data);
return data;
}
};

i18next-xhr-backend is deprecated, use i18next-http-backend instead...
Then try to set i18next debug option to true and have a look at the developer console logs.
Btw: the load path looks strange to me. It should be more like this: https://github.com/i18next/i18next-http-backend#backend-options

Related

how to incorporate API data in i18next instead of static file

I have saved file in public folder in react for different language for translation. now I have requirement to get that data from API. I am able to call API and get data. I also know how to use translation. but I am not able to incorporate API call in translation code file.
API.
axios
.get(
'http://localhost:8080/file_download/' +
navigator.lnaguage
)
.then((res) => {
console.log(res.data);
});
});
}
Below is my translation code fr static files.
for ex in TRANSLATIONS_FR I need to store output of API.
i18file.js:-
import i18next from 'i18next';
const LanguageDetector = require('i18next-browser-languagedetector');
const initReactI18next = require('react-i18next');
import xios from 'axios';
import { TRANSLATIONS_FR } from './../public/locales/fr/fr.js';
import { TRANSLATIONS_EN } from '../public/locales/en/en.js';
i18next
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources: {
en: TRANSLATIONS_EN,
fr: TRANSLATIONS_FR,
},
fallbackLng: 'fr',
});
i18next.changeLanguage(navigator.language);
export default i18next;
I have to insert that API code into i18file.js file. if browser detecting en then I have to call for english and assign in resources. if browser detecting frech then I have to do for same.
Could you please suggest. I will write the code.
Edit1:-
I am writing below code and it is throwing error.
Below API will send the json data from database. this data I am not storing in file but directly using.
http://localhost:8080//file_download/en
Below is the code
import i18next from 'i18next';
import Backend from 'i18next-http-backend';
const LanguageDetector = require('i18next-browser-languagedetector');
const initReactI18next = require('react-i18next');
i18next
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
backend: {
loadPath: 'http://localhost:8080//file_download/en' //this is API path which will return result.
},
resources: {
en:res.data //I need to assign here.
},
fallbackLng: 'fr',
});
i18next.changeLanguage(navigator.language);
export default i18next;
How can I incorporate the API in above code and use its data for eg in en language.
Updated
As await at top level is not supported
I would suggest to use another plugin i18next-http-backend as i18next has mentioned here
You need to install the plugin first npm i i18next-http-backend
Then just define the backend. There are some examples here
// import { TRANSLATIONS_FR } from './../public/locales/fr/fr.js';
// import { TRANSLATIONS_EN } from '../public/locales/en/en.js';
import Backend from 'i18next-http-backend';
i18next
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
backend: {
loadPath: 'http://localhost:8080/file_download/{{lng}}/{{ns}}.json'
}, // your backend options.
// More info here: https://github.com/i18next/i18next-http-backend
resources: {
en,
fr
},
fallbackLng: 'fr',
});
i18next.changeLanguage(navigator.language);
export default i18next;
import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import backend from 'i18next-http-backend';
import api from "../api";
var lang=navigator.language;
let loadResources= apiDelegate.getTranslations(lang);
const backendOptions = {
loadPath: 'http://localhost:8080/code/'+lang,
request: (options, url, payload, callback) => {
try {
loadResources.then((result) => {
callback(null, {
data: result,
status: 200,
});
});
} catch (e) {
console.error(e);
callback(null, {
status: 500,
});
}
},
};
i18n
.use(LanguageDetector)
.use(backend)
.init({
backend: backendOptions,
fallbackLng: "fr",
debug: false,
load:"languageOnly",
ns: ["translations"],
defaultNS: "translations",
keySeparator: false,
interpolation: {
escapeValue: false,
formatSeparator: ","
},
react: {
wait: true
}
});
i18n.changeLanguage(navigator.language);
export default i18n;
you don't need to write http://localhost:8080 as prefix of your get URL. just an / refers to public folder.
just change your axios URL in API file to :
axios
.get(
'/file_download' +
navigator.lnaguage
) + '.json' // or any other format that your language file is
.then((res) => {
console.log(res.data);
});
});
}
There's a great article explaining exactly how to adapt your code to load the translations via api here: https://dev.to/adrai/how-to-properly-internationalize-a-react-application-using-i18next-3hdb#separate
It introduces the i18next-http-backend module, shows you where to place the translation files:
and how to adapt the code:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';
i18n
// i18next-http-backend
// loads translations from your server
// https://github.com/i18next/i18next-http-backend
.use(Backend)
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
.use(LanguageDetector)
// pass the i18n instance to react-i18next.
.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
debug: true,
fallbackLng: 'en',
interpolation: {
escapeValue: false // not needed for react as it escapes by default
}
});
export default i18n;
The full example can be found here.
—-
Alternatively, you can also lazy load the translations like this: https://github.com/i18next/i18next-resources-to-backend#you-can-also-lazy-load-the-in-memory-translations-ie-when-using-webpack
btw: i18next-http-backend is also able to inject a custom request function:
https://github.com/i18next/i18next-http-backend#backend-options
like here: https://github.com/i18next/i18next-http-backend/issues/34#issuecomment-729908814

How to spy on npm library function (Node.js)?

I'm using express-prom-bundle to provide a prometheus statistics endpoint, this library creates a middleware that is applied to an Express app:
import promBundle from 'express-prom-bundle';
...
app.use(promBundle({
includeMethod: process.env.PROMETHEUS_INCLUDE_METHOD === 'true' ? true : false,
includePath: process.env.PROMETHEUS_INCLUDE_PATH === 'true' ? true : false,
includeStatusCode: process.env.PROMETHEUS_INCLUDE_STATUS_CODE === 'true' ? true : false,
}));
This is what I'm currently trying to do:
import * as promBundle from 'express-prom-bundle';
...
spyOn(promBundle, 'default');
spyOnProperty(promBundle, 'default').and.returnValue({});
expect(promBundle).toHaveBeenCalled();
but I'm receiving the following error:
Error: <spyOn> : default is not declared writable or has no setter Usage: spyOn(<object>, <methodName>)
How it could be mocked and tested by Jasmine?
Coverage shows that lines where process.env variables are checking need to be tested.

React i18next return a fallback language

I'm using i18next in my React app in this way to translate files:
i18next.js (original)
import i18next from 'i18next';
import XHR from 'i18next-xhr-backend';
import detector from 'i18next-browser-languagedetector';
window.userLang = navigator.language || navigator.userLanguage;
i18next
.use(detector)
.use(XHR)
.init({
lng: window.userLang,
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false,
},
});
export default i18next;
Using the code above, I am able to successfully translate pages based on the user's browser data. If the language is not available, it falls back to English as intended.
However, I ran into some components which were not translated into the keys provided. They are set up in the following way in the menu component:
Menu Component
const projectArchives = i18next.t('LEFT_MENU.projectArchives');
export default {
[KEY_ARCHIVE_MODEL]: {
en: projectArchives,
it: projectArchives,
},
Output in browser:
LEFT_MENU.projectArchives
I changed my i18next.js file to have a variable (var data =) which would get the language from a string:
i18next.js (Updated)
import i18next from 'i18next';
import XHR from 'i18next-xhr-backend';
import detector from 'i18next-browser-languagedetector';
window.userLang = navigator.language || navigator.userLanguage;
let language = window.userLang.split('-')[0];
var data = require(`../../public/locales/${language}/translation.json`);
const FALLBACK_LOCALE = 'en';
i18next
.use(detector)
.use(XHR)
.init({
lng: window.userLang,
fallbackLng: FALLBACK_LOCALE,
debug: true,
resources: {
otherLanguages: FALLBACK_LOCALE,
it: {
translation: data,
},
en: {
translation: data,
},
},
});
export default i18next;
Now, the menu is able to display the translated keys but now, if a language is not available as in the original code, the following error is returned:
Error: Cannot find module './fr/translation.json'.
because there is ofcourse there is not an available French translation file.
The error occurs because of the following line:
var data = require(`../../public/locales/${language}/translation.json`);
I've tried connecting the FALLBACK_LOCALE variable directly to the English translation file but this also failed.
var FALLBACK_LOCALE = require(`../../public/locales/en/translation.json`);
How can I force the fallback language or create a conditional for the expected behavior?
import i18next from 'i18next';
import XHR from 'i18next-xhr-backend';
import detector from 'i18next-browser-languagedetector';
window.userLang = navigator.language || navigator.userLanguage;
let language = window.userLang.split('-')[0];
const FALLBACK_LOCALE = 'en';
i18next
.use(detector)
.use(XHR)
.init({
lng: window.userLang,
fallbackLng: FALLBACK_LOCALE,
debug: true,
resources: {
otherLanguages: FALLBACK_LOCALE,
it: {
translation: require(`../../public/locales/it/translation.json`),
},
en: {
translation: require(`../../public/locales/en/translation.json`),
},
},
});
export default i18next;

Use i18next with XHR backend in client-side javascript

The documentation at i18next-xhr-backend tells me to use import to load their module. But when I use the import-statement, nothing happens and Firefox gives me a SyntaxError in the developer console:
SyntaxError: import declarations may only appear at top level of a module
So how can I use i18next library with the XHR-backend? The following code example works if the .use(XHR)-line and the corresponding import is commented out (Warning: i18next::backendConnector: No backend was added via i18next.use. Will not load resources.). But it fails, if it is not: ReferenceError: XHR is not defined
//import Fetch from 'i18next-fetch-backend';
let t = null;
i18next
.use(XHR)
.init({
debug: true,
fallbackLng: ['en'],
preload: ['en'],
ns: 'translation',
defaultNS: 'translation',
keySeparator: false, // Allow usage of dots in keys
nsSeparator: false,
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
},
}, (err, _t) => {
if (err) {
reject(err);
return;
}
t = _t;
//resolve();
});
jqueryI18next.init(i18next, $, {
tName: 't', // --> appends $.t = i18next.t
i18nName: 'i18n', // --> appends $.i18n = i18next
handleName: 'localize', // --> appends $(selector).localize(opts);
selectorAttr: 'data-i18n', // selector for translating elements
targetAttr: 'i18n-target', // data-() attribute to grab target element to translate (if different than itself)
optionsAttr: 'i18n-options', // data-() attribute that contains options, will load/set if useOptionsAttr = true
useOptionsAttr: false, // see optionsAttr
parseDefaultValueFromContent: true // parses default values from content ele.val or ele.text
});
$(".nav").localize();
I needed to use i18nextXHRBackend instead of just XHR, since that is the name the class gets loaded as if no loader is used. As the README.md says:
If you don't use a module loader it will be added to window.i18nextXHRBackend
I didn't see that before, and I didn't know that this will happen automatically, but it seems that you have to find that out on your own if not using a module loader. Lesson learned, hopefully this will help some other newbies being stuck on how to use modules in javascript. Therefore, my complete localisation.js looks like this:
$(document).ready(function() {
i18next
.use(i18nextXHRBackend)
.use(i18nextBrowserLanguageDetector)
.init({
debug: true,
backend: {
loadPath: 'locales/{{lng}}/{{ns}}.json',
addPath: 'locales/add/{{lng}}/{{ns}}'
}
}, function(err, t) {
jqueryI18next.init(i18next, $);
$('.translatable').localize();
$('.language-button').click(function() {
i18next.changeLanguage(this.firstElementChild.alt).then(function(t) {
$('.translatable').localize();
$('#signupPassword').pwstrength("forceUpdate");
$('#signupPasswordConfirm').pwstrength("forceUpdate");
});
});
});
});

Sails.js, Nunjucks & LiveReload

I've managed to get Nunjucks working with Sails.js, however it seems that the changes are not being picked up until I restart the server. I'll automatically see the changes reflected once or twice, but after that, even manually refreshing the browser will not show my changes.
I implemented LiveReload with the advice here:
Get livereload to work with Sails.js
but I don't suspect it's an issue with LiveReload.
Has anyone else gotten Sails.js and Nunjucks to play nicely together? If so, how?
The problem is nunjucks itself. It has a watch option which by default is set to false:
You can enable it in sails/config/bootstrap.js:
var nunjucks = require('nunjucks')
module.exports.bootstrap = function(cb) {
nunjucks.configure({
watch:true
})
// It's very important to trigger this callback method when you are finished
// with the bootstrap! (otherwise your server will never lift, since it's waiting on the bootstrap)
cb();
};
In combination with the livereload everything works fine.
in /config/views.js
engine: {
ext: 'html',
fn: function (str, options, fn) {
var engine = require('nunjucks');
engine.configure('views', {
autoescape : true,
throwOnUndefined : true,
trimBlocks : true,
lstripBlocks : true,
express : sails.hooks.http.app,
watch : true,
noCache : false,
web : {
useCache : true,
async : false
}
});
engine.render(str, options, fn);
}
},
For Sails.js 1 the solution has slightly changed:
In /config/views.js
module.exports.views = {
...
getRenderFn: () => {
// Import nunjucks.
const nunjucks = require('nunjucks');
// Configure nunjucks.
const env = nunjucks.configure('views', {
autoescape : false,
throwOnUndefined : true,
trimBlocks : true,
lstripBlocks : true,
watch : true,
noCache : false,
web : {
useCache : true,
async : false
}
});
// Here you can add filter
env.addFilter('filtername', (name) => {
return name;
});
return nunjucks.render;
}
}
Hope this will help someone ;)

Categories

Resources