Easy Internationalization and in React Native

Internationalization can be a pain For the Brink mobile app, we developed a system that we didn't hate and scaled well.

This is how we organized the i18n logic.

src
├── i18n
│   ├── __tests__
│   │   └── i18n.test.js
│   ├── i18n.js
│   └── locales
│       ├── en.js
│       └── es.js

This next part is where the magic happens. On app load, we pull in the system language so we don't need to ask the user what their preferred language is.

// src/i18n.js
import DeviceInfo from "react-native-device-info"
import I18n from "react-native-i18n"
import en from "./locales/en"
import es from "./locales/es"

/**
 * Grab the system locale ('en_US' or 'es_US') from the phones local settings
 * and convert it to a two letter language code used by I18n.js. Android returns
 * a 4 letter locale, iOS returns two.
 * @return {String}
 */
export const getSystemLocale = () => DeviceInfo.getDeviceLocale().substring(0, 2)

I18n.defaultLocale = "en"
I18n.locale = getSystemLocale()
I18n.fallbacks = true
I18n.translations = {
  en,
  es,
}
export default I18n

Then, you write your translations for each language as a JavaScript object:

// en.js
const translations = {
  Nav: {
    today: {
      title: "Today",
      a11yTitle: "Navigate to Today",
    },
    profile: {
      title: "My Profile",
      a11yTitle: "Navigate to My Profile",
    },
  },
}

Then, in your JSX files, you can reference your localizations:

import { createBottomTabNavigator } from "react-navigation"
import TodayContainer from "~/components/TodayContainer"
import ProfileContainer from "~/components/ProfileContainer"
import i18n from "~/i18n/i18n"

const { nav } = i18n.t("Nav")

const tabNavigator = createMaterialTopTabNavigator(
{
    Today: {
      screen: TodayContainer,
      navigationOptions: {
        title: nav.today.title,
        tabBarAccessibilityLabel: nav.today.a11yTitle,
      },
    },
    Profile: {
      screen: ProfileContainer,
      navigationOptions: {
        title: nav.profile.title,
        tabBarAccessibilityLabel: nav.profile.a11yTitle,
      },
    },
  },
}
)