Technology has made the world smaller and brought us all closer. Anyone on the other side of the globe is reachable and accessible and may be interested in knowing what you do inspite of not knowing your language. This creates the requirement for localized apps which are adaptable to the needs of local languages and formats.
As per “whatis.com”, Internationalization is the process of planning and implementing products and services so that they can easily be adapted to specific local languages and cultures, a process called localization.
In any application development, the localization enablement has become a key ask. In this blog, we will see how we can achieve internationalization(sometimes referred to as i18n for the 18 letters between the ‘I’ and the ’n’ in the word Internationalization. ) in React applications.
I18next is one of the most popular and established JavaScript frameworks to cater to complete internationalization requirements. React-i18next uses the i18next ecosystem and makes it available to React applications in the right way.
In this blog, we will use these 2 packages and see how we can implement the common internationalization requirements of content translation and localized formatting, for a React application.
Dependencies
Let’s start by building a react app using create-react-app.
1 |
npx create-react-app my-i18n-app |
We will add some basic content on the app on which we will apply localization and formatting. For this purpose, we will use semantic-ui-react, so let’s install this.
1 |
npm install semantic-ui-react semantic-ui-css --save |
Next, let’s install the react-i18next and i18next, the internationalization related dependencies:
1 |
npm install react-i18next i18next --save |
We will be using i18next to detect user language and load translation for which we need to install the below 2 additional dependencies.
1 |
npm install i18next-xhr-backend i18next-browser-languagedetector --save |
With all the dependencies installed, next we move to configuration.
I18next configuration
Create i18n.js for the i18next configuration basic options in the src folder.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import Backend from 'i18next-xhr-backend'; import LanguageDetector from 'i18next-browser-languagedetector'; i18n .use(Backend) // detect user language .use(LanguageDetector) // pass the i18n instance to react-i18next. .use(initReactI18next) // init i18next .init({ fallbackLng: 'en', debug: true, interpolation: { escapeValue: false, // not needed for react as it escapes by default }, react: { bindI18n: 'languageChanged', bindI18nStore: '', transEmptyNodeValue: '', transSupportBasicHtmlNodes: true, transKeepBasicHtmlNodesFor: ['br', 'strong', 'i'], useSuspense: false, } }); export default i18n; |
The above file should be imported in the index.js along with the semantic.min.css as below
1 2 3 |
import 'semantic-ui-css/semantic.min.css'; // import i18n (needs to be bundled ) import './i18n'; |
Setting up Translations
With the configuration ready and imported in the application bundle, let’s define the translation files.
We will create a subfolder under a public folder called locales which will contain the language specific folders. In this case, we have created 2 subfolders inside locale, namely en and de to handle English and German.
Inside each language folder, define a translations.json(the default namespace if not provided as part of config) with the below structure of key-value pairs:
1 2 3 |
{ "title": "Welcome to Internationalization in React" } |
Using the key, the value is derived so the key(“title” here) should be the same for all other languages as shown below for german:
1 2 3 |
{ "title": "Willkommen bei Internationalization in React" } |
You can create multiple key-value pairs and multiple namespaces in the same folder which you can explicitly specify while using the translation.
By default, if no namespace is given, it uses the translation.json.
I have also created another namespace file called messages.json to showcase the capability of having multiple files and namespaces.
Putting i18next to use for translation
Now the i18next is ready and available for the application to use in two main aspects:
- The t function which is used to perform the translation
- i18n instance which is mainly used to change the language based on user interaction using the i18n.changeLanguage function.
Below listed are the different ways this can be used to translate the language content.
- useTranslation Hook
- withTranslation HOC
- Translation render prop
useTranslation Hook
The useTranslation Hook provided as a part of React Hooks can be used in Functional components.
The first component we define is the HeaderPart component where we will make use of useTranslation hook.
Use the hook as below which returns the t function and i18n instance :
1 |
const { t, i18n } = useTranslation("messages"); |
messages are the namespace which is explicitly passed to the hook. It is an optional parameter in the absence of which the default will be taken.
Now i18n.changeLanguage function can be used to set the language.
The t function can be used to get the content defined for the key for the selected locale. t(“title”) will return the translation of the key “title” if defined for the language.
The complete HeaderPart component code looks as below where a semantic button group is added to provide the language changing capability:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import React from 'react'; import { Header, Button } from 'semantic-ui-react'; import { useTranslation} from 'react-i18next'; const HeaderPart = () => { const { t, i18n } = useTranslation("messages"); const changeLanguage = lng => { i18n.changeLanguage(lng); }; return(<Header as='h2' block color="teal" textAlign="center"> {t("title")} <Button.Group floated='right'> <Button onClick={() => changeLanguage('en')}>English</Button> <Button.Or /> <Button onClick={() => changeLanguage('de')}>German</Button> </Button.Group> </Header> ); } export default HeaderPart; |
withTranslation HOC
The withTranslation higher order component also provides the t function and i18n instance as a part of props for the component. It can be imported and wrapped around the base component for the i18n specific features to be available as part of props.
You can get a local instance of the variables from the props and use it as in the earlier example.
1 2 3 4 |
let { t, i18n } = this.props; <p> {t("accordion.details1")} </p> |
Translation render prop
This is similar to withTranslation and gives a t function and i18n instance to work with the translation requirements. You can use it as below by providing the namespace ns parameter optionally.
1 2 3 4 5 |
<Translation ns="messages"> { (t, { i18n }) => <p>{t("accordionMsg.details2")}</p> } </Translation> |
In the next AccordionSample component, we will see the use of both useTranslation and Translation render prop, both provided as part of ‘react-i18next’.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
import React, { Component } from 'react'; import { Accordion, Icon } from 'semantic-ui-react'; import { withTranslation, Translation } from 'react-i18next'; class AccordionSample extends Component { state = { activeIndex: 0 } handleClick = (e, titleProps) => { const { index } = titleProps const { activeIndex } = this.state const newIndex = activeIndex === index ? -1 : index this.setState({ activeIndex: newIndex }) } render() { const { activeIndex } = this.state; let { t } = this.props; return ( <Accordion fluid styled> <Accordion.Title active={activeIndex === 0} index={0} onClick={this.handleClick}> <Icon name='dropdown' /> {t("accordion.title1")} </Accordion.Title> <Accordion.Content active={activeIndex === 0}> <p> {t("accordion.details1")} </p> </Accordion.Content> <Accordion.Title active={activeIndex === 1} index={1} onClick={this.handleClick}> <Icon name='dropdown' /> {t("accordion.title2")} </Accordion.Title> <Accordion.Content active={activeIndex === 1}> <Translation ns="messages"> { (t, { i18n }) => <p>{t("accordionMsg.details2")}</p> } </Translation> </Accordion.Content> </Accordion> ) } } export default withTranslation()(AccordionSample); |
Now the components can be rendered in App.js as below
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import HeaderPart from "./HeaderPart"; import AccordionSample from './AccordionSample'; function App() { return ( <div className="App"> <HeaderPart/> <AccordionSample/> </div> ); } export default App; |
With the 2 internationalised components and locale changing between en(English) to de(German), the app looks like below by default/selecting English:
The app looks like below on selecting German where the translation key-values are defined in the json file under public/locales/de(translation as provided by Google):
You can find the complete code for this project here.
Conclusion
This blog introduces you to how basic internationalization can be set up and used in React applications using react-i18next and i18next. In the next blog in this series, we will see how we can use interpolation and formatting for locale specific requirements.
People who are looking for localisation in the react i guess the below url gives more understanding if you miss anything in this blog post.
https://medium.com/@jishnu61/6-easy-steps-to-localize-your-react-application-internationalization-with-i18next-8de9cc3a66a1
I see the sample shared have two accordion views created where each accordion displays one in english and the other in German. What would be the case when i’m going to support 50 languages.
This example has a button group to enable the user to explicitly select a language from the given 2 choices(English or German). Locales(under public folder or within i18n configuration) need to set up for the translations of any number of languages we need, and we can let the language be picked from default language settings of the user which will be detected by the LanguageDetector in ‘i18next-browser-languagedetector’.
You can fork this sandbox for the blog example https://codesandbox.io/s/reacti18nextexample-fslxi and try it.