DEV Community

Cover image for Architecting Internationalization In React Apps: Comprehensive Guide to Scalable Localization
Ahmed Rakan
Ahmed Rakan

Posted on

Architecting Internationalization In React Apps: Comprehensive Guide to Scalable Localization

Introduction

Over the past three months, I’ve been working solo on a Mega SaaS idea. While it’s been an exciting journey, the challenges have been immense. As I approach the last two weeks, delivering high-priority use cases while maintaining quality has been my top priority.

One of the key decisions I faced was whether to integrate internationalization (i18n) to support multiple languages. Initially, I leaned toward launching with an English-only version, leveraging LLMs for translations in the future. However, as a one-person team, I chose to focus on a single lucrative market for now.

Although optional for my project, internationalization is essential in professional settings due to legal and regulatory requirements. This blog explores how to design a scalable and efficient i18n architecture, avoiding pitfalls like high complexity or poor structure—insights that can benefit both solo developers and teams.

Since I decided not to implement i18n in my project, I’m sharing this guide to help others (and my future self!).


Goals

A good internationalization system should:

  • Scalability: Support seamless collaboration across teams for translations and language updates.
  • Modularity: Maintain a simple structure that is easy to extend without overhead.
  • Predictability: Follow consistent, enforceable patterns for localization.
  • Beginner-Friendly: Be accessible to developers of varying skill levels.

Available Tools

For internationalization in JavaScript, here are some popular tools:

  • i18next: A mature, feature-rich library ideal for scalable, professional-grade localization.
  • Alternatives: FormatJS, Polyglot.js, LinguiJS, GlobalizeJS, Fluent by Mozilla.

Each tool has its pros and cons. For simplicity, this guide focuses on i18next.


Designing the Architecture

Folder Structure for Internationalization

The architecture centers around an i18n folder containing three key components:

  1. Translations Folder: Stores JSON files for each language (e.g., en.json, ar.json, ch.json) and a base.json template for new languages.

  2. index.js: Configures and initializes the i18n library (e.g., i18next), setting fallback languages and other options.

  3. keys.js: A centralized structure defining translation keys, ensuring consistency and avoiding duplication.

Example Folder Structure:

src/
├── i18n/
│   ├── translations/
│   │   ├── en.json       # English translations
│   │   ├── ar.json       # Arabic translations
│   │   ├── ch.json       # Chinese translations
│   │   └── base.json     # Template for new languages
│   ├── index.js          # i18n configuration
│   └── keys.js           # Centralized keys for consistency
Enter fullscreen mode Exit fullscreen mode

keys.js as the Central Hub

The keys.js file mirrors the project’s structure, grouping keys by feature or module. This structure makes managing keys intuitive and scalable.

Example keys.js:

const keys = {
  components: {
    featureA: {
      buttonText: "components.featureA.buttonText",
      label: "components.featureA.label",
    },
    featureB: {
      header: "components.featureB.header",
    },
  },
};

export default keys;
Enter fullscreen mode Exit fullscreen mode

Translation Files

Translation JSON files align with the structure in keys.js, ensuring consistency.

Example en.json:

{
  "components": {
    "featureA": {
      "buttonText": "Submit",
      "label": "Enter your name"
    },
    "featureB": {
      "header": "Welcome to Feature B"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Example ar.json:

{
  "components": {
    "featureA": {
      "buttonText": "إرسال",
      "label": "أدخل اسمك"
    },
    "featureB": {
      "header": "مرحبًا بكم في الميزة ب"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Setting Up i18next

Install i18next and its React integration:

npm install i18next react-i18next
Enter fullscreen mode Exit fullscreen mode

i18n/index.js:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import en from "./translations/en.json";
import ar from "./translations/ar.json";

i18n.use(initReactI18next).init({
  resources: { en: { translation: en }, ar: { translation: ar } },
  lng: "en", // Default language
  fallbackLng: "en",
  interpolation: { escapeValue: false }, // React handles escaping
});

export default i18n;
Enter fullscreen mode Exit fullscreen mode

Integrating i18n into Components

Example Component (FeatureA):

import React from "react";
import { useTranslation } from "react-i18next";
import keys from "../../i18n/keys";

const FeatureA = () => {
  const { t } = useTranslation();

  return (
    <div>
      <h2>Feature A</h2>
      <button>{t(keys.components.featureA.buttonText)}</button>
      <label>{t(keys.components.featureA.label)}</label>
    </div>
  );
};

export default FeatureA;
Enter fullscreen mode Exit fullscreen mode

Adding a Language Switcher

A language switcher allows users to toggle between languages.

LanguageSwitcher.jsx:

import React from "react";
import { useTranslation } from "react-i18next";

const LanguageSwitcher = () => {
  const { i18n } = useTranslation();

  const changeLanguage = (lang) => {
    i18n.changeLanguage(lang);
  };

  return (
    <div>
      <button onClick={() => changeLanguage("en")}>English</button>
      <button onClick={() => changeLanguage("ar")}>العربية</button>
    </div>
  );
};

export default LanguageSwitcher;
Enter fullscreen mode Exit fullscreen mode

Final Folder Structure

src/
├── components/
│   ├── featureA/
│   │   ├── index.jsx
│   │   └── featureAStyles.css
│   └── shared/
│       └── LanguageSwitcher.jsx
├── i18n/
│   ├── translations/
│   │   ├── en.json
│   │   ├── ar.json
│   │   └── base.json
│   ├── keys.js
│   └── index.js
├── App.jsx
├── index.js
Enter fullscreen mode Exit fullscreen mode

Going Beyond

  1. Leverage AI for Translations: Use LLMs for rapid translations. For example, prompt:

    "Translate the following JSON to Chinese: {contents of en.json}."

  2. Backend-Driven Translations: Centralize translations on the backend to enable dynamic updates without code deployments. Options include GitOps or a dedicated backend service.


Demo

Sandbox: https://codesandbox.io/p/sandbox/785hpz


Conclusion

Internationalization is a critical step for scaling applications globally. By following this guide, you’ll have a scalable, modular, and beginner-friendly architecture that supports seamless localization for solo projects or large teams.

Happy coding!

Ahmed R. Aldhafeeri

Top comments (0)