# Plane i18n Architecture Using i18next: Translation Structure and Implementation

> Discover Plane's i18n architecture using i18next. Learn how translations are structured and implemented with automatic TypeScript generation for type-safe internationalization.

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: architecture
- Published: 2026-06-22

---

**Plane implements a type-safe, lazy-loaded internationalization system using a dedicated `@plane/i18n` package that wraps i18next with react-i18next, organizing translations into language-specific namespaces with automatic TypeScript generation.**

Plane manages internationalization through a centralized `@plane/i18n` package that abstracts i18next complexity while providing React-specific bindings. The architecture follows a strict layered approach with clear separation between instance creation, initialization logic, and React integration. This setup enables efficient lazy loading of translation resources while maintaining compile-time type safety across the makeplane/plane codebase.

## Core Architecture Layers

The `@plane/i18n` package organizes its responsibilities into distinct layers, each handling a specific aspect of the internationalization lifecycle.

### The Instance Layer

The foundation resides in [`src/core/instance.ts`](https://github.com/makeplane/plane/blob/main/src/core/instance.ts), which creates a singleton `i18nInstance` using `i18n.createInstance()`. This file registers three critical plugins: **i18next-icu** for ICU message formatting (handling pluralization and date/number formatting), **initReactI18next** for React binding, and **i18next-resources-to-backend** for lazy loading translation files via dynamic `import()` calls using the pattern `../locales/${language}/${namespace}.json`.

### The Initialization Layer

Within the same [`src/core/instance.ts`](https://github.com/makeplane/plane/blob/main/src/core/instance.ts) file, the initialization sequence performs the initial `i18n.init` call and stores the resulting promise as `initPromise`. The configuration object supplies `supportedLngs` from the `SUPPORTED_LANGUAGES` constant, declares all available namespaces, sets `react.useSuspense: false` to disable React Suspense for async loading, and configures `fallbackNS` to include all namespaces except the default. This allows any `t('key')` call to search across every namespace without triggering component re-renders.

### The Provider Layer

The [`src/provider/index.tsx`](https://github.com/makeplane/plane/blob/main/src/provider/index.tsx) file exposes the ready `i18nInstance` to the React tree via `<I18nextProvider>`. The provider subscribes to `initPromise` and returns `null` until initialization completes, preventing any UI that depends on translations from rendering prematurely. Once ready, the provider renders children with full access to the configured i18next instance.

### The Hooks Layer

Located in [`src/hooks/use-translation.ts`](https://github.com/makeplane/plane/blob/main/src/hooks/use-translation.ts), this layer provides convenience hooks including `useTranslation`, `useTranslationNamespace`, and `useTranslationLanguage`. These typed wrappers abstract direct i18next calls and add Plane-specific TypeScript definitions, ensuring components consume translations through a consistent, type-safe API.

### Configuration Constants

Two critical files define the static configuration: [`src/constants/language.ts`](https://github.com/makeplane/plane/blob/main/src/constants/language.ts) lists `SUPPORTED_LANGUAGES`, `FALLBACK_LANGUAGE`, and `LANGUAGE_STORAGE_KEY` for localStorage persistence, while [`src/constants/namespaces.ts`](https://github.com/makeplane/plane/blob/main/src/constants/namespaces.ts) declares all translation namespaces and the default namespace used throughout the UI.

## How i18next Initialization Works in Plane

The initialization process follows a strict sequence to ensure translations are available before components render:

1. **Singleton Creation** – `i18n.createInstance()` builds the isolated instance.

2. **Plugin Registration** – The ICU plugin, React integration, and backend loader are registered to handle formatting, React bindings, and dynamic JSON imports respectively.

3. **Language Resolution** – The initial language (`initialLng`) resolves from `localStorage[LANGUAGE_STORAGE_KEY]` or falls back to `FALLBACK_LANGUAGE`.

4. **Core Configuration** – The `init` call configures `supportedLngs`, `ns` (namespaces), `defaultNS`, and `fallbackNS` to enable cross-namespace key lookups.

5. **Namespace Pre-loading** – After core initialization, `i18nInstance.loadNamespaces(NAMESPACES)` eagerly caches every namespace for the initial language. This eliminates the cascade of re-renders that would occur if components loaded namespaces on-demand.

6. **Provider Activation** – The `<TranslationProvider>` waits for `initPromise` resolution before rendering the application tree, ensuring `t('my.key')` calls resolve instantly without async delays.

## Translation File Structure and Namespace Organization

Translations are stored under `packages/i18n/src/locales/` following a hierarchical pattern that enables efficient code splitting and clear ownership.

### Directory Structure

```

locales/
 ├─ <language-code>/          # e.g., zh-CN, vi-VN, it, de, en-US

 │   ├─ <namespace>.json      # one file per namespace

 │   │   └─ { "key": "value", … }
 │   └─ …
 └─ …

```

### Namespace Organization

Namespaces defined in [`src/constants/namespaces.ts`](https://github.com/makeplane/plane/blob/main/src/constants/namespaces.ts) correspond to logical UI sections:

- **common** – Generic UI strings (buttons, labels, confirmations)
- **navigation** – Menu items, breadcrumbs, routing labels
- **project** – Project-specific terminology and actions
- **workspace** – Workspace-level entities and settings
- **auth** – Authentication flows and error messages
- **editor** – Rich text editor controls and formatting
- **automation** – Workflow automation labels

Each JSON file contains a flat key/value map. For example, [`common.json`](https://github.com/makeplane/plane/blob/main/common.json) might include:

```json
{
  "save": "Save",
  "cancel": "Cancel",
  "delete": "Delete"
}

```

While [`navigation.json`](https://github.com/makeplane/plane/blob/main/navigation.json) could hold:

```json
{
  "dashboard": "Dashboard",
  "issues": "Issues",
  "settings": "Settings"
}

```

This structure enables **tree-shaking** (only required namespaces load), **parallel loading** (each namespace fetches via separate dynamic imports), and **clear ownership** (UI modules import only relevant namespaces).

## Type Safety and Development Workflow

Plane maintains compile-time safety for translation keys through an automated generation pipeline.

### The Type Generation Script

The [`scripts/generate-types.ts`](https://github.com/makeplane/plane/blob/main/scripts/generate-types.ts) file (invoked by the CI pipeline) parses all translation JSON files and produces [`src/generated/types.d.ts`](https://github.com/makeplane/plane/blob/main/src/generated/types.d.ts). This declaration file provides TypeScript definitions that enforce valid namespace:key combinations when using the `t()` function.

### Adding New Translations

To extend translations:

1. Add the key-value pair to the appropriate `<namespace>.json` file for each supported language under `src/locales/<language-code>/`.

2. Run the type generation script to update [`src/generated/types.d.ts`](https://github.com/makeplane/plane/blob/main/src/generated/types.d.ts).

3. Import the namespace in your component using the typed `useTranslation` hook from [`src/hooks/use-translation.ts`](https://github.com/makeplane/plane/blob/main/src/hooks/use-translation.ts).

```typescript
import { useTranslation } from "@plane/i18n";

// Type-safe access to the 'common' namespace
const { t } = useTranslation("common");

// 'save' is validated at compile time
return <button>{t("save")}</button>;

```

## Summary

- **Plane's i18n architecture** centers on a dedicated `@plane/i18n` package that wraps i18next and react-i18next in a multi-layered abstraction.

- **Instance initialization** in [`src/core/instance.ts`](https://github.com/makeplane/plane/blob/main/src/core/instance.ts) creates a singleton with ICU formatting, React bindings, and lazy-loading backend configuration.

- **Translation files** are organized by language code and namespace under `src/locales/`, enabling granular loading and clear separation of concerns.

- **Namespace pre-loading** during initialization prevents UI re-render cascades by ensuring all translation resources are cached before the first component renders.

- **Type safety** is enforced through [`scripts/generate-types.ts`](https://github.com/makeplane/plane/blob/main/scripts/generate-types.ts), which generates TypeScript definitions from JSON translation files to validate keys at compile time.

## Frequently Asked Questions

### How does Plane handle language switching at runtime?

Plane reads the initial language from `localStorage` using `LANGUAGE_STORAGE_KEY` defined in [`src/constants/language.ts`](https://github.com/makeplane/plane/blob/main/src/constants/language.ts), falling back to a configured `FALLBACK_LANGUAGE`. When switching languages, the application updates the storage value and re-initializes the i18next instance, leveraging the lazy-loading backend to fetch only the required language namespaces on demand.

### What is the purpose of namespaces in Plane's i18n setup?

Namespaces in [`src/constants/namespaces.ts`](https://github.com/makeplane/plane/blob/main/src/constants/namespaces.ts) logical group translation keys by UI domain (such as `common`, `project`, or `navigation`). This organization enables tree-shaking so only necessary translations load, prevents key collisions across features, and allows parallel loading of multiple JSON files via dynamic imports.

### How does Plane ensure type safety for translation keys?

The [`scripts/generate-types.ts`](https://github.com/makeplane/plane/blob/main/scripts/generate-types.ts) utility scans all JSON files in `src/locales/` and generates TypeScript definitions in [`src/generated/types.d.ts`](https://github.com/makeplane/plane/blob/main/src/generated/types.d.ts). The custom hooks in [`src/hooks/use-translation.ts`](https://github.com/makeplane/plane/blob/main/src/hooks/use-translation.ts) consume these generated types, ensuring that `t('namespace:key')` calls reference only valid keys and namespaces defined in the translation files.

### Why does Plane disable React Suspense for i18next?

The initialization configuration sets `react.useSuspense: false` to prevent React Suspense from handling async translation loading. Instead, Plane uses the `<TranslationProvider>` in [`src/provider/index.tsx`](https://github.com/makeplane/plane/blob/main/src/provider/index.tsx) to block rendering until `initPromise` resolves, providing explicit control over loading states and avoiding UI flicker during namespace resolution.