Recientemente trabajé en un proyecto emocionante que implicaba crear un sitio web capaz de cambiar entre idiomas para atraer a una audiencia más amplia. Esto me hizo comprender mejor el concepto de “localización”, que generalmente implica adaptar el contenido para que sea relevante, accesible y fácilmente identificable para los usuarios en diferentes idiomas y regiones.

La localización no se trata solo de traducir palabras, se trata de crear una experiencia que haga que los usuarios se sientan como en casa, sin importar su idioma. Por ejemplo, plataformas globales como Amazon hacen que el cambio de idioma sea tan fluido que se siente casi mágico. Más allá de mejorar la experiencia del usuario, esta característica juega un papel crucial en impulsar negocios al llegar a una audiencia más amplia y fomentar conexiones más fuertes con los clientes en todo el mundo.

Tabla de Contenidos

¿Qué es i18n y por qué usarlo?

i18n, que significa internacionalización, significa que una aplicación admite múltiples idiomas. “i18n” se deriva del hecho de que hay 18 letras entre la primera “i” y la última “n” en “internacionalización”. Se trata de hacer que tu aplicación sea adaptable para audiencias globales mediante la traducción de texto, el formato de fechas y números, la gestión de monedas y el cumplimiento de convenciones regionales.

Al habilitar la internacionalización, tu aplicación se convierte no solo en una herramienta, sino en una plataforma inclusiva que se dirige directamente a las preferencias y la cultura del usuario.

¡Comencemos

Crearemos una aplicación web multilingüe de demostración muy simple con una función de cambio a modo oscuro para demostrar cómo lograr este concepto.

Requisitos previos

  1. Conocimientos básicos de React: Debes entender cómo crear componentes, gestionar el estado y utilizar Hooks como useState y useEffect. Si eres nuevo en React, te recomiendo empezar con la documentación oficial de React para tener una base sólida.

  2. Familiaridad con los Conceptos de Internacionalización – Conocer los conceptos básicos de internacionalización (i18n) y por qué es importante te dará contexto para el proyecto. Las secciones anteriores de este artículo cubren lo esencial.

  3. Tailwind CSS – Usaremos Tailwind CSS para el estilo. Es un marco de trabajo CSS de utilidad primero que te ayuda a construir diseños modernos y receptivos sin salir de tu HTML. Si no estás familiarizado, revisa la documentación de Tailwind.

  4. Node.js – Asegúrate de que Node.js esté instalado en tu sistema para manejar las dependencias. Puedes descargar la última versión desde Node.js.

  5. Gestor de paquetes: Se necesita npm (incluido con Node.js) o yarn para gestionar las dependencias del proyecto

Herramientas que utilizaremos

  1. Editor de código

  2. Biblioteca de localización: react-i18next

  3. Biblioteca de iconos: hero-icons

Paso 1: Cómo configurar el proyecto

Inicializar el proyecto

Usar Vite para una configuración rápida:

npm create vite@latest multilingual-demo

Siga las instrucciones que aparecen en su terminal, seleccionando React y TypeScript para el desarrollo como se muestra en la imagen a continuación:

Instalar dependencias

Ejecute los siguientes comandos en su terminal para instalar las dependencias requeridas para este proyecto:

npm install i18next react-i18next i18next-browser-languagedetector i18next-http-backend heroicons 
npm install tailwindcss postcss autoprefixer  
npx tailwindcss init

Configurar TailwindCSS

Actualice el archivo tailwind.config.ts:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  darkMode: "class", // Para nuestra funcionalidad de modo oscuro
  theme: {
    container: {
      center: true,
      padding: "1.25rem",
      screens: {
        sm: "1200px",
      },
    },
    extend: {},
  },
  plugins: [],
};

Agregue TailwindCSS a src/index.css:

@tailwind base;  
@tailwind components;  
@tailwind utilities;

Paso 2: Cómo configurar la internacionalización con i18next

Inicialice i18next

Cree un archivo i18n.tsx en la carpeta src y configure i18next:

import i18next from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import Backend from "i18next-http-backend";

i18next.use(LanguageDetector).use(initReactI18next).use(Backend).init({
  returnObjects: true,
  fallbackLng: "en", // Idioma al que se cambiará si el seleccionado no está configurado
  debug: true, // Para permitirnos ver errores
  //   lng: "en", // Idioma predeterminado en inglés
});

Echemos un vistazo rápido al contenido de este archivo, ya que desempeña un papel clave en habilitar la funcionalidad de traducción. Este archivo es responsable de configurar el núcleo del proceso de traducción y asegurarse de que la función de cambio de idioma funcione sin problemas en su aplicación.

  • i18next: La biblioteca de internacionalización central que estamos utilizando para la traducción.

  • LanguageDetector: Nos ayuda a detectar automáticamente el idioma preferido del usuario, basándose en la configuración del navegador.

  • initReactI18next: Es responsable de integrar el plugin i18next con React y proporciona Hooks como el Hook useTranslation y otras utilidades.

  • Backend: Obtiene datos de traducción dinámicamente de una fuente externa. En este caso, utilizaremos archivos JSON.

Importa este archivo en el archivo main.tsx:

//main.tsx

import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import "./i18n.tsx";  //Importar aquí

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <React.Suspense fallback="loading">
      <App />
    </React.Suspense>
  </StrictMode>
);

Crea Archivos de Traducción

En el directorio public/locales, crea subcarpetas para cada idioma (por ejemplo, en, fr) e incluye archivos translation.json:

en/translation.json

{
    "greeting": "Welcome to the Language Playground",
    "detail": {
        "line1": "Did you know that over 7,000 languages are spoken worldwide?",
        "line2": "This Playground demonstrates how web applications can support users in multiple languages, making them accessible and inclusive to people from different backgrounds."
    }
}

fr/translation.json

{
    "greeting": "Bienvenue sur le terrain de jeu linguistique",
    "detail": {
        "line1": "Saviez-vous que plus de 7 000 langues sont parlées dans le monde ?",
        "line2": "Ce terrain de jeu démontre comment les applications web peuvent prendre en charge les utilisateurs dans plusieurs langues, les rendant accessibles et inclusives aux personnes de différents horizons."
    }
}

Aquí, puedes agregar tantos idiomas con sus archivos de traducción que se suministrarán a i18next. Ten en cuenta que las claves en los archivos JSON son las mismas que se usarían como referencias al mostrarlas en el sitio web.

Paso 3: Cómo Construir Componentes

Crea una carpeta components en el directorio src y agrega los siguientes componentes:

Selector de Idioma

Crea el componente LanguageSelector – que contiene un elemento select para ayudar a los usuarios a cambiar de idioma dinámicamente:

import { useEffect, useState } from "react";
import i18next from "i18next";
import { useTranslation } from "react-i18next";

type languageOption = { language: string; code: string };

const languageOptions: languageOption[] = [
  {
    language: "English",
    code: "en",
  },
  { language: "French", code: "fr" },
  { language: "German", code: "de" },
  { language: "Spanish", code: "es" },
  { language: "Arabic", code: "ar" },
  { language: "Yoruba", code: "yo" },
];

const LanguageSelector = () => {
  // Establecer el idioma inicial detectado por i18next o el idioma predeterminado
  const [language, setLanguage] = useState(i18next.language);

  const { i18n } = useTranslation();

  const handleLanguageChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    const selectedLanguage = e.target.value;
    setLanguage(selectedLanguage);
    i18next.changeLanguage(selectedLanguage); // Actualizar el idioma en i18next
  };

  useEffect(() => {
    document.body.dir = i18n.dir(); // Establecer el cuerpo en ltr o rtl
  }, [i18n, i18n.language]);

  return (
    <select
      id="language"
      value={language}
      onChange={handleLanguageChange}
      className="p-2 border border-gray-300 rounded-md shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
        dark:bg-gray-800 dark:border-gray-600 dark:text-gray-200 dark:focus:border-indigo-400 dark:focus:ring-indigo-700 dark:focus:ring-opacity-50"
    >
      {languageOptions.map(({ language, code }, key) => (
        <option value={code} key={key}>
          {language}
        </option>
      ))}
    </select>
  );
};

export default LanguageSelector;
  • Inicializa el idioma con el idioma detectado por i18next o el idioma establecido por defecto.

  • El Hook useTranslation expone la instancia i18n de i18next para interactuar con la configuración de internacionalización.

  • La función handleLanguageChange se utilizaría para actualizar el idioma seleccionado por el usuario. Se activa cuando el usuario selecciona un nuevo idioma en el menú desplegable.

Implementando la Dirección del Texto

El atributo dir en HTML es una característica crítica para garantizar la accesibilidad y la inclusividad en las aplicaciones web, especialmente al tratar con idiomas que difieren en la dirección del texto. Por ejemplo:

  • Izquierda a Derecha (LTR): La mayoría de los idiomas, incluyendo inglés, francés y español, siguen esta dirección.

    Derecha a Izquierda (RTL): Idiomas como árabe y hebreo requieren que el alineamiento del texto y el diseño se inviertan para mantener la legibilidad y el contexto cultural.

Para lograr esto en nuestra aplicación, establecemos el document.body.dir al dir de i18n mientras escuchamos los cambios en la selección del idioma usando el gancho useEffect

Alternar Modo Oscuro

Crea el componente DarkModeToggle para cambiar entre el modo claro y oscuro según lo prefiera el usuario.

import { useEffect, useState } from "react";
import { SunIcon, MoonIcon } from "@heroicons/react/solid";

const DarkModeToggle = () => {
  const [darkMode, setDarkMode] = useState(false);

  useEffect(() => {
    // Comprobar almacenamiento local o preferencia del sistema en la primera carga
    const isDark =
      localStorage.getItem("theme") === "dark" ||
      (!localStorage.getItem("theme") &&
        window.matchMedia("(prefers-color-scheme: dark)").matches);
    setDarkMode(isDark);
    document.documentElement.classList.toggle("dark", isDark);
  }, []);

  const toggleDarkMode = () => {
    setDarkMode(!darkMode);
    document.documentElement.classList.toggle("dark", !darkMode);
    localStorage.setItem("theme", !darkMode ? "dark" : "light");
  };

  return (
    <button
      aria-label="Toggle dark mode"
      onClick={toggleDarkMode}
      className="p-1 rounded"
    >
      {darkMode ? (
        <SunIcon
          className="w-6 h-6 text-yellow-500 "
          onClick={toggleDarkMode}
        />
      ) : (
        <MoonIcon className="w-6 h-6 text-gray-900 " onClick={toggleDarkMode} />
      )}
    </button>
  );
};

export default DarkModeToggle;

Componente de Encabezado

El componente Header sirve como un componente padre para los componentes DarkModeToggle y languageSelector.

import DarkModeToggle from "./DarkModeToggle";
import LanguageSelector from "./LanguageSelector";

const Header = () => {
  return (
    <header className="container flex justify-between">
      <DarkModeToggle />
      <LanguageSelector />
    </header>
  );
};

export default Header;

Paso 4: Componente Principal de la Aplicación

En el archivo src/app, incluye lo siguiente:

import { useTranslation } from "react-i18next";
import Header from "./components/Header";

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

  const line1 = t("detail.line1");
  const line2 = t("detail.line2");

  return (
    <div className="h-[100vh] bg-white text-black dark:bg-gray-900 dark:text-white py-8">
      <Header />
      <div className="container text-center max-w-2xl mt-28">
        <h1 className="text-4xl font-bold">{t("greeting")}</h1>
        <p className="mt-8">{line1}</p>
        <p className="mt-2">{line2}</p>
      </div>
    </div>
  );
};

export default App;
  • El Hook useTranslation de react-i18next expone la función t, que se utiliza para recuperar texto traducido.

  • Recupera la cadena traducida basada en una clave de sus archivos de traducción (por ejemplo, en.json, fr.json).

Al seguir estos pasos, su aplicación debería estar completamente funcional con traducciones integradas de manera transparente. Así es como se ve el resultado final de nuestra aplicación:

Consulte la demostración en vivo y el código fuente en GitHub

Conclusión

Crear sitios web que permitan a los usuarios seleccionar su idioma preferido no es solo un logro técnico, sino un paso hacia hacer que la web sea más inclusiva y acogedora.

Al combinar la internacionalización (i18n) con herramientas como React-i18next y el estilo con Tailwind CSS, puede construir aplicaciones que sean flexibles, fáciles de usar y accesibles para una audiencia global.

En este proyecto, recorrimos la configuración de i18n, agregamos un selector de idioma e incluimos el “modo oscuro” para una mejor usabilidad.

Referencias

https://react.i18next.com/

https://www.youtube.com/watch?v=dltHi9GWMIo