Files
NightSpire/web-next/components/ClientDownload.tsx
T
Inna 651d8deafc i18n: traducir toda la app (ES + EN) — páginas y componentes con texto hardcodeado
Se externaliza el texto español hardcodeado de ~55 archivos a next-intl y se añaden
traducciones al inglés, con paridad de claves es/en. Nuevos namespaces: History,
CharService, CharServiceB, Points, Legal, UI, Misc (+ altas en Common/Admin).

- Historiales (PD/PV, transacciones, sanciones, seguridad), servicios de personaje
  (transfer, send-gift, quest, restore-*, change-*, customize, level-up, gold, rename),
  PD/pagos (d-points, trade, promo, transfer-dp, rename-guild, DPointsTabs), páginas
  legales (cookies, privacidad, términos, reembolsos, aviso legal, contacto), layout
  (cabecera, footer, cookies, 2FA, descargas, jugadores, recluta) y páginas varias
  (home, reino, ayuda, addons, 2falogin).
- Textos con markup inline via t.rich; interpolación con ICU.
- Componente <NoteLegend/> para la leyenda NOTA/NOTE compartida.
- payLabel/confirmText de los servicios de pago traducidos.
- Verificado: tsc OK, next build OK, todas las claves t() resuelven en es y en,
  todos los t.rich casan etiquetas, páginas 200 en /es/ y /en/ sin claves crudas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 18:58:54 +00:00

