Photo by Capturing the human heart. on Unsplash

Localization With React Navigation 4 and the Context API

Marie Starck
The Startup
Published in
5 min readFeb 10, 2020

--

A couple of weeks ago, I wrote a tutorial on how to add localization with React Navigation. This guide used the global screenProps to pass down functions and instances necessary to translate our content.

But while researching for that article, one sentence in the React Navigation documentation piqued my interest.

You may want to also use React’s context API […] in order to make it easier to access the translate function from a variety of components.

Feeling curious, I took an afternoon to see how that would work. The following tutorial is my approach to this problem. I will not be using the global props screenProps and instead rely solely on the context API.

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

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 that 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.

We start by creating our translations (line 5). As it keeps growing, I suggest you create a separate JSON file for each language and import them into this file. We then create our i18n instance by setting a few configurations.

Don’t forget initReactI18next as this is necessary for React projects (line 24).

LocalizationContext.js

Our LocalizationContext.js file is where we create our Context object.

We do so by passing it a default value whether it’s a string or an object. In our case, it’s the latter and it consists of two things:

  1. the t function which is used to translate our content. It will accept two parameters: the unique key of the string we want + any additional data (say a person’s name).
  2. the i18n instance (used to change locale with i18n.changeLanguage(‘fr’)).

The default value is only used when a component doesn’t find a matching Provider above it in the React tree. So, it is not enough to create a Context object. We also need a Provider.

LocalizationProvider.js

If we created our context object with a default value, now it’s time to pass it some real data. Whatever we pass will be available to all the descendants in the tree.

First, we import our i18n file and withTranslation from react-i18next (line 2 + 3). Second, we wrap our LocalizationProvider with withTranslation (line 18). Doing so allows us to access our translation function and i18n instance from props (line 9). Third, we import our Context object created in LocalizationContext and create our provider by passing in t and i18n essentially overwriting the default value that we used in LocalizationContext.js.

Because our provider will wrap itself around components, we also add children so we can render the descendants correctly (line 12).

App.js

Our brand-new provider will wrap itself around our application so that any components may access our new context to translate our content.

Access LocalizationContext.js

Our setup is complete. Now, it’s time to use our localization context. Here is an example in a simple screen.

After importing our localization context object, we pass it in useContext (line 6).

useContext accepts a context object (created with createContext) and returns the current context value which is in turn determined by the closest matching provider up the tree. In our case, it will be our LocalizationProvider that we included in App.js. Also remember that in our LocalizationProvider.js file, we passed our translate function (t) and the i18n instance. Both can now be retrieved and used (line 6).

For our content, we have the t function (i.e title={t(‘change_language_english’)}) and to change the language, i18n (i.e i18n.changeLanguage(‘en’)).

Navigation

This is where a few approaches are available to you.

If you simply want to get a project up and running quickly, then the easiest way is to make use of screenProps. Just like we did in Screen1.js, import the LocalizationContext object, get t and i18n with useContext and pass it as a props with screenProps.

Here is what it would look like:

Note: Scroll down in this guide to MainNavigator.js for how to use screenProp to get the t function to translate your tab/drawer labels and headers.

A second approach is to use custom components for your labels and headers. In those components, we will be able to call useContext and retrieve our translate function. Lucky for us, React Navigation offers a few options that accept React components.

Note: I will be using StackNavigator for my tab/drawer. If you are not familiar with it, it works by bundling up all the screens that fall under a particular tab/drawer into a stack and then putting all the different stacks together to create your drawer/tab navigator. Here is a guide that explains it (and the different types of navigators) in more details.

Header

For example, we can make use of headerTitle to create a custom header and pass two props (the translation key and any data that we want to add).

Even luckier, HeaderTitle can be imported from react-navigation-stack so we can get the default styling. in this component, we get t from useContext (we are pretty familiar with this now) along with the translation key and data to get the correct translated content.

Tab Label

We have translated our headers. Now we move on to our tabs. You can get the focused boolean and pass it to my TabBarLabel to know when the tab is selected and apply the appropriate styling.

Here is my TabBarLabel component. Unfortunately, React Navigation does not have any pre-built component for me to import but I made it work with a simple Text. I used the focused boolean to change the color depending on if the tab is selected or not.

Drawer Label

The logic for the drawer label is not that much different. Instead of tabBarLabel, we use drawerLabel.

The complete file for each navigator can be accessed here:

In summary:

  1. Create your project with all the necessary dependencies
  2. Initialize your internationalization framework
  3. Create your context object
  4. Create your provider and wrap it around your application
  5. Access your context value thanks to React.useContext and translate your content as needed

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

--

--

Marie Starck
The Startup

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