Files
Inna 3a0b9dd5ee Foro: nombres de categorías y foros según el locale (ES/EN)
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>
2026-07-16 12:42:51 +00:00

120 lines
4.2 KiB
TypeScript

import { notFound } from 'next/navigation'
import { getTranslations, setRequestLocale } from 'next-intl/server'
import { Link } from '@/i18n/navigation'
import { getForum, getForumPath, countTopics, getTopics, resolvePosters } from '@/lib/forum'
import { formatForumDate } from '@/lib/forum-format'
import { getSession } from '@/lib/session'
import { forumIsModerator } from '@/lib/forum-perm'
import { PageShell } from '@/components/PageShell'
import { ForumBreadcrumb } from '@/components/ForumBreadcrumb'
import { Pagination } from '@/components/Pagination'
export const dynamic = 'force-dynamic'
const PER_PAGE = 10
export default async function SubforumPage({
params,
searchParams,
}: {
params: Promise<{ locale: string; forumId: string }>
searchParams: Promise<{ page?: string }>
}) {
const { locale, forumId } = await params
setRequestLocale(locale)
const t = await getTranslations('Forum')
const id = Number(forumId)
const forum = await getForum(id, locale)
if (!forum) notFound()
const session = await getSession()
const isMod = await forumIsModerator(session)
const canCreate = Boolean(session.username)
const total = await countTopics(id)
const totalPages = Math.max(1, Math.ceil(total / PER_PAGE))
const page = Math.min(Math.max(1, Number((await searchParams).page) || 1), totalPages)
const topics = await getTopics(id, (page - 1) * PER_PAGE, PER_PAGE, isMod)
const posters = await resolvePosters(
topics.flatMap((tp) => [tp.poster, tp.last_poster].filter(Boolean) as number[]),
)
const name = (pid: number | null) => (pid != null ? posters.get(pid)?.name ?? `#${pid}` : '')
const path = await getForumPath(id, locale)
const hrefFor = (p: number) => `/forum/${id}?page=${p}`
return (
<PageShell title={forum.name}>
<div className="main-wide">
{path && <ForumBreadcrumb items={[{ label: t('title'), href: '/forum' }, { label: path.forum }]} />}
<div className="forum-padding">
<div className="forum_header">
<div className="forum_title">
<h1>{forum.name}</h1>
{forum.description && <h3>{forum.description}</h3>}
</div>
<h4>
<b>{total}</b> {t('topics')}
</h4>
</div>
<div className="actions_c">
{canCreate ? (
<Link href={`/forum/create/${id}`} className="nice_button">
{t('createTopic')}
</Link>
) : (
<span />
)}
<Pagination page={page} totalPages={totalPages} hrefFor={hrefFor} />
</div>
{topics.length === 0 ? (
<h2 className="forum-empty">{t('noTopics')}</h2>
) : (
topics.map((tp) => (
<ul className="topic_row" key={tp.id}>
<li className="icon">
<img
src={`/forum/forum_icons/topic_${tp.locked ? 'read_locked' : tp.deleted ? 'read_mine' : 'unread'}.png`}
height={39}
width={55}
alt=""
/>
</li>
<li className="topic_title_by_date">
<h1>
<Link href={`/forum/topic/${tp.id}`}>
{tp.deleted && `[${t('deleted')}] `}
{tp.sticky && `[${t('sticky')}] `}
{tp.locked && `[${t('locked')}] `}
{tp.name}
</Link>
</h1>
<p>
{t('createdBy')} <span className="username">{name(tp.poster)}</span>,{' '}
{formatForumDate(tp.created, locale)}
</p>
</li>
<li className="lastpost">
<h4>
{t('by')} {name(tp.last_poster)}
</h4>
<h5>{formatForumDate(tp.time_updated, locale)}</h5>
</li>
</ul>
))
)}
<div className="actions_c" style={{ marginTop: 20 }}>
<span />
<Pagination page={page} totalPages={totalPages} hrefFor={hrefFor} />
</div>
</div>
</div>
</PageShell>
)
}