Handle React translations on component level - reactjs

What i want to achieve is that i have translations available for all components itself. When i install those components they should render on its own. Another thing that i want to achieve is that my project can overrule translations on component level. This in case if somebody decides that the translations that is in there is not what we want.
What i did in my components is loading i18n:
import i18n from 'i18next';
import XHR from 'i18next-xhr-backend';
import Cache from 'i18next-localstorage-cache';
import LanguageDetector from 'i18next-browser-languagedetector';
i18n
.use(XHR)
.use(Cache)
.use(LanguageDetector)
.init({
backend: {
loadPath: '../dist/locales/{{lng}}/{{ns}}.json',
},
fallbackLng: 'en',
react: {
wait: true
},
ns: ['authentication'],
defaultNS: 'authentication',
debug: false,
cache: {
enabled: true
},
interpolation: {
escapeValue: false, // not needed for react!!
formatSeparator: ',',
format: function(value, format) {
if (format === 'uppercase') return value.toUpperCase();
return value;
}
}
});
export default i18n;
This is working fine in my storybook but when i load this in my main project, it is using ../ from my build root which is actually not the component itself but my main implementation project.
How can i solve it and how can i ensure that i can even extend this?
UPDATE:
Below a list of the current folder structure like asked in below questions:
#Component
dist
--.gitkeep
--locales
--commonjs
--es
src
--actions
----index.js
--components
----compname
------index.js
--reducers
----foo.js
--i18n.js
--index.js
test
.babelrc
package.json
Jenkinsfile
#Project
react
--assets
----css
------app.scss
--dist
----.gitkeep
--src
----index.js
----store.js
webpack.config.js
Thanks

Related

React-i18next does not load translations on page reload

I am using react-I18next in my nextjs application. I don't want to use next-I18next because I tried to use it but couldn't make it work. But react-i18next is working in my application for now and I am able to change language from english to german and back. However if I reload the page I get this error.
TypeError: i18n.changeLanguage is not a function
What could be the possibel cause of this error and how can I fix it?
P.S In my app.ts file I am not using Suspense because it gives me this error
ReactDOMServer does not yet support Suspense.
Here is my i18.ts file
/* eslint-disable no-duplicate-imports */
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import backend from 'i18next-xhr-backend';
import { initReactI18next } from 'react-i18next';
import loginDE from '../../public/locales/de/loginDE.json';
import loginEN from '../../public/locales/en/loginEN.json';
// the translations
// (tip move them in a JSON file and import them)
const resources = {
en: {
translation: loginEN,
},
de: {
translation: loginDE,
},
};
void i18n
.use(backend)
.use(LanguageDetector)
.use(initReactI18next) // passes i18n down to react-i18next
.init({
fallbackLng: 'en',
interpolation: {
escapeValue: false, // react already safes from xss
},
resources,
lng: 'en',
keySeparator: false, // we do not use keys in form messages.welcome
react: {
useSuspense: true,
},
});
export default i18n;
ReactDOMServer does not yet support Suspense.
Change your useSuspense value like that:
react: { useSuspense: false },
TypeError: i18n.changeLanguage is not a function
Make sure you wrapped a component with I18nextProvider

i18n load translations from s3bucket

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 what 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?
Based on your responses in the comments, looks like you are missing the translation part, and it pre-appending undefined as a namespace in the url.
Try this:
// i18n
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 () {
return `${AWS_CONFIG.aws_app_translations_path}/{{lng}}/{{ns}}.json`
},
crossDomain: true
}
}
Based on the loadPath config doc:
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
Once I changed the way I was importing the config file it worked. I am guessing there is a loading issue on live, but having changed my i18n file from
/*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;
to
import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import Backend from "i18next-http-backend";
import { initReactI18next } from "react-i18next";
import {config} from './config';
const {aws_app_translations_path} = config;
const options = {
order: ['navigator', 'localStorage'],
caches: ['localStorage'],
fallbackLng: "en-GB",
debug: true,
defaultNS: 'translation',
load: 'currentOnly',
interpolation: {
escapeValue: false // not needed for react as it escapes by default
},
backend: {
loadPath: (lng, ns) => {
return `${aws_app_translations_path}/${lng}/${ns}.json`;
},
crossDomain: true
}
}
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init(options);
export default i18n;
Fixed it. Initially AWS_CONFIG was being returned as undefined despite working for the rest of the entire app. Changing the config file to live in the root directory of the project and the way it was imported solved the issue.

nextjs with i18next and i18next-node-fs-backend

I am working on a multilingual site , with nextjs for SSR and i18next for making multi-language mechanism.
Due to using next SSR, I could not use i18next-xhr-backend and I should use i18next-node-fs-backend but in this case I figure out this error for fs :
Module not found: Can't resolve 'fs' in '/Users/macbook/Desktop/project/node_modules/i18next-node-fs-backend/lib'
Is there any universal way to fetch i18next language json file?
After burning one day, I found how to do this. I use i18next-fetch-backend (actually with little edit in this library, which I'll send a PR), in my i18n.js componenet, add isomorphic-fetch as regular fetch API in config obj.
import i18n from 'i18next'
import Backend from 'i18next-fetch-backend'
import { initReactI18next } from 'react-i18next'
import fetch from 'isomorphic-fetch'
i18n
.use(Backend)
.use(initReactI18next)
.init({
lng: 'fa',
backend: {
/* translation file path */
loadPath: '/static/i18n/{{ns}}/{{lng}}.json',
fetch
},
fallbackLng: 'fa',
debug: true,
/* can have multiple namespace, in case you want to divide a huge translation into smaller pieces and load them on demand */
ns: ['translations'],
defaultNS: 'translations',
keySeparator: false,
interpolation: {
escapeValue: false,
formatSeparator: ','
},
react: {
wait: true
}
})
export default i18n
for i18next-fetch-backend library I add below condition for default config , on line181 of i18next-fetch-backend.cjs.js file :
fetch: typeof fetch === "function" ? fetch : (() => {}),

