Translating Your React Native App With i18n-js & Expo-Localization
I’ll apologise in advance for the back and forth on the spelling of the word ‘locali(s|z)e’. I’m British but the library uses the American spelling.
During the development of JiffyCV I never really gave much thought to the localisation needs of an app until I went to submit to the different app stores and realised the scale of markets being supported.
Localisation is a tricky task to get right, especially in the domain JiffyCV exists in as it’s not as simple as translating text, but there are considerations into the format of data like dates (in the UK we use DD/MM/YYYY, in the US it’s MM/DD/YYYY).
Localisation also covers the names of entities and concepts. For instance in the UK the terms CV and Resume are interchangable and refer to a short summary of your skills (ideally it’s a single page) but in the US the term CV isn’t really used and a Resume can often span multiple pages.
With localisation having such an impact into how the app looks and how users understand the app’s concepts there needs to be an easy way to implement the logic to determine what to show based on the locale the user is using.
Luckily Expo provides a library called
expo-localization to get the user’s locale from their device and this can be combined with
i18n-js to make adding translations to your app really easy.
There are also some development patterns you can follow to integrate localisation into your code base that you should think about, even if you’re not adding localisation at this point in time.
Centralising your app’s text
Depending on the size of the app and the development approach taken, centralising your app’s text may take some time but it’s the important first step that will make localisation easier to implement.
In order to centralise your app’s text you’ll need to go through every part of your code base to identify text that is shown to the user and extract it out of a single object that can be imported to replace the current string with the value for the key that represents the text.
When centralising the text in JiffyCV I chose to create a set of enums that represented the key for the text in a specific component and then created a heirarchy that represented the different screens and the components rendered on them.
This made it easier to understand the context of the key being used to retreive a localised string as I could clearly see the heirarchy and where that localised string would be used.
During this process you might find that you have duplicated strings spread across a number of files and it can be tempting to remove this duplication. Before doing so, it’s important to think about the context that those duplicated strings appear in as when those strings are translated they might require different text to convey those contextual differences.
If you have dynamic strings that use variables such as those using template literals you can replace these with strings that use placeholders instead. The format for these placeholders is
name would be the name of the variable that you’ll be passing in via a context (more on that later).
Getting the user’s locale
The user’s locale is controlled by the user‘s device settings so we need to use the
locale constant from
expo-localization to read this value and use it in our code.
Other locale information that
expo-localization provides that can be useful is the user’s timezone (via
timezone constant) and if the language reads right to left or left to right (via
On Android the user is able to change their locale on the fly and return to your app so if you use the constants when your app boots these may be out of date. The
getLocalizationAsync function allows you to query the device for these updated locale settings and returns the same set of constants for you to use.
Setting up i18n-js to return localised strings
The Expo documentation uses
i18n-js for storing the localised strings but if you’re using another library you should be able to integrate with this by passing in the
locale value from
Returning localised strings using
i18n-js is relatively simple. The
i18n object has a
translations object that takes a key in either a language code (e.g.
en ) or language and region separated by a hyphen (e.g.
en-GB ) format.
The value for the language in
translations is an object with keys that match the keys we used when centralising our app’s text. As we already built an object as part of that centralisation we can just import and use that for the default language.
For additional locales we can then create copies of the existing centralised string object and update the values to be the localised versions of that string.
In order to set the locale to be used we pass the
locale value from
i18n.locale . This means that later when we want to get a string via the
i18n.t() function that it returns strings for that locale.
Another setting to enable is the
i18n.fallbacks value, setting this to
true will allow keys that aren’t found in a locale to be pulled from locales that have them defined. This can be useful when you’re localising regional differences and don’t want to duplicate entries.
Using localised strings in your app with i18n-js
Once we have the user’s locale and we have
i18n-js set up we can start adding localised versions of our app’s strings into our app.
In order to get a localised string we need to import our
i18n instance and use the
i18n.t() function, passing in the key for the string we want to get the value of.
The enum structure I set up for JiffyCV works wonders here as I can create a path through that to return the appropriate key. In order to make my life even easier I added an export of that structure to my
i18n module allowing me to import both from one place.
Working with dynamic strings
If you converted your template literal strings in to strings with placeholders as part of the centralisation effort then you can set the value of the placeholders by passing in a context object.
This context object uses the placeholder name as a key and will set the substitute placeholder with the provided value when returning the localised string.
Advanced usage of i18n-js
i18n-js library offers a lot more functionality that I’ve not personally used so won’t be covering but documentation can be found on the libraries’ Github.
The below Snack shows using
i18n to provide localisations for
en-US . If you run the Snack on your local device and use those locales you’ll see different text.
By using useful data structures and design patterns you can make localisation easy enough to implement and in doing so we vastly increase the range of markets the app can be made available in.
Even if you’re not actively looking to implement localisation into your app you can still refactor your app to support it without impacting your development approach.