IntlPull
Guide
18 min read

Tutorial react-i18next: Guía completa de React i18n (vs react-intl) 2026

Domina react-i18next para apps React. Tutorial completo que cubre configuración, hooks, namespaces, pluralización, carga perezosa, TypeScript y mejores prácticas de producción.

IntlPull Team
IntlPull Team
Jan 17, 2026
On this page
Summary

Domina react-i18next para apps React. Tutorial completo que cubre configuración, hooks, namespaces, pluralización, carga perezosa, TypeScript y mejores prácticas de producción.

Respuesta rápida

react-i18next es la librería i18n más popular para React con más de 3.5M de descargas semanales en npm. Instálala con npm install react-i18next i18next, crea archivos de traducción JSON, configura i18next con i18n.init(), luego usa el hook useTranslation: const { t } = useTranslation(); return <h1>{t('welcome')}</h1>. Para aplicaciones de producción, combínalo con IntlPull para la gestión de traducción impulsada por IA y actualizaciones OTA.


¿Qué es react-i18next?

react-i18next es un binding de React para i18next, el framework de internacionalización de JavaScript más probado en batalla. Proporciona:

  • React hooks (useTranslation) para componentes funcionales
  • HOCs y render props para componentes de clase
  • Soporte de Suspense para carga de traducción asíncrona
  • Compatibilidad SSR con Next.js, Remix y Gatsby
  • Ecosistema de plugins para backends, detección de idioma y caché

¿Por qué elegir react-i18next?

Característicareact-i18nextreact-intlnext-intl
Descargas semanales3.5M+1.2M800K
Tamaño del Bundle~15KB~25KB~12KB
FrameworkCualquiera ReactCualquiera ReactSolo Next.js
Curva de aprendizajeBajaMediaBaja
Sistema de PluginExtensoLimitadoN/A
TypeScriptExcelenteBuenoExcelente

Instalación y Configuración

Paso 1: Instalar Dependencias

Terminal
1npm install react-i18next i18next
2# o
3yarn add react-i18next i18next
4# o
5pnpm add react-i18next i18next

Plugins opcionales:

Terminal
1# Detección de idioma
2npm install i18next-browser-languagedetector
3
4# Cargar traducciones desde backend
5npm install i18next-http-backend
6
7# Para React Native
8npm install react-i18next i18next @os-team/i18next-react-native-language-detector

Paso 2: Crear Archivos de Traducción

src/
├── locales/
│   ├── en/
│   │   ├── common.json
│   │   └── home.json
│   ├── es/
│   │   ├── common.json
│   │   └── home.json
│   └── fr/
│       ├── common.json
│       └── home.json
└── i18n.ts

en/common.json:

JSON
1{
2  "welcome": "Welcome to our app",
3  "nav": {
4    "home": "Home",
5    "about": "About",
6    "contact": "Contact"
7  },
8  "buttons": {
9    "submit": "Submit",
10    "cancel": "Cancel",
11    "save": "Save"
12  }
13}

es/common.json:

JSON
1{
2  "welcome": "Bienvenido a nuestra aplicación",
3  "nav": {
4    "home": "Inicio",
5    "about": "Acerca de",
6    "contact": "Contacto"
7  },
8  "buttons": {
9    "submit": "Enviar",
10    "cancel": "Cancelar",
11    "save": "Guardar"
12  }
13}

Paso 3: Configurar i18next

TypeScript
1// src/i18n.ts
2import i18n from 'i18next';
3import { initReactI18next } from 'react-i18next';
4import LanguageDetector from 'i18next-browser-languagedetector';
5
6// Importar traducciones
7import enCommon from './locales/en/common.json';
8import enHome from './locales/en/home.json';
9import esCommon from './locales/es/common.json';
10import esHome from './locales/es/home.json';
11
12i18n
13  .use(LanguageDetector)
14  .use(initReactI18next)
15  .init({
16    resources: {
17      en: {
18        common: enCommon,
19        home: enHome,
20      },
21      es: {
22        common: esCommon,
23        home: esHome,
24      },
25    },
26    defaultNS: 'common',
27    fallbackLng: 'en',
28    supportedLngs: ['en', 'es', 'fr'],
29
30    interpolation: {
31      escapeValue: false, // React ya escapa valores
32    },
33
34    detection: {
35      order: ['localStorage', 'navigator', 'htmlTag'],
36      caches: ['localStorage'],
37    },
38  });
39
40export default i18n;

Paso 4: Inicializar en tu App

TSX
1// src/main.tsx o src/index.tsx
2import React from 'react';
3import ReactDOM from 'react-dom/client';
4import './i18n'; // Importar configuración i18n
5import App from './App';
6
7ReactDOM.createRoot(document.getElementById('root')!).render(
8  <React.StrictMode>
9    <App />
10  </React.StrictMode>
11);

