Internacionalización
Next.js te permite configurar el enrutamiento y la renderización de contenido para soportar múltiples idiomas. Hacer que tu sitio se adapte a diferentes configuraciones regionales incluye contenido traducido (localización) y rutas internacionalizadas.
Terminología
- Configuración regional (Locale): Un identificador para un conjunto de preferencias de idioma y formato. Esto generalmente incluye el idioma preferido del usuario y posiblemente su región geográfica.
en-US
: Inglés como se habla en Estados Unidosnl-NL
: Holandés como se habla en los Países Bajosnl
: Holandés, sin región específica
Visión general del enrutamiento
Se recomienda usar las preferencias de idioma del usuario en el navegador para seleccionar qué configuración regional usar. Cambiar tu idioma preferido modificará el encabezado Accept-Language
que llega a tu aplicación.
Por ejemplo, usando las siguientes bibliotecas, puedes examinar una Request
entrante para determinar qué configuración regional seleccionar, basándote en los Headers
, las configuraciones regionales que planeas soportar y la configuración regional predeterminada.
import { match } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'
let headers = { 'accept-language': 'en-US,en;q=0.5' }
let languages = new Negotiator({ headers }).languages()
let locales = ['en-US', 'nl-NL', 'nl']
let defaultLocale = 'en-US'
match(languages, locales, defaultLocale) // -> 'en-US'
El enrutamiento puede ser internacionalizado ya sea por sub-ruta (/fr/products
) o dominio (my-site.fr/products
). Con esta información, ahora puedes redirigir al usuario basándote en la configuración regional dentro del Middleware.
let locales = ['en-US', 'nl-NL', 'nl']
// Obtiene la configuración regional preferida, similar al ejemplo anterior o usando una biblioteca
function getLocale(request) { ... }
export function middleware(request) {
// Verifica si hay alguna configuración regional soportada en el pathname
const { pathname } = request.nextUrl
const pathnameHasLocale = locales.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
)
if (pathnameHasLocale) return
// Redirige si no hay configuración regional
const locale = getLocale(request)
request.nextUrl.pathname = `/${locale}${pathname}`
// ej. la solicitud entrante es /products
// La nueva URL ahora es /en-US/products
return Response.redirect(request.nextUrl)
}
export const config = {
matcher: [
// Omite todas las rutas internas (_next)
'/((?!_next).*)',
// Opcional: solo ejecutar en la URL raíz (/)
// '/'
],
}
Finalmente, asegúrate de que todos los archivos especiales dentro de app/
estén anidados bajo app/[lang]
. Esto permite que el enrutador de Next.js maneje dinámicamente diferentes configuraciones regionales en la ruta, y reenvíe el parámetro lang
a cada layout y página. Por ejemplo:
// Ahora tienes acceso a la configuración regional actual
// ej. /en-US/products -> `lang` es "en-US"
export default async function Page({ params: { lang } }) {
return ...
}
El layout raíz también puede estar anidado en la nueva carpeta (ej. app/[lang]/layout.js
).
Localización
Cambiar el contenido mostrado basado en la configuración regional preferida del usuario, o localización, no es algo específico de Next.js. Los patrones descritos a continuación funcionarían igual con cualquier aplicación web.
Supongamos que queremos soportar contenido tanto en inglés como en holandés dentro de nuestra aplicación. Podríamos mantener dos "diccionarios" diferentes, que son objetos que nos dan un mapeo de alguna clave a una cadena localizada. Por ejemplo:
{
"products": {
"cart": "Add to Cart"
}
}
{
"products": {
"cart": "Toevoegen aan Winkelwagen"
}
}
Luego podemos crear una función getDictionary
para cargar las traducciones para la configuración regional solicitada:
import 'server-only'
const dictionaries = {
en: () => import('./dictionaries/en.json').then((module) => module.default),
nl: () => import('./dictionaries/nl.json').then((module) => module.default),
}
export const getDictionary = async (locale) => dictionaries[locale]()
Dado el idioma actualmente seleccionado, podemos obtener el diccionario dentro de un layout o página.
import { getDictionary } from './dictionaries'
export default async function Page({ params: { lang } }) {
const dict = await getDictionary(lang) // en
return <button>{dict.products.cart}</button> // Add to Cart
}
Debido a que todos los layouts y páginas en el directorio app/
son por defecto Componentes del Servidor, no necesitamos preocuparnos por el tamaño de los archivos de traducción afectando el tamaño del paquete JavaScript del lado del cliente. Este código solo se ejecutará en el servidor, y solo el HTML resultante se enviará al navegador.
Generación estática
Para generar rutas estáticas para un conjunto dado de configuraciones regionales, podemos usar generateStaticParams
con cualquier página o layout. Esto puede ser global, por ejemplo, en el layout raíz:
export async function generateStaticParams() {
return [{ lang: 'en-US' }, { lang: 'de' }]
}
export default function Root({ children, params }) {
return (
<html lang={params.lang}>
<body>{children}</body>
</html>
)
}