React Native i18n Internationalization Mobile Development

Complete Guide to React Native Internationalization (i18n)

Learn how to implement internationalization in your React Native app using react-i18next

5 min read

Complete Guide to React Native Internationalization (i18n)

In today’s global app marketplace, supporting multiple languages isn’t just a nice-to-have—it’s essential. Let’s explore how to implement internationalization (i18n) in React Native, making your app accessible to users worldwide! 🌎

What is i18n?

Internationalization (i18n) helps adapt your app for different languages and regions. The term “i18n” comes from the word “internationalization” - there are 18 letters between the ‘i’ and the ‘n’!

Setting Up i18n in React Native

1. Installation

First, let’s install the necessary packages:

yarn add i18next react-i18next
# or
npm install i18next react-i18next

2. Creating Language Files

Create a languages folder with JSON files for each language:

// languages/en.json
{
    "translation": {
        "welcome": "Welcome",
        "choose": "Select a language",
        "name": "Name",
        "portuguese": "Portuguese",
        "english": "English",
        "spanish": "Spanish"
    }
}

// languages/es.json
{
    "translation": {
        "welcome": "Bienvenido",
        "choose": "Seleccione un idioma",
        "name": "Nombre",
        "portuguese": "Portugués",
        "english": "Inglés",
        "spanish": "Español"
    }
}

// languages/pt.json
{
    "translation": {
        "welcome": "Seja Bem-Vindo",
        "choose": "Selecione um idioma",
        "name": "Nome",
        "portuguese": "Português",
        "english": "Inglês",
        "spanish": "Espanhol"
    }
}

3. Configuring i18n

Create an i18n.ts configuration file:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";

import en from "./languages/en.json";
import es from "./languages/es.json";
import pt from "./languages/pt.json";

const resources = {
  en,
  es,
  pt,
};

i18n.use(initReactI18next).init({
  compatibilityJSON: "v3",
  resources,
  lng: "en", // default language
  fallbackLng: "en",
  interpolation: {
    escapeValue: false,
  },
});

export default i18n;

Implementation Methods

Let’s look at three different ways to implement language selection in your app:

1. Manual Language Selection with Dropdown

import React, { useState } from "react";
import { View, Text } from "react-native";
import { useTranslation } from "react-i18next";
import DropDownPicker from "react-native-dropdown-picker";

export default function LanguageSelector() {
  const { t, i18n } = useTranslation();
  const [open, setOpen] = useState(false);
  const [value, setValue] = useState(i18n.language);

  const [items] = useState([
    { label: "English", value: "en" },
    { label: "Español", value: "es" },
    { label: "Português", value: "pt" },
  ]);

  return (
    <View style={styles.container}>
      <Text style={styles.title}>{t("choose")}</Text>
      <DropDownPicker
        open={open}
        value={value}
        items={items}
        setOpen={setOpen}
        setValue={setValue}
        onChangeValue={(value) => {
          i18n.changeLanguage(value);
        }}
        style={styles.dropdown}
      />
    </View>
  );
}

2. Device Location Based Language

import { useEffect } from "react";
import * as Location from "expo-location";
import { useTranslation } from "react-i18next";

export function useLocationBasedLanguage() {
  const { i18n } = useTranslation();

  useEffect(() => {
    async function setLanguageByLocation() {
      try {
        const { status } = await Location.requestForegroundPermissionsAsync();
        if (status !== "granted") return;

        const location = await Location.getCurrentPositionAsync({});
        const geocode = await Location.reverseGeocodeAsync({
          latitude: location.coords.latitude,
          longitude: location.coords.longitude,
        });

        const countryCode = geocode[0]?.isoCountryCode;
        const languageMap = {
          US: "en",
          ES: "es",
          BR: "pt",
          // Add more country-to-language mappings
        };

        const language = languageMap[countryCode] || "en";
        i18n.changeLanguage(language);
      } catch (error) {
        console.error("Error setting language by location:", error);
      }
    }

    setLanguageByLocation();
  }, []);
}

3. Device Language Based

import { useEffect } from "react";
import * as Localization from "expo-localization";
import { useTranslation } from "react-i18next";

export function useDeviceLanguage() {
  const { i18n } = useTranslation();

  useEffect(() => {
    const deviceLanguage = Localization.getLocales()[0].languageCode;
    const supportedLanguages = ["en", "es", "pt"];

    const language = supportedLanguages.includes(deviceLanguage)
      ? deviceLanguage
      : "en";

    i18n.changeLanguage(language);
  }, []);
}

Using Translations in Your Components

Here’s how to use translations in your components:

import React from "react";
import { View, Text } from "react-native";
import { useTranslation } from "react-i18next";

export default function WelcomeScreen() {
  const { t } = useTranslation();

  return (
    <View style={styles.container}>
      <Text style={styles.title}>{t("welcome")}</Text>
      <Text>{t("name", { name: "John" })}</Text>
    </View>
  );
}

Best Practices

  1. Organize Translation Keys:
// Structured translation files
{
  "translation": {
    "common": {
      "welcome": "Welcome",
      "save": "Save"
    },
    "auth": {
      "login": "Login",
      "signup": "Sign Up"
    }
  }
}
  1. Handle Loading States:
function App() {
  const { t, i18n } = useTranslation();

  if (!i18n.isInitialized) {
    return <LoadingScreen />;
  }

  return <MainApp />;
}
  1. Remember Platform Differences:
// Configure based on platform
i18n.init({
  compatibilityJSON: Platform.OS === "android" ? "v3" : undefined,
  // other config...
});

Performance Tips

  1. Namespace Splitting: Split translations into namespaces for lazy loading
  2. Memoize Components: Use React.memo for components that only depend on translations
  3. Avoid Nested Translations: Keep translation keys flat when possible
// Good
{
  "welcome_message": "Welcome to our app",
  "user_greeting": "Hello, {{name}}"
}

// Avoid
{
  "messages": {
    "welcome": {
      "title": "Welcome to our app"
    }
  }
}

Testing Internationalization

Here’s a simple test setup:

import { render, screen } from "@testing-library/react-native";
import { useTranslation } from "react-i18next";

jest.mock("react-i18next", () => ({
  useTranslation: () => ({
    t: (key) => key,
    i18n: {
      changeLanguage: jest.fn(),
    },
  }),
}));

test("renders welcome message", () => {
  render(<WelcomeScreen />);
  expect(screen.getByText("welcome")).toBeTruthy();
});

Conclusion

Implementing internationalization in your React Native app opens it up to a global audience. Whether you choose manual language selection, location-based, or device-language-based implementation, the key is to plan your translation structure early and maintain it consistently.

Remember:

  • Start with a good translation key structure
  • Choose the right implementation method for your needs
  • Consider performance implications
  • Test your translations

Need help setting this up? Drop your questions in the comments below! 👇

Happy coding! 🚀