Photo by chuttersnap on Unsplash

Localization in React Native with React Navigation

Marie Starck
Level Up Coding
Published in
5 min readJan 15, 2020

--

In this article, I will go over how to internationalize your React Native app with React Navigation. It will expand on an already existing guide by React Navigation on localization for a screen by adding the steps necessary for a tab or drawer navigator.

Here is the full code if you want to use it to follow along:

drawer navigator: https://github.com/mariesta/localization-drawer-react-navigation

tabs navigator: https://github.com/mariesta/localization-tabs-react-navigation

Main technologies used in this tutorial:

  • React Native 0.61
  • React Navigation 4
  • i18next 19 & react-i18next 10 (but you can substitute it for your own framework)
  • Expo SDK 36 (again, personal preference. You don’t have to use it)

I will add all the documentation at the bottom should you need it.

Notes: I will be using react-i18next as my internationalization framework. If you decide to also, please check your version of React Native. The latest version of react-i18next (v10) uses hooks which are only available on react native version v0.59 or higher. If you have an older version of React Native, fear not. You can use the legacy version of react-i18next (v9) which works just as well but some of the functions have been renamed so watch out for those.

Project Setup

I personally use Expo to create and run my React Native apps. To create a new project, just run expo init [project name] and then cd into your project.

Install i18next and react-i18next with npm (or yarn)

npm install i18next react-i18next

If you need icons for the drawer navigator, may I suggest React Native elements?

npm install react-native-elements

i18n.js

In our project root, we will create an i18n.js file. Again, this is dependent on your internationalization framework.

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
// the translations
// (tip: move them in separate JSON files and import them)
const resources = {
en: {
translation: {
"screen": "Screen {{ order }}",
"tab": "Tab {{ order }}",
"change_language_english": "In english",
"change_language_french": "In french"
}
},
fr: {
translation: {
"screen": "Écran {{ order }}",
"tab": "Onglet {{ order }}",
"change_language_english": "En anglais",
"change_language_french": "En français"
}
}
};
i18n
.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
resources,
lng: "en",
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false
}
});
export default i18n;

First, we create our translations. As it keeps growing, I suggest you create a separate JSON file for each language and import them into this file. Second, we create our i18n instance by setting a few configurations.

App.js

Once the i18n file has been completed, we import it into our App.js file.

import React, { useState } from 'react';
...
import AppNavigator from './navigation/AppNavigator';
import './i18n';
export default function App(props) {
return (
<View>
...
<AppNavigator />
</View>
);
}

AppNavigator.js

AppNavigator.js is where our AppContainer lives.

React Navigation 4 has a very handy global props called screenProps that allows us to pass props which will be accessible to all the children navigators. This will be very useful for us because it will allow us to translate all the labels in tabs, drawers, and whatnot.

import React from 'react';
import { createAppContainer } from 'react-navigation';
import { withTranslation } from 'react-i18next';
import MainNavigator from './MainNavigator';const AppContainer = createAppContainer(MainNavigator);class AppNavigator extends React.Component { render() {
const { t, i18n } = this.props;
return (
<AppContainer
screenProps={{
t,
i18n
}}
/>
);
}
}
export default withTranslation()(AppNavigator);

First, we import our main navigator which we will cover in the next step. Second, we create our AppContainer with it. Third, we wrap our AppNavigator with withTranslation() which will add the i18n instance and the t function. The i18n instance is used for changing the language (say i18n.changeLanguage(‘fr’)) while the t function is helpful for translating our content by passing in the key and getting the content based on the current language (i.e. t(‘hi’) would return “Hello” if the language is English and “Bonjour” for French — granted that you have the correct translations set in your i18n file).

Once we get those two, we add it to the screenProps that I mentioned above.

MainNavigator.js

Now that we added those two, how do we access them?

1) Tab Navigator

To create our bottomTabNavigator, I will use a series of Stacks.

Note: If you don’t know what stacks are, there are tons of documentation including this particular article.

In essence, a stackNavigator looks like this:

const Tab1Stack = createStackNavigator(
{
Home: {
screen: Screen1
}
... // include all the screens that will fall under Tab 1
}
);

Remember screenProps from the last step? We will use it to not only define a tab label but also translate it.

Tab1Stack.navigationOptions = ({ screenProps: { t } }) => (
{
tabBarLabel: t('tab', { order: 1 })
}
);

We get our t function from screenProps and use it to get the correct label for our tab.

If you wanted to translate the header of the screen, you can use the same logic and slightly tweak your stackNavigator.

const Tab1Stack = createStackNavigator(
{
Home: {
screen: Screen1,
navigationOptions: ({ screenProps: { t } }) => ({
title: t('screen', { order: 1 })
})
}
}
);

Create as many stacks as you have tabs and then end your file with:

const tabNavigator = createBottomTabNavigator({
Tab1Stack,
Tab2Stack,
Tab3Stack,
});
export default tabNavigator;

Here is the full code for MainNavigator.js

2) Drawer Navigator

It’s not much different from the tab Navigator. First, we have a stack:

const Drawer1Stack = createStackNavigator(
{
Home: {
screen: Screen1,
},
... // include all the screens for your first drawer option
}
);

Only when we set the navigation option for the stack, instead of setting tabBarLabel and tabBarIcon, we set drawerLabel.

Drawer1Stack.navigationOptions = ({ screenProps: { t } }) => ({
drawerLabel: t('screen', { order: 1 }),
});

The navigationOptions for a particular screen header in the stack is also similar. Apart from the fact that I added an Icon on the left for toggling the drawer.

const Drawer1Stack = createStackNavigator(
{
Home: {
screen: Screen1,
navigationOptions: ({ navigation, screenProps: { t } }) => ({
title: t('screen', { order: 1 }),
headerLeft:
<Icon
name='bars'
size={18}
type='font-awesome'
containerStyle={{paddingLeft: 10}}
onPress={() => navigation.toggleDrawer()} />
})
}
}
);

Create as many stacks as you need and finish your file with your drawerNavigator:

const drawerNavigator = createDrawerNavigator({
Drawer1Stack,
Drawer2Stack,
Drawer3Stack
});
export default drawerNavigator;

Here is the full code for DrawerNavigator.js

Change language

Now that we have set up our navigator, how can we test it by changing the language? Thankfully, screenProps is not just limited to stacks and navigators. It can also be accessed in a screen. This allows us to do this:

import React from 'react';
import { Button, View, StyleSheet } from 'react-native';
export default class Screen1 extends React.Component {render() {
let { t, i18n} = this.props.screenProps;
return (
<View style={styles.container}>
<Button
transparent
onPress={() => i18n.changeLanguage('en')}
title={t('change_language_english')}/>
<Button
transparent
onPress={() => i18n.changeLanguage('fr')}
title={t('change_language_french')}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingTop: 20
},
});

The i18n instance and t function are now both available and allow us to not only translate the button title but also change the language.

If you liked the article, you can follow me on Twitter.

--

--

Freelance Full-Stack JS developer & entrepreneur (mariestarck.com) | I work with designers and entrepreneurs to bring their ideas from paper to code