React Internationalization (i18n) with react-i18next and Intlayer
Overview
- Intlayer helps you manage translations via component-level content declaration files.
- react-i18next is a popular React integration for i18next that provides hooks like useTranslation to fetch localized strings in your components.
When combined, Intlayer can export dictionaries in i18next-compatible JSON so that react-i18next can consume them at runtime.
Why Use Intlayer with react-i18next?
Intlayer content declaration files offer a better developer experience because they are:
Flexible in File Placement
Put each content declaration file right next to the component that needs it. This structure allows you to keep translations co-located, preventing orphaned translations when components move or get deleted.bash.└── src └── components └── MyComponent ├── index.content.ts # Content declaration file └── index.tsx
Centralized Translations
A single content declaration file collects all necessary translations for a component, making missing translations easier to catch.
With TypeScript, you even get compile-time errors if translations are missing.
Installation
In a Create React App project, install these dependencies:
# With npmnpm install intlayer react-i18next i18next i18next-resources-to-backend
# With yarnyarn add intlayer react-i18next i18next i18next-resources-to-backend
# With pnpmpnpm add intlayer react-i18next i18next i18next-resources-to-backend
What Are These Packages?
- intlayer – The CLI and core library for managing i18n configurations, content declarations, and building dictionary outputs.
- react-intlayer – React-specific integration for Intlayer, providing notably some script to automate the build of dictionaries.
- react-i18next – React-specific integration library for i18next, including the useTranslation hook.
- i18next – The underlying framework for translation handling.
- i18next-resources-to-backend – An i18next backend that dynamically imports JSON resources.
Configuring Intlayer to Export i18next Dictionaries
Create (or update) your intlayer.config.ts in the root of your project:
import { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = { internationalization: { // Add as many locales as you wish locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH], defaultLocale: Locales.ENGLISH, }, content: { // Tell Intlayer to create i18next-compatible JSON dictionaryOutput: ["i18next"], // Choose an output directory for the generated resources // This folder will be created if it doesn't exist yet. i18nextResourcesDir: "./i18next/resources", },};export default config;
Note: If you’re not using TypeScript, you can create this config file as .cjs, .mjs, or .js (see the Intlayer docs for details).
Building the i18next Resources
Once your content declarations are in place (next section), run the Intlayer build command:
# With npmnpx run intlayer build
# With yarnyarn intlayer build
# With pnpmpnpm intlayer build
This will generate your i18next resources inside the ./i18next/resources directory by default.
A typical output might look like this:
.└── i18next └── resources ├── en │ └── my-content.json ├── fr │ └── my-content.json └── es └── my-content.json
Where each Intlayer declaration key is used as an i18next namespace (e.g., my-content.json).
Importing Dictionaries into Your react-i18next Configuration
To dynamically load these resources at runtime, use i18next-resources-to-backend. For instance, create an i18n.ts (or .js) file in your project:
import i18next from "i18next";import { initReactI18next } from "react-i18next";import resourcesToBackend from "i18next-resources-to-backend";i18next // react-i18next plugin .use(initReactI18next) // dynamically load resources .use( resourcesToBackend((language: string, namespace: string) => { // Adjust the import path to your resources directory return import(`../i18next/resources/${language}/${namespace}.json`); }) ) // Initialize i18next .init({ // Fallback locale fallbackLng: "en", // You can add other i18next config options here, see: // https://www.i18next.com/overview/configuration-options });export default i18next;
import i18next from "i18next";import { initReactI18next } from "react-i18next";import resourcesToBackend from "i18next-resources-to-backend";i18next .use(initReactI18next) .use( resourcesToBackend( (language, namespace) => import(`../i18next/resources/${language}/${namespace}.json`) ) ) .init({ fallbackLng: "en", });export default i18next;
Then, in your root or index file (e.g., src/index.tsx), import this i18n setup before rendering the App:
import React from "react";import ReactDOM from "react-dom/client";// Initialize i18n before anything elseimport "./i18n";import App from "./App";ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( <React.StrictMode> <App /> </React.StrictMode>);
Creating and Managing Your Content Declarations
Intlayer extracts translations from “content declaration files” located anywhere under ./src (by default).
Here’s a minimal example in TypeScript:
import { t, type DeclarationContent } from "intlayer";const content = { // The "key" will be your i18next namespace (e.g., "my-component") key: "my-component", content: { // Each "t" call is a separate translation node heading: t({ en: "Hello World", fr: "Bonjour le monde", es: "Hola Mundo", }), description: t({ en: "My i18n description text...", fr: "Ma description en i18n...", es: "Mi descripción en i18n...", }), },} satisfies DeclarationContent;export default content;
If you prefer JSON, .cjs, or .mjs, refer to the Intlayer docs.
By default, valid content declarations match the file extension pattern:
*.content.{ts,tsx,js,jsx,mjs,cjs,json}
Using the Translations in React Components
After you’ve built your Intlayer resources and configured react-i18next, you can directly use the useTranslation hook from react-i18next.
For instance:
import { FC } from "react";import { useTranslation } from "react-i18next";/** * The i18next "namespace" is the Intlayer `key` from "MyComponent.content.ts" * so we'll pass "my-component" to useTranslation(). */const MyComponent: FC = () => { const { t } = useTranslation("my-component"); return ( <div> <h1>{t("heading")}</h1> <p>{t("description")}</p> </div> );};export default MyComponent;
Note that the t function references keys inside your generated JSON. For an Intlayer content entry named heading, you’ll use t("heading").
Optional: Integrate with Create React App Scripts (CRACO)
react-intlayer provides a CRACO-based approach for custom builds and dev server configuration. If you want Intlayer’s build step integrated seamlessly, you can:
- Install react-intlayer (if you haven’t): bash npm install react-intlayer --save-dev
Adjust your package.json scripts to use react-intlayer scripts:
jsonc"scripts": { "start": "react-intlayer start", "build": "react-intlayer build", "transpile": "intlayer build"}
react-intlayer scripts are based on CRACO. You can also implement your own setup based on the intlayer craco plugin. See example here.
Now, running npm run build, yarn build, or pnpm build triggers both Intlayer and CRA builds.
TypeScript Configuration
Intlayer provides autogenerated type definitions for your content. To ensure TypeScript picks them up, add types (or types if you configured differently) to your tsconfig.json include array:
{ "compilerOptions": { // ... }, "include": ["src", "types"],}
This will let TypeScript infer the shape of your translations for better autocompletion and error detection.
Git Configuration
It is recommended to ignore auto-generated files and folders from Intlayer. Add this line to your .gitignore:
# Ignore the files generated by Intlayer.intlayeri18next
You typically do not commit these resources or .intlayer internal build artifacts, as they can be regenerated on each build.
If you have an idea for improving this blog, please feel free to contribute by submitting a pull request on GitHub.
GitHub link to the blog