const traducciones = { es: { editor: { titulo: "Editor de Formación", tipo: "Tipo", modalidad: "Modalidad", agregar: "Agregar Atleta", seleccionarAtleta: "Seleccionar atleta", rol: "Rol", rolSeleccionar: "Seleccionar rol", volador: "Volador", pilar: "Pilar", otro: "Otro", idPers: "ID personalizado", idEj: "Ej: A, 1, V2", figura: "Figura técnica", figuraEj: "Ej: Barracuda, Flamingo...", tipoElemento: "Tipo de elemento", codigoElemento: "Código del elemento", codigoEj: "Ej: TRE 1A, H4...", hibrido: "Híbrido", transicion: "Transición", btnAgregar: "Añadir Atleta", editar: "Editar formación", eliminar: "Eliminar Formación", guardar: "Guardar Formación", linea: "Línea de Tiempo", piscina: "Piscina", tipoPiscina: "Tipo de piscina", olimpica: "Olímpica (50m x 25m)", semiolimpica: "Semiolímpica (25m x 12.5m)", fosa: "Fosa/Poza (20m x 20m)", duracion: "Duración (segundos)", duracionEj: "Ej: 5", nombre: "Nombre", nombreEj: "Nombre de la formación", notas: "Notas tácticas", notasEj: "Instrucciones, marcajes, etc.", datos: "Datos de Formación", play: "▶ Reproducir", pause: "⏸︎ Pausar", actualizar: "Actualizar Formación", sinFigura: "Sin figura", direccion: "Añadir Dirección", eliminarDir: "Eliminar Dirección", edicionActiva: "🟡 En edición activa", alertaFaltanDatos: "Faltan datos del atleta", alertaMaxAtletas: "Modalidad \"{MOD}\" permite un máximo de {MAX} atletas.", alertaClickPiscina: "Haz clic en la piscina para colocarlo", alertaNombreFaltante: "Agrega nombre de formación y al menos un atleta", alertaSeleccionaFormacion: "Selecciona una formación desde la línea de tiempo primero.", confirmEliminar: { titulo: "¿Eliminar formación?", texto: "Esta acción no se puede deshacer", confirmar: "Sí, eliminar", cancelar: "Cancelar", eliminado: "La formación ha sido eliminada.", error: "No se pudo eliminar la formación." } }, nav: { init: "Inicializar Rutina", equip: "Equipos Disponibles" }, logout: "Salir" }, en: { editor: { titulo: "Formation Editor", tipo: "Type", modalidad: "Modality", agregar: "Add Athlete", seleccionarAtleta: "Select athlete", rol: "Role", rolSeleccionar: "Select role", volador: "Flyer", pilar: "Base", otro: "Other", idPers: "Custom ID", idEj: "E.g.: A, 1, V2", figura: "Technical figure", figuraEj: "E.g.: Barracuda, Flamingo...", tipoElemento: "Element type", codigoElemento: "Element code", codigoEj: "E.g.: TRE 1A, H4...", hibrido: "Hybrid", transicion: "Transition", btnAgregar: "Add Athlete", editar: "Edit formation", eliminar: "Delete Formation", guardar: "Save Formation", linea: "Timeline", piscina: "Pool", tipoPiscina: "Pool type", olimpica: "Olympic (50m x 25m)", semiolimpica: "Semi-Olympic (25m x 12.5m)", fosa: "Pit (20m x 20m)", duracion: "Duration (seconds)", duracionEj: "E.g.: 5", nombre: "Name", nombreEj: "Formation name", notas: "Tactical notes", notasEj: "Instructions, markings, etc.", datos: "Formation Data", play: "▶ Play", pause: "⏸︎ Pause", actualizar: "Update Formation", sinFigura: "No figure", direccion: "Add Direction", eliminarDir: "Remove Direction", edicionActiva: "🟡 Editing active", alertaFaltanDatos: "Missing athlete data", alertaMaxAtletas: "Modality \"{MOD}\" allows a maximum of {MAX} athletes.", alertaClickPiscina: "Click on the pool to place the athlete", alertaNombreFaltante: "Add formation name and at least one athlete", alertaSeleccionaFormacion: "Select a formation from the timeline first.", confirmEliminar: { titulo: "Delete formation?", texto: "This action cannot be undone", confirmar: "Yes, delete", cancelar: "Cancel", eliminado: "Formation has been deleted.", error: "Failed to delete formation." } }, nav: { init: "Initialize Routine", equip: "Available Teams" }, logout: "Logout" }, fr: { editor: { titulo: "Éditeur de Formation", tipo: "Type", modalidad: "Modalité", agregar: "Ajouter un Athlète", seleccionarAtleta: "Sélectionner un athlète", rol: "Rôle", rolSeleccionar: "Sélectionner un rôle", volador: "Voltigeur", pilar: "Pilier", otro: "Autre", idPers: "ID personnalisé", idEj: "Ex. : A, 1, V2", figura: "Figure technique", figuraEj: "Ex. : Barracuda, Flamant...", tipoElemento: "Type d'élément", codigoElemento: "Code de l'élément", codigoEj: "Ex. : TRE 1A, H4...", hibrido: "Hybride", transicion: "Transition", btnAgregar: "Ajouter un Athlète", editar: "Modifier la formation", eliminar: "Supprimer la Formation", guardar: "Enregistrer la Formation", linea: "Chronologie", piscina: "Piscine", tipoPiscina: "Type de piscine", olimpica: "Olympique (50m x 25m)", semiolimpica: "Semi-olympique (25m x 12,5m)", fosa: "Fosse (20m x 20m)", duracion: "Durée (secondes)", duracionEj: "Ex. : 5", nombre: "Nom", nombreEj: "Nom de la formation", notas: "Notes tactiques", notasEj: "Instructions, repères, etc.", datos: "Données de la Formation", play: "▶ Lire", pause: "⏸︎ Pause", actualizar: "Mettre à jour la Formation", sinFigura: "Pas de figure", direccion: "Ajouter une Direction", eliminarDir: "Supprimer la Direction", edicionActiva: "🟡 Édition active", alertaFaltanDatos: "Données de l'athlète manquantes", alertaMaxAtletas: "La modalité \"{MOD}\" permet un maximum de {MAX} athlètes.", alertaClickPiscina: "Cliquez sur la piscine pour placer l'athlète", alertaNombreFaltante: "Ajoutez un nom de formation et au moins un athlète", alertaSeleccionaFormacion: "Sélectionnez d'abord une formation depuis la chronologie.", confirmEliminar: { titulo: "Supprimer la formation ?", texto: "Cette action est irréversible", confirmer: "Oui, supprimer", cancelar: "Annuler", eliminado: "La formation a été supprimée.", error: "Échec de la suppression de la formation." } }, nav: { init: "Démarrer la routine", equip: "Équipes disponibles" }, logout: "Se déconnecter" } }; window.figurasTraducidas = { "Pez Volador con Giro": { en: "Flying Fish with Twist", fr: "Poisson Volant avec Torsion" }, "Pez Volador": { en: "Flying Fish", fr: "Poisson Volant" }, "Rodilla Flexionada con Giro Completo": { en: "Bent Knee with Full Twist", fr: "Genou Fléchi avec Rotation Complète" }, "Rodilla Flexionada con Medio Giro": { en: "Bent Knee with Half Twist", fr: "Genou Fléchi avec Demi-Tour" }, "Fouetté con Giro 720°": { en: "Fouetté with 720° Turn", fr: "Fouetté avec Rotation de 720°" }, "Fouetté con Giro 360°": { en: "Fouetté with 360° Turn", fr: "Fouetté avec Rotation de 360°" }, "Mariposa": { en: "Butterfly", fr: "Papillon" }, "Split con Giro 180°": { en: "Split with 180° Turn", fr: "Grand Écart avec Rotation de 180°" }, "Split a Rodilla Flexionada": { en: "Split to Bent Knee", fr: "Grand Écart vers Genou Fléchi" } }; window.descripcionesTraducidas = { "Impulso a vertical desde pike, transición a cola de pez aérea, regreso a vertical y giro de 180°.": { en: "Thrust to vertical from pike, transition to aerial fish tail, return to vertical and 180° twist.", fr: "Poussée en vertical depuis pike, transition vers queue de poisson aérienne, retour en vertical et torsion de 180°." }, "Impulso a vertical desde pike, transición a cola de pez aérea, regreso a vertical y descenso.": { en: "Thrust to vertical from pike, transition to aerial fish tail, return to vertical and descent.", fr: "Poussée en vertical depuis pike, transition vers queue de poisson aérienne, retour en vertical et descente." }, "Vertical con giro completo a rodilla flexionada, otro giro a vertical, apertura 180° a split y walkout.": { en: "Vertical with full twist to bent knee, another twist to vertical, 180° split and walkout.", fr: "Vertical avec rotation complète vers genou fléchi, nouvelle rotation vers vertical, ouverture à 180° en écart et sortie." }, "Vertical con medio giro a rodilla flexionada, otro medio giro a vertical, split y walkout.": { en: "Vertical with half twist to bent knee, another half twist to vertical, split and walkout.", fr: "Vertical avec demi-tour vers genou fléchi, autre demi-tour vers vertical, écart et sortie." }, "Desde cola de pez, dos giros Fouetté, paso a vertical y giro continuo de 720°.": { en: "From fish tail, two Fouetté turns, transition to vertical and continuous 720° turn.", fr: "Depuis queue de poisson, deux rotations Fouetté, passage en vertical et rotation continue de 720°." }, "Desde cola de pez, dos giros Fouetté, paso a vertical y giro rápido de 360°.": { en: "From fish tail, two Fouetté turns, transition to vertical and quick 360° turn.", fr: "Depuis queue de poisson, deux rotations Fouetté, passage en vertical et rotation rapide de 360°." }, "Secuencia dinámica con pike, cola de pez, split, giros, arco superficial y salida en layout.": { en: "Dynamic sequence with pike, fish tail, split, turns, surface arch and layout exit.", fr: "Séquence dynamique avec pike, queue de poisson, écart, rotations, arc en surface et sortie en layout." }, "Impulso a vertical, split aéreo, giro 180° a rodilla flexionada y descenso vertical.": { en: "Thrust to vertical, aerial split, 180° turn to bent knee and vertical descent.", fr: "Poussée en vertical, grand écart aérien, rotation de 180° vers genou fléchi et descente verticale." }, "Impulso a vertical, split aéreo, transición a rodilla flexionada y descenso vertical.": { en: "Thrust to vertical, aerial split, transition to bent knee and vertical descent.", fr: "Poussée en vertical, grand écart aérien, transition vers genou fléchi et descente verticale." } }; let lang = window.tLang || localStorage.getItem('lang') || 'es'; window.tLang = lang; const t = (key, replacements = {}) => { const keys = key.split('.'); let value = keys.reduce((obj, k) => obj?.[k], traducciones[lang]) ?? key; for (const [k, v] of Object.entries(replacements)) { value = value.replace(`{${k}}`, v); } return value; }; function aplicarTraducciones() { // Traduce elementos con data-i18n document.querySelectorAll('[data-i18n]').forEach(el => { const key = el.dataset.i18n; el.textContent = t(key); }); // Traduce placeholders document.querySelectorAll('[data-i18n-placeholder]').forEach(el => { const key = el.dataset.i18nPlaceholder; el.placeholder = t(key); }); // Traducción de