Comenzando con la Internacionalización (i18n) con Intlayer y Next.js usando Page Router
¿Qué es Intlayer?
Intlayer es una biblioteca innovadora y de código abierto para la internacionalización (i18n) diseñada para simplificar el soporte multilingüe en aplicaciones web modernas. Intlayer se integra perfectamente con el último framework Next.js, incluyendo su tradicional Page Router.
Con Intlayer, puedes:
- Gestionar fácilmente las traducciones utilizando diccionarios declarativos a nivel de componente.
- Localizar dinámicamente metadatos, rutas y contenido.
- Garantizar soporte para 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.
Intlayer es compatible con Next.js 12, 13, 14 y 15. Si estás utilizando Next.js App Router, consulta la guía de App Router. Para Next.js 15, sigue esta guía.
Guía Paso a Paso para Configurar Intlayer en una Aplicación Next.js Usando Page Router
Paso 1: Instalar Dependencias
Instala los paquetes necesarios utilizando tu gestor de paquetes preferido:
npm install intlayer next-intlayer
intlayer
El paquete principal que proporciona herramientas de internacionalización para la gestión de configuraciones, traducciones, declaración de contenido, transpilación y comandos CLI.
next-intlayer
El paquete que integra Intlayer con Next.js. Proporciona proveedores de contexto y hooks para la internacionalización en Next.js. Además, incluye el plugin de Next.js para integrar Intlayer con Webpack o Turbopack, así como middleware para detectar el idioma preferido del usuario, gestionar cookies y manejar redirecciones de URL.
Paso 2: Configurar Tu Proyecto
Crea un archivo de configuración para definir los idiomas soportados por tu aplicación:
// Configuración de idiomas soportadosimport { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = { internationalization: { locales: [ Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH, // Agrega tus otros locales aquí ], 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 logs 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 con la Configuración de Next.js
Modifica tu configuración de Next.js para incorporar Intlayer:
import { withIntlayer } from "next-intlayer/server";/** @type {import('next').NextConfig} */const nextConfig = { // Tu configuración existente de Next.js};export default withIntlayer(nextConfig);
El plugin withIntlayer() de Next.js se utiliza para integrar Intlayer con Next.js. Garantiza la construcción de archivos de declaración de contenido y los monitorea en modo de desarrollo. Define variables de entorno de Intlayer dentro de los entornos de Webpack o Turbopack. Además, proporciona alias para optimizar el rendimiento y garantizar la compatibilidad con componentes del servidor.
Paso 4: Configurar Middleware para la Detección de Locales
Configura middleware para detectar y manejar automáticamente el idioma preferido del usuario:
export { intlayerMiddleware as middleware } from "next-intlayer/middleware";export const config = { matcher: "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\..*|_next).*)",};
Adapta el parámetro matcher para que coincida con las rutas de tu aplicación. Para más detalles, consulta la documentación de Next.js sobre la configuración del matcher.
Paso 5: Definir Rutas Dinámicas de Locales
Implementa enrutamiento dinámico para servir contenido localizado basado en el idioma del usuario.
Crear Páginas Específicas por Idioma:
Renombra tu archivo de página principal para incluir el segmento dinámico [locale].
bashmv src/pages/index.tsx src/pages/[locale]/index.tsx
Actualizar _app.tsx para Manejar la Localización:
Modifica tu _app.tsx para incluir proveedores de Intlayer.
src/pages/_app.tsximport type { FC } from "react";import type { AppProps } from "next/app";import { IntlayerClientProvider } from "next-intlayer";const App = FC<AppProps>({ Component, pageProps }) => { const { locale } = pageProps; return ( <IntlayerClientProvider locale={locale}> <Component {...pageProps} /> </IntlayerClientProvider> );}export default MyApp;
Configurar getStaticPaths y getStaticProps:
En tu [locale]/index.tsx, define las rutas y propiedades para manejar diferentes idiomas.
src/pages/[locale]/index.tsximport type { FC } from "react";import type { GetStaticPaths, GetStaticProps } from "next";import { type Locales, getConfiguration } from "intlayer";const HomePage: FC = () => <div>{/* Tu contenido aquí */}</div>;export const getStaticPaths: GetStaticPaths = () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false };};export const getStaticProps: GetStaticProps = ({ params }) => { const locale = params?.locale as string; return { props: { locale, }, };};export default HomePage;
getStaticPaths y getStaticProps aseguran que tu aplicación preconstruya las páginas necesarias para todos los idiomas en el Page Router de Next.js. Este enfoque reduce el cálculo en tiempo de ejecución y mejora la experiencia del usuario. Para más detalles, consulta la documentación de Next.js sobre getStaticPaths y getStaticProps.
Paso 6: Declarar Tu Contenido
Crea y gestiona tus declaraciones de contenido para almacenar traducciones.
import { t, type Dictionary } from "intlayer";const homeContent = { key: "home", content: { title: t({ en: "Welcome to My Website", fr: "Bienvenue sur mon site Web", es: "Bienvenido a mi sitio web", }), description: t({ en: "Get started by editing this page.", fr: "Commencez par éditer cette page.", es: "Comience por editar esta página.", }), },} satisfies Dictionary;export default homeContent;
Para más información sobre cómo declarar contenido, consulta la guía de declaración de contenido.
Paso 7: Utilizar Contenido en Tu Código
Accede a tus diccionarios de contenido en toda tu aplicación para mostrar contenido traducido.
import type { FC } from "react";import { useIntlayer } from "next-intlayer";import { ComponentExample } from "@components/ComponentExample";const HomePage: FC = () => { const content = useIntlayer("home"); return ( <div> <h1>{content.title}</h1> <p>{content.description}</p> <ComponentExample /> {/* Componentes adicionales */} </div> );};// ... Resto del código, incluyendo getStaticPaths y getStaticPropsexport default HomePage;
import type { FC } from "react";import { useIntlayer } from "next-intlayer";export const ComponentExample: FC = () => { const content = useIntlayer("component-example"); // Asegúrate de tener una declaración de contenido correspondiente return ( <div> <h2>{content.title}</h2> <p>{content.content}</p> </div> );};
Al usar traducciones en atributos de tipo string (por ejemplo, alt, title, href, aria-label), llama al valor de la función de la siguiente manera:
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 8: Internacionalizar Tus Metadatos
Para internacionalizar metadatos como títulos y descripciones de páginas, utiliza la función getStaticProps junto con la función getTranslation de Intlayer.
import { GetStaticPaths, GetStaticProps } from "next";import { type IConfigLocales, getTranslation, Locales } from "intlayer";import { useIntlayer } from "next-intlayer";interface HomePageProps { locale: string; metadata: Metadata;}const HomePage = ({ metadata }: HomePageProps) => { // Los metadatos pueden ser utilizados en el head u otros componentes según sea necesario return ( <div> <Head> <title>{metadata.title}</title> <meta name="description" content={metadata.description} /> </Head> {/* Contenido adicional */} </div> );};export const getStaticProps: GetStaticProps = async ({ params }) => { const locale = params?.locale as string; const t = <T,>(content: IConfigLocales<T>) => getTranslation(content, locale); const metadata = { title: t({ en: "My Website", fr: "Mon Site Web", es: "Mi Sitio Web", }), description: t({ en: "Welcome to my website.", fr: "Bienvenue sur mon site Web.", es: "Bienvenido a mi sitio web.", }), }; return { props: { locale, metadata, }, };};export default HomePage;// ... Resto del código incluyendo getStaticPaths
(Opcional) Paso 9: Cambiar el Idioma de Tu Contenido
Para permitir que los usuarios cambien de idioma dinámicamente, utiliza la función setLocale proporcionada por el hook useLocale.
import { Locales, getHTMLTextDir, getLocaleName, getLocalizedUrl,} from "intlayer";import { useLocalePageRouter } from "next-intlayer";import { type FC } from "react";import Link from "next/link";const LocaleSwitcher: FC = () => { const { locale, pathWithoutLocale, availableLocales, setLocale } = useLocalePageRouter(); return ( <div> <button popoverTarget="localePopover">{getLocaleName(locale)}</button> <div id="localePopover" popover="auto"> {availableLocales.map((localeItem) => ( <Link href={getLocalizedUrl(pathWithoutLocale, localeItem)} hrefLang={localeItem} key={localeItem} aria-current={locale === localeItem ? "page" : undefined} onClick={(e) => { e.preventDefault(); setLocale(localeItem); }} > <span> {/* Locale - e.g. FR */} {localeItem} </span> <span> {/* Idioma en su propio Local - e.g. Français */} {getLocaleName(localeItem, locale)} </span> <span dir={getHTMLTextDir(localeItem)} lang={localeItem}> {/* Idioma en el Local actual - e.g. Francés con el local actual configurado en Locales.SPANISH */} {getLocaleName(localeItem)} </span> <span dir="ltr" lang={Locales.ENGLISH}> {/* Idioma en Inglés - e.g. French */} {getLocaleName(localeItem, Locales.ENGLISH)} </span> </Link> ))} </div> </div> );};
La API useLocalePageRouter es la misma que useLocale. Para aprender más sobre el hook useLocale, consulta la documentación.
Referencias de la documentación:
(Opcional) Paso 10: Crear un Componente de Enlace Localizado
Para garantizar que la navegación de tu aplicación respete el idioma actual, puedes crear un componente personalizado Link. Este componente automáticamente agrega el prefijo de idioma a las URLs internas, de modo que, por ejemplo, cuando un usuario que habla francés haga clic en un enlace a la página "Acerca de", sea redirigido a /fr/about en lugar de /about.
Este comportamiento es útil por varias razones:
- SEO y Experiencia de Usuario: Las URLs localizadas ayudan a los motores de búsqueda a indexar correctamente las páginas específicas por idioma y proporcionan a los usuarios contenido en su idioma preferido.
- Consistencia: Al usar un enlace localizado en toda tu aplicación, garantizas que la navegación se mantenga dentro del idioma actual, evitando cambios inesperados de idioma.
- Mantenibilidad: Centralizar la lógica de localización en un único componente simplifica la gestión de URLs, haciendo que tu base de código sea más fácil de mantener y ampliar a medida que tu aplicación crece.
A continuación, se muestra la implementación de un componente Link localizado en TypeScript:
"use client";import { getLocalizedUrl } from "intlayer";import NextLink, { type LinkProps as NextLinkProps } from "next/link";import { useLocale } from "next-intlayer";import { forwardRef, PropsWithChildren, type ForwardedRef } from "react";/** * Función de utilidad para verificar si una URL es externa. * Si la URL comienza con http:// o https://, se considera externa. */export const checkIsExternalLink = (href?: string): boolean => /^https?:///.test(href ?? "");/** * Un componente de enlace personalizado que adapta el atributo href según el idioma actual. * Para enlaces internos, utiliza `getLocalizedUrl` para agregar el prefijo del idioma a la URL (por ejemplo, /fr/about). * Esto garantiza que la navegación se mantenga dentro del contexto del idioma actual. */export const Link = forwardRef< HTMLAnchorElement, PropsWithChildren<NextLinkProps>>(({ href, children, ...props }, ref: ForwardedRef<HTMLAnchorElement>) => { const { locale } = useLocale(); const isExternalLink = checkIsExternalLink(href.toString()); // Si el enlace es interno y se proporciona un href válido, obtiene la URL localizada. const hrefI18n: NextLinkProps["href"] = href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href; return ( <NextLink href={hrefI18n} ref={ref} {...props}> {children} </NextLink> );});Link.displayName = "Link";
Cómo Funciona
Detección de Enlaces Externos:
La función auxiliar checkIsExternalLink determina si una URL es externa. Los enlaces externos se dejan sin cambios porque no necesitan localización.Recuperación del Idioma Actual:
El hook useLocale proporciona el idioma actual (por ejemplo, fr para francés).Localización de la URL:
Para enlaces internos (es decir, no externos), se utiliza getLocalizedUrl para agregar automáticamente el prefijo del idioma actual a la URL. Esto significa que si tu usuario está en francés, pasar /about como el href lo transformará en /fr/about.Devolución del Enlace:
El componente devuelve un elemento <a> con la URL localizada, asegurando que la navegación sea consistente con el idioma.
Al integrar este componente Link en toda tu aplicación, mantienes una experiencia de usuario coherente y consciente del idioma, además de beneficiarte de un mejor SEO y usabilidad.
Configurar TypeScript
Intlayer utiliza la ampliación de módulos para obtener beneficios de TypeScript y fortalecer tu base de código.
Asegúrate de que tu configuración de TypeScript incluya los tipos autogenerados.
{ // ... Tus configuraciones existentes de TypeScript "include": [ // ... Tus configuraciones existentes de TypeScript ".intlayer/**/*.ts", // Incluye los tipos autogenerados ],}
Configuración de Git
Para mantener tu repositorio limpio y evitar cometer archivos generados, se recomienda ignorar los archivos creados por Intlayer.
Agrega las siguientes líneas a tu archivo .gitignore:
# Ignorar los archivos generados por Intlayer.intlayer
Recursos Adicionales
- Documentación de Intlayer: Repositorio de GitHub
- Guía de Diccionario: Diccionario
- Documentación de Configuración: Guía de Configuración
Siguiendo esta guía, puedes integrar eficazmente Intlayer en tu aplicación Next.js utilizando el Page Router, habilitando un soporte de internacionalización robusto y escalable para tus proyectos web.
Ir Más Allá
Para ir más allá, puedes implementar el editor visual o externalizar tu contenido utilizando el CMS.
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