From 3a0b9dd5eee2dcb6d808280b2d399e8378c4a264 Mon Sep 17 00:00:00 2001 From: adevopg Date: Thu, 16 Jul 2026 12:42:51 +0000 Subject: [PATCH] =?UTF-8?q?Foro:=20nombres=20de=20categor=C3=ADas=20y=20fo?= =?UTF-8?q?ros=20seg=C3=BAn=20el=20locale=20(ES/EN)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../app/[locale]/forum/[forumId]/page.tsx | 4 +- .../[locale]/forum/create/[forumId]/page.tsx | 4 +- .../app/[locale]/forum/edit/[postId]/page.tsx | 2 +- web-next/app/[locale]/forum/page.tsx | 2 +- .../[locale]/forum/topic/[topicId]/page.tsx | 2 +- web-next/lib/forum-i18n.ts | 88 +++++++++++++++++++ web-next/lib/forum.ts | 36 +++++--- 7 files changed, 117 insertions(+), 21 deletions(-) create mode 100644 web-next/lib/forum-i18n.ts diff --git a/web-next/app/[locale]/forum/[forumId]/page.tsx b/web-next/app/[locale]/forum/[forumId]/page.tsx index 1727a41..4f852ab 100644 --- a/web-next/app/[locale]/forum/[forumId]/page.tsx +++ b/web-next/app/[locale]/forum/[forumId]/page.tsx @@ -25,7 +25,7 @@ export default async function SubforumPage({ const t = await getTranslations('Forum') const id = Number(forumId) - const forum = await getForum(id) + const forum = await getForum(id, locale) if (!forum) notFound() const session = await getSession() @@ -42,7 +42,7 @@ export default async function SubforumPage({ ) const name = (pid: number | null) => (pid != null ? posters.get(pid)?.name ?? `#${pid}` : '') - const path = await getForumPath(id) + const path = await getForumPath(id, locale) const hrefFor = (p: number) => `/forum/${id}?page=${p}` return ( diff --git a/web-next/app/[locale]/forum/create/[forumId]/page.tsx b/web-next/app/[locale]/forum/create/[forumId]/page.tsx index 1534a5e..4bb483b 100644 --- a/web-next/app/[locale]/forum/create/[forumId]/page.tsx +++ b/web-next/app/[locale]/forum/create/[forumId]/page.tsx @@ -18,13 +18,13 @@ export default async function CreateTopicPage({ const t = await getTranslations('Forum') const id = Number(forumId) - const forum = await getForum(id) + const forum = await getForum(id, locale) if (!forum) notFound() const session = await getSession() if (!session.username) redirect(`/${locale}/forum/${id}`) - const path = await getForumPath(id) + const path = await getForumPath(id, locale) return ( diff --git a/web-next/app/[locale]/forum/edit/[postId]/page.tsx b/web-next/app/[locale]/forum/edit/[postId]/page.tsx index 930fd14..7ef5a15 100644 --- a/web-next/app/[locale]/forum/edit/[postId]/page.tsx +++ b/web-next/app/[locale]/forum/edit/[postId]/page.tsx @@ -30,7 +30,7 @@ export default async function EditPostPage({ if (!canEditPost(session, isMod, post.poster)) redirect(`/${locale}/forum/topic/${post.topic}`) const topic = await getTopic(post.topic, true) - const path = await getTopicPath(post.topic) + const path = await getTopicPath(post.topic, locale) return ( diff --git a/web-next/app/[locale]/forum/page.tsx b/web-next/app/[locale]/forum/page.tsx index 9c07086..281665b 100644 --- a/web-next/app/[locale]/forum/page.tsx +++ b/web-next/app/[locale]/forum/page.tsx @@ -10,7 +10,7 @@ export default async function ForumIndexPage({ params }: { params: Promise<{ loc const { locale } = await params setRequestLocale(locale) const t = await getTranslations('Forum') - const categories = await getForumIndex() + 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[]) diff --git a/web-next/app/[locale]/forum/topic/[topicId]/page.tsx b/web-next/app/[locale]/forum/topic/[topicId]/page.tsx index 6c316be..e7dbcfe 100644 --- a/web-next/app/[locale]/forum/topic/[topicId]/page.tsx +++ b/web-next/app/[locale]/forum/topic/[topicId]/page.tsx @@ -51,7 +51,7 @@ export default async function TopicPage({ await Promise.all(uniquePosters.map(async (pid) => [pid, await getUserPostCount(pid)] as const)), ) - const path = await getTopicPath(id) + const path = await getTopicPath(id, locale) const canReply = Boolean(session.username) && (!topic.locked || isMod) return ( diff --git a/web-next/lib/forum-i18n.ts b/web-next/lib/forum-i18n.ts new file mode 100644 index 0000000..84f7445 --- /dev/null +++ b/web-next/lib/forum-i18n.ts @@ -0,0 +1,88 @@ +// Traducción del contenido del foro que viene del seed (categorías, foros, +// descripciones, clases e idiomas). El valor guardado en la BD es el inglés (que +// respeta el SQL original); aquí se mapea a español según el locale, sin tocar el +// esquema. Si un texto no está en el mapa (p. ej. un foro nuevo creado desde el +// admin), se devuelve tal cual. +const ES: Record = { + // Categorías + News: 'Noticias', + Reports: 'Reportes', + General: 'General', + 'Community Forums': 'Foros de la comunidad', + 'Localized Forums': 'Foros por idioma', + 'Class Discussions': 'Discusiones de clase', + + // Foros + 'Latest News': 'Últimas noticias', + 'Rules and Regulations': 'Normas y reglas', + 'Appeal a Ban': 'Apelar un baneo', + 'Report a Player': 'Reportar a un jugador', + 'Report a Gm/Dev': 'Reportar a un GM/Dev', + 'General Discussion': 'Discusión general', + Suggestions: 'Sugerencias', + 'Guides & Tutorials': 'Guías y tutoriales', + 'Support & Q/A': 'Soporte y preguntas', + Addons: 'Addons', + 'Guild Recruitment': 'Reclutamiento de hermandades', + 'Introduce Yourself': 'Preséntate', + 'Dungeons and Raids': 'Mazmorras y bandas', + 'World PvP, Arenas & Battlegrounds': 'JcJ mundial, arenas y campos de batalla', + 'Specific Discussions': 'Discusiones específicas', + + // Idiomas + English: 'Inglés', + Persian: 'Persa', + Romania: 'Rumano', + Russian: 'Ruso', + Bulgaria: 'Búlgaro', + Spanish: 'Español', + French: 'Francés', + German: 'Alemán', + Italian: 'Italiano', + Polish: 'Polaco', + + // Clases + 'Death Knight': 'Caballero de la Muerte', + 'Demon Hunter': 'Cazador de Demonios', + Druid: 'Druida', + Evoker: 'Evocador', + Hunter: 'Cazador', + Mage: 'Mago', + Monk: 'Monje', + Paladin: 'Paladín', + Priest: 'Sacerdote', + Rogue: 'Pícaro', + Shaman: 'Chamán', + Warlock: 'Brujo', + Warrior: 'Guerrero', + + // Descripciones + 'Latest news for the community.': 'Últimas noticias para la comunidad.', + 'Laws to abide by to keep your game experience intact.': + 'Normas que respetar para mantener intacta tu experiencia de juego.', + 'Appeal a ban if you think you where wrongly banned.': 'Apela un baneo si crees que fue injusto.', + 'Report a player/s for breaking rules in-game.': + 'Reporta a jugadores que incumplan las normas dentro del juego.', + 'Report a Gm/Dev.': 'Reporta a un GM/Dev.', + 'For anything and everything this server related.': 'Para cualquier cosa relacionada con el servidor.', + 'Give us your thoughts on anything to do with the server.': + 'Danos tu opinión sobre cualquier cosa del servidor.', + 'Create guides to help the community.': 'Crea guías para ayudar a la comunidad.', + 'Having trouble with something or just want to help players out?': + '¿Tienes algún problema o quieres ayudar a otros jugadores?', + 'Helpful and interesting Addons.': 'Addons útiles e interesantes.', + 'New player? Tell us a bit about yourself.': '¿Jugador nuevo? Cuéntanos algo sobre ti.', + 'Everything about raiding, organization and shared experience.': + 'Todo sobre bandas, organización y experiencia compartida.', + 'Discuss builds, city raids, arenas and battlegrounds.': + 'Habla de builds, asaltos a ciudades, arenas y campos de batalla.', + 'Discussion about your favorite Faction, Roleplay and ...': + 'Discusión sobre tu facción favorita, rol y ...', +} + +/** Traduce un texto del foro (categoría/foro/descripción) al locale. en = original. */ +export function localizeForum(text: string | null | undefined, locale: string): string { + if (!text) return '' + if (locale === 'en') return text + return ES[text] ?? text +} diff --git a/web-next/lib/forum.ts b/web-next/lib/forum.ts index c752936..54e77fc 100644 --- a/web-next/lib/forum.ts +++ b/web-next/lib/forum.ts @@ -1,5 +1,6 @@ import type { RowDataPacket } from 'mysql2' import { db, DB } from './db' +import { localizeForum } from './forum-i18n' // Foro estilo FusionCMS (ver sql/forum_fusion.sql). Esquema: // forum_categories(id, order, name) @@ -101,7 +102,7 @@ function avatarForClass(classId: number | undefined): string { const STAFF_GMLEVEL = Number(process.env.FORUM_MOD_GMLEVEL || 2) /** Índice del foro: categorías ordenadas, cada una con sus foros y agregados. */ -export async function getForumIndex(): Promise { +export async function getForumIndex(locale = 'es'): Promise { try { const [cats] = await db(DB.web).query( 'SELECT id, name FROM forum_categories ORDER BY `order`, name', @@ -132,8 +133,8 @@ export async function getForumIndex(): Promise { id: f.id, category: f.category, order: f.order, - name: f.name, - description: f.description, + name: localizeForum(f.name, locale), + description: localizeForum(f.description, locale), icon: f.icon, colortitle: f.colortitle, type: f.type, @@ -148,14 +149,14 @@ export async function getForumIndex(): Promise { byCat.get(row.category)!.push(row) } return cats - .map((c) => ({ id: c.id, name: c.name, forums: byCat.get(c.id) ?? [] })) + .map((c) => ({ id: c.id, name: localizeForum(c.name, locale), forums: byCat.get(c.id) ?? [] })) .filter((c) => c.forums.length > 0) } catch { return [] } } -export async function getForum(id: number): Promise { +export async function getForum(id: number, locale = 'es'): Promise { try { const [rows] = await db(DB.web).query( 'SELECT id, name, description, icon, colortitle, type FROM forum_forums WHERE id = ?', @@ -163,7 +164,14 @@ export async function getForum(id: number): Promise { ) const r = rows[0] return r - ? { id: r.id, name: r.name, description: r.description, icon: r.icon, colortitle: r.colortitle, type: r.type } + ? { + id: r.id, + name: localizeForum(r.name, locale), + description: localizeForum(r.description, locale), + icon: r.icon, + colortitle: r.colortitle, + type: r.type, + } : null } catch { return null @@ -180,7 +188,7 @@ export async function forumExists(id: number): Promise { } } -export async function getForumPath(forumId: number): Promise { +export async function getForumPath(forumId: number, locale = 'es'): Promise { try { const [rows] = await db(DB.web).query( `SELECT c.id AS category_id, c.name AS category, f.id AS forum_id, f.name AS forum, f.description AS forum_description @@ -192,10 +200,10 @@ export async function getForumPath(forumId: number): Promise { return r ? { category_id: r.category_id, - category: r.category, + category: localizeForum(r.category, locale), forum_id: r.forum_id, - forum: r.forum, - forum_description: r.forum_description, + forum: localizeForum(r.forum, locale), + forum_description: localizeForum(r.forum_description, locale), } : null } catch { @@ -203,7 +211,7 @@ export async function getForumPath(forumId: number): Promise { } } -export async function getTopicPath(topicId: number): Promise { +export async function getTopicPath(topicId: number, locale = 'es'): Promise { try { const [rows] = await db(DB.web).query( `SELECT c.id AS category_id, c.name AS category, f.id AS forum_id, f.name AS forum, @@ -218,10 +226,10 @@ export async function getTopicPath(topicId: number): Promise { return r ? { category_id: r.category_id, - category: r.category, + category: localizeForum(r.category, locale), forum_id: r.forum_id, - forum: r.forum, - forum_description: r.forum_description, + forum: localizeForum(r.forum, locale), + forum_description: localizeForum(r.forum_description, locale), topic_id: r.topic_id, topic: r.topic, }