164 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useRef, useState } from 'react'
import { useTranslations } from 'next-intl'
// Lista de trackers común a todos los magnet (idéntica en el original).
const TRACKERS =
'&tr=http%3A%2F%2F184.105.151.164%3A6969%2Fannounce&tr=http%3A%2F%2F184.105.151.164%3A6969%2Fannounce&tr=http%3A%2F%2F182.150.53.61%3A8080%2Fannounce&tr=http%3A%2F%2F62.210.202.61%3A80%2Fannounce&tr=http%3A%2F%2F78.30.254.12%3A2710%2Fannounce&tr=http%3A%2F%2F91.207.136.85%3A80%2Fannounce&tr=http%3A%2F%2F176.113.71.19%3A6961%2Fannounce&tr=http%3A%2F%2F34.92.10.197%3A6789%2Fannounce&tr=http%3A%2F%2F80.209.252.132%3A1337%2Fannounce&tr=udp%3A%2F%2F62.138.0.158%3A6969%2Fannounce&tr=udp%3A%2F%2F188.241.58.209%3A6969%2Fannounce&tr=udp%3A%2F%2F93.158.213.92%3A1337%2Fannounce&tr=udp%3A%2F%2F62.210.97.59%3A1337%2Fannounce&tr=udp%3A%2F%2F151.80.120.112%3A2710%2Fannounce&tr=udp%3A%2F%2F80.209.252.132%3A1337%2Fannounce'
// Hash btih por idioma (Windows y Mac OS Catalina comparten el mismo torrent).
const BTIH: Record<string, string> = {
wotlkeses: 'AIV765TUMAIZSGLSVGN7MRYASNJW6UPD',
wotlkesmx: 'EL64Q3QUXM42PBGJC4TUHNAH2UUTAJXB',
wotlkenus: 'XAJ4DBISLXFEV2N74IIUZLA5XXVGVRG7',
wotlkengb: '7U3ATWR7YN2IR63L53EFPPHZ4SI4RZKX',
}
function magnetFor(lang: string): string {
return `magnet:?xt=urn:btih:${BTIH[lang]}${TRACKERS}`
}
const LANG_INFO_KEY: Record<string, string> = {
wotlkeses: 'clientDownload.langEses',
wotlkesmx: 'clientDownload.langEsmx',
wotlkenus: 'clientDownload.langEnus',
wotlkengb: 'clientDownload.langEngb',
}
export function ClientDownload() {
const t = useTranslations('UI')
const [lang, setLang] = useState('')
const [os, setOs] = useState('')
const [error, setError] = useState(false)
const timer = useRef<ReturnType<typeof setTimeout> | null>(null)
function handleSubmit(e: React.FormEvent) {
e.preventDefault()
if (!lang || !os) {
setError(true)
if (timer.current) clearTimeout(timer.current)
timer.current = setTimeout(() => setError(false), 5000)
return
}
window.location.href = magnetFor(lang)
}
return (
<>
<div className="box-content">
<div className="title-box-content" id="torrent">
<h2>{t('clientDownload.torrentTitle')}</h2>
</div>
<div className="body-box-content justified">
<p><span>{t('clientDownload.infoLabel')}</span></p>
<p>{t('clientDownload.info1')}</p>
<p>{t('clientDownload.info2')}</p>
<p>{t('clientDownload.info3')}</p>
<br />
<p><span>{t('clientDownload.requirementsLabel')}</span></p>
<p>{t('clientDownload.requirementsText')}</p>
<ul>
<li><a href="https://www.qbittorrent.org/download.php" target="_blank" rel="noopener noreferrer">qBittorrent</a></li>
<li><a href="https://www.winrar.es/descargas" target="_blank" rel="noopener noreferrer">WinRAR</a></li>
</ul>
<p>{t('clientDownload.popupNote')}</p>
<br />
<p><span>{t('clientDownload.stepByStep')}</span></p>
<p>{t.rich('clientDownload.stepOptional', { winrar: (c) => <a href="https://www.winrar.es/descargas" target="_blank" rel="noopener noreferrer">{c}</a> })}</p>
<p>{t.rich('clientDownload.step1', { qbit: (c) => <a href="https://www.qbittorrent.org/download.php" target="_blank" rel="noopener noreferrer">{c}</a> })}</p>
<p>{t('clientDownload.step2')}</p>
<p>{t('clientDownload.step3')}</p>
<p>{t('clientDownload.step4')}</p>
<p>{t('clientDownload.step5')}</p>
<p>{t('clientDownload.step6')}</p>
<p>{t('clientDownload.step7')}</p>
<p>{t('clientDownload.step8')}</p>
<p>{t('clientDownload.step9')}</p>
<p>{t('clientDownload.step10')}</p>
<p>{t('clientDownload.step11')}</p>
<p>{t('clientDownload.step12')}</p>
<p>{t('clientDownload.step13')}</p>
<p>{t('clientDownload.step14')}</p>
<p>{t('clientDownload.step15')}</p>
<br />
<hr />
<br />
<div className="centered">
<form name="uw-client-dowload" id="uw-client-download" autoComplete="off" onSubmit={handleSubmit}>
<p><span>{t('clientDownload.languageLabel')}</span></p>
<br />
<select
name="game-language"
id="game-language"
className="game-download-select"
value={lang}
onChange={(e) => { setLang(e.target.value); setOs(''); }}
>
<option value="" disabled>{t('clientDownload.chooseLanguage')}</option>
<option value="wotlkeses">{t('clientDownload.clientEses')}</option>
<option value="wotlkesmx">{t('clientDownload.clientEsmx')}</option>
<option value="wotlkenus">{t('clientDownload.clientEnus')}</option>
<option value="wotlkengb">{t('clientDownload.clientEngb')}</option>
</select>
{lang && (
<div id="select-op">
<br />
<p id="game-info">{lang ? t(LANG_INFO_KEY[lang]) :' '}</p>
<br />
<p><span>{t('clientDownload.osLabel')}</span></p>
<br />
<select
name="operating-system"
id="operating-system"
className="game-download-select"
value={os}
onChange={(e) => setOs(e.target.value)}
>
<option value="" disabled>{t('clientDownload.chooseOs')}</option>
<option value="windows">{t('clientDownload.windows')}</option>
<option value="macosc">{t('clientDownload.macCatalina')}</option>
</select>
<br />
<button type="submit" className="g-download-button" id="download-c-button">{t('clientDownload.downloadBtn')}</button>
</div>
)}
</form>
<br />
<hr />
<div id="error-message" className="red-form-response">
{error ? t('clientDownload.errorChoose') : ''}
</div>
<br />
</div>
</div>
{os === 'macosc' && (
<div id="op-info" className="box-content">
<div className="title-box-content">
<h2>{t('clientDownload.macGuideTitle')}</h2>
</div>
<div className="body-box-content">
<p>{t('clientDownload.mac1')}</p>
<p>{t('clientDownload.mac2')}</p>
<br />
<span>{t('clientDownload.macInstructions')}</span>
<p>{t.rich('clientDownload.macStep1', { crossover: (c) => <a href="https://insmac.org/macosx/3728-crossover-19.html" target="_blank" rel="noopener noreferrer">{c}</a> })}</p>
<p>{t('clientDownload.macStep2')}</p>
<p>{t('clientDownload.macStep3')}</p>
<p>{t('clientDownload.macStep4')}</p>
<p>{t('clientDownload.macStep5')}</p>
<p>{t('clientDownload.macStep6')}</p>
<p>{t('clientDownload.macStep7')}</p>
<p>{t('clientDownload.macStep8')}</p>
<p>{t('clientDownload.macStep9')}</p>
<p>{t('clientDownload.macStep10')}</p>
<p>{t('clientDownload.macStep11')}</p>
</div>
</div>
)}
</div>
</>
)
}