Files
NightSpire/web-next/app/[locale]/page.tsx
T
Inna 032bd0020b 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>
2026-07-12 22:31:27 +00:00

73 lines
2.8 KiB
TypeScript

import { getTranslations, setRequestLocale } from 'next-intl/server'
import { getNoticias, getServerStatus, type Noticia, type ServerStatus } from '@/lib/home'
// SSR por petición: los datos se leen DIRECTAMENTE de MySQL desde el servidor Next.
export const dynamic = 'force-dynamic'
async function getData(): Promise<{ noticias: Noticia[]; status: ServerStatus | null }> {
const [news, status] = await Promise.allSettled([getNoticias(), getServerStatus()])
return {
noticias: news.status === 'fulfilled' ? news.value : [],
status: status.status === 'fulfilled' ? status.value : null,
}
}
export default async function HomePage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params
setRequestLocale(locale)
const t = await getTranslations('Home')
const { noticias, status } = await getData()
return (
<main className="mx-auto max-w-3xl px-4 py-8">
<h1 className="text-3xl font-bold text-amber-500">{t('brand')}</h1>
<section className="my-6 rounded-lg border border-amber-900/60 bg-[#241812] p-4">
<h2 className="mb-3 text-lg font-semibold">{t('serverStatus')}</h2>
{status ? (
<ul className="space-y-1 text-sm">
<li>
{t('server')}: <span className="text-amber-400">{status.server_name ?? '—'}</span> ({status.expansion})
</li>
<li>
{t('onlineCharacters')}: {status.online_characters}
</li>
<li>
{t('login')}:{' '}
<span className={status.status === 'Online' ? 'text-green-400' : 'text-red-400'}>{status.status}</span>
</li>
<li>
{t('address')}: {status.address ?? '—'}
</li>
</ul>
) : (
<p>{t('notAvailable')}</p>
)}
</section>
<section>
<h2 className="mb-3 text-lg font-semibold">{t('news')}</h2>
{noticias.length === 0 ? (
<p className="text-amber-200/70">{t('noNews')}</p>
) : (
noticias.map((n) => (
<article key={n.id} className="mb-4 border-b border-amber-900/60 pb-4">
<h3 className="text-xl font-semibold">{n.titulo}</h3>
{n.fecha && <small className="text-amber-200/60">{new Date(n.fecha).toLocaleString(locale)}</small>}
<div className="prose prose-invert mt-2 max-w-none" dangerouslySetInnerHTML={{ __html: n.contenido }} />
{n.enlace && (
<p className="mt-2">
{t('link')}:{' '}
<a className="text-sky-400 underline" href={n.enlace} target="_blank" rel="noopener noreferrer">
{t('here')}
</a>
</p>
)}
</article>
))
)}
</section>
</main>
)
}