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, }