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>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
|
||||
const LOGOS = '/nw-themes/nw-ryu/nw-images/nw-logos'
|
||||
|
||||
@@ -18,13 +19,14 @@ async function post(action: string, extra: Record<string, string>): Promise<{ su
|
||||
|
||||
/** Ojo para mostrar/ocultar un campo de contraseña/token. */
|
||||
function EyeToggle({ shown, onToggle }: { shown: boolean; onToggle: () => void }) {
|
||||
const t = useTranslations('UI')
|
||||
return (
|
||||
<span
|
||||
className={`far ${shown ? 'fa-eye-slash' : 'fa-eye'} toggle-password`}
|
||||
onClick={onToggle}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={shown ? 'Ocultar' : 'Mostrar'}
|
||||
aria-label={shown ? t('twofa.eyeHide') : t('twofa.eyeShow')}
|
||||
></span>
|
||||
)
|
||||
}
|
||||
@@ -41,6 +43,7 @@ export function TwoFactorLogin({ enabled }: { enabled: boolean }) {
|
||||
|
||||
/* ------------------------------ Activar + QR ------------------------------- */
|
||||
function ActivateFlow() {
|
||||
const t = useTranslations('UI')
|
||||
const [password, setPassword] = useState('')
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
const [token, setToken] = useState('')
|
||||
@@ -66,7 +69,7 @@ function ActivateFlow() {
|
||||
setMsg({ success: !!d.success, text: d.message })
|
||||
if (d.success && d.qrCodeUrl && d.secretKey) setQr({ qrCodeUrl: d.qrCodeUrl, secretKey: d.secretKey })
|
||||
} catch {
|
||||
setMsg({ success: false, text: 'Algo ha salido mal. Por favor intente más tarde.' })
|
||||
setMsg({ success: false, text: t('twofa.errGeneric') })
|
||||
} finally {
|
||||
setBusy(false)
|
||||
}
|
||||
@@ -82,7 +85,7 @@ function ActivateFlow() {
|
||||
setVMsg({ success: !!d.success, text: d.message })
|
||||
if (d.success) setDone(true)
|
||||
} catch {
|
||||
setVMsg({ success: false, text: 'Algo ha salido mal. Por favor intente más tarde.' })
|
||||
setVMsg({ success: false, text: t('twofa.errGeneric') })
|
||||
} finally {
|
||||
setVBusy(false)
|
||||
}
|
||||
@@ -108,7 +111,7 @@ function ActivateFlow() {
|
||||
maxLength={16}
|
||||
name="password"
|
||||
id="password"
|
||||
placeholder="Contraseña"
|
||||
placeholder={t('twofa.passwordPlaceholder')}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={!!qr}
|
||||
@@ -123,7 +126,7 @@ function ActivateFlow() {
|
||||
maxLength={6}
|
||||
name="security-token"
|
||||
id="security-token"
|
||||
placeholder="Token de seguridad"
|
||||
placeholder={t('twofa.tokenPlaceholder')}
|
||||
value={token}
|
||||
onChange={(e) => setToken(e.target.value)}
|
||||
disabled={!!qr}
|
||||
@@ -135,7 +138,7 @@ function ActivateFlow() {
|
||||
<tr>
|
||||
<td>
|
||||
<button type="submit" className="activate-2fa-button" disabled={busy}>
|
||||
{busy ? 'Activando 2FA' : 'Activar 2FA'}
|
||||
{busy ? t('twofa.activating') : t('twofa.activate')}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -152,7 +155,7 @@ function ActivateFlow() {
|
||||
|
||||
{qr && (
|
||||
<div id="qr-code-container">
|
||||
<p>Paso 1) Descarga la aplicación de autenticación:</p>
|
||||
<p>{t('twofa.step1')}</p>
|
||||
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" target="_blank" rel="noopener noreferrer" title="Google Authenticator (Android)">
|
||||
<img src={`${LOGOS}/google-play.png`} alt="Google Play" className="google-play-logo" />
|
||||
</a>
|
||||
@@ -161,16 +164,16 @@ function ActivateFlow() {
|
||||
</a>
|
||||
<br />
|
||||
<br />
|
||||
<p>Paso 2) Escanea este código QR con tu aplicación de autenticación:</p>
|
||||
<p>{t('twofa.step2')}</p>
|
||||
<img id="qr-code" src={qr.qrCodeUrl} alt="QR Code" />
|
||||
<br />
|
||||
<br />
|
||||
<p>Si no puedes escanear el código QR copia y pega esta clave en tu aplicación de autenticación:</p>
|
||||
<p>{t('twofa.copyKeyNote')}</p>
|
||||
<div className="copy-secret-container">
|
||||
<input type="text" id="secret-key" className="centered" value={qr.secretKey} readOnly />
|
||||
<span
|
||||
className={`fa ${copied ? 'fa-check' : 'fa-copy'} copy-btn second-brown`}
|
||||
title={copied ? '¡Copiado!' : 'Copiar clave'}
|
||||
title={copied ? t('twofa.copied') : t('twofa.copyKey')}
|
||||
onClick={copySecret}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
@@ -178,7 +181,7 @@ function ActivateFlow() {
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<p>Paso 3) Ingresa el código generado por tu aplicación de autenticación:</p>
|
||||
<p>{t('twofa.step3')}</p>
|
||||
<form onSubmit={verify} id="uw-2fa-verify-form" autoComplete="off">
|
||||
<table className="middle-center-table">
|
||||
<tbody>
|
||||
@@ -189,7 +192,7 @@ function ActivateFlow() {
|
||||
maxLength={6}
|
||||
name="authenticator-code"
|
||||
id="authenticator-code"
|
||||
placeholder="Código de 6 dígitos"
|
||||
placeholder={t('twofa.codePlaceholder')}
|
||||
required
|
||||
className="centered"
|
||||
inputMode="numeric"
|
||||
@@ -203,7 +206,7 @@ function ActivateFlow() {
|
||||
<tr>
|
||||
<td>
|
||||
<button type="submit" className="verify-2fa-button" disabled={vBusy || done}>
|
||||
{done ? 'Verificado ✔️' : vBusy ? 'Verificando...' : 'Verificar código'}
|
||||
{done ? t('twofa.verified') : vBusy ? t('twofa.verifying') : t('twofa.verifyCode')}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -225,6 +228,7 @@ function ActivateFlow() {
|
||||
|
||||
/* ------------------------------- Desactivar -------------------------------- */
|
||||
function DisableForm() {
|
||||
const t = useTranslations('UI')
|
||||
const [code, setCode] = useState('')
|
||||
const [busy, setBusy] = useState(false)
|
||||
const [msg, setMsg] = useState<Msg>(null)
|
||||
@@ -240,7 +244,7 @@ function DisableForm() {
|
||||
setMsg({ success: !!d.success, text: d.message })
|
||||
if (d.success) setDone(true)
|
||||
} catch {
|
||||
setMsg({ success: false, text: 'Algo ha salido mal. Por favor intente más tarde.' })
|
||||
setMsg({ success: false, text: t('twofa.errGeneric') })
|
||||
} finally {
|
||||
setBusy(false)
|
||||
}
|
||||
@@ -248,8 +252,8 @@ function DisableForm() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className="green-info"><i className="fas fa-shield-alt"></i> La verificación en 2 pasos está activada en esta cuenta.</p>
|
||||
<p>Para desactivarla, introduce un código actual de tu aplicación de autenticación.</p>
|
||||
<p className="green-info"><i className="fas fa-shield-alt"></i> {t('twofa.enabledNotice')}</p>
|
||||
<p>{t('twofa.disableInstruction')}</p>
|
||||
<br />
|
||||
<form onSubmit={disable} id="uw-2fa-disable-form">
|
||||
<table className="middle-center-table">
|
||||
@@ -260,7 +264,7 @@ function DisableForm() {
|
||||
type="text"
|
||||
maxLength={6}
|
||||
name="authenticator-code"
|
||||
placeholder="Código de 6 dígitos"
|
||||
placeholder={t('twofa.codePlaceholder')}
|
||||
required
|
||||
className="centered"
|
||||
inputMode="numeric"
|
||||
@@ -274,7 +278,7 @@ function DisableForm() {
|
||||
<tr>
|
||||
<td>
|
||||
<button type="submit" className="deactivate-2fa-button" disabled={busy || done}>
|
||||
{done ? '2FA desactivado ✔️' : busy ? 'Desactivando...' : 'Desactivar 2FA'}
|
||||
{done ? t('twofa.disabled') : busy ? t('twofa.disabling') : t('twofa.disable')}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user