How to use cookies in i18next configuration? - reactjs

I want to use react-cookie in my i18n.js file to set a language.
I was trying like this, but it send this error:
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
import LanguageDetector from 'i18next-browser-languagedetector';
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import * as en from './locales/en.json'
import * as pl from './locales/pl.json'
import { useCookies } from 'react-cookie'
const { cookies } = useCookies(['i18n_locale'])
const resources = {
en: {
translation: en.default
},
"pl-PL": {
translation: pl.default
}
}
i18n
.use(initReactI18next)
.use(LanguageDetector)
.init({
resources,
lng: cookies.i18n_locale ? cookies.i18n_locale : window.navigator.language,
keySeparator: false,
interpolation: {
escapeValue: false
}
})
export default useCookies(i18n)
I want it working!

React hooks have certain rules associated with their use (see this page). One of them is mentioned right there in the error message:
Hooks can only be called inside of the body of a function component.
Regarding react-cookie usage: first off, wrap your root element in the provider component. This will allow invoking useCookies down the tree in any function-based component. Check out this example from lib's docs.

Related

Best way to store text messages, instructions,... to user (and language translations) in React app

Let's say I want to display messages on my React app: the message "Send code" on a certain button, instructions like "Introduce the code below" and so on.
Currently, I display them directly on the button, let's say:
<Button className="Log_in_button" block size="lg" onClick={() => this.requestForValidationVerificationCode()}>
{'Send code'}
</Button>
But there must be a way to store this message on some configuration file to fetch it from. What should be the structure of such a file and how should I fetch the message on my React file?
Furthermore, I am sure there is a proper way to store the translation into other languages: Spanish, German, Russian,... So if your user has chosen that language from the dropdown, you display the same texts but in the language chosen. How should I introduce the dependency on the language: having the texts in all languages in the same file and selecting the language via a parameter (say language='ES'), having a different file for each language,...?
Any help/guidance on how to properly handle this in a React-like way would be much appreciated.
Thanks in advance!
You are looking at internationalization! And one of the best rich libraries for this is i18next library for react! Simple to use! Good doc! This answer will be more then sufficient to get it well done!
https://react.i18next.com/
To note: (i18n => internationalization => i <18 chars> n)!
Getting started is a good place to start! The doc is well!
https://react.i18next.com/getting-started
For simple translations you use somethig like bellow:
<div>{t('simpleContent')}</div>
If no translation is provided the whatever was passed to t() method will be used! Otherwise you can provide translation files as much as you want!
For all languages! There is the fallback lang config too!
How to init and use example
import React from "react";
import ReactDOM from "react-dom";
import i18n from "i18next";
import { useTranslation, initReactI18next } from "react-i18next";
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.init({
// the translations
// (tip move them in a JSON file and import them,
// or even better, manage them via a UI: https://react.i18next.com/guides/multiple-translation-files#manage-your-translations-with-a-management-gui)
resources: {
en: {
translation: {
"Welcome to React": "Welcome to React and react-i18next"
}
}
},
lng: "en", // if you're using a language detector, do not define the lng option
fallbackLng: "en",
interpolation: {
escapeValue: false
}
});
function App() {
const { t } = useTranslation();
return <h2>{t('Welcome to React')}</h2>;
}
// append app to dom
ReactDOM.render(
<App />,
document.getElementById("root")
);
You can see how to init!
And how translations are passed! In this case they are inline! Or directly passed! (see till the end! There is more options)
Production
For production different options are availables! Mainly have translation part of the react app (bundle)! Or on the server side! For a web app! Having them server side can be more suitable!
This page will give a strong overview:
https://react.i18next.com/guides/quick-start
Configure i18n
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
// the translations
// (tip move them in a JSON file and import them,
// or even better, manage them via a UI: https://react.i18next.com/guides/multiple-translation-files#manage-your-translations-with-a-management-gui)
const resources = {
en: {
translation: {
"Welcome to React": "Welcome to React and react-i18next"
}
},
fr: {
translation: {
"Welcome to React": "Bienvenue à React et react-i18next"
}
}
};
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.init({
resources,
lng: "en", // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
// if you're using a language detector, do not define the lng option
interpolation: {
escapeValue: false // react already safes from xss
}
});
export default i18n;
Then you make sure to import it in the index.js (ts)! So init script execute! And i18next instance get created!
import React, { Component } from "react";
import ReactDOM from "react-dom";
import './i18n'; // <<<<<<<<--here
import App from './App';
// append app to dom
ReactDOM.render(
<App />,
document.getElementById("root")
);
A more complete config and options
https://react.i18next.com/latest/using-with-hooks
But before that install command:
npm install react-i18next i18next --save
// if you'd like to detect user language and load translation
npm install i18next-http-backend i18next-browser-languagedetector --save
Check the comments in the code bellow:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
// don't want to use this?
// have a look at the Quick start guide
// for passing in lng and translations on init
i18n
// load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
// learn more: https://github.com/i18next/i18next-http-backend
// want your translations to be loaded from a professional CDN? => https://github.com/locize/react-tutorial#step-2---use-the-locize-cdn
.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({
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
}
});
export default i18n;
You can see that there is a plugin that help with getting translation from the backend! Check the link here!
One too can make an ajax call! And get json response! That hold the exact translation then load it! you can check how in the section before the last!
Make sure to check the comments! And docs links! Check what each plugin do! the languageDetector! Is helpful!
Usage and translation
To translate your content ((usage)) (what you wanted above in the button:
useTranslation() hook!
import React from 'react';
// the hook
import { useTranslation } from 'react-i18next';
function MyComponent () {
const { t, i18n } = useTranslation();
return <h1>{t('Welcome to React')}</h1>
}
See the page for more options and details! Using HOC or Translation component! Also Trans component!
Well i'll include the examples:
import React from 'react';
// the hoc
import { withTranslation } from 'react-i18next';
function MyComponent ({ t }) {
return <h1>{t('Welcome to React')}</h1>
}
export default withTranslation()(MyComponent);
import React from 'react';
// the render prop
import { Translation } from 'react-i18next';
export default function MyComponent () {
return (
<Translation>
{
t => <h1>{t('Welcome to React')}</h1>
}
</Translation>
)
}
import React from 'react';
import { Trans } from 'react-i18next';
export default function MyComponent () {
return <Trans><H1>Welcome to React</H1></Trans>
}
// the translation in this case should be
"<0>Welcome to React</0>": "<0>Welcome to React and react-i18next</0>"
More details about trans here
Clearly the cooler way is the hook one!
Otherwise Trans is the way to go for translation with dom elements included (jsx)!
Translations and place
Two options! Have them in server side! Or part of the app bundle!
Server Side
For the server side it's pretty simple you can use the plugin already mentionned above! Check the link here!
Or even simply have an endpoint that serve translation files! And load them yourself! Make sure to load translations on demand! for the one specific lang! And at change! The plugin already allow all this!
Know that to dynamically load translation! You can use:
i18n.addResources() or i18n.addResourcesBundle()!
The second may be more suitable as it give deep and overite as extra options!
https://www.i18next.com/overview/api#addresource
And clearly! With a backend all go as json responses! Which automatically will be loaded as js objects!
To mention in server side! Translation are good to go in files! Both clean! And can be passed to no IT people to update them or add new languages translations.
Part of the app
Otherwise the other option! Is to have them part of the app! For web app you generally don't want to do that to keep the bundle small! But if you work on a desktop app like using electron! Then having it locally make sense!
A good way would be:
Generally you'll make an i18n folder
My config would be!
[![enter image description here][2]][2]
`index.js` is thge i18n module initializing and config file! The one already listed twice!
```ts
// ....
import translationEN from './locales/en/translation.json';
import translationFR from './locales/fr/translation.json';
import detector from 'i18next-browser-languagedetector'
// the translations
const resources = {
en: {
translation: translationEN
},
fr: {
translation: translationFR
}
};
// ....
I used json files here! And can have people Write the translations! Then just add them! And it's more organized!
One can use a js file instead of a json file too!
Change language
https://www.i18next.com/overview/api#changelanguage
import i18n exported instance!
i18n.changeLanguage('fr') // return promise
i18next library and instance!
Know that here we are using the i18n library as per
https://www.i18next.com/
And the react binding! (the init in config)
Know that you can use the i18next instance and all it's api! (import it from the config module! As we export it there!
Here go it's api!
https://www.i18next.com/overview/api
just in case! Generally you don't need to bother while in react! But you may! As for example dynamic loading of resources (translations) (not preloading)! As already mentioned!
Locize a gui managment tool and internationalization platform
https://react.i18next.com/guides/multiple-translation-files
Mentioned here!
You can both profit and check the multi translation! And too name spaces!
Otherwise for big projects! Locize may seems intersting!
And in big projects! Translations should always be separated!
In server side! You can always have the translations go in json files! And normal people can manage them! Like real hired translators!
You can use Context in this case, i.e LanguageProvider in my codes:
You should store languages in a separate .json file.
import React, { useContext, useState } from 'react';
const LanguageContext = React.createContext('en');
function LanguageProvider(props) {
const languages = {
"en": {
sendCodeButton: 'Send code'
}, // en
"vi": {
sendCodeButton: 'Gửi mã'
} // vi
} // languages
const [languageCode, setLanguage] = useState('en');
const changeLanguage = (languageCode) => {
setLanguage(languageCode);
}
return (
<LanguageContext.Provider
value={{ languageCode, changeLanguage, language: languages[languageCode] }}
>
{props.children}
</LanguageContext.Provider>
); // return
} // LanguageProvider
function SomeComponent(props) {
const { languageCode, changeLanguage, language } = useContext(LanguageContext);
return (
<>
<Button>
{language.sendCodeButton}
</Button>
</>
) // return
} // SomeComponent
function App() {
return (
<LanguageProvider>
<SomeComponent />
</LanguageProvider>
) // return
} // App

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

react-i18next - import i18n instance & use directly vs using useTranslation hook

I have a project written in React & support hooks.
I'm trying to use react-i18next to support translations.
Everything works well as I've follow the documentation.
However I stumble upon some problems when I want to use the t() function on helpers / non-component .js files.
Then, I solved it by importing i18n directly from the init file ./i18n.ts that looks something like this
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
i18n
.use(initReactI18next)
.init({
resources,
ns: [
'common',
'dashboard',
'landing'
],
defaultNS: 'common',
fallbackLng: 'en',
supportedLngs: ['de', 'en'],
interpolation: {
escapeValue: false,
},
});
export default i18n;
and I realized that I don't have to use the hook at all since I can just use it like this anywehre across the code, even on my functional component file
import i18n from "#root/i18n"
...
i18n.t('namespace:path')
I would like to know why is it recommended to use the useTranslation hook / withTranslation HOC if you can just import it like this?
I read that useTranslation apply suspense but it seems like the initReactI18next also have suspense applied by default.
I'm curious on if there's any side-effect on not using the recommended hook / HOC ?
I found this answer on their github issues.
But those constants never will be updated on languageChange! So
either:
update them to new language value based on event triggered by i18next: https://www.i18next.com/overview/api#onlanguagechanged
or better have only the keys in the constants and translate as close to the rendering as possible

How to fix 'no fallback UI was specified' in react i18next using hooks

I am trying to implement i18next in my react component using the useTranslation hook, but it keeps saying:
Uncaught Error: Test suspended while rendering, but no fallback UI was specified.
Add a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.
How can I add the <Suspense> any higher than what I've got? What am I doing wrong? How do I fix this? It seems to work OK when I use the <Translation> component. Naturally, it also seems to work OK if I turn Suspense off and try to handle it myself, but that sort of defeats the purpose, I think. How can I make this actually work? Do I have the Fetch backend misconfigured?
Test.js
import React, { Suspense, useState, useEffect, lazy } from "react";
import PropTypes from "prop-types";
import i18n from './i18n';
import { useTranslation } from 'react-i18next';
export default function Test( props ){
const { t, i18n } = useTranslation( 'bingo_game' );
return (
<Suspense fallback={<div>Loading...</div>}>
<div>
Pant leg
</div>
</Suspense>
)
}
Test.propTypes = {
token: PropTypes.string.isRequired,
};
and
i18n.js
import i18n from "i18next";
import LngDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import Fetch from "i18next-fetch-backend";
i18n
.use(LngDetector) //language detector
.use(Fetch)
.use(initReactI18next)
.init({
backend: {
loadPath: "/infra/locales/{{ns}}.json",
// path to post missing resources
addPath: "locales/add/{{ns}}",
// define how to stringify the data when adding missing resources
stringify: JSON.stringify
},
defaultNS: "base",
fallbackLng: "en",
debug: true,
initImmediate: false
});
export default i18n;
I've gone over the docs that I could find as well as this SO post that is not my issue.
in your i18n file add this, it will do the magic :)
.init({
react: {
useSuspense: false,
...
});
It's not best way but I can recommend you to turn off suspense by adding this lines to your's config
i18n
.init({
...
// react i18next special options (optional)
react: {
useSuspense: false,
wait: false,
},
});
Using useSuspense: false is ok but you need to use ready state in this case as well, otherwise your translation keys appear at interface until i18next is ready... Similar to this:
const { t, i18n, ready } = useTranslation()
and in the render section:
{ready && (<SampleComponent />)}
You can set useSuspense: false in useTranslation options.
const { t } = useTranslation("your-namespace", {useSuspense: false});
https://react.i18next.com/latest/usetranslation-hook#not-using-suspense

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.

Resources