Usando el Hook useTranslation

El hook useTranslation es la forma principal de acceder a traducciones en componentes funcionales.

Uso Básico

TSX
1import { useTranslation } from 'react-i18next';
2
3function Header() {
4  const { t } = useTranslation();
5
6  return (
7    <header>
8      <h1>{t('welcome')}</h1>
9      <nav>
10        <a href="/">{t('nav.home')}</a>
11        <a href="/about">{t('nav.about')}</a>
12      </nav>
13    </header>
14  );
15}

Con Namespaces

TSX
1import { useTranslation } from 'react-i18next';
2
3function HomePage() {
4  // Cargar namespace específico
5  const { t } = useTranslation('home');
6
7  return <h1>{t('hero.title')}</h1>;
8}
9
10function MultiNamespace() {
11  // Cargar múltiples namespaces
12  const { t } = useTranslation(['common', 'home']);
13
14  return (
15    <div>
16      <h1>{t('home:hero.title')}</h1>
17      <button>{t('common:buttons.submit')}</button>
18    </div>
19  );
20}

Cambiando Idioma

TSX
1import { useTranslation } from 'react-i18next';
2
3function LanguageSwitcher() {
4  const { i18n } = useTranslation();
5
6  const changeLanguage = (lng: string) => {
7    i18n.changeLanguage(lng);
8  };
9
10  return (
11    <div>
12      <button onClick={() => changeLanguage('en')}>English</button>
13      <button onClick={() => changeLanguage('es')}>Español</button>
14      <button onClick={() => changeLanguage('fr')}>Français</button>
15    </div>
16  );
17}

Interpolación de Variables

Variables Básicas

JSON
1{
2  "greeting": "Hola, {{name}}!",
3  "itemCount": "Tienes {{count}} artículos en tu carrito"
4}
TSX
1function Greeting({ user }) {
2  const { t } = useTranslation();
3
4  return (
5    <div>
6      <h1>{t('greeting', { name: user.name })}</h1>
7      <p>{t('itemCount', { count: user.cartItems })}</p>
8    </div>
9  );
10}

Formateando Valores

JSON
1{
2  "price": "Total: {{price, currency}}",
3  "date": "Creado el {{date, datetime}}"
4}
TypeScript
1// i18n.ts - Añadir formateros
2i18n.init({
3  // ... otra configuración
4  interpolation: {
5    escapeValue: false,
6    format: (value, format, lng) => {
7      if (format === 'currency') {
8        return new Intl.NumberFormat(lng, {
9          style: 'currency',
10          currency: 'USD',
11        }).format(value);
12      }
13      if (format === 'datetime') {
14        return new Intl.DateTimeFormat(lng).format(value);
15      }
16      return value;
17    },
18  },
19});

Pluralización

Plurales Básicos

JSON
1{
2  "item_one": "{{count}} artículo",
3  "item_other": "{{count}} artículos",
4  "item_zero": "Sin artículos"
5}
TSX
1function CartCount({ count }) {
2  const { t } = useTranslation();
3
4  return <span>{t('item', { count })}</span>;
5  // count=0: "Sin artículos"
6  // count=1: "1 artículo"
7  // count=5: "5 artículos"
8}

Plurales Complejos (Ruso, Árabe, etc.)

JSON
1{
2  "item_zero": "нет товаров",
3  "item_one": "{{count}} товар",
4  "item_few": "{{count}} товара",
5  "item_many": "{{count}} товаров",
6  "item_other": "{{count}} товаров"
7}

Ordinales

JSON
1{
2  "place_ordinal_one": "{{count}}er lugar",
3  "place_ordinal_two": "{{count}}do lugar",
4  "place_ordinal_few": "{{count}}er lugar",
5  "place_ordinal_other": "{{count}}to lugar"
6}
TSX
1t('place', { count: 1, ordinal: true }); // "1er lugar"
2t('place', { count: 2, ordinal: true }); // "2do lugar"
3t('place', { count: 3, ordinal: true }); // "3er lugar"
4t('place', { count: 4, ordinal: true }); // "4to lugar"

Texto Rico y Componentes

Usando el Componente Trans

JSON
1{
2  "terms": "Al registrarte, aceptas nuestros <link>Términos de Servicio</link>",
3  "welcome": "Bienvenido <bold>{{name}}</bold> a nuestra plataforma!"
4}
TSX
1import { Trans, useTranslation } from 'react-i18next';
2
3function TermsText() {
4  return (
5    <Trans
6      i18nKey="terms"
7      components={{
8        link: <a href="/terms" class="text-blue-600" />,
9      }}
10    />
11  );
12}
13
14function WelcomeMessage({ name }) {
15  return (
16    <Trans
17      i18nKey="welcome"
18      values={{ name }}
19      components={{
20        bold: <strong class="font-bold" />,
21      }}
22    />
23  );
24}

HTML Anidado

JSON
{
  "description": "Mira nuestras <link>nuevas características</link> y <button>comienza</button> hoy!"
}
TSX
1<Trans
2  i18nKey="description"
3  components={{
4    link: <a href="/features" />,
5    button: <button onClick={handleStart} />,
6  }}
7/>

Carga Perezosa de Traducciones

Para apps grandes, carga traducciones bajo demanda para reducir el tamaño del bundle inicial.

HTTP Backend

TypeScript
1import i18n from 'i18next';
2import { initReactI18next } from 'react-i18next';
3import HttpBackend from 'i18next-http-backend';
4import LanguageDetector from 'i18next-browser-languagedetector';
5
6i18n
7  .use(HttpBackend)
8  .use(LanguageDetector)
9  .use(initReactI18next)
10  .init({
11    fallbackLng: 'en',
12    ns: ['common'],
13    defaultNS: 'common',
14
15    backend: {
16      loadPath: '/locales/{{lng}}/{{ns}}.json',
17    },
18
19    react: {
20      useSuspense: true,
21    },
22  });
23
24export default i18n;

Con Suspense

TSX
1import { Suspense } from 'react';
2
3function App() {
4  return (
5    <Suspense fallback={<LoadingSpinner />}>
6      <MainContent />
7    </Suspense>
8  );
9}

Cargar Namespace Bajo Demanda

TSX
1import { useTranslation } from 'react-i18next';
2import { Suspense, lazy } from 'react';
3
4// Lazy load component con su namespace
5const SettingsPage = lazy(() => import('./SettingsPage'));
6
7function App() {
8  return (
9    <Suspense fallback={<LoadingSpinner />}>
10      <SettingsPage />
11    </Suspense>
12  );
13}
14
15// SettingsPage.tsx
16function SettingsPage() {
17  const { t, ready } = useTranslation('settings', { useSuspense: false });
18
19  if (!ready) return <LoadingSpinner />;
20
21  return <h1>{t('title')}</h1>;
22}

Integración con TypeScript

Traducciones Type-Safe

TypeScript
1// src/types/i18next.d.ts
2import 'i18next';
3import common from '../locales/en/common.json';
4import home from '../locales/en/home.json';
5
6declare module 'i18next' {
7  interface CustomTypeOptions {
8    defaultNS: 'common';
9    resources: {
10      common: typeof common;
11      home: typeof home;
12    };
13  }
14}

Ahora TypeScript autocompletará las claves de traducción:

TSX
const { t } = useTranslation();
t('nav.home'); // ✅ Autocompleta
t('nav.invalid'); // ❌ Error TypeScript

Con Múltiples Namespaces

TSX
1const { t } = useTranslation(['common', 'home']);
2
3t('buttons.submit'); // De common (default)
4t('home:hero.title'); // De namespace home

Server-Side Rendering (SSR)

Next.js Pages Router

TypeScript
1// next-i18next.config.js
2module.exports = {
3  i18n: {
4    defaultLocale: 'en',
5    locales: ['en', 'es', 'fr'],
6  },
7};
TSX
1// pages/index.tsx
2import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
3import { useTranslation } from 'next-i18next';
4
5export default function Home() {
6  const { t } = useTranslation('common');
7  return <h1>{t('welcome')}</h1>;
8}
9
10export async function getStaticProps({ locale }) {
11  return {
12    props: {
13      ...(await serverSideTranslations(locale, ['common'])),
14    },
15  };
16}

Next.js App Router

Para Next.js App Router, recomendamos usar next-intl en su lugar. Mira nuestra guía de next-intl.

Remix

TSX
1// app/entry.server.tsx
2import { createInstance } from 'i18next';
3import { initReactI18next } from 'react-i18next';
4
5export default async function handleRequest(request, ...) {
6  const i18n = createInstance();
7  await i18n.use(initReactI18next).init({
8    lng: locale,
9    resources: { [locale]: translations },
10  });
11
12  // Render con instancia i18n
13}

Probando con react-i18next

Configuración Mock

TypeScript
1// test/setup.ts
2import i18n from 'i18next';
3import { initReactI18next } from 'react-i18next';
4
5i18n.use(initReactI18next).init({
6  lng: 'en',
7  fallbackLng: 'en',
8  resources: {
9    en: {
10      common: {
11        welcome: 'Welcome',
12        'buttons.submit': 'Submit',
13      },
14    },
15  },
16});
17
18export default i18n;

Probando Componentes

TSX
1import { render, screen } from '@testing-library/react';
2import { I18nextProvider } from 'react-i18next';
3import i18n from '../test/setup';
4import Header from './Header';
5
6describe('Header', () => {
7  it('renders translated welcome message', () => {
8    render(
9      <I18nextProvider i18n={i18n}>
10        <Header />
11      </I18nextProvider>
12    );
13
14    expect(screen.getByText('Welcome')).toBeInTheDocument();
15  });
16});

Mejores Prácticas de Producción

1. Organización de Namespace

locales/
├── en/
│   ├── common.json      # UI compartido (botones, nav, errores)
│   ├── auth.json        # Login, registro, reseteo password
│   ├── dashboard.json   # Específico de Dashboard
│   ├── settings.json    # Página de configuración
│   └── errors.json      # Mensajes de error

2. Convención de Nombres de Claves

JSON
1{
2  "page.section.element": "Valor",
3  "auth.login.title": "Iniciar Sesión",
4  "auth.login.button": "Continuar",
5  "dashboard.stats.totalUsers": "Usuarios Totales",
6  "errors.network.timeout": "Tiempo de espera agotado"
7}

3. Manejo de Claves Faltantes

TypeScript
1i18n.init({
2  saveMissing: process.env.NODE_ENV === 'development',
3  missingKeyHandler: (lng, ns, key) => {
4    console.warn(`Falta traducción: ${lng}/${ns}/${key}`);
5    // Reportar a seguimiento de errores
6  },
7});

4. Optimización de Rendimiento

TypeScript
1// Pre-cargar namespaces críticos
2i18n.loadNamespaces(['common', 'auth']);
3
4// No re-renderizar en cambio de idioma para contenido estático
5const { t } = useTranslation('common', { useSuspense: false });

Escalando con IntlPull

A medida que tu app crece, gestionar archivos JSON manualmente se vuelve doloroso. IntlPull proporciona:

Integración CLI

Terminal
1# Inicializar IntlPull en tu proyecto
2npx @intlpullhq/cli init
3
4# Extraer claves del código
5npx @intlpullhq/cli extract
6
7# Subir a IntlPull
8npx @intlpullhq/cli upload
9
10# Descargar traducciones
11npx @intlpullhq/cli download

Traducción IA

IntlPull traduce automáticamente tus claves usando GPT-4, Claude o DeepL mientras mantiene el contexto y la voz de la marca.

Actualizaciones OTA

Actualiza traducciones sin lanzamientos de app:

TypeScript
1import { IntlPullBackend } from '@intlpull/i18next-backend';
2
3i18n.use(IntlPullBackend).init({
4  backend: {
5    projectId: 'your-project-id',
6    apiKey: process.env.INTLPULL_API_KEY,
7  },
8});

Patrones Comunes

Traducciones Condicionales

JSON
1{
2  "greeting_morning": "¡Buenos días!",
3  "greeting_afternoon": "¡Buenas tardes!",
4  "greeting_evening": "¡Buenas noches!"
5}
TSX
1function Greeting() {
2  const { t } = useTranslation();
3  const hour = new Date().getHours();
4
5  const getGreetingKey = () => {
6    if (hour < 12) return 'greeting_morning';
7    if (hour < 18) return 'greeting_afternoon';
8    return 'greeting_evening';
9  };
10
11  return <h1>{t(getGreetingKey())}</h1>;
12}

Claves Dinámicas

TSX
1function StatusBadge({ status }: { status: 'pending' | 'approved' | 'rejected' }) {
2  const { t } = useTranslation();
3
4  // Claves: status.pending, status.approved, status.rejected
5  return <span>{t(`status.${status}`)}</span>;
6}

Contenido de Respaldo

TSX
1const { t } = useTranslation();
2
3// Proveer fallback
4const title = t('page.title', 'Título por defecto');
5
6// Verificar si la clave existe
7const { exists } = useTranslation();
8if (exists('optional.feature')) {
9  // Mostrar contenido específico de la característica
10}

Solución de problemas

Advertencias de "Traducción no encontrada"

TypeScript
1// Verificar si el namespace está cargado
2i18n.hasLoadedNamespace('myNamespace');
3
4// Asegurar que la clave existe
5i18n.exists('myKey');
6
7// Modo debug
8i18n.init({ debug: true });

Componente no se re-renderiza al cambiar idioma

TSX
1// Usa el hook correctamente - se suscribe a cambios
2const { t, i18n } = useTranslation();
3
4// Forzar re-render con key
5<MyComponent key={i18n.language} />
Tags
react-i18next
i18next
react
i18n
internacionalización
tutorial
2026
IntlPull Team
IntlPull Team
Engineering

Building tools to help teams ship products globally. Follow us for more insights on localization and i18n.