Translations in a sub package

Im trying to translate some stuff in my React application. This works fine with i18next and react-i18next. Im using the withNamespaces HOC to render the translations and scan them with PoEdit. So far so good.
There are however two issues that i am facing. I also have a library that holds all my UI components. More like all the styling which extends from semantic-ui itself. Over there also some translations are applicable and i wanted to use the same react-i18next there as well. While testing in storybook all looks good however when i run npm link and link the package to my main application i suddenly get this error:
caught TypeError: (0 , _reactI18next.withNamespaces) is not a function
The second question i do have is that how can i extend the translations? For example i am having this library which translates field A to be "How are you doing?". However when running a project for a customer i notice that the customer wants another translation for something that is part of the lib.
Is there a way then still to overwrite it? Since the fact the translations are bundled of course and loaded internally in the component.
Below is some code how it looks:
import i18n from 'i18next';
import { reactI18nextModule } from 'react-i18next';
import XHR from 'i18next-xhr-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
i18n
.use(XHR)
.use(LanguageDetector)
.use(reactI18nextModule)
.init({
load: 'languageOnly',
backend: {
loadPath: '../dist/locales/{{lng}}/{{ns}}.json'
},
fallbackLng: {
'en-US': ['en']
},
ns: ['uielements'],
defaultNS: 'uielements',
fallbackNS: 'uielements',
debug: false,
keySeparator: '##',
interpolation: {
escapeValue: false // not needed for react!!
},
react: {
wait: true,
bindI18n: 'languageChanged loaded',
bindStore: 'added removed',
nsMode: 'default'
}
});
export default i18n;
And the components itself:
export default withNamespaces()(Dialog);
I had the same issue. It turned out that withNamespaces HOC component was introduced in react-i18next version 8.0.0. Ensure that you have the latest version:
npm i i18next#latest react-i18next#latest
I just checked i18next v12.0.0 and react-i18next v8.3.8. Everything works fine.

react-i18next not loading json translation files in React app created with create-react-app

I've created a React application with create-react-app
I want to implement translations and I've discovered react-i18next
After installing required packages I've setup my i18n.js config file:
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import XHR from 'i18next-xhr-backend';
i18n
.use(XHR)
.use(LanguageDetector)
.init({
debug: true,
lng: 'en',
nsSeparator: false,
keySeparator: false,
fallbackLng: false,
backend: {
loadPath: 'locales/{{lng}}/{{ns}}.json'
}
});
export default i18n;
I'm getting this error: i18next::backendConnector: loading namespace translation for language en failed failed parsing locales/en/translation.json to json
It happens because webpack doesn't find the json file and is returning the index.html file contents instead:
I'm not sure where you put the locale files, but I see two issues:
You have specified a relative URL so you load /kiosk/parents/locales rather than /locales. You need to add a slash at the beginning of the load path.
For Create React App to serve static files, you need to put them into the public folder. This is described in its User Guide in more detail. So make sure locales is inside the public folder in the project root.
Hope this helps!
Just in case anyone needs this like I did:
If you happen to have changed your homepage path in your package.json file like so:
...
"homepage": "/tom/",
...
you also need to add this part to i18n like so:
i18n
.use(XHR)
.use(LanguageDetector)
.init({
debug: true,
lng: 'en',
nsSeparator: false,
keySeparator: false,
fallbackLng: false,
backend: {
loadPath: '/tom/locales/{{lng}}/{{ns}}.json'
}
});
export default i18n;

Resources