Comenzando la Internacionalización (i18n) con Intlayer, Vite y React
¿Qué es Intlayer?
Intlayer es una innovadora biblioteca de internacionalización (i18n) de código abierto diseñada para simplificar el soporte multilingüe en aplicaciones web modernas.
Con Intlayer, puedes:
- Administrar fácilmente las traducciones utilizando diccionarios declarativos a nivel de componente.
- Localizar dinámicamente metadatos, rutas y contenido.
- Asegurar el soporte de TypeScript con tipos autogenerados, mejorando la autocompletación y la detección de errores.
- Beneficiarte de características avanzadas, como la detección y cambio dinámico de locales.
Guía Paso a Paso para Configurar Intlayer en una Aplicación Vite y React
Paso 1: Instalar Dependencias
Instala los paquetes necesarios usando npm:
npm install intlayer react-intlayer
intlayer
El paquete principal que proporciona herramientas de internacionalización para la gestión de configuraciones, traducción, declaración de contenido, transpilation y comandos de CLI.
react-intlayer El paquete que integra Intlayer con la aplicación React. Proporciona proveedores de contexto y hooks para la internacionalización de React. Además, incluye el plugin Vite para integrar Intlayer con el bundler Vite, así como middleware para detectar el locale preferido del usuario, gestionar cookies y manejar la redirección de URL.
Paso 2: Configuración de tu proyecto
Crea un archivo de configuración para configurar los idiomas de tu aplicación:
import { Locales, type IntlayerConfig } from "intlayer";
const config: IntlayerConfig = {
internationalization: {
locales: [
Locales.ENGLISH,
Locales.FRENCH,
Locales.SPANISH,
// Tus otros locales
],
defaultLocale: Locales.ENGLISH,
},
};
export default config;
A través de este archivo de configuración, puedes configurar URLs localizadas, redirección de middleware, nombres de cookies, la ubicación y extensión de tus declaraciones de contenido, desactivar los registros de Intlayer en la consola, y más. Para una lista completa de parámetros disponibles, consulta la documentación de configuración.
Paso 3: Integrar Intlayer en tu Configuración Vite
Agrega el plugin intlayer en tu configuración.
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import { intLayerPlugin } from "react-intlayer/vite";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), intLayerPlugin()],
});
El plugin intLayerPlugin() de Vite se utiliza para integrar Intlayer con Vite. Asegura la construcción de archivos de declaración de contenido y los monitorea en el modo de desarrollo. Define variables de entorno de Intlayer dentro de la aplicación Vite. Además, proporciona alias para optimizar el rendimiento.
Paso 4: Declarar Tu Contenido
Crea y gestiona tus declaraciones de contenido para almacenar traducciones:
import { t, type DeclarationContent } from "intlayer";
import type { ReactNode } from "react";
const appContent = {
key: "app",
content: {
viteLogo: t({
en: "Vite logo",
fr: "Logo Vite",
es: "Logo Vite",
}),
reactLogo: t({
en: "React logo",
fr: "Logo React",
es: "Logo React",
}),
title: "Vite + React",
count: t({
en: "count is ",
fr: "le compte est ",
es: "el recuento es ",
}),
edit: t<ReactNode>({
// No olvides importar React si utilizas un nodo de React en tu contenido
en: (
<>
Edit <code>src/App.tsx</code> and save to test HMR
</>
),
fr: (
<>
Éditez <code>src/App.tsx</code> et enregistrez pour tester HMR
</>
),
es: (
<>
Edita <code>src/App.tsx</code> y guarda para probar HMR
</>
),
}),
readTheDocs: t({
en: "Click on the Vite and React logos to learn more",
fr: "Cliquez sur les logos Vite et React pour en savoir plus",
es: "Haga clic en los logotipos de Vite y React para obtener más información",
}),
},
} satisfies DeclarationContent;
export default appContent;
Tus declaraciones de contenido pueden definirse en cualquier parte de tu aplicación tan pronto como se incluyan en el directorio contentDir (por defecto, ./src). Y coincidan con la extensión del archivo de declaración de contenido (por defecto, .content.{ts,tsx,js,jsx,mjs,cjs}). Para más detalles, consulta la documentación de declaración de contenido. Si tu archivo de contenido incluye código TSX, debes considerar importar import React from "react"; en tu archivo de contenido.
Paso 5: Utilizar Intlayer en Tu Código
Accede a tus diccionarios de contenido a lo largo de tu aplicación:
import { useState, type FC } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";
import { IntlayerProvider, useIntlayer } from "react-intlayer";
const AppContent: FC = () => {
const [count, setCount] = useState(0);
const content = useIntlayer("app");
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt={content.viteLogo.value} />
</a>
<a href="https://react.dev" target="_blank">
<img
src={reactLogo}
className="logo react"
alt={content.reactLogo.value}
/>
</a>
</div>
<h1>{content.title}</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
{content.count}
{count}
</button>
<p>{content.edit}</p>
</div>
<p className="read-the-docs">{content.readTheDocs}</p>
</>
);
};
const App: FC = () => (
<IntlayerProvider>
<AppContent />
</IntlayerProvider>
);
export default App;
Si deseas usar tu contenido en un atributo de tipo string, como alt, title, href, aria-label, etc., debes llamar al valor de la función, como:
jsx<img src={content.image.src.value} alt={content.image.value} />
Para aprender más sobre el hook useIntlayer, consulta la documentación.
(Opcional) Paso 6: Cambiar el idioma de tu contenido
Para cambiar el idioma de tu contenido, puedes usar la función setLocale proporcionada por el hook useLocale. Esta función permite configurar el locale de la aplicación y actualizar el contenido en consecuencia.
import type { FC } from "react";
import { Locales } from "intlayer";
import { useLocale } from "react-intlayer";
const LocaleSwitcher: FC = () => {
const { setLocale } = useLocale();
return (
<button onClick={() => setLocale(Locales.English)}>
Change Language to English
</button>
);
};
Para aprender más sobre el hook useLocale, consulta la documentación.
(Opcional) Paso 7: Añadir Rutas Localizadas a tu Aplicación
El propósito de este paso es hacer rutas únicas para cada idioma. Esto es útil para SEO y URLs amigables para SEO. Ejemplo:
- https://example.com/about
- https://example.com/es/about
- https://example.com/fr/about
Por defecto, las rutas no tienen prefijo para el locale por defecto. Si deseas prefijar el locale por defecto, puedes establecer la opción middleware.prefixDefault en true en tu configuración. Consulta la documentación de configuración para más información.
Para añadir enrutamiento localizado a tu aplicación, puedes crear un componente LocaleRouter que envuelva las rutas de tu aplicación y maneje el enrutamiento basado en el locale. Aquí hay un ejemplo usando React Router:
// Importando las dependencias y funciones necesarias
import { Locales, getConfiguration, getPathWithoutLocale } from "intlayer"; // Funciones y tipos de utilidad de 'intlayer'
import type { FC, PropsWithChildren } from "react"; // Tipos de React para componentes funcionales y props
import { IntlayerProvider } from "react-intlayer"; // Proveedor para el contexto de internacionalización
import {
BrowserRouter,
Routes,
Route,
useParams,
Navigate,
useLocation,
} from "react-router-dom"; // Componentes de Router para gestionar la navegación
// Desestructurando la configuración de Intlayer
const { internationalization, middleware } = getConfiguration();
const { locales, defaultLocale } = internationalization;
/**
* Un componente que maneja la localización y envuelve a los hijos con el contexto de locale apropiado.
* Maneja la detección de locale basada en URL y validación.
*/
const AppLocalized: FC<PropsWithChildren> = ({ children }) => {
const path = useLocation().pathname; // Obtener la ruta URL actual
const { locale } = useParams<{ locale: Locales }>(); // Extraer el parámetro locale de la URL
// Determinar el locale actual, retrocediendo al default si no se proporciona
const currentLocale = locale ?? defaultLocale;
// Eliminar el prefijo de locale de la ruta para construir una ruta base
const pathWithoutLocale = removeLocaleFromUrl(
path // Ruta URL actual
);
/**
* Si middleware.prefixDefault es verdadero, el locale por defecto siempre debe tener prefijo.
*/
if (middleware.prefixDefault) {
// Validar el locale
if (!locale || !locales.includes(locale)) {
// Redirigir al locale por defecto con la ruta actualizada
return (
<Navigate
to={`/${defaultLocale}/${pathWithoutLocale}`}
replace // Reemplazar la entrada actual del historial con la nueva
/>
);
}
// Envolver a los hijos con el IntlayerProvider y establecer el locale actual
return (
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
);
} else {
/**
* Cuando middleware.prefixDefault es falso, el locale por defecto no tiene prefijo.
* Asegurarse de que el locale actual sea válido y no el locale por defecto.
*/
if (
currentLocale.toString() !== defaultLocale.toString() &&
!locales
.filter(
(locale) => locale.toString() !== defaultLocale.toString() // Excluir el locale por defecto
)
.includes(currentLocale) // Comprobar si el locale actual está en la lista de locales válidos
) {
// Redirigir a la ruta sin prefijo de locale
return <Navigate to={pathWithoutLocale} replace />;
}
// Envolver a los hijos con el IntlayerProvider y establecer el locale actual
return (
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
);
}
};
/**
* Un componente de router que configura rutas específicas de locale.
* Utiliza React Router para gestionar la navegación y renderizar componentes localizados.
*/
export const LocaleRouter: FC<PropsWithChildren> = ({ children }) => (
<BrowserRouter>
<Routes>
<Route
// Patrón de ruta para capturar el locale (e.g., /en/, /fr/) y coincidir con todas las rutas siguientes
path="/:locale/*"
element={<AppLocalized>{children}</AppLocalized>} // Envuelve a los hijos con la gestión de locale
/>
{
// Si se desactiva el prefijo del locale por defecto, renderiza los hijos directamente en la ruta raíz
!middleware.prefixDefault && (
<Route
path="*"
element={<AppLocalized>{children}</AppLocalized>} // Envuelve a los hijos con la gestión de locale
/>
)
}
</Routes>
</BrowserRouter>
);
En paralelo, también puedes utilizar el intLayerMiddlewarePlugin para añadir enrutamiento del lado del servidor a tu aplicación. Este plugin automáticamente detectará el locale actual basado en la URL y establecerá la cookie de locale apropiada. Si no se especifica ningún locale, el plugin determinará el locale más apropiado basado en las preferencias de idioma del navegador del usuario. Si no se detecta ningún locale, redirigirá al locale por defecto.
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import { intLayerPlugin, intLayerMiddlewarePlugin } from "react-intlayer/vite";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), intLayerPlugin(), intLayerMiddlewarePlugin()],
});
(Opcional) Paso 8: Cambiar la URL cuando cambie el locale
Para cambiar la URL cuando cambie el locale, puedes usar la prop onLocaleChange proporcionada por el hook useLocale. En paralelo, puedes usar los hooks useLocation y useNavigate de react-router-dom para actualizar la ruta de la URL.
import { useLocation, useNavigate } from "react-router-dom";
import {
Locales,
getHTMLTextDir,
getLocaleName,
getLocalizedUrl,
} from "intlayer";
import { useLocale } from "react-intlayer";
import { type FC } from "react";
const LocaleSwitcher: FC = () => {
const location = useLocation(); // Obtener la ruta URL actual. Ejemplo: /fr/about
const navigate = useNavigate();
const changeUrl = (locale: Locales) => {
// Construir la URL con el locale actualizado
// Ejemplo: /es/about con el locale establecido en español
const pathWithLocale = getLocalizedUrl(location.pathname, locale);
// Actualizar la ruta de la URL
navigate(pathWithLocale);
};
const { locale, availableLocales, setLocale } = useLocale({
onLocaleChange: changeUrl,
});
return (
<ol>
{availableLocales.map((localeItem) => (
<li key={localeItem}>
<a
href={getLocalizedUrl(location, localeItem)}
hrefLang={localeItem}
aria-current={locale === localeItem ? "page" : undefined}
onClick={(e) => {
e.preventDefault();
setLocale(localeItem);
}}
>
<span>
{/* Idioma en su propio Locale - e.g. Français */}
{getLocaleName(localeItem, locale)}
</span>
<span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
{/* Idioma en el Locale actual - e.g. Francés con el locale actual establecido en Locales.SPANISH */}
{getLocaleName(localeItem)}
</span>
<span dir="ltr" lang={Locales.ENGLISH}>
{/* Idioma en inglés - e.g. French */}
{getLocaleName(localeItem, Locales.ENGLISH)}
</span>
<span>
{/* Idioma en su propio Locale - e.g. FR */}
{localeItem}
</span>
</a>
</li>
))}
</ol>
);
};
Referencias de documentación:
Configurar TypeScript
Intlayer utiliza la ampliación de módulos para obtener beneficios de TypeScript y hacer tu base de código más fuerte.
Asegúrate de que tu configuración de TypeScript incluya los tipos autogenerados.
{
// tu configuración personalizada
"include": [
"src",
"types", // <- Incluir los tipos autogenerados
],
}
Configuración de Git
Se recomienda ignorar los archivos generados por Intlayer. Esto te permite evitar comprometerlos en tu repositorio de Git.
Para hacerlo, puedes agregar las siguientes instrucciones a tu archivo .gitignore:
# Ignorar los archivos generados por Intlayer
.intlayer
Si tienes una idea para mejorar esta documentación, no dudes en contribuir enviando una pull request en GitHub.
Enlace de GitHub a la documentación