3a0b9dd5ee
Los nombres de categorías, foros, descripciones, clases e idiomas venían del seed solo en inglés. Ahora se muestran en el idioma de la página. Como es un conjunto fijo y conocido, se resuelve con un mapa de traducción en código (lib/forum-i18n.ts, inglés→español) que se aplica al vuelo, sin tocar el esquema (el inglés sigue siendo el valor guardado). Un texto no mapeado (p. ej. un foro nuevo creado desde el admin) se muestra tal cual. getForumIndex/getForum/getForumPath/getTopicPath aceptan el locale y localizan name/description/category (nunca el título de un tema, que es contenido de usuario). Las páginas pasan su locale. Verificado en producción: /es/forum en español (Noticias, Caballero de la Muerte, Foros por idioma) y /en/forum en inglés. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
132 lines
4.6 KiB
TypeScript
132 lines
4.6 KiB
TypeScript
import { getTranslations, setRequestLocale } from 'next-intl/server'
|
|
import { Link } from '@/i18n/navigation'
|
|
import { getForumIndex, resolvePosters, type ForumRow } from '@/lib/forum'
|
|
import { formatForumDate } from '@/lib/forum-format'
|
|
import { PageShell } from '@/components/PageShell'
|
|
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
export default async function ForumIndexPage({ params }: { params: Promise<{ locale: string }> }) {
|
|
const { locale } = await params
|
|
setRequestLocale(locale)
|
|
const t = await getTranslations('Forum')
|
|
const categories = await getForumIndex(locale)
|
|
|
|
// Nombres de los últimos posteadores de todos los foros, en una sola consulta.
|
|
const posterIds = categories.flatMap((c) => c.forums.map((f) => f.last_poster).filter(Boolean) as number[])
|
|
const posters = await resolvePosters(posterIds)
|
|
|
|
function topicsLabel(n: number) {
|
|
return `${n} ${n === 1 ? t('topic') : t('topics')}`
|
|
}
|
|
|
|
function NormalRow({ f }: { f: ForumRow }) {
|
|
const iconSrc = f.type === 3 ? f.icon || '' : `/forum/forum_icons/${f.icon || 'forum_read'}.png`
|
|
return (
|
|
<div className="forum-row">
|
|
<div className="icon">
|
|
<Link href={`/forum/${f.id}`}>
|
|
<img src={iconSrc} height={53} width={56} alt={f.name} />
|
|
</Link>
|
|
</div>
|
|
<div className="forum_title_desc">
|
|
<Link href={`/forum/${f.id}`}>
|
|
<h1 style={f.colortitle && f.colortitle !== '0' ? { color: f.colortitle } : undefined}>{f.name}</h1>
|
|
{f.description && <h2>{f.description}</h2>}
|
|
</Link>
|
|
</div>
|
|
<div className="post" title={t('posts')}>
|
|
<p>{f.post_count}</p>
|
|
</div>
|
|
<div className="topics" title={t('topics')}>
|
|
<p>{f.topic_count}</p>
|
|
</div>
|
|
{f.last_topic_id && (
|
|
<div className="lastpost">
|
|
<p className="topic_title">
|
|
<Link href={`/forum/topic/${f.last_topic_id}`}>{f.last_topic_name}</Link>
|
|
</p>
|
|
{f.last_poster != null && (
|
|
<p className="by">{posters.get(f.last_poster)?.name ?? `#${f.last_poster}`}</p>
|
|
)}
|
|
<p className="postdate">{formatForumDate(f.last_time, locale)}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function ClassTile({ f }: { f: ForumRow }) {
|
|
return (
|
|
<div className={`class-row ${f.icon ?? ''}`}>
|
|
<div className="icon">
|
|
<Link href={`/forum/${f.id}`}>
|
|
<div className="image_icon" />
|
|
</Link>
|
|
</div>
|
|
<div className="info">
|
|
<Link href={`/forum/${f.id}`}>
|
|
<h1 style={f.colortitle && f.colortitle !== '0' ? { color: f.colortitle } : undefined}>{f.name}</h1>
|
|
<h2>{topicsLabel(f.topic_count)}</h2>
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function FlagTile({ f }: { f: ForumRow }) {
|
|
return (
|
|
<div className="class-row">
|
|
<div className="icon">
|
|
<Link href={`/forum/${f.id}`}>
|
|
<img className="image_icon" src={f.icon || ''} height={36} width={36} alt={f.name} />
|
|
</Link>
|
|
</div>
|
|
<div className="info">
|
|
<Link href={`/forum/${f.id}`}>
|
|
<h1>{f.name}</h1>
|
|
<h2>{topicsLabel(f.topic_count)}</h2>
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<PageShell title={t('title')}>
|
|
<div className="forum-container">
|
|
{categories.length === 0 ? (
|
|
<p className="forum-empty">{t('noForums')}</p>
|
|
) : (
|
|
categories.map((cat) => {
|
|
const tiles = cat.forums.filter((f) => f.type === 1 || f.type === 2)
|
|
const rows = cat.forums.filter((f) => f.type !== 1 && f.type !== 2)
|
|
return (
|
|
<div key={cat.id} className="forum-category" style={{ marginBottom: 40 }}>
|
|
<div className="category-title">{cat.name}</div>
|
|
{tiles.length > 0 && (
|
|
<div
|
|
style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))',
|
|
gap: 12,
|
|
marginBottom: rows.length ? 12 : 0,
|
|
}}
|
|
>
|
|
{tiles.map((f) => (f.type === 1 ? <ClassTile key={f.id} f={f} /> : <FlagTile key={f.id} f={f} />))}
|
|
</div>
|
|
)}
|
|
<div style={{ display: 'grid', gap: 8 }}>
|
|
{rows.map((f) => (
|
|
<NormalRow key={f.id} f={f} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
})
|
|
)}
|
|
</div>
|
|
</PageShell>
|
|
)
|
|
}
|