Files
eminuxandCursor d52434fb2e Limita una diretta attiva per account e valorizza la copertina Premium Full.
Blocca la seconda diretta sullo stesso utente, registra gli abusi in admin e presenta la copertina come grafica caricata dalla società, con demo Team MLTV.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 12:52:26 +02:00

612 lines
24 KiB
YAML

es:
admin:
layout:
title: "Match Live TV — Admin"
nav:
dashboard: Panel
ops: Ops
announcements: Avisos
clubs: Clubes y equipos
billing: Facturación
youtube: YouTube
sessions: Sesiones
stream_concurrency: Abuso de cuenta
analytics: Analytics
costs: Costes
stream_nodes: Nodos stream
password: Contraseña
logout: Salir
flash:
login_required: Inicia sesión para continuar
login_success: Sesión iniciada correctamente
invalid_credentials: Usuario o contraseña no válidos
logout_success: Sesión cerrada
password_current_incorrect: La contraseña actual no es correcta
password_too_short: La nueva contraseña debe tener al menos 8 caracteres
password_too_long: La nueva contraseña no puede superar los 72 caracteres
password_too_weak: "La nueva contraseña debe incluir al menos 3 entre: minúsculas, mayúsculas, números y símbolos"
password_same_as_current: La nueva contraseña debe ser distinta de la actual
password_mismatch: Las contraseñas no coinciden
password_updated: Contraseña actualizada
ops_acknowledged: Incidencia asumida
ops_resolved: Incidencia resuelta
ops_muted: Notificaciones silenciadas durante 24 horas
stream_node_created: "Nodo lab %{slug} provisionado."
stream_node_destroyed: "Nodo %{slug} eliminado."
stream_node_draining: "Nodo %{slug} en drain."
autoscale_kill_on: "Kill-switch del autoscaler activado. Sin scale-out automático."
autoscale_kill_off: "Kill-switch del autoscaler desactivado (hace falta STREAM_AUTOSCALE_ENABLED=1)."
comped_granted: "Suscripción de cortesía %{plan} activada para %{club}."
comped_revoked: "Suscripción de cortesía revocada para %{club}."
session_already_terminated: "La sesión ya ha finalizado (%{status})."
session_stopped: Sesión finalizada.
session_stop_error: "Error al cerrar la sesión: %{error}"
session_terminated_no_regia: "Sesión finalizada: no se puede generar el enlace de regie."
session_regia_generated: Enlace de regie generado.
invoice_upload_hint: Sube el PDF desde la lista «Pagos por facturar».
invoice_draft_created: Borrador creado. Sube el PDF desde la página de Facturación o aquí abajo.
invoice_issued: "Factura %{number} emitida y enviada a %{email}."
invoice_updated: "Factura %{number} actualizada."
invoice_uploaded: "Factura subida y enviada a %{email}."
youtube_not_configured: Configura YOUTUBE_CLIENT_ID y YOUTUBE_CLIENT_SECRET en .env
announcement_created: Aviso guardado
announcement_updated: Aviso actualizado
announcement_destroyed: Aviso eliminado
cost_entry_created: Partida de coste guardada.
cost_entry_updated: Partida de coste actualizada.
cost_entry_destroyed: Partida de coste eliminada.
common:
free_plan: Free
yes: "Sí"
no: "No"
dash: "—"
live_page: Página en directo
auth:
new:
title: Acceso admin
username_label: Usuario
password_label: Contraseña
submit: Acceder
initial_credentials_html: "Credenciales iniciales: <code>admin</code> / <code>AdminPass123</code>. Cambia la contraseña después del primer acceso."
passwords:
edit:
title: Cambiar contraseña
current_password_label: Contraseña actual
new_password_label: "Nueva contraseña (mín. 8, al menos 3 tipos de caracteres)"
confirm_password_label: Confirma la nueva contraseña
submit: Guardar contraseña
cancel: Cancelar
dashboard:
kpi:
active_sessions: Sesiones activas
active_sessions_sub: en directo ahora
today: Emitidas hoy
today_sub: "%{duration} de emisión"
month: Emitidas este mes
month_sub: "%{duration} en total"
teams: Equipos
teams_sub: "%{count} usuarios"
cpu: CPU
cpu_sub: "carga %{one} / %{five} / %{fifteen}"
ram: RAM
ram_sub: "%{used} / %{total}"
network: Ancho de banda (total)
network_sub: "↑ %{tx} · ↓ %{rx}"
recordings: Repeticiones archivadas
recordings_sub: "%{count} grabaciones"
charts:
cpu: CPU (%)
ram: RAM (%)
ops_panel:
title: Estado de la plataforma (Ops)
critical: "%{count} incidencia(s) crítica(s) abierta(s)"
view_dashboard: Ver panel de Ops
warnings: "%{count} aviso(s)"
dashboard_link: Panel de Ops
none_html: "No hay incidencias abiertas. %{link}"
concurrency_panel:
title: Abuso de cuenta (segundo directo)
count: "%{count} intento(s) en los últimos 30 días"
view_all: Ver todos
none: No hay intentos registrados.
none_html: "Ningún intento de segundo directo con la misma cuenta. %{link}"
disk:
system_title: Disco del sistema
free_label: "Libre: %{free} (%{percent}% usado)"
not_available: Métricas de disco no disponibles
recordings_title: Archivo de grabaciones
recordings_content: "Contenido de las grabaciones: %{size}"
recordings_not_mounted: "Ruta %{path} no montada"
sessions:
title: Sesiones activas
table:
match: Partido
status: Estado
client: Cliente
ingest: Ingest
start: Inicio
link: Enlace
none: No hay ninguna sesión activa en este momento.
detail: Detalle
stop: Finalizar
stop_confirm: "¿Finalizar esta sesión?"
regia: Regie
teams:
title: Equipos
matches_count: "%{count} partidos"
view_all: "Ver clubes (%{count} equipos)"
stream_nodes:
title: Nodos streaming
providers: "Cloud: %{cloud} · DNS: %{dns}"
actions_title: Acciones
table_title: Nodos registrados
empty: No hay nodos registrados.
free_slots:
one: "%{count} libre"
other: "%{count} libres"
kpi:
autoscaler: Autoscaler
kill_switch: KILL
kind: "modo %{kind}"
free_slots: Slots libres
soft_slots: "umbral soft ≤ %{soft}"
spare: Spare
warm_spare: warm spares listos
overflow: Overflow
overflow_nodes: nodos cloud/lab
budget: Presupuesto estimado
budget_of: "de €%{budget}/mes (%{ok})"
autoscale: "Autoscaler %{enabled} · free_slots=%{free} (soft≤%{soft}) · spare=%{spare}/%{warm} · overflow=%{overflow}/%{max} · kind=%{kind}"
autoscale_budget: "presupuesto €%{eur}/€%{budget} (%{ok})"
kill_switch_active: "KILL-SWITCH ACTIVO"
engage_kill_switch: "Kill-switch ON"
clear_kill_switch: "Kill-switch OFF"
kill_switch_confirm: "¿Bloquear el autoscaler inmediatamente?"
provision_lab: Provisionar lab
provision_cloud: Provisionar Hetzner
provision_cloud_confirm: "¿Crear un servidor Hetzner Cloud + DNS en mltv-stream.net?"
hetzner_token_missing: "Configura HCLOUD_TOKEN para habilitar el provisioning Cloud."
drain: Drain
destroy: Eliminar
destroy_confirm: "¿Eliminar el nodo %{slug}?"
hosts_title: DNS lab (/etc/hosts)
col:
slug: Slug
role: Rol
status: Estado
slots: Slots
hostname: Hostname
provider: Provider
ops:
kpi:
critical: Críticas abiertas
warning: Avisos abiertos
total: Total abiertas
open:
title: Incidencias abiertas
table:
severity: Gravedad
kind: Tipo
title: Título
occurrences: N
last_seen: Última vez
none: No hay incidencias abiertas. La plataforma parece estar sana.
actions:
acknowledge: Asumida
resolve: Resolver
mute: Silenciar 24h
resolved:
title: Resueltas (últimos 30 días)
table:
resolved_at: Resuelta
none: No se han resuelto incidencias recientemente.
stream_concurrency:
index:
title: Abuso de cuenta
lead: Intentos de iniciar un segundo directo con la misma cuenta mientras ya había otro en curso (connecting, live, reconnecting o en pausa).
results: "Mostrando %{shown} de %{total}"
none: No hay intentos registrados.
kpi:
lookback: Últimos 30 días
lookback_sub: intentos bloqueados
two_devices: Dos dispositivos
two_devices_sub: modelos de teléfono distintos
filters:
q: Buscar
q_placeholder: Email, club, partido…
two_devices: Filtro
two_devices_hint: Solo intentos desde dispositivos distintos
apply: Filtrar
reset: Restablecer
table:
when: Cuándo
account: Cuenta
club: Club
occupying: Directo ya en curso
attempted: Intento bloqueado
action:
start: inicio
resume: reanudación
badge:
two_devices: 2 teléfonos
same_or_unknown: mismo dispositivo / n. d.
clubs:
index:
title: Clubes y equipos
subtitle_html: "Suscripciones de cortesía (patrocinadores / promociones) y estado de YouTube por equipo. Pagos de Stripe: %{billing_link}."
billing_link: Facturación
table:
club: Club
plan: Plan
teams: Equipos
comped: Cortesía
stripe: Stripe
manage: Gestionar
invoices: Facturas
show:
back: "← Clubes y equipos"
sport_label: "Deporte: %{sport}"
recordings_archive: Archivo de repeticiones
billing_link: Pagos y facturas
youtube_platform_link: Canal de YouTube de la plataforma
youtube_club_label: "YouTube del club:"
youtube_connected: (conectado)
youtube_premium_full_not_connected: Premium Full — canal del club no conectado (usa Match Live TV)
no_channel_hint_html: "Sin un canal conectado, la app usa el canal de Match Live TV. %{link}."
link_channel: Conectar canal (página del club)
teams_title: Equipos
no_teams: No hay equipos registrados.
table:
team: Equipo
sport: Deporte
matches_and_details: Partidos y detalles
replay: Repeticiones
concurrency_title: Intentos de segundo directo
concurrency_lead: La misma cuenta intentó iniciar otro directo mientras ya había uno en curso.
concurrency_none: No hay intentos registrados para este club.
concurrency_all: Ver todos los abusos de cuenta
comped:
title: Suscripción de cortesía
description: "Patrocinador o promoción: concede Premium Light/Full sin pago en Stripe. Revocable en cualquier momento."
active_label: "Activa:"
since_label_html: "Desde el %{date}"
by_admin: "· admin %{username}"
stripe_note: "Nota: también existe una suscripción de Stripe vinculada; la cortesía tiene prioridad sobre el plan."
revoke_button: Revocar cortesía (volver a Free o restaurar Stripe)
revoke_confirm: "¿Revocar la suscripción de cortesía para %{club}?"
current_plan: "Plan actual: %{plan}"
stripe_active: "· pago de Stripe activo"
plan_label: Plan
reason_label: "Motivo (p. ej. patrocinador 2026)"
reason_placeholder: "Patrocinador, promoción…"
grant_button: Conceder cortesía
teams:
show:
back: "← %{club}"
sport_label: "Deporte: %{sport}"
club_label_html: "Club: %{club} · %{replay_link} · %{billing_link}"
replay_link: Repeticiones del equipo
billing_link: Pagos y facturas
youtube_label: "YouTube:"
youtube_platform_default_no_oauth: ", predeterminado sin OAuth del club"
youtube_platform_channel: "(canal de la plataforma%{suffix})"
youtube_club_channel: (canal del club)
youtube_not_ready: no está listo
no_channel_hint_html: "Sin un canal conectado, la app usa el canal de Match Live TV. %{link}."
link_channel: Conectar canal (página del club)
matches_title: Partidos
sessions:
index:
title: Sesiones de emisión
lead: Busca por club, equipo, rival o sede. Filtra por estado, plataforma, nodo y periodo.
results: "Mostrando %{shown} de %{total}"
none: Ninguna sesión coincide con estos filtros.
filters:
q: Buscar
q_placeholder: Club, equipo, rival…
status: Estado
platform: Plataforma
club: Club
node: Nodo ingest
from: Desde
to: Hasta
any: Todos
apply: Filtrar
reset: Restablecer
table:
club: Club
match: Partido
status: Estado
started: Inicio
ended: Fin
duration: Duración
ingest: Ingest
disconnects: Desconexiones
client: Cliente
link: Enlace
detail: Detalle
regia: Regie
show:
title: "Sesión %{id}"
heading: Detalle de sesión
back: "← Volver a sesiones"
club_label: "Club:"
summary_title: Resumen
timing_title: Tiempos
ingest_title: Ingest y medios
devices_title: Dispositivos
events_title: Eventos
events_none: No hay eventos registrados.
concurrency_title: Intentos de segundo directo
concurrency_lead: Esta cuenta intentó iniciar otro directo mientras este ya estaba en curso (o fue bloqueada por otra sesión de la misma cuenta).
status_label: "Estado:"
stop_button: Finalizar sesión
stop_confirm: "¿Finalizar la emisión? La ruta RTMP se eliminará y el estado pasará a finalizada."
match_label: "Partido: %{team} vs %{opponent}"
youtube_studio: YouTube Studio
broadcast: Emisión
open_replays: Abrir replays del club
audio_muted: Audio silenciado
audio_on: Audio activo
ingest_node: "Nodo ingest:"
rtmp_ingest: "Ingesta RTMP:"
fields:
id: ID
club: Club
team: Equipo
opponent: Rival
operator: Operador
platform: Plataforma
client_os: Sistema
app_version: Versión app
device: Dispositivo
os_version: Versión OS
carrier: Operador móvil
privacy: Privacidad
quality: Calidad
min_quality: Calidad mínima
audio: Audio
created: Creada
started: Inicio
ended: Fin
duration: Duración
disconnects: Desconexiones
scheduled: Partido programado
location: Sede
node: Nodo
rtmp: Ingesta RTMP
hls: HLS
recording: Replay
devices:
role: Rol
network: Red
battery: Batería
bitrate: Bitrate
fps: FPS
last_seen: Último contacto
table:
type: Tipo
when: Cuándo
meta: Detalles
links:
watch_title: Enlace en directo
none: No hay ningún enlace de vídeo disponible para esta sesión.
regia_title: Enlace de regie
terminated: Sesión finalizada — enlace de regie no disponible.
regia_label: Regie
valid_until: "Válido hasta %{date}"
active_exists: Ya existe un enlace de regie activo (la URL no se puede recuperar). Genera uno nuevo si necesitas compartirlo de nuevo.
none_active: No hay ningún enlace de regie activo. Genera un enlace para compartir con quien gestione el marcador.
generate_button: Generar enlace de regie
ingest:
none: "—"
decommissioned: "(nodo eliminado)"
role:
home: Home-lab
lab: Lab
cloud: Hetzner
analytics:
missing_path: Selecciona una página.
filters:
from: Desde
to: Hasta
device: Dispositivo
any: Todos
apply: Filtrar
reset: Restablecer
layer: Capa
layer_move: Movimientos del ratón
layer_click: Clics
devices:
mobile: Móvil
tablet: Tablet
desktop: Escritorio
index:
title: Analytics del sitio
lead: Heatmaps de movimientos/clics y scroll agregados (first-party), solo con consentimiento estadístico. Sin datos personales.
none: No hay datos en el periodo seleccionado.
heatmap: Heatmap
table:
path: Página
pageviews: Pageviews
moves: Movimientos
clicks: Clics
avg_scroll: Scroll medio
max_scroll: Scroll máx
show:
title: Heatmap de página
back: "← Volver a Analytics"
summary: Resumen
scroll_depth: Profundidad de scroll
scroll_hint: "Media %{avg}% · máximo %{max}%"
heatmap_moves: Mapa de movimientos
heatmap_clicks: Mapa de clics
heatmap_hint: Superposición de color sobre la captura %{device}. Verde = poco, rojo = mucho. Los datos no se mezclan entre dispositivos.
device_tabs_label: Dispositivo
preview_title: Captura de página
preview_unavailable: Vista previa no disponible para rutas con placeholder (p. ej. /clubs/:id).
snapshot_missing: Aún no hay captura para %{device}. Visita la página con ese dispositivo y consentimiento analytics, y vuelve a intentarlo.
snapshot_meta: "Captura %{device} · %{at}"
no_points: No hay puntos agregados para esta capa en el periodo.
heatmap_title: Mapa de clics
no_clicks: No hay clics agregados para esta página en el periodo.
preview:
title: Vista previa sin seguimiento
lead: Navega el sitio público sin registrar pageviews, clics, movimientos ni capturas heatmap. Útil para pruebas en producción.
active: Vista previa staff activa en este navegador (7 días o hasta desactivar).
enable: Activar vista previa staff
disable: Desactivar vista previa
open_site: Abrir sitio
enabled: Vista previa staff activada. Este navegador no registrará analytics.
disabled: Vista previa staff desactivada.
billing:
index:
title: Pagos por facturar
description: Pagos de Stripe cobrados sin PDF de factura. Genera el PDF en tus sistemas y súbelo aquí; se asociará al pago y se enviará por email al cliente.
filter_label: "Filtrar clubes:"
filter_all: Todos
incomplete_profile_warning: Datos de facturación incompletos
no_billing_data: No hay datos de facturación — el cliente debe completar su perfil.
pdf_label: PDF de la factura
upload_button: Subir PDF y enviar al cliente
none_pending: "No hay pagos pendientes de facturar%{club}."
none_pending_for_club: " para %{club}"
uploaded_title: Facturas ya subidas
table:
date: Fecha
club: Club
description: Descripción
amount: Importe
invoice: Factura
status: Estado
back_dashboard: "← Panel"
billing_invoices:
index:
title: "Facturación — %{club}"
back_dashboard: "← Panel"
new_invoice: Nueva factura (sin pago)
payments_title: Pagos
table:
date: Fecha
description: Descripción
amount: Importe
invoice: Factura
pdf_suffix: " (PDF)"
edit_send_pdf: Editar / enviar PDF
create_invoice: Crear factura
no_payments: No hay pagos registrados para este club.
all_invoices_title: Todas las facturas
table2:
number: Número
date: Fecha
amount: Importe
status: Estado
payment: Pago
edit: Editar
no_invoices: No hay facturas.
new:
title: "Nueva factura — %{club}"
linked_payment: "Vinculada al pago del %{date} (%{amount})."
number_label: Número de factura
issued_on_label: Fecha de emisión
amount_label: "Importe (€)"
notes_label: Notas internas (opcional)
hint: Guarda el borrador y luego sube el PDF y envíalo al cliente desde la siguiente pantalla.
submit: Crear borrador de factura
back: "← Pagos y facturas"
edit:
title: "Factura %{number} — %{club}"
status_label: "Estado:"
sent_at: "· Enviada el %{date}"
number_label: Número de factura
issued_on_label: Fecha de emisión
amount_label: "Importe (€)"
payment_info: "Pago: %{description} — %{amount}"
pdf_label: PDF de la factura
pdf_already: "PDF ya subido: %{filename}"
notes_label: Notas internas
save_draft: Guardar borrador
issue_send: Emitir y enviar por email
issue_confirm: "¿Enviar la factura a %{email}?"
back: "← Pagos y facturas"
youtube:
platform_token:
title: Token del canal de Match Live TV
page_title: "Token de YouTube — Match Live TV"
channel_label: "Canal: %{title}"
success: "Conexión correcta. Copia esta línea ahora mismo:"
env_hint_html: "Añade en <code>/opt/matchlivetv/infra/.env</code>:"
not_shown_again_html: "No se volverá a mostrar. Luego: <code>docker compose -f docker-compose.prod.yml restart rails sidekiq</code>"
no_refresh_token_html: "Google no ha devuelto un refresh token. En tu <a href=\"https://myaccount.google.com/permissions\" target=\"_blank\" rel=\"noopener\">cuenta de Google → acceso de apps</a>, revoca «Match Live TV» y repite el proceso desde <a href=\"/admin/youtube/platform\">/admin/youtube/platform</a>."
back_dashboard: "← Panel admin"
club_recordings:
index:
title: "Repeticiones — %{club}"
announcements:
index:
title: Avisos
lead: Mensajes hacia las apps, el sitio público y el área privada — mantenimiento, actualizaciones o comunicados.
new: Nuevo aviso
table_title: Avisos
none: Todavía no hay avisos. Crea uno para notificar a las apps y al sitio.
new:
title: Nuevo aviso
edit:
title: Editar aviso
table:
status: Estado
kind: Tipo
title: Mensaje
channels: Destinos
window: Ventana
actions:
edit: Editar
delete: Eliminar
delete_confirm: ¿Eliminar este aviso? Ya no se verá en los destinos elegidos.
kind:
info: Información
maintenance: Mantenimiento
update: Actualización
severity:
info: Info
warning: Atención
critical: Crítico
status:
draft: Borrador
published: Publicado
archived: Archivado
channels:
android: App Android
ios: App iOS
web_public: Sitio público
web_private: Área privada
window:
always: Siempre (mientras esté publicado)
from: "Desde %{time}"
until: "Hasta %{time}"
form:
kind: Tipo
severity: Gravedad
status: Estado
channels: Dónde mostrarlo
channels_hint: "Elige uno o más destinos. Android e iOS son independientes: si el texto debe ser distinto, crea dos avisos. El área privada se ve tras iniciar sesión (club, equipos, cuenta)."
title: Título
body: Texto
starts_at: Visible desde
ends_at: Visible hasta
window_hint: Déjalo vacío para mostrarlo de inmediato mientras esté publicado.
action_url: Enlace (opcional)
action_url_hint: En actualizaciones, una URL vacía abre la tienda de esa plataforma (Play Store en Android, App Store en iOS).
action_label: Texto del botón
locales_legend: Texto por idioma
locales_hint: Completa todos los idiomas de la app (italiano, inglés, francés, alemán, español). El italiano es obligatorio; si falta un idioma, se usa inglés y luego italiano.
locale_required: obligatorio
dismissible: El usuario puede cerrar el aviso
dismissible_hint: Quita la marca para avisos de mantenimiento que deben permanecer visibles.
submit_create: Guardar aviso
submit_update: Actualizar aviso
cancel: Cancelar