DEV Community

Cover image for A React Native & Lynx i18n solution that helps you keep your translations organized
Aymeric PINEAU
Aymeric PINEAU

Posted on

1

A React Native & Lynx i18n solution that helps you keep your translations organized

If you're working on making your React Native (or even web) application multilingual, you've probably already tried integrating react-i18next, i18n-js, LinguiJS or other alternatives.

In every project I’ve worked on, the same issues arise:

❌ Unused key-value pairs are never removed.
❌ Content is often duplicated.
❌ Ensuring format consistency across all languages and verifying that each translation is present and accurate becomes challenging, especially when managing more than five locale directories locally.
❌ Even if third-party tools can to solve this problem, by default i18next doesn’t generate TypeScript types, which means you can reference a key like t("my.key") even if it has been deleted.
❌ Additionally, localization platforms like Localize, Lokalise, or Locize can quickly become costly.

Tired of this complexity, I started looking for a solution to address these problems. I waited, and waited… before finally developing Intlayer.


✨ Key Features of Intlayer

✅ Available for React Native and Lynx
✅ Simple and quick integration

Content declaration in the same directory as your component (or everywhere in your project)

Autogenerated TypeScript Types – No more guessing or runtime errors due to missing keys

✅ Content declaration in either JSON, JS, or TS format

✅ Allows embedding external files (Markdown, TXT, etc.)
✅ Instantly fetch external data with automatic typing

✅ Intlayer natively provides a way to externalize your content and make it editable via a CMS


⚡ Getting Started (React Native)

1️⃣ Install the Required Packages

Run the following command in your project:

npm install intlayer react-intlayer react-native-intlayer
Enter fullscreen mode Exit fullscreen mode

2️⃣ Configure Your Locales

Create an intlayer.config.ts file in your project root:

import { Locales, type IntlayerConfig } from "intlayer";

const config: IntlayerConfig = {
  internationalization: {
    locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
    defaultLocale: Locales.ENGLISH,
  },
};

export default config;
Enter fullscreen mode Exit fullscreen mode

3️⃣ Enable Metro Support

Add the Metro bundler plugin to your metro.config.js:

const { getDefaultConfig } = require("expo/metro-config");
const { configMetroIntlayer } = require("react-native-intlayer/metro");

module.exports = (async () => {
  const defaultConfig = getDefaultConfig(__dirname);
  return await configMetroIntlayer(defaultConfig);
})();
Enter fullscreen mode Exit fullscreen mode

4️⃣ Wrap Your App with the Intlayer Provider

Modify your _layout.tsx file to initialize Intlayer and detect the user's language:

import { Stack } from "expo-router";
import { getLocales } from "expo-localization";
import { IntlayerProviderContent } from "react-intlayer";
import { intlayerPolyfill } from "react-native-intlayer";

intlayerPolyfill();

const getDeviceLocale = () => getLocales()[0]?.languageTag;

const RootLayout = () => {
  return (
    <IntlayerProviderContent defaultLocale={getDeviceLocale()}>
      <Stack>
        <Stack.Screen name="(tabs)" />
      </Stack>
    </IntlayerProviderContent>
  );
};

export default RootLayout;
Enter fullscreen mode Exit fullscreen mode

5️⃣ Define Translations Close to Your Components

Instead of maintaining large JSON files, define your translations inside the component's directory:

import { t, md, file, type Dictionary } from "intlayer";

const homeScreenContent = {
  key: "home-screen",
  content: {
    title: t({
      en: "My Title",
      fr: "Mon titre",
      es: "Mi título",
    }),
    description: t({
      en: md(file("./myDescription.en.md")),
      fr: md(file("./myDescription.fr.md")),
      es: md(file("./myDescription.es.md")),
    }),
    contentFetch: fetch("https://example.com").then((res) => res.text()),
  },
} satisfies Dictionary;

export default homeScreenContent;
Enter fullscreen mode Exit fullscreen mode

Now, you can use translations inside your components like this:

import { Text, View } from "react-native";
import { useIntlayer } from "react-intlayer";

const MyComponent = () => {
  const { title, description, contentFetch } = useIntlayer("my-component");

  return (
    <View>
      <Text>{title}</Text>
      <Text>{description}</Text>
      <Text>{contentFetch}</Text>
    </View>
  );
};
Enter fullscreen mode Exit fullscreen mode

🔄 Switching Languages Dynamically

To allow users to change languages at runtime, use the useLocale hook:

import { View, Text, TouchableOpacity } from "react-native";
import { getLocaleName } from "intlayer";
import { useLocale } from "react-intlayer";

const LocaleSwitcher = () => {
  const { setLocale, availableLocales } = useLocale();

  return (
    <View>
      {availableLocales.map((locale) => (
        <TouchableOpacity key={locale} onPress={() => setLocale(locale)}>
          <Text>{getLocaleName(locale)}</Text>
        </TouchableOpacity>
      ))}
    </View>
  );
};

export default LocaleSwitcher;
Enter fullscreen mode Exit fullscreen mode

Resources

GitHub issues & feedback: Intlayer Repository

📄 Docs: React Native & Expo Docs

🔹 Template: React Native Template

Sentry mobile image

App store rankings love fast apps - mobile vitals can help you get there

Slow startup times, UI hangs, and frozen frames frustrate users—but they’re also fixable. Mobile Vitals help you measure and understand these performance issues so you can optimize your app’s speed and responsiveness. Learn how to use them to reduce friction and improve user experience.

Read full post →

Top comments (0)

Billboard image

📊 A side-by-side product comparison between Sentry and Crashlytics

A free guide pointing out the differences between Sentry and Crashlytics, that’s it. See which is best for your mobile crash reporting needs.

See Comparison

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay