Quick i18n setup in React native with autocomplete
Tags: reactnative, typescript, tutorial, javascript
Published: 2022-11-17 17:22:49

## Introduction

I always make sure to set up i18n (internationalization) in my apps even if there will probably be only one language used. The pain of adapting an app that wasn't thought to be multilingual in the first place is too big to procrastinate on it.
Fortunately, I now have an easy setup, with auto-completion with typescript, autodetection of the user language and more.

---

## Setup

To get the user language and handle our translations, we use 2 libraries:

[react-native-localization](https://github.com/zoontek/react-native-localize): a toolbox with a simple API to get the user's locale, units and timezone. (nb: if you use expo, expo-localization is also a good library to use)

[react-i18next](https://github.com/i18next/react-i18next): a powerful and popular framework for internationalization in React / React-native.

```bash
npm install react-native-localize react-18next i18next
```

After the installation, we need only one configuration file and JSON files that will contain our translations for each language.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s65iun0ircait2dj9t4a.png)


```javascript
import AsyncStorage from '@react-native-async-storage/async-storage'
import i18n, {
  LanguageDetectorAsyncModule
} from 'i18next'
import {
  initReactI18next
} from 'react-i18next'
import * as RNLocalize from 'react-native-localize'

import fr from './dictionaries/fr.json'

export const locales = {
  fr: {
    translation: fr
  },
}

export const DEFAULT_LOCALE = 'fr'

export const defaultLanguage =
  RNLocalize.findBestAvailableLanguage(Object.keys(locales))?.languageTag || DEFAULT_LOCALE

export const currentLanguage = i18n.language || defaultLanguage

const useLanguageStorage: LanguageDetectorAsyncModule = {
  type: 'languageDetector',
  async: true,
  detect: async (callback) => {
    AsyncStorage.getItem('language').then((lang) => {
      if (lang) return callback(lang)
    })
  },
  init: () => null,
  cacheUserLanguage: async (language: string) => {
    AsyncStorage.setItem('language', language)
  },
}

i18n
  .use(useLanguageStorage)
  .use(initReactI18next)
  .init({
    fallbackLng: defaultLanguage,
    resources: locales,
    react: {
      useSuspense: false,
    },
  })

export default i18n
```

Then you will have to import that file in your `App.tsx` and that's it. 

---


## Autocompletion with Typescript

One annoying thing when using translation keys is typing the paths again and again. To improve the developer experience I add a small definition to get suggestions with Typescript, it helps to avoid mistakes and reduce the coding time.

Create a file react-18next.d.ts at the root of your project directory or in a `types` folder and add this code:

```javascript
import 'react-i18next'

import fr from './i18n/dictionaries/fr.json'

declare module 'react-i18next' {
  // and extend them!
  interface Resources {
    translation: typeof fr
  }
}
```

With this setup, you should have something like this in VScode

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8l4a9u1m68e35dmbi4h.png)

---

## Usage
  

#### Most common usage

Almost every time a text is used in a screen, you will add that text linked to a key in the JSON file, use the hook provided by `i18next` which is called `useTranslation` and use the key in your text


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a7vjsaomn0fd0wo7usgq.png)

---

#### Access outside of components

Sometimes you may need to use your translations outside of your components in config files or whatever. To do that you can directly import your i18n file and access the `t` method

```javascript
import i18next from './i18n'

i18next.t('my.key')
```

---
#### Nested texts/multiple styles

One powerful feature of react-i18next is the `Trans` component which can interpret custom tags in your translations to use a component for a part of your text.

```JSON
"myKey":"it is my text <color>colored part</color> end of text"
```

```javascript
<Text>
  <Trans 
    components={{ color: <Text font="bold" 
    color="primary" /> }}
  >
    {t("path.to.my.key"}
  </Trans>
<Text>
```

---

## Update the language

To update the language, you use the same hook

```javascript
const { t, i18n } = useTranslation('');
...
<Button onPress={()=>i18n.changeLanguage("de")>{i18n("switchLanguage")}<Button/>
```

---

## Handle dates, currencies, API values

To handle dates, my go-to library is [dayjs](https://github.com/iamkun/dayjs) because it is a very popular library, complete and it has the same API as Momentjs (which I used by in the days).

```
import 'dayjs/locale/fr' // load on demand
dayjs.locale('fr') // use French locale globally
dayjs('2018-05-05').locale('fr-FR').format() // use on a specific text
```

For currencies you have multiple ways to tackle this problem, now I use [Intl](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat)

```
const { i18n } = useTranslation()
<Text>{Intl.NumberFormat(i18n.language, {
    style: 'currency',
    currency: 'EUR'}).format(45012)}</Text>
```

On a final note, make sure that all your form/dynamic values are linked to a slug or a key that is related to your API. With that setup, add another language will be extremely fast instead of a tedious task.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/urtgmnyhyrip6w7iy8kz.png)



---


#### 😄 Thanks for reading! If you found this article useful, consider following me on [Twitter](https://twitter.com/AntoineMesnil), I share tips on development, design and share my journey to create my own startup studio