La necesidad de incorporar internacionalización es una consideración fundamental en muchos proyectos web. En este artículo, exploraremos una forma efectiva de incorporar internacionalización a tu proyecto, sin depender de la conocida librería i18next.
Ya sea que estés buscando respuestas a consultas frecuentes sobre cómo implementar la internacionalización en tus proyectos o simplemente deseas una alternativa a i18next, has llegado al lugar adecuado. A lo largo de esta guía, te presento una solución que simplificará tu código y reducirá la cantidad de dependencias, lo que resulta especialmente valioso en proyectos más pequeños.
I18next sigue siendo una elección sólida para proyectos grandes y complejos que requieren funciones avanzadas de internacionalización, si lo necesitas te dejo esta guía Internacionalización en NextJs-13 con I18Next para integrar I18Next a tu proyecto con NextJs 13. Pero, en otras ocaciones, podemos lograr los mismos resultados de manera más sencilla para proyectos más modestos. Así que, si estás listo para descubrir cómo internacionalizar tu aplicación Next.js 13 de manera efectiva y sin las complejidades de i18next, ¡continúa leyendo!
Crear el proyecto
Empecemos creando un proyecto con Next.js 13. Si ya tienes uno, puedes saltarte este paso.
npx create-next-app@latest// What is your project named? … next13-internationalization// Would you like to use TypeScript? … No / Yes ✅// Would you like to use ESLint? … No / Yes ✅// Would you like to use Tailwind CSS? … No / Yes ❌// Would you like to use src/ directory? … No / Yes ✅// Would you like to use App Router? (recommended) … No / Yes ✅// Would you like to customize the default import alias? … No / Yes ❌cd next13-internationalization
Read-only
Tests
Instalar dependencias
npm install @formatjs/intl-localematcher negotiatornpm i --save-dev @types/negotiator
Read-only
Tests
Estos dos módulos nos ayudarán a adapatarnos a las preferencias del usuario al seleccionar automáticamente el idioma más apropiado para el usuario en función de las preferencias de idioma proporcionadas en las cabeceras HTTP y la configuración de internacionalización que definiremos.
Enrutamiento
1. Nueva estructura
Crearemos la nueva estructura del proyecto donde usaremos el idioma para crear rutas dinámicas.
Estas rutas se crean agregando segmentos dinámicos a partir de datos dinámicos que se completan en el momento de la solicitud o se renderizan previamente en el momento de la compilación.
Este segmento dinámico lo creamos colocando el nombre de la carpeta entre corchetes, que en nuestro caso será [lng], y lo tendremos disponible como parámetros en layout, page, route y generateMetadata.
En este proyecto usaré los idiomas español e inglés, pero puedes agregar tantos como quieras.
La ruta de nuestro proyecto se verá cómo:
Ruta=>app/[lng]/page.tsxURL=>http://localhost:3000/[lng] => [lng] será reemplazado por el lenguaje que usemos.por ejemplo http://localhost:3000/es o http://localhost:3000/en donde 'es' y 'en' son nuestros datos dinámicos para la url en español e inglés respectivamente.Parámetros=>{lng:['es','en']}
En este archivo definimos los idiomas que vamos a utilizar y el idioma por defecto.
También definimos las secciones que tendrán los diccionarios de traducciones que crearemos, por ejemplo podemos definir por pages: 'home', 'second-page'. Esta división de secciones nos ayudará a mantener una estructura más clara y limpia para manejar las traducciones.
4. Creamos el diccionario de traducciones que vamos a utilizar:
El diccionario es la base de nuestra internacionalización que nos permite ofrecer la experiencia de usuario en diferentes idiomas.
En una carpeta llamada dictionaries, organizaremos los archivos de internacionalización de una manera estructurada. Creamos carpetas dentro de dictionaries con el nombre de un idioma específico, como 'es' para español, 'en' para inglés, como definimos en locales en el archivo lng-config.ts.
Dentro de cada una de estas carpetas de idioma, crearemos los archivos .json que contendrán las traducciones para cada sección que también definimos en el archivo lng-config.ts, como 'home' y 'second-page'. Todos los json deben respetar la misma estructura en cada carpeta.
// la carpeta `en` y `es` serán el diccionario para las traducciones en inglés y español respectivamente.// Dentro tendremos los json que serán cada sección que definimos en lng-config.ts:
|__ src
|__dictionaries
|__en
|__home.json
|__second-page.json
|__es
|__home.json
|__second-page.json
Read-only
Tests
5. Creamos la función para traer el diccionario que necesitamos según el lenguaje seleccionado:
Aquí tendremos una función que recibe el lenguaje que estamos utilizando y la sección que solicitamos. Esto devuelve el archivo json que creamos en dictionaries que
corresponde a ese lenguaje y sección respectivamente.
import'server-only';import{lngConfig,Locale}from'./lng-config';type DictionaryFunction = (section: string)=>Promise<any>;constdictionaries:{[key: string]: DictionaryFunction } = {};lngConfig.locales.forEach((language)=>{dictionaries[language] = (section: string)=>import(`./dictionaries/${language}/${section}.json`).then((module)=>module.default);});// Agregar una función para el idioma predeterminado ('en') que no necesita 'section'dictionaries.en = (section: string)=>import(`./dictionaries/en/${section}.json`).then((module)=>module.default);exportconstgetDictionary = async(locale: Locale,section: string)=>{constdictionaryFunction = dictionaries[locale];if(dictionaryFunction){returndictionaryFunction(section);}else{// Si el idioma no está disponible, devolver el diccionario en inglés por defectoreturndictionaries.en(section);}};
Read-only
Tests
6. En el archivo layout.tsx agregamos los lenguajes:
Ahora que ya tenemos todas las configuraciones hechas y nuestros diccionarios definidos, utilizamos generateStaticParams para añadir el idioma al HTML.
import{Inter}from'next/font/google';import{lngConfig}from'../../lng-config';import'../globals.css';constinter = Inter({subsets:['latin']});exportasyncfunctiongenerateStaticParams(){returnlngConfig.locales.map((locale)=>({lang:locale}));}exportdefaultfunctionRoot({children,params}:{children: React.ReactNode;params:{lang: string }}){return(<htmllang={params.lang}className={inter.className}><body>{children}</body></html>);}exportconstmetadata = {title:'i18n within app directory - Vercel Examples',description:'How to do i18n in Next.js 13 within app directory',};
Read-only
Tests
7. Usar las traducciones en Server Component:
El componente Home utiliza la función getDictionary para obtener las traducciones y configuraciones necesarias para mostrar contenido multilingüe en nuestra aplicación. Para asegurarnos de que la aplicación funcione de manera fluida y que no se bloquee mientras espera que las traducciones se carguen, utilizamos async/await al llamar a getDictionary.
La función getDictionary requiere dos parámetros:
El lenguaje: estará disponible por parámetros, cómo definimos en el layout.tsx.
La sección: la importamos desde la definición de sections en el archivo lng-config.ts para evitar errores tipográficos.
Entonces getDictionary me devolverá el archivo json de la sección que indicamos y del diccionario que estemos utilizando.
// src/dictionaries/es/home.json{"note":"Esta es la página de inicio"}// src/dictionaries/en/home.json{"note":"This is the home page"}
Read-only
Tests
8. Usar las traducciones en Client Component:
Un Client Component recibirá las traducciones por parámetro, directamente el String
que va a mostrar.
Recordar agregar en el componente el type para este parámetro, ejemplo:
// type del parámetro dictionary que recibe el client component:
dictionary:Record<string,string>
// Desde un server component padre:constdictionary = awaitgetDictionary(lang,i18n.sections.home);<CustomClientComponentlng={lng}dictionary={dictionary.note}/>
Read-only
Tests
9. Crear un switch de idioma:
Para cambiar de idioma, lo que hacemos es usar la ruta dinámica que creamos con la carpeta [lng]. Entonces debemos navegar a la ruta del idioma que queramos.
El componente será un client component porque usaremos el hook usePathname.
Este hook (usePathname) es necesario para hacer la navegación en caso de que tengamos una url con más partes que el dominio, por ejemplo /second-page que tenemos definida en esta estructura. Tomamos el path completo y luego con una expresión regular, reemplazamos el idioma por el que elegimos en el switch.
En langRegex modificamos nuestro array de idiomas definido en lng-config.ts para usarlo en la expresión regular y no tener que agregar manualmente al switch algún idioma extra.
En el atributo href del elemento Link, sustituimos el idioma actual en la ruta de acceso (pathname) con el idioma que hemos seleccionado en el switch. Esto nos permite navegar a la misma página con el nuevo idioma elegido.