How to use ESLint to improve your workflow
ESLint ecosystem can be super handy for JS and TS codebases, but are you using it right? Keep reading to learn about useful ESLint rules and a get a little bit more out it.
React i18next is a library that allows to set up internationalization on your website. This library is based on i18next.
The first thing we will do is add the library to our project:
npm install react-i18next --save
Then we need to configure the way we are going to use it.
For that, I will create a file helpers/i18n.js
:
import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; i18n .use(initReactI18next) .init({ debug: true, // useful to see events that occur during development lng: 'en', fallbackLng: 'en', resources: { en: { translations: { // default namespace 'home.hello': 'Hello! Welcome to my app!' }, }, }, }); export default i18n;
And I will import it into index.js
of my application:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import i18n from './helpers/i18n'; // initialisation of i18next
ReactDOM.render(
<App />,
document.getElementById("root")
);
Here is the App.js
file with a simple example:
import React from 'react';
import { useTranslation } from 'react-i18next';
const App = () => {
const { t } = useTranslation();
return (
{t('home.hello')}
);
};
export default App;
Here we use the useTranslation
hook to access the translation service, but you can also use the HOC withTranslation
:
import React from 'react';
import { withTranslation } from 'react-i18next';
const App = ({ t }) => {
return (
{t('home.hello')}
);
};
export default withTranslation()(App);
If you want to pass variables, this can also be done very easily. Let's say our translation key looks like this:
translations: { 'home.hello': 'Hello, {{ name }}! Welcome to my app!' },
To pass the name
variable in our translation key we can do:
{t('home.hello'), { name: 'Astronaut' }}
If you need to put HTML in the translation, or if your translation key contains some, you can use the Trans
component:
import React from 'react';
import { Trans } from 'react-i18next';
const App = () => {
return (
<Trans values={{ name: 'Astronaut' }}><h1>home.hello</h1></Trans>
);
};
export default App;
Of course, we also need to handle the case where the translation varies according to a number or quantity.
translations: { 'message': 'You have one message', 'message_plural': 'You have several messages', },
In this case, we will specify an additional argument, count
, like this:
{t('home.hello'), { count: 5 }} <Trans count={5}><h1>home.hello</h1></Trans>
Since we are working on a multi-language site, we want the user's preferred language to be automatically detected. In general, it is the browser's language. For this, we will add a new dependency:
npm install i18next-browser-languagedetector --save
And we will change our configuration as follows:
import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; // <= new i18n .use(LanguageDetector) // <= new .use(initReactI18next) // ... ; export default i18n;
Now that our application is able to detect the user's browser language, the user may want to change it. For this, let's add a button:
import React from 'react';
import { useTranslation } from 'react-i18next';
import Button from '@material-ui/core/Button';
const LanguageSwitcher = () => {
const { i18n } = useTranslation();
const changeLanguage = async (lng) => {
await i18n.changeLanguage(lng); // i18n.changeLanguage returns a Promise
};
return (
<div>
<Button onClick={() => changeLanguage('en')}>English</Button>
<Button onClick={() => changeLanguage('fr')}>Français</Button>
</div>
);
}
export default LanguageSwitcher;
Obviously, we will want to put the translations into dedicated files, rather than keeping them in the configuration directly. Translation files are simple JSON files. We can imagine the following structure in our project:
public/ locales/ en/ common.json translations.json other.json fr/ common.json translations.json other.json
i18next works with namespaces, and you can have multiple namespaces per language. As a reminder, the default namespace is translations
. In our example, common
, translations
and other
are namespaces.
In this case, each time we want to access the keys that are in a particular namespace, we will do:
const { t } = useTranslation(['ns1', 'ns2', 'ns3']); t('key'); // loaded from namespace 'ns1' t('ns2:key'); // loaded from namespace 'ns2'
It is also possible to define a custom default namespace in the configuration:
import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; i18n .use(LanguageDetector) .use(initReactI18next) .init({ defaultNS: 'common', // <= new // ... }); ; export default i18n;
Now that the translations are in separate JSON files, we need to indicate in the i18next configuration how to retrieve them. For that, we will use i18next-xhr-backend
:
npm install i18next-xhr-backend --save
Let's update the configuration file:
import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import Backend from 'i18next-xhr-backend'; // <= new i18n .use(LanguageDetector) .use(Backend) // <= new .use(initReactI18next) .init({ backend: { // <= new loadPath: '/locales/{{lng}}/{{ns}}.json' } // ... }); ; export default i18n;
Note that the path specified in loadPath
is in the public
folder at the root of your project.
We have presented in a previous article that we use Localise.biz service and save translation files on a server in the cloud. Hence, we need to recover the files hosted on a remote server:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-xhr-backend';
i18n
.use(LanguageDetector)
.use(Backend)
.use(initReactI18next)
.init({
debug: true,
lng: 'en',
fallbackLng: 'en',
defaultNS: 'common',
backend: {
loadPath: `${process.env.TRANSLATIONS_ENDPOINT_URI}/{{ns}}.{{lng}}.json`, // we simply indicate the full URL to retrieve files
}
});
;
export default i18n;
React i18next can be configured to work with SSR. Here is the page that explains how to put this in place: https://react.i18next.com/latest/ssr.
Nevertheless, we had a problem activating the SSR - the loading of the translations from the remote server was not done on the server side. This happens because i18next-xhr-backend
uses fetch
to recover the files, and fetch
is not available on the server side.
As a result, we needed to write a custom backend based on the documentation here: https://www.i18next.com/misc/creating-own-plugins#backend. We used the cross-fetch
library that works on both the client and server sides.
Author(s)
Marie Minasyan
Astronaute Raccoon @ ElevenLabs_🚀 De retour dans la Galaxie.
You wanna know more about something in particular?
Let's plan a meeting!
Our experts answer all your questions.
Contact usDiscover other content about the same topic
ESLint ecosystem can be super handy for JS and TS codebases, but are you using it right? Keep reading to learn about useful ESLint rules and a get a little bit more out it.
Tutorial on the basics of NextJS for building a website.
You may not be using the React states optimally and I'll explain why