i18n con next-intl: raíz multilenguaje (es por defecto, /en) + textos externalizados

- next-intl 4 (compatible Next 16): routing (es default en RAÍZ sin prefijo, en con
  /en, localePrefix 'as-needed'), middleware, request config, navigation helpers.
- Estructura app/[locale]/ (layout con <html lang>, NextIntlClientProvider, metadata
  SEO por idioma vía generateMetadata; page home con getTranslations).
- Catálogos messages/es.json y en.json: TODOS los textos de la UI (nada hardcodeado
  en los .tsx, se usan con t()).

Verificado: / -> español (lang=es, título ES), /en -> inglés (lang=en, título EN),
datos SSR desde MySQL. Añadir idioma = locale en routing + messages/<loc>.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 22:31:27 +00:00
parent c36b543184
commit 032bd0020b
12 changed files with 816 additions and 52 deletions
+5
View File
@@ -0,0 +1,5 @@
import { createNavigation } from 'next-intl/navigation'
import { routing } from './routing'
// Wrappers de navegación conscientes del idioma (Link, useRouter, redirect...).
export const { Link, redirect, usePathname, useRouter, getPathname } = createNavigation(routing)
+12
View File
@@ -0,0 +1,12 @@
import { getRequestConfig } from 'next-intl/server'
import { hasLocale } from 'next-intl'
import { routing } from './routing'
export default getRequestConfig(async ({ requestLocale }) => {
const requested = await requestLocale
const locale = hasLocale(routing.locales, requested) ? requested : routing.defaultLocale
return {
locale,
messages: (await import(`../messages/${locale}.json`)).default,
}
})
+9
View File
@@ -0,0 +1,9 @@
import { defineRouting } from 'next-intl/routing'
// Idiomas soportados. El por defecto (es) se sirve en RAÍZ (sin prefijo);
// los demás con prefijo (/en). Añadir un idioma = añadirlo aquí + messages/<loc>.json.
export const routing = defineRouting({
locales: ['es', 'en'],
defaultLocale: 'es',
localePrefix: 'as-needed',
})