diff --git a/backend/migrations/core/106_seed_es_email_template_translations.js b/backend/migrations/core/106_seed_es_email_template_translations.js new file mode 100644 index 00000000..06db0e78 --- /dev/null +++ b/backend/migrations/core/106_seed_es_email_template_translations.js @@ -0,0 +1,121 @@ +/** + * Migration: seed Spanish (es) email-template translations (#510). + * + * Contributed by @AloePacci on issue #510. Covers the four + * customer-facing gallery delivery templates that already had + * en/de/nl/pt/ru rows from migration 075. Templates without an `es` + * row (admin_*, backup_*, restore_*, customer_*, version_update_*) + * continue to fall back to `en` via the resolution chain in + * emailProcessor.processTemplate — no functional gap, just untranslated + * copy until someone fills them in. + * + * Same idempotency pattern as 099_seed_missing_email_template_translations: + * checks (template_id, language) before inserting so re-runs are safe. + */ + +const TRANSLATIONS = { + gallery_created: { + es: { + subject: 'Su galería de fotos está lista!', + body_html: `

Galería creada con éxito

+

Estimado {{host_name}},

+

Su galería de fotos "{{event_name}}" ha sido creada con éxito!

+

Detalles de la galería:

+ +

Comparta este enlace y contraseña con sus invitados para que puedan ver y descargar las fotos.

+{{#if welcome_message}}

{{welcome_message}}

{{/if}}`, + body_text: 'Galería creada con éxito\n\nEstimado {{host_name}},\n\nSu galería de fotos "{{event_name}}" ha sido creada con éxito!\n\nEnlace de la galería: {{gallery_link}}\nContraseña: {{gallery_password}}\nExpira en: {{expiry_date}}', + }, + }, + + expiration_warning: { + es: { + subject: 'Su galería de fotos expirará pronto', + body_html: `

Galería expirando pronto

+

Estimado {{host_name}},

+

Su galería de fotos "{{event_name}}" expirará en {{days_remaining}} días.

+

Después de la expiración, la galería será archivada y ya no estará accesible para los invitados.

+

Visitar galería

`, + body_text: 'Galería expirando pronto\n\nEstimado {{host_name}},\n\nSu galería de fotos "{{event_name}}" expirará en {{days_remaining}} días.\n\nGalería: {{gallery_link}}', + }, + }, + + gallery_expired: { + es: { + subject: 'Su galería de fotos está caducada', + body_html: `

Galería vencida

+

Estimado {{host_name}},

+

Su galería de fotos "{{event_name}}" ha caducado y por tanto ya no es accesible.

+

Las fotos han sido archivadas. Si necesita acceso, por favor póngase en contacto con el administrador a través de {{admin_email}}.

`, + body_text: 'Galería caducada\n\nEstimado {{host_name}},\n\nSu galería de fotos "{{event_name}}" ha caducado y ya no está accesible.\n\nContacto: {{admin_email}}', + }, + }, + + archive_complete: { + es: { + subject: 'Archivado completado: {{event_name}}', + body_html: `

Archivado completado

+

Estimado {{host_name}},

+

Su galería de fotos "{{event_name}}" ha sido archivada con éxito.

+

Detalles del archivo:

+`, + body_text: 'Archivado completado\n\nEstimado {{host_name}},\n\nSu galería de fotos "{{event_name}}" ha sido archivada con éxito.\n\nFotos: {{photo_count}}\nTamaño: {{archive_size}}', + }, + }, +}; + +exports.up = async function(knex) { + if (!(await knex.schema.hasTable('email_templates'))) return; + if (!(await knex.schema.hasTable('email_template_translations'))) return; + + const rows = await knex('email_templates') + .whereIn('template_key', Object.keys(TRANSLATIONS)) + .select('id', 'template_key'); + const keyToId = Object.fromEntries(rows.map((r) => [r.template_key, r.id])); + + let inserted = 0; + let skipped = 0; + + for (const [key, perLocale] of Object.entries(TRANSLATIONS)) { + const templateId = keyToId[key]; + if (!templateId) continue; + + for (const [language, content] of Object.entries(perLocale)) { + const existing = await knex('email_template_translations') + .where({ template_id: templateId, language }) + .first(); + if (existing) { + skipped += 1; + continue; + } + await knex('email_template_translations').insert({ + template_id: templateId, + language, + subject: content.subject, + body_html: content.body_html, + body_text: content.body_text, + created_at: new Date(), + updated_at: new Date(), + }); + inserted += 1; + } + } + + console.log(`106_seed_es_email_template_translations: inserted=${inserted}, skipped=${skipped}`); +}; + +exports.down = async function(knex) { + // No-op: same rationale as 099. An admin may have hand-edited the + // `es` rows in the Templates UI after this migration ran, and we + // can't tell apart inserted-by-us rows from edited-by-admin rows. + // Rollback by hand if you truly need to drop them. +}; diff --git a/backend/src/services/emailProcessor.js b/backend/src/services/emailProcessor.js index ecab43cb..27cca8de 100644 --- a/backend/src/services/emailProcessor.js +++ b/backend/src/services/emailProcessor.js @@ -140,6 +140,7 @@ async function getRecipientLanguage(email, eventId = null) { { domains: ['.nl', '.be'], language: 'nl' }, { domains: ['.br', '.pt'], language: 'pt' }, { domains: ['.ru', '.su'], language: 'ru' }, + { domains: ['.es'], language: 'es' }, ]; for (const { domains, language: lang } of domainLanguageMap) { if (domains.some(d => domain.endsWith(d))) { @@ -497,6 +498,7 @@ async function processTemplate(template, variables, language = 'en') { nl: '(Om veiligheidsredenen niet weergegeven)', pt: '(Não exibido por motivos de segurança)', ru: '(Не показано в целях безопасности)', + es: '(No se muestra por razones de seguridad)', }; const noPasswordI18n = { en: 'No password required', @@ -504,6 +506,7 @@ async function processTemplate(template, variables, language = 'en') { nl: 'Geen wachtwoord vereist', pt: 'Nenhuma senha necessária', ru: 'Пароль не требуется', + es: 'No se requiere contraseña', }; // Sent by the publish-from-draft flow (adminEvents.js): by the time the // event is published, only the bcrypt hash is stored, so the plaintext @@ -515,6 +518,7 @@ async function processTemplate(template, variables, language = 'en') { nl: 'Het wachtwoord dat u bij het aanmaken van de galerij hebt ingesteld', pt: 'A senha definida ao criar a galeria', ru: 'Пароль, заданный при создании галереи', + es: 'La contraseña que estableciste al crear la galería', }; if (processedVariables.gallery_password === '{{password_security_message}}') { diff --git a/frontend/src/components/common/LanguageSelector.tsx b/frontend/src/components/common/LanguageSelector.tsx index 56598ba8..bb587e75 100644 --- a/frontend/src/components/common/LanguageSelector.tsx +++ b/frontend/src/components/common/LanguageSelector.tsx @@ -54,6 +54,14 @@ const FRFlag: React.FC<{ className?: string }> = ({ className = "w-5 h-5" }) => ); +const ESFlag: React.FC<{ className?: string }> = ({ className = "w-5 h-5" }) => ( + + + + + +); + export const SUPPORTED_LANGUAGES = [ { code: 'en', name: 'English', Flag: GBFlag }, { code: 'de', name: 'Deutsch', Flag: DEFlag }, @@ -61,6 +69,7 @@ export const SUPPORTED_LANGUAGES = [ { code: 'pt', name: 'Português', Flag: PTBRFlag }, { code: 'nl', name: 'Nederlands', Flag: NLFlag }, { code: 'fr', name: 'Français', Flag: FRFlag }, + { code: 'es', name: 'Español', Flag: ESFlag }, ]; export const LanguageSelector: React.FC = () => { diff --git a/frontend/src/i18n/locales/es.json b/frontend/src/i18n/locales/es.json new file mode 100644 index 00000000..dc7ea7aa --- /dev/null +++ b/frontend/src/i18n/locales/es.json @@ -0,0 +1,2472 @@ +{ + "userManagement": { + "title": "Gestión de usuarios", + "subtitle": "Administrar usuarios administradores e invitaciones", + "loading": "Cargando usuarios...", + "loadError": "Error al cargar usuarios. Por favor, inténtalo de nuevo.", + "inviteUser": "Invitar usuario", + "createInvitation": "Crear invitación", + "email": "Correo electrónico", + "emailPlaceholder": "usuario@ejemplo.com", + "role": "Rol", + "selectRole": "Seleccionar un rol", + "sendInvitation": "Enviar invitación", + "editUser": "Editar usuario", + "editingUser": "Editando usuario", + "saveChanges": "Guardar cambios", + "deactivateUser": "Desactivar usuario", + "cancelInvitation": "Cancelar invitación", + "invitationSent": "Invitación enviada con éxito", + "invitationError": "Error al enviar la invitación", + "invitationCancelled": "Invitación cancelada", + "cancelInvitationError": "Error al cancelar la invitación", + "userUpdated": "Usuario actualizado con éxito", + "updateUserError": "Error al actualizar el usuario", + "userDeactivated": "Usuario desactivado con éxito", + "deactivateUserError": "Error al desactivar el usuario", + "noRole": "Sin rol", + "neverLoggedIn": "Nunca ha iniciado sesión", + "expired": "Expirado", + "deactivate": "Desactivar", + "cancel": "Cancelar", + "tabs": { + "users": "Usuarios", + "invitations": "Invitaciones" + }, + "stats": { + "totalUsers": "Usuarios totales", + "activeUsers": "Usuarios activos", + "pendingInvitations": "Invitaciones pendientes", + "inactiveUsers": "Usuarios inactivos" + }, + "status": { + "active": "Activo", + "inactive": "Inactivo" + }, + "table": { + "user": "Usuario", + "email": "Correo", + "role": "Rol", + "status": "Estado", + "lastLogin": "Último acceso", + "actions": "Acciones", + "invitedBy": "Invitado por", + "expires": "Expiración" + }, + "validation": { + "emailRequired": "El correo es obligatorio", + "emailInvalid": "Formato de correo inválido", + "roleRequired": "El rol es obligatorio" + }, + "searchUsersPlaceholder": "Buscar usuarios...", + "searchInvitationsPlaceholder": "Buscar invitaciones...", + "noUsers": "No se encontraron usuarios", + "noUsersFound": "Ningún usuario coincide con tu búsqueda", + "noInvitations": "No hay invitaciones pendientes", + "noInvitationsFound": "Ninguna invitación coincide con tu búsqueda", + "confirmDeactivate": { + "title": "Desactivar usuario", + "message": "¿Seguro que quieres desactivar a {{name}}? Ya no podrá iniciar sesión." + }, + "confirmCancelInvitation": { + "title": "Cancelar invitación", + "message": "¿Seguro que quieres cancelar la invitación para {{email}}?" + } + }, + "common": { + "loading": "Cargando...", + "error": "Error", + "save": "Guardar", + "cancel": "Cancelar", + "delete": "Eliminar", + "edit": "Editar", + "add": "Agregar", + "search": "Buscar", + "filter": "Filtrar", + "sortBy": "Ordenar por", + "yes": "Sí", + "no": "No", + "back": "Atrás", + "next": "Siguiente", + "previous": "Anterior", + "close": "Cerrar", + "logout": "Cerrar sesión", + "menu": "Menú", + "change": "Cambiar", + "remove": "Quitar", + "download": "Descargar", + "downloadAll": "Descargar todo", + "uploading": "Subiendo...", + "uploaded": "Subido", + "photo": "foto", + "photos": "fotos", + "video": "video", + "videos": "videos", + "media": "medios", + "restore": "Restaurar", + "actions": "Acciones", + "refresh": "Actualizar", + "preview": "Vista previa", + "processing": "Procesando...", + "upload": "Subir", + "days": "días", + "customize": "Personalizar", + "hide": "Ocultar", + "unknown": "Desconocido", + "notSet": "No establecido", + "of": "de", + "up": "Arriba", + "select": "Seleccionar", + "selected": "Seleccionado", + "chunk": "Fragmento", + "optional": "opcional" + }, + "upload": { + "photoCategory": "Categoría de foto", + "noCategory": "Sin categoría", + "eventSpecific": "(Específico del evento)", + "clickToUpload": "Haz clic para subir o arrastra y suelta", + "fileRequirements": "JPEG, PNG o WebP (máx. 50MB por archivo, {{limit}} archivos por subida)", + "fileRequirementsMedia": "Imágenes JPEG, PNG o WebP y videos MP4/MOV/WEBM (máx. 50MB por archivo, {{limit}} archivos por subida)", + "unsupportedFiles": "Algunos archivos se omitieron porque el formato no es compatible (usa JPEG/PNG/WebP/MP4/MOV/WEBM).", + "selectedFiles": "Archivos seleccionados", + "uploading": "Subiendo...", + "transferring": "Transfiriendo", + "processing": "Procesando fotos...", + "processingHint": "Los archivos están subidos. PicPeak está generando miniaturas y leyendo metadatos. Puedes salir de esta página — el trabajo sigue en segundo plano.", + "processingProgress": "{{complete}} de {{total}} completado", + "processingFailed": "{{count}} foto(s) fallaron al procesar", + "retryFailed": "Reintentar fallo", + "uploadComplete": "¡Subida completada!", + "uploadFailed": "Error en la subida", + "someFilesFailed": "Algunos archivos fallaron al subir", + "replaceByName": "Reemplazar fotos existentes con el mismo nombre", + "replacedFiles": "{{count}} foto(s) reemplazada(s)", + "uploadPhotos": "Subir fotos", + "uploadMedia": "Subir fotos y videos", + "importExternal": "Importar desde carpeta externa", + "externalImportInfo": "Todas las imágenes de la carpeta seleccionada serán importadas.", + "selectExternalFolder": "Seleccionar carpeta externa bajo /external-media", + "importFromSelectedFolder": "Importar desde la carpeta seleccionada", + "maxFilesReached": "Máximo {{limit}} archivos permitidos", + "someFilesSkipped": "Solo se pueden agregar {{allowed}} archivos más (límite {{limit}})", + "tooManyFiles": "Máximo {{limit}} archivos pueden subirse a la vez", + "limitInfo": "{{selected}} de {{limit}} archivos seleccionados ({{remaining}} restantes)", + "limitReached": "Límite de subida alcanzado ({{limit}} archivos por lote)", + "uploadingChunks": "Subiendo {{count}} archivos en {{total}} lotes...", + "mediaCategory": "Categoría de medios", + "uploadAction": "Subir {{count}} archivos" + }, + "navigation": { + "dashboard": "Panel", + "events": "Eventos", + "archives": "Archivos", + "settings": "Ajustes", + "eventTypes": "Tipos de evento", + "branding": "Firma", + "analytics": "Analíticas", + "emailSettings": "Ajustes de correo", + "backup": "Copia de seguridad", + "cmsPages": "Páginas CMS", + "users": "Usuarios" + }, + "archives": { + "title": "Archivos", + "subtitle": "Administrar galerías de fotos archivadas", + "loadingArchives": "Cargando archivos...", + "totalArchives": "Archivos totales", + "storageUsed": "Almacenamiento usado", + "totalPhotos": "Fotos totales", + "avgArchiveSize": "Tamaño medio de archivo", + "searchPlaceholder": "Buscar archivos...", + "allTypes": "Todos los tipos", + "wedding": "Boda", + "birthday": "Cumpleaños", + "corporate": "Corporativo", + "other": "Otro", + "sortByDate": "Ordenar por fecha", + "sortByName": "Ordenar por nombre", + "sortBySize": "Ordenar por tamaño", + "tableHeaders": { + "event": "Evento", + "type": "Tipo", + "archivedDate": "Fecha archivado", + "size": "Tamaño", + "photos": "Fotos", + "actions": "Acciones" + }, + "noArchivesFound": "No se encontraron archivos", + "eventDateNA": "Fecha del evento: N/D", + "processing": "Procesando...", + "download": "Descargar", + "restore": "Restaurar", + "delete": "Eliminar", + "showing": "Mostrando {{from}} a {{to}} de {{total}} archivos", + "page": "Página {{current}} de {{total}}", + "storageManagement": "Gestión de almacenamiento", + "storageInfo": "Los archivos se almacenan de forma permanente a menos que se eliminen manualmente. Considera implementar una política de retención para gestionar costes de almacenamiento.", + "confirmRestore": "¿Seguro que quieres restaurar este archivo? El evento volverá a estar activo.", + "confirmDelete": "¿Seguro que quieres eliminar permanentemente este archivo? Esta acción no se puede deshacer.", + "restoreSuccess": "Archivo restaurado con éxito", + "deleteSuccess": "Archivo eliminado permanentemente" + }, + "auth": { + "login": "Iniciar sesión", + "password": "Contraseña", + "enterPassword": "Introduce la Contraseña de la Galería", + "passwordPlaceholder": "Introduce la contraseña de la galería", + "invalidPassword": "Contraseña inválida", + "wrongPassword": "Contraseña incorrecta. Comprueba tu contraseña e inténtalo de nuevo.", + "tooManyAttempts": "Demasiados intentos fallidos. Por favor, inténtalo más tarde.", + "sessionExpired": "Sesión expirada", + "pleaseEnterPassword": "Por favor, introduce una contraseña", + "passwordHint": "La contraseña la proporcionó el organizador del evento. Contacta con ellos si no la tienes." + }, + "gallery": { + "title": "Galería de fotos", + "welcomeMessage": "Mensaje de bienvenida", + "expiresOn": "Expira el", + "expires": "Expira", + "expired": "Expirado", + "daysRemaining": "Quedan {{days}} días", + "dayRemaining": "Queda 1 día", + "hoursRemaining": "Quedan {{hours}} horas", + "expiredMessage": "Esta galería expiró el {{date}}", + "contactOrganizer": "Contacta con el organizador del evento si necesitas acceso a estas fotos.", + "searchPhotos": "Buscar fotos por nombre de archivo...", + "sortByDate": "Ordenar por fecha", + "sortByName": "Ordenar por nombre", + "sortBySize": "Ordenar por tamaño", + "allPhotos": "Todas las fotos", + "filter": "Filtrar", + "feedbackFilter": "Filtro de comentarios", + "all": "Todas", + "liked": "Me gusta", + "favorited": "Favoritas", + "favorites": "Favoritos", + "downloadSelected": "Descargar {{count}} seleccionadas", + "shareGallery": "Compartir galería", + "needHelp": "¿Necesitas ayuda? Contáctanos en", + "noPhotosFound": "No se encontraron fotos", + "failedToLoad": "Error al cargar las fotos", + "tryAgain": "Intentar de nuevo", + "loading": "Cargando galería...", + "expiredOn": "Esta galería expiró el {{date}}.", + "expiresIn": "La galería expira en {{count}} día", + "expiresIn_plural": "La galería expira en {{count}} días", + "downloadBefore": "Descarga tus fotos antes de que dejen de estar disponibles.", + "publicGalleryTitle": "Esta galería es accesible públicamente", + "publicGallerySubtitle": "Cargando las fotos...", + "viewGallery": "Ver galería", + "downloadAll": "Descargar todo", + "downloading": "Descargando {{count}} foto...", + "downloading_plural": "Descargando {{count}} fotos...", + "downloadedPhotos": "¡{{count}} foto descargada!", + "downloadedPhotos_plural": "¡{{count}} fotos descargadas!", + "downloadError": "Algunas fotos fallaron al descargar", + "selectPhotos": "Seleccionar fotos", + "cancelSelection": "Cancelar selección", + "photosSelected": "{{count}} seleccionadas", + "selectAll": "Seleccionar todo", + "deselectAll": "Deseleccionar todo", + "deleteSelected": "Eliminar seleccionadas", + "photosCount": "{{count}} foto", + "photosCount_plural": "{{count}} fotos", + "searchByFilename": "Buscar por nombre de archivo...", + "uncategorized": "Sin categoría", + "sortAscending": "Orden ascendente", + "sortDescending": "Orden descendente", + "remaining": "restantes", + "selectPhotosHint": "Consejo: Usa Ctrl+Click (Cmd+Click en Mac) para seleccionar varias fotos rápidamente", + "filters": "Filtros", + "openFilters": "Abrir filtros", + "toggleSidebar": "Alternar barra lateral", + "toggleMenu": "Alternar menú", + "allCategories": "Todas las categorías", + "categories": "Categorías", + "mediaType": "Medio", + "allMedia": "Todos los medios", + "photosOnly": "Fotos", + "videosOnly": "Videos", + "download": "Descargar", + "noMedia": "No se han subido medios aún", + "searchPlaceholder": "Buscar fotos...", + "sortBy": "Ordenar por", + "sortByCaptureDate": "Ordenar por fecha de captura", + "sortByRating": "Ordenar por calificación", + "photoGallery": "Galería de fotos", + "photos": "Fotos", + "allRightsReserved": "Todos los derechos reservados", + "searchMemories": "Buscar recuerdos...", + "thankYou": "Gracias", + "thankYouMessage": "Gracias por ser parte de nuestra historia y hacer nuestro día inolvidable.", + "feedback": { + "title": "Comentarios", + "shareThoughts": "Comparte tus pensamientos sobre \"{{name}}\"", + "rateThisMoment": "Valora este momento", + "comments": "Comentarios", + "noComments": "No hay comentarios aún. ¡Sé el primero!", + "yourName": "Tu nombre", + "yourEmail": "Tu correo", + "writeLovelyNote": "Escribe una nota agradable...", + "postComment": "Publicar comentario", + "anonymous": "Anónimo" + }, + "rated": "Valorado", + "commented": "Comentado" + }, + "categories": { + "title": "Categorías de fotos", + "global": "Categorías globales", + "eventSpecific": "Categorías específicas del evento", + "addCategory": "Agregar categoría", + "organizationInfo": "Organiza tus fotos en categorías. Las categorías ayudan a los invitados a navegar y encontrar tipos específicos de fotos.", + "eventSpecificCategories": "Categorías específicas del evento", + "noEventSpecificCategories": "No hay categorías específicas del evento. Las categorías globales están disponibles por defecto.", + "globalCategoriesAlwaysAvailable": "Categorías globales (siempre disponibles):", + "deleteCategoryTitle": "Eliminar categoría", + "categoryCreatedSuccess": "Categoría creada con éxito", + "categoryDeletedSuccess": "Categoría eliminada con éxito", + "failedToCreateCategory": "Error al crear la categoría", + "failedToDeleteCategory": "Error al eliminar la categoría", + "categoryName": "Nombre de la categoría", + "noCategory": "Sin categoría", + "noCategoriesYet": "No hay categorías todavía. Crea tu primera categoría para organizar fotos.", + "deleteConfirm": "¿Seguro que quieres eliminar \"{{name}}\"?", + "cannotDelete": "No se puede eliminar la categoría con fotos. Reasigna las fotos primero.", + "setCoverPhoto": "Establecer foto de portada", + "removeCoverPhoto": "Quitar foto de portada", + "coverPhotoSet": "Foto de portada establecida con éxito", + "coverPhotoRemoved": "Foto de portada eliminada", + "failedToSetCoverPhoto": "Error al establecer la foto de portada", + "categoryHeroHint": "Si no se establece una foto de portada para una categoría, se usará la foto hero por defecto." + }, + "events": { + "title": "Eventos", + "create": "Crear", + "createEvent": "Crear evento", + "totalViews": "Vistas totales", + "totalDownloads": "Descargas totales", + "uniqueVisitors": "Visitantes únicos", + "createNewEvent": "Crear nuevo evento", + "setupNewGallery": "Configura una nueva galería de fotos para tu evento", + "createNewEventSubtitle": "Configura una nueva galería de fotos para tu evento", + "eventNamePlaceholder": "p. ej., Boda de Juan y María", + "welcomeMessageOptional": "Mensaje de bienvenida (opcional)", + "welcomeMessagePlaceholder": "¡Bienvenido a nuestro día especial! Siéntete libre de descargar y compartir estos recuerdos...", + "hostEmailPlaceholder": "cliente@ejemplo.com", + "adminEmailPlaceholder": "admin@ejemplo.com", + "adminEmailPickFromAdmins": "Elegir entre administradores:", + "adminEmailCustom": "Correo personalizado", + "securityAndAccess": "Seguridad y acceso", + "accessAndSecurity": "Acceso y seguridad", + "enterPassword": "Introduce contraseña", + "passwordPlaceholder": "Introduce una contraseña segura", + "confirmPasswordPlaceholder": "Confirmar contraseña", + "galleryExpiresOn": "La galería expirará el {{date}}", + "guestsWillReceiveWarning": "Los invitados recibirán un email de aviso 7 días antes de la expiración.", + "types": { + "wedding": "Boda", + "birthday": "Cumpleaños", + "corporate": "Corporativo", + "other": "Otro" + }, + "themes": { + "default": "Predeterminado", + "oceanBlue": "Azul océano", + "royalPurple": "Púrpura real", + "roseGold": "Oro rosado", + "sunsetAmber": "Ámbar atardecer" + }, + "eventDetails": "Detalles del evento", + "eventName": "Nombre del evento", + "eventType": "Tipo de evento", + "eventDate": "Fecha del evento", + "hostEmail": "Correo del cliente", + "hostName": "Nombre del cliente", + "hostNamePlaceholder": "Antonio Pérez", + "adminEmail": "Correo del administrador", + "adminNotificationEmail": "Correo de notificación al administrador", + "expirationDate": "Fecha de expiración", + "active": "Activo", + "archived": "Archivado", + "photoCount": "{{count}} fotos", + "totalSize": "Tamaño total", + "shareLink": "Enlace para compartir", + "copyLink": "Copiar enlace", + "linkCopied": "Enlace copiado!", + "viewGallery": "Ver galería", + "uploadPhotos": "Subir fotos", + "archiveEvent": "Archivar evento", + "archiveConfirm": "¿Seguro que quieres archivar este evento? Esta acción no se puede deshacer.", + "extendExpiration": "Extender {{days}} días", + "backToEvents": "Volver a eventos", + "loadingEventDetails": "Cargando detalles del evento...", + "saveChanges": "Guardar cambios", + "eventExpired": "Este evento ha expirado", + "eventExpiresIn": "Este evento expira en {{days}} días", + "guestsNoAccess": "Los invitados ya no pueden acceder a la galería. Considera archivar este evento.", + "warningEmailsSent": "Se han enviado emails de aviso al cliente.", + "overview": "Resumen", + "photos": "Fotos", + "categories": "Categorías", + "eventInformation": "Información del evento", + "sourceMode": "Modo de origen", + "sourceModeManaged": "Gestionado (subir a PicPeak)", + "sourceModeReference": "Referenciar carpeta externa", + "sourceModeHelp": "Usa el modo gestionado para subidas directas o apunta a una carpeta /external-media montada cuando uses almacenamiento local.", + "externalFolder": "Carpeta externa", + "externalFolderHint": "Estas carpetas se leen desde el montaje /external-media dentro de tu contenedor u host.", + "externalFolderRequired": "Por favor, selecciona una carpeta externa antes de guardar.", + "externalFolderEmpty": "Sin subcarpetas", + "clearSelection": "Limpiar", + "welcomeMessage": "Mensaje de bienvenida", + "noWelcomeMessage": "No hay mensaje de bienvenida", + "created": "Creado", + "expires": "Expira", + "shareWithGuests": "Comparte este enlace con los invitados. Necesitarán la contraseña para acceder a la galería.", + "shareWithGuestsPublic": "Comparte este enlace con los invitados. No se requiere contraseña para esta galería.", + "resetGalleryPassword": "Restablecer contraseña de la galería", + "resendCreationEmail": "Reenviar correo de creación", + "creationEmailResent": "El correo de creación se ha puesto en cola para enviar", + "failedToResendEmail": "Error al reenviar el correo de creación", + "photoStatistics": "Estadísticas de fotos", + "totalPhotos": "Total de fotos", + "managePhotos": "Gestionar fotos", + "actions": "Acciones", + "archivingInfo": "Al archivar se creará un archivo ZIP con todas las fotos y se quitará la galería del acceso público.", + "statistics": "Estadísticas", + "views": "Vistas", + "downloads": "Descargas", + "noStatistics": "No hay estadísticas disponibles aún", + "archiveStatus": "Estado del archivo", + "archivedOn": "Archivado el", + "downloadArchive": "Descargar archivo", + "loadingPhotos": "Cargando fotos...", + "photoCategories": "Categorías de fotos", + "organizeCategoriesInfo": "Organiza tus fotos en categorías. Las categorías ayudan a los invitados a navegar y encontrar tipos específicos de fotos.", + "categoriesTip": "Consejo: Las categorías son específicas para cada evento. Puedes crear categorías como \"Ceremonia\", \"Recepción\", \"Retratos\", etc.", + "contactInformation": "Información de contacto", + "hostEmailHelp": "El cliente recibirá notificaciones de creación y expiración de la galería", + "adminEmailHelp": "Recibirá notificaciones del sistema y confirmaciones de archivo", + "securityAccess": "Seguridad y acceso", + "galleryPassword": "Contraseña de la galería", + "requirePasswordToggle": "Requerir contraseña para esta galería", + "requirePasswordToggleHelp": "Desactiva esto si quieres compartir la galería sin contraseña. Cualquiera con el enlace podrá ver las fotos.", + "publicGalleryWarning": "Las galerías públicas son accesibles para cualquiera con el enlace. Considera habilitar marcas de agua en descargas y monitorizar la actividad.", + "passwordHelperText": "Puedes usar fechas como \"04.07.2025\" o cualquier texto con 6+ caracteres", + "confirmPassword": "Confirmar contraseña", + "showPasswords": "Mostrar contraseñas", + "newPasswordLabel": "Nueva contraseña de la galería", + "passwordReset": { + "title": "Restablecer contraseña de la galería", + "newTitle": "Nueva contraseña", + "description": "Establece una nueva contraseña para {{eventName}}, o deja ambos campos vacíos para generar una automáticamente.", + "newPasswordLabel": "Nueva contraseña", + "placeholder": "Dejar vacío para generar automáticamente", + "helperText": "Usa 6+ caracteres, o deja vacío para generar automáticamente", + "confirmLabel": "Confirmar contraseña", + "sendEmail": "Enviar notificación por correo", + "sendEmailHelp": "Notificar al anfitrión sobre el cambio de contraseña", + "warning": "Nota: La contraseña anterior dejará de funcionar. Asegúrate de compartir la nueva contraseña con el anfitrión.", + "submit": "Restablecer contraseña", + "successHeading": "¡Contraseña restablecida con éxito!", + "emailSentNote": "Se ha enviado una notificación por correo al anfitrión.", + "generatedLabel": "Contraseña generada automáticamente", + "saveSecurelyNote": "Importante: Guarda esta contraseña de forma segura. No puede recuperarse una vez cierres esta ventana.", + "done": "Listo", + "toastSuccess": "Contraseña restablecida con éxito", + "toastError": "Error al restablecer la contraseña", + "toastCopied": "Contraseña copiada al portapapeles", + "errorMinLength": "La contraseña debe tener al menos 6 caracteres", + "errorMismatch": "Las contraseñas no coinciden" + }, + "gallerySettings": "Ajustes de la galería", + "themeAndStyle": "Tema y estilo", + "colorTheme": "Tema de color", + "galleryExpiration": "Expiración de la galería", + "galleryExpiresIn": "La galería expira en", + "daysAfterEvent": "días después de la fecha del evento", + "expiresOn": "Expira el", + "galleryWillExpireOn": "La galería expirará el {{date}}", + "expirationWarning": "Los invitados recibirán un email de aviso 7 días antes de la expiración.", + "noExpiration": "Sin expiración", + "noExpirationHelp": "Esta galería permanecerá activa hasta que se archive manualmente.", + "photoCap": "Límite de fotos", + "photoCapHelp": "Número máximo de fotos permitidas. 0 = ilimitado", + "userUploads": "Ajustes de subidas de usuarios", + "allowUserUploads": "Permitir que los invitados suban fotos", + "allowUserUploadsHelp": "Permite a los invitados subir sus propias fotos a esta galería", + "allowUserUploadsDescription": "Permite a los invitados subir sus propias fotos a esta galería", + "uploadCategory": "Categoría de subida", + "selectCategory": "Selecciona una categoría para las subidas de usuarios", + "uploadCategoryHelp": "Todas las fotos subidas por usuarios se añadirán a esta categoría", + "userUploadWarning": "Las subidas de usuarios serán moderadas y pueden ser eliminadas por los administradores en cualquier momento.", + "allowDownloads": "Permitir descargas de fotos", + "allowDownloadsHelp": "Permitir a los invitados descargar fotos de esta galería", + "downloadPermissions": "Permisos de descarga", + "downloadsEnabled": "Descargas habilitadas", + "downloadsDisabled": "Descargas deshabilitadas", + "downloadProtection": "Protección de descargas", + "disableRightClick": "Bloquear menú de clic derecho", + "watermarkDownloads": "Agregar marca de agua a las descargas", + "enableDevtoolsProtection": "Detectar herramientas de desarrollo", + "useCanvasRendering": "Renderizado con canvas (protección avanzada)", + "protectionInfo": "Las funciones de protección ayudan a prevenir descargas no autorizadas, pero no pueden bloquear todos los métodos.", + "protectionLevelBasic": "Básico - Solo bloqueo de clic derecho", + "protectionLevelStandard": "Estándar - Atajos de teclado bloqueados", + "protectionLevelEnhanced": "Mejorado - Detección de impresión de pantalla", + "protectionLevelMaximum": "Máximo - Detección de DevTools y renderizado con canvas", + "heroLogoSettings": "Ajustes del logo hero", + "heroLogoVisible": "Mostrar logo en la sección hero", + "heroLogoSize": "Tamaño del logo", + "heroLogoSizeSmall": "Pequeño", + "heroLogoSizeMedium": "Mediano", + "heroLogoSizeLarge": "Grande", + "heroLogoSizeXLarge": "Extra grande", + "heroLogoPosition": "Posición del logo", + "heroLogoPositionTop": "Arriba (sobre el título)", + "heroLogoPositionCenter": "Centro (entre título y fechas)", + "heroLogoPositionBottom": "Abajo (debajo de las fechas)", + "heroLogoInfo": "Estos ajustes se aplican cuando la galería usa el diseño Hero. Puedes ocultar el logo o personalizar su tamaño y posición.", + "eventCustomLogo": "Logo personalizado del evento", + "eventCustomLogoDescription": "Sube un logo personalizado para este evento. Esto sustituye al logo global de marca solo para esta galería.", + "uploadEventLogo": "Subir logo", + "replaceLogo": "Reemplazar", + "removeLogo": "Quitar", + "eventLogoUploaded": "Logo del evento subido con éxito", + "eventLogoUploadFailed": "Error al subir el logo del evento", + "eventLogoRemoved": "Logo del evento eliminado con éxito", + "eventLogoRemoveFailed": "Error al eliminar el logo del evento", + "heroLogoVisibleLabel": "Logo visible", + "heroLogoSizeLabel": "Tamaño", + "heroLogoPositionLabel": "Posición", + "heroLogoHidden": "Logo oculto", + "heroPhoto": "Foto hero", + "heroPhotoHelp": "Selecciona una foto hero por defecto para la galería. Puedes sobrescribirla por categoría.", + "selectHeroPhoto": "Seleccionar foto hero", + "noHeroPhotoSelected": "No se ha seleccionado foto hero", + "heroPhotoSelected": "Foto hero seleccionada", + "heroImageAnchor": "Posición de recorte de la imagen hero", + "heroImageAnchorDescription": "Haz clic en la imagen para establecer el punto focal para el recorte.", + "heroImageAnchorTop": "Arriba", + "heroImageAnchorCenter": "Centro", + "heroImageAnchorBottom": "Abajo", + "heroPreview": "Vista previa hero", + "noPhotosAvailable": "No hay fotos disponibles", + "processingRequest": "Procesando tu solicitud...", + "eventTypeWedding": "Boda", + "eventTypeBirthday": "Cumpleaños", + "eventTypeCorporate": "Corporativo", + "eventTypeOther": "Otro", + "days30": "30 días", + "days60": "60 días", + "days90": "90 días", + "days365": "1 año", + "inactive": "Inactivo", + "expired": "Expirado", + "draft": "Borrador", + "publishAndNotify": "Publicar y notificar al cliente", + "publishConfirm": "Esto hará que la galería sea accesible y enviará el correo de notificación al cliente. ¿Continuar?", + "publishSuccess": "¡Galería publicada y cliente notificado!", + "draftBanner": "Esta galería está en modo borrador. Sube tus fotos y publica cuando estés listo.", + "daysLeft": "({{count}} día restante)", + "daysLeft_plural": "({{count}} días restantes)", + "subtitle": "Gestiona tus galerías de fotos y eventos", + "loadingEvents": "Cargando eventos...", + "failedToLoadEvents": "Error al cargar eventos", + "tryAgain": "Intentar de nuevo", + "bulkArchiveSuccess": "Se archivaron correctamente {{count}} eventos", + "bulkArchivePartial": "Archivados {{success}} eventos, {{failed}} fallaron", + "deleteSelected": "Eliminar seleccionadas", + "bulkDelete": { + "title": "¿Eliminar permanentemente {{count}} eventos?", + "warning": "Esto eliminará permanentemente los eventos seleccionados, todas sus fotos, archivos y registros de auditoría. Esta acción no se puede deshacer.", + "passwordLabel": "Vuelve a ingresar tu contraseña para confirmar", + "passwordPlaceholder": "Tu contraseña de administrador", + "passwordHelp": "Solicitamos tu contraseña como salvaguarda contra eliminaciones masivas accidentales.", + "submit": "Eliminar {{count}} eventos", + "processing": "Eliminando {{count}} eventos. Esto puede tardar unos minutos — por favor no cierres esta ventana.", + "incorrectPassword": "Contraseña incorrecta. No se eliminaron eventos.", + "successAll": "Se eliminaron permanentemente {{count}} eventos", + "successPartial": "Eliminados {{success}} eventos, {{failed}} fallaron", + "errorGeneric": "Error al eliminar eventos" + }, + "searchEventsPlaceholder": "Buscar eventos...", + "all": "Todos", + "expiring": "A punto de expirar", + "eventsSelected": "{{count}} evento seleccionado", + "eventsSelected_plural": "{{count}} eventos seleccionados", + "clear": "Limpiar", + "archiveSelected": "Archivar seleccionados", + "publicAccess": "Acceso público", + "passwordProtected": "Protegido por contraseña", + "newPasswordRequired": "Por favor establece una contraseña antes de habilitar la protección.", + "event": "Evento", + "type": "Tipo", + "date": "Fecha", + "status": "Estado", + "noEventsFound": "No se encontraron eventos", + "stats": { + "totalEvents": "Eventos totales", + "activeEvents": "Eventos activos", + "totalPhotos": "Fotos totales", + "expiringEvents": "Próximos a expirar" + }, + "viewDetails": "Ver detalles", + "archiveEventAction": "Archivar evento", + "downloadArchiveAction": "Descargar archivo", + "deleteEvent": "Eliminar evento", + "deleteEventConfirm": "¿Seguro que quieres eliminar este evento?", + "downloadArchiveSoon": "Descarga de archivo próximamente", + "eventExpiredMessage": "Este evento ha expirado", + "guestsCannotAccessGallery": "Los invitados ya no pueden acceder a la galería. Considera archivar este evento.", + "warningEmailsHaveBeenSent": "Se han enviado correos de aviso al cliente.", + "extendSevenDays": "Extender 7 días", + "welcomeMessageLabel": "Mensaje de bienvenida", + "noWelcomeMessageSet": "No hay mensaje de bienvenida", + "createdOn": "Creado", + "copy": "Copiar", + "copied": "¡Copiado!", + "organizingPhotosInfo": "Organiza tus fotos en categorías. Las categorías ayudan a los invitados a navegar y encontrar tipos específicos de fotos.", + "archiveStatusTitle": "Estado del archivo", + "downloadingArchive": "Descargando archivo {{name}}...", + "downloadStarted": "Descarga iniciada", + "failedToDownloadArchive": "Error al descargar el archivo", + "statisticsNotAvailable": "No hay estadísticas disponibles aún", + "photoFilters": "Filtros de fotos", + "noStatisticsAvailableYet": "No hay estadísticas disponibles aún", + "addPlus": "Agregar+", + "galleryTheme": "Tema de la galería", + "customizeTheme": "Personalizar tema", + "noThemeSet": "No hay tema configurado", + "customizingTheme": "Personalizando el tema de la galería", + "customizingThemeFor": "Personalizando el tema para {{event}}", + "customCssTemplate": "Plantilla CSS personalizada", + "customCssTemplateDesc": "Aplica una plantilla CSS personalizada para estilizar la galería con efectos visuales únicos.", + "noTemplate": "Sin plantilla", + "useThemeOnly": "Usar solo el preset de tema", + "customTemplate": "Plantilla personalizada", + "rename": { + "button": "Renombrar", + "title": "Renombrar evento", + "validating": "Validando nuevo nombre...", + "renamingFiles": "Renombrando archivos...", + "complete": "¡Completado!", + "failed": "Error al renombrar", + "filesRenamed": "{{count}} archivos actualizados", + "confirm": "Renombrar evento" + }, + "activeFilter": "Activo", + "archivedFilter": "Archivado", + "sortByName": "Por nombre", + "sortByDate": "Por fecha", + "sortByExpiration": "Por expiración", + "photosCount": "Fotos", + "moreActions": "Más acciones", + "copyLinkTooltip": "Copiar enlace", + "viewGalleryTooltip": "Ver galería", + "uploadPhotosTooltip": "Subir fotos", + "editTooltip": "Editar", + "archiveTooltip": "Archivar", + "noEvents": "No se encontraron eventos", + "noEventsDescription": "Crea tu primer evento para comenzar.", + "bulkArchive": "Archivar", + "confirmBulkArchive": "¿Seguro que quieres archivar {{count}} evento(s)?", + "confirmBulkArchiveDescription": "Esta acción no se puede deshacer. Los eventos archivados dejarán de ser accesibles públicamente." + }, + "settings": { + "title": "Ajustes del sistema", + "subtitle": "Configura ajustes y preferencias a nivel del sistema", + "loadingSettings": "Cargando ajustes...", + "general": { + "title": "General", + "siteConfiguration": "Configuración del sitio", + "siteUrl": "URL del sitio", + "siteUrlHelp": "Se utiliza para generar enlaces de galería en correos", + "defaultExpiration": "Expiración por defecto (días)", + "defaultExpirationHelp": "Cuánto tiempo permanecen activas las galerías por defecto", + "maxFileSize": "Tamaño máximo de archivo (MB)", + "maxFileSizeHelp": "Tamaño máximo por foto subida", + "maxFilesPerUpload": "Máx. archivos por subida", + "maxFilesPerUploadHelp": "Número máximo de fotos permitido en un solo lote de subida (1-{{max}}).", + "allowedFileTypes": "Tipos de archivo permitidos", + "allowedFileTypesHelp": "Lista separada por comas de extensiones permitidas", + "featureToggles": "Activar funciones", + "enableAnalytics": "Habilitar seguimiento de analíticas", + "enableRegistration": "Permitir auto-registro de administradores", + "enableShortGalleryUrls": "Usar URLs cortas para galerías", + "enableShortGalleryUrlsHelp": "Elimina el slug del evento en los nuevos enlaces, manteniendo los enlaces existentes.", + "maintenanceMode": "Habilitar modo mantenimiento", + "language": "Idioma", + "defaultLanguage": "Idioma por defecto", + "defaultLanguageHelp": "Idioma que se muestra a los invitados antes de iniciar sesión", + "defaultWelcomeMessage": "Mensaje de bienvenida por defecto", + "welcomeMessage": "Mensaje de bienvenida", + "welcomeMessagePlaceholder": "Introduce un mensaje de bienvenida por defecto que se incluirá en los correos de creación de galería", + "welcomeMessageHelp": "Este mensaje se incluirá en todos los correos de creación de galería a menos que se sobrescriba al crear un evento", + "saveSettings": "Guardar ajustes generales", + "saveGeneralSettings": "Guardar ajustes generales", + "dateTimeFormat": "Formato de fecha y hora", + "dateFormat": "Formato de fecha", + "dateFormatHelp": "Cómo se muestran las fechas en correos y en la aplicación", + "accountSection": "Cuenta de administrador", + "accountUsername": "Usuario administrador", + "accountUsernameHelp": "Se muestra en la interfaz de administración y se usa en los registros de actividad.", + "accountUsernameRequired": "El nombre de usuario es obligatorio", + "accountUsernameLength": "El nombre de usuario debe tener al menos 3 caracteres", + "accountEmail": "Correo del administrador", + "accountEmailHelp": "Se usa para iniciar sesión y recibir notificaciones de seguridad.", + "accountEmailRequired": "Se requiere una dirección de correo", + "accountEmailInvalid": "Introduce una dirección de correo válida", + "accountSaveButton": "Guardar detalles de la cuenta", + "accountSaveSuccess": "Detalles de la cuenta actualizados" + }, + "publicSite": { + "tabLabel": "Sitio público", + "badge": "Landing pública", + "title": "Página de aterrizaje pública", + "subtitle": "Publica una página de bienvenida personalizada para los invitados cuando visiten tu dominio.", + "loading": "Cargando ajustes del sitio público...", + "enabled": "Habilitado", + "disabled": "Deshabilitado", + "htmlLabel": "HTML de la página de aterrizaje", + "htmlPlaceholder": "Proporciona el marcado HTML para la sección hero, secciones y llamadas a la acción.", + "htmlHelp": "El HTML bruto se sanea al guardar. Las etiquetas no seguras, scripts inline y atributos no permitidos se eliminan automáticamente. Tokens como {{company_name}}, {{company_tagline}} y {{support_email}} serán sustituidos por valores de Marca.", + "cssLabel": "CSS personalizado", + "cssPlaceholder": "Overrides CSS opcionales para ajustar diseño, colores o tipografía.", + "cssHelp": "Se eliminan imports y URLs JavaScript. Usa selectores CSS estándar para sobrescribir el estilo por defecto.", + "saveCta": "Guardar sitio público", + "saving": "Guardando...", + "saveSuccess": "Ajustes del sitio público guardados.", + "saveError": "Error al guardar los ajustes del sitio público.", + "resetCta": "Restablecer a predeterminado", + "resetting": "Restableciendo...", + "resetSuccess": "La plantilla del sitio público se restableció a los valores por defecto.", + "resetError": "Error al restablecer la plantilla del sitio público.", + "previewTitle": "Vista previa en vivo", + "previewSandboxed": "Vista previa en sandbox", + "previewDisabled": "Habilita el sitio público para ver la vista previa en vivo.", + "sanitizationNotice": "Se eliminan scripts, frames embebidos y atributos inseguros antes de publicar. Solo se permite HTML, enlaces e imágenes seguros.", + "htmlRequired": "Proporciona contenido HTML antes de habilitar el sitio público." + }, + "storage": { + "title": "Almacenamiento", + "overview": "Resumen de almacenamiento", + "totalUsed": "Total usado", + "archiveStorage": "Almacenamiento de archivos", + "storageLimit": "Límite de almacenamiento", + "storageLimitHelper": "Establece un límite para el uso de almacenamiento de fotos. Este límite es solo orientativo y se resaltará al alcanzarse.", + "softLimitInputLabel": "Límite (GB)", + "softLimitHelper": "Introduce el máximo preferido de uso de almacenamiento en gigabytes.", + "recommendedSoftLimit": "Límite sugerido", + "diskCapacity": "Capacidad del disco", + "diskCapacityReported": "Capacidad del disco (reportada)", + "diskAvailable": "Disponible", + "diskAvailableReported": "Disponible (reportado)", + "diskFree": "Libre", + "diskFreeReported": "Libre (reportado)", + "diskMetricsUnavailable": "Las métricas de disco no están disponibles en Docker Desktop o entornos virtualizados.", + "applyRecommended": "Usar recomendado", + "applyAvailable": "Igualar disponible", + "invalidSoftLimit": "Introduce un número válido para el límite.", + "saveSoftLimit": "Guardar límite", + "limitNotEnforced": "Este límite es solo orientativo y no se aplica automáticamente.", + "overrideTitle": "Anulación manual de capacidad", + "diskOverrideEnvNote": "La capacidad de disco se controla mediante variables de entorno y no puede editarse aquí.", + "diskOverrideSettingsHelp": "Establece valores de capacidad personalizados cuando Docker reporte cifras poco realistas.", + "overrideCapacityLabel": "Capacidad total (GB)", + "overrideCapacityHelper": "Deja en blanco para usar la lectura automática.", + "overrideAvailableLabel": "Espacio disponible (GB)", + "overrideAvailableHelper": "Opcional. Deja en blanco para calcular a partir de la capacidad total menos el almacenamiento usado.", + "saveOverride": "Guardar anulación de capacidad", + "capacityRequiredForAvailable": "Introduce una capacidad total antes de establecer el espacio disponible.", + "availableExceedsCapacity": "El espacio disponible no puede exceder la capacidad total.", + "storageUsage": "Uso de almacenamiento", + "storageByEvent": "Almacenamiento por evento", + "storageManagement": "Gestión de almacenamiento", + "storageManagementHelp": "Considera archivar o eliminar eventos antiguos para liberar espacio. Los eventos archivados se comprimen y usan menos almacenamiento que las galerías activas.", + "noEventsUsingStorage": "Ningún evento usa almacenamiento", + "unlimited": "Ilimitado" + }, + "security": { + "title": "Seguridad", + "passwordSettings": "Ajustes de contraseña", + "minPasswordLength": "Longitud mínima de contraseña", + "minPasswordLengthHelp": "Número mínimo de caracteres para contraseñas de galería", + "passwordComplexity": "Complejidad de la contraseña", + "passwordComplexityHelp": "Nivel de seguridad requerido para contraseñas de galería", + "complexitySimple": "Simple (6+ caracteres, cualquier texto)", + "complexityModerate": "Moderada (8+ caracteres, mayúsculas/números)", + "complexityStrong": "Fuerte (12+ caracteres, mayúsculas/minúsculas/números)", + "complexityVeryStrong": "Muy fuerte (12+ caracteres, todos los tipos de caracteres)", + "sessionAuth": "Sesión y autenticación", + "sessionTimeout": "Tiempo de expiración de sesión (minutos)", + "sessionTimeoutHelp": "Tiempo de expiración de sesión de administrador en minutos", + "maxLoginAttempts": "Máx. intentos de inicio", + "maxLoginAttemptsHelp": "Máximo de intentos fallidos por IP antes del bloqueo", + "attemptWindowMinutes": "Ventana de intentos (minutos)", + "attemptWindowMinutesHelp": "Tiempo atrás para contar intentos fallidos", + "lockoutDurationMinutes": "Duración del bloqueo (minutos)", + "lockoutDurationMinutesHelp": "Tiempo que la galería o cuenta permanece bloqueada tras demasiados fallos", + "enable2FA": "Habilitar autenticación de dos factores para administradores", + "recaptchaSettings": "Ajustes reCAPTCHA", + "enableRecaptcha": "Habilitar reCAPTCHA en formularios de inicio", + "siteKey": "Clave pública (site key)", + "siteKeyHelp": "Tu clave pública reCAPTCHA v2 (pública)", + "secretKey": "Clave secreta", + "secretKeyHelp": "Tu clave secreta reCAPTCHA v2 (mantener privada)", + "recaptchaHelp": "Obtén tus claves reCAPTCHA desde", + "saveSettings": "Guardar ajustes de seguridad", + "saveSecuritySettings": "Guardar ajustes de seguridad" + }, + "categories": { + "title": "Categorías", + "about": "Acerca de las categorías de fotos", + "aboutText": "Las categorías globales están disponibles para todos los eventos. También puedes crear categorías específicas del evento al editar eventos individuales. Las categorías ayudan a organizar las fotos y permiten a los invitados filtrar por tipo en la galería." + }, + "systemStatus": { + "title": "Estado del sistema", + "storageOverview": "Resumen de almacenamiento", + "systemInfo": "Información del sistema", + "databaseInfo": "Información de la base de datos", + "platform": "Plataforma", + "nodeVersion": "Versión de Node", + "uptime": "Tiempo en ejecución", + "cpuCores": "Núcleos CPU", + "memoryUsage": "Uso de memoria", + "memoryUsed": "Memoria usada", + "photos": "Fotos", + "admins": "Administradores", + "dbSize": "Tamaño de la base de datos", + "services": "Servicios en segundo plano", + "fileWatcher": "Vigilante de archivos", + "fileWatcherDesc": "Monitorea nuevas fotos", + "expirationChecker": "Comprobador de expiración", + "expirationCheckerDesc": "Archiva galerías expiradas", + "emailProcessor": "Procesador de correo", + "emailProcessorDesc": "Envía correos en cola", + "emailQueue": "Estado de la cola de correos", + "pending": "Pendiente", + "sent": "Enviado", + "failed": "Fallado", + "lastUpdate": "Última actualización" + }, + "photoDimensions": { + "title": "Dimensiones de foto", + "totalPhotos": "Fotos totales", + "withDimensions": "Con dimensiones", + "missingDimensions": "Faltan dimensiones", + "repairButton": "Reparar dimensiones", + "repairing": "Reparando...", + "alreadyRunning": "La reparación ya está en curso", + "started": "Reparación iniciada para {{count}} fotos", + "noneToRepair": "Todas las fotos ya tienen dimensiones", + "resultSuccess": "Última reparación: {{success}} actualizadas, {{failed}} fallidas", + "description": "Rellenar ancho/alto faltantes para fotos subidas antes de que se agregara seguimiento de dimensiones. Requerido para diseños Masonry y Mosaic." + }, + "updateNotifications": { + "title": "Notificaciones de actualización", + "description": "Recibe notificaciones por correo cuando nuevas versiones de PicPeak están disponibles.", + "enableEmails": "Habilitar notificaciones por correo", + "enableEmailsDesc": "Enviar correo a administradores cuando hay una nueva versión disponible", + "recipients": "Destinatarios de correo", + "recipientsPlaceholder": "admin@example.com, otro@example.com", + "recipientsHelper": "Direcciones de correo separadas por comas. Dejar vacío para enviar a todos los administradores.", + "lastNotified": "Última notificación enviada para versión: {{version}}", + "checkNow": "Comprobar y notificar", + "sendTest": "Enviar correo de prueba", + "saved": "Ajustes guardados", + "saveError": "Error al guardar ajustes", + "emailSent": "Correo de notificación enviado a {{count}} destinatarios", + "emailFailed": "Error al enviar notificación", + "checkSuccess": "Notificación enviada para nueva versión", + "checkNoAction": "Sin necesidad de notificación: {{reason}}", + "checkError": "Error al comprobar actualizaciones" + }, + "events": { + "title": "Creación de eventos", + "requiredFields": "Campos obligatorios", + "requiredFieldsDescription": "Configura qué campos de contacto son obligatorios al crear nuevos eventos.", + "requireCustomerName": "Requerir nombre del cliente", + "requireCustomerNameHelp": "El nombre del cliente debe proporcionarse para nuevos eventos", + "requireCustomerEmail": "Requerir correo del cliente", + "requireCustomerEmailHelp": "El correo del cliente debe proporcionarse para nuevos eventos", + "customerEmailWarning": "Requerido para enviar invitaciones de galería", + "requireAdminEmail": "Requerir correo de administrador", + "requireAdminEmailHelp": "El correo de administrador debe proporcionarse para nuevos eventos", + "adminEmailWarning": "Requerido para recibir notificaciones de eventos", + "requireEventDate": "Requerir fecha del evento", + "requireEventDateHelp": "La fecha del evento debe proporcionarse al crear eventos", + "eventDateWarning": "Las URLs de galería usarán identificadores aleatorios en lugar de fechas", + "requireExpiration": "Requerir fecha de expiración", + "requireExpirationHelp": "Las galerías deben tener una fecha de expiración", + "expirationWarning": "Las galerías sin expiración permanecerán activas hasta ser archivadas manualmente", + "saveSettings": "Guardar ajustes de eventos", + "noteTitle": "Nota", + "noteText": "Estos ajustes solo afectan a la creación de nuevos eventos. Los eventos existentes no se ven afectados. El comportamiento por defecto requiere todos los campos." + }, + "imageSecurity": { + "title": "Proteger Imagen", + "saveSuccess": "Ajustes de seguridad de imagen guardados", + "saveError": "Error al guardar ajustes", + "loadError": "Error al cargar ajustes de seguridad de imagen", + "defaultProtection": "Ajustes de protección por defecto", + "defaultProtectionHelp": "Estos ajustes se aplican a todos los nuevos eventos. Los eventos individuales pueden anular estos valores por defecto.", + "protectionLevel": "Nivel de protección por defecto", + "imageQuality": "Calidad de imagen por defecto", + "fragmentationLevel": "Nivel de fragmentación", + "enableDevtools": "Habilitar detección de DevTools por defecto", + "enableCanvas": "Habilitar renderizado con canvas por defecto (protección avanzada)", + "rateLimiting": "Limitación de velocidad", + "rateLimitingHelp": "Limita cuántas imágenes pueden solicitarse para prevenir scraping.", + "requestsPerMinute": "Solicitudes por minuto", + "requestsPer5Minutes": "Solicitudes por 5 min", + "requestsPerHour": "Solicitudes por hora", + "securityMonitoring": "Monitoreo de seguridad", + "suspiciousThreshold": "Umbral de actividad sospechosa", + "autoBlockThreshold": "Umbral de bloqueo automático", + "enableMonitoring": "Habilitar monitoreo de seguridad", + "blockSuspiciousIps": "Bloquear automáticamente IPs sospechosas", + "logEvents": "Registrar eventos de seguridad en la base de datos", + "infoTitle": "Acerca de la protección de imágenes", + "infoText": "Estas funciones de protección ayudan a prevenir descargas y copias ocasionales, pero no pueden bloquear todos los métodos. Los usuarios determinados aún pueden encontrar formas de capturar imágenes. Considera usar marcas de agua y acuerdos legales para protección integral." + }, + "moderation": { + "title": "Moderación", + "wordFilters": "Filtros de palabras", + "description": "Gestiona palabras que deben filtrarse o bloquearse en comentarios", + "addFilter": "Agregar nuevo filtro", + "enterWord": "Introduce palabra a filtrar", + "searchFilters": "Buscar filtros...", + "filterAdded": "Filtro de palabra añadido con éxito", + "filterExists": "Este filtro de palabra ya existe", + "addError": "Error al añadir filtro de palabra", + "filterUpdated": "Filtro de palabra actualizado con éxito", + "updateError": "Error al actualizar filtro de palabra", + "filterDeleted": "Filtro de palabra eliminado con éxito", + "deleteError": "Error al eliminar filtro de palabra", + "wordRequired": "Por favor introduce una palabra a filtrar", + "confirmDelete": "¿Seguro que quieres eliminar este filtro de palabra?", + "loading": "Cargando filtros de palabras...", + "noMatchingFilters": "No se encontraron filtros coincidentes", + "noFilters": "Aún no hay filtros de palabras configurados", + "severityLow": "Bajo", + "severityModerate": "Moderado", + "severityHigh": "Alto", + "severityBlock": "Bloquear", + "severityLevels": "Niveles de gravedad", + "lowDescription": "Palabra marcada para revisión pero no bloqueada automáticamente", + "moderateDescription": "El comentario requiere aprobación manual antes de ser visible", + "highDescription": "El comentario se oculta automáticamente y requiere revisión del administrador", + "blockDescription": "El comentario se rechaza inmediatamente y no puede enviarse" + }, + "styling": { + "title": "CSS personalizado" + }, + "thumbnails": { + "title": "Miniaturas", + "dimensionsTitle": "Dimensiones y calidad de miniaturas", + "dimensionsHelp": "Configura el tamaño y la calidad de las miniaturas generadas automáticamente. Valores más altos producen vistas previas mejores pero aumentan el almacenamiento y los tiempos de carga.", + "width": "Ancho (px)", + "widthHelp": "50-1000 píxeles", + "height": "Alto (px)", + "heightHelp": "50-1000 píxeles", + "quality": "Calidad", + "qualityHelp": "1-100; más alto = mejor calidad pero archivos más grandes", + "format": "Formato", + "fit": "Modo de ajuste", + "fitHelp": "Cómo se redimensionan las imágenes para ajustarse a las dimensiones de la miniatura. \"Cover\" recorta para llenar, \"Contain\" ajusta dentro de los límites.", + "fit_cover": "Portada (recortar para llenar)", + "fit_contain": "Contener (ajustar dentro)", + "fit_fill": "Llenar (estirar)", + "fit_inside": "Interior (reducir para ajustar)", + "fit_outside": "Exterior (expandir para cubrir)", + "regenerateTitle": "Regenerar miniaturas", + "regenerateHelp": "Después de cambiar los ajustes de miniaturas, regenera todas las miniaturas existentes para aplicar la nueva configuración. Esto se ejecuta en segundo plano y puede tomar un tiempo para galerías grandes.", + "regenerateButton": "Regenerar todas las miniaturas", + "regenerateStarted": "Regeneración de miniaturas iniciada", + "regenerateError": "Error al iniciar la regeneración de miniaturas", + "saveSuccess": "Ajustes de miniaturas guardados", + "saveError": "Error al guardar los ajustes de miniaturas", + "loadError": "Error al cargar los ajustes de miniaturas", + "infoTitle": "Acerca de las miniaturas", + "infoText": "Las miniaturas son imágenes de vista previa más pequeñas generadas a partir de tus originales. Aumentar el tamaño o la calidad mejora cómo se ven las fotos en la cuadrícula pero usa más almacenamiento y ancho de banda. Después de cambiar los ajustes, usa \"Regenerar todas las miniaturas\" para actualizar las fotos existentes." + }, + "seo": { + "title": "SEO y robots", + "indexingTitle": "Indexación en motores de búsqueda", + "allowIndexing": "Permitir indexación en motores de búsqueda", + "allowIndexingHelp": "Cuando está deshabilitado, todos los rastreadores se bloquean a través de robots.txt. Se recomienda deshabilitado para plataformas privadas de fotos.", + "sitemapUrl": "URL del mapa del sitio", + "sitemapUrlHelp": "Opcional. Se añade a robots.txt si se proporciona.", + "aiBlockingTitle": "Bloqueo de IA y bots", + "blockAiCrawlers": "Bloquear rastreadores de IA/LLM", + "blockAiCrawlersHelp": "Prevenir que bots de entrenamiento de IA accedan a tu contenido.", + "blockedAgents": "Agentes de IA bloqueados", + "addAgentPlaceholder": "Introduce nombre del agente...", + "blockSocialBots": "Bloquear bots de vista previa de redes sociales", + "blockSocialBotsHelp": "Prevenir vistas previas en Twitter, Facebook, LinkedIn, etc.", + "metaTagsTitle": "Etiquetas meta y reglas personalizadas", + "metaNoindex": "Agregar etiqueta meta noindex", + "metaNoindexHelp": "Indica a los motores de búsqueda que no indexen páginas (nivel HTML, complementa robots.txt).", + "metaNofollow": "Agregar etiqueta meta nofollow", + "metaNofollowHelp": "Indica a los motores de búsqueda que no sigan enlaces en páginas.", + "metaNoai": "Agregar etiqueta meta noai/noimageai", + "metaNoaiHelp": "Indica que el contenido no debe usarse para entrenamiento de IA.", + "customRules": "Reglas personalizadas de robots.txt", + "ruleAgentPlaceholder": "Agente de usuario", + "rulePathPlaceholder": "Ruta a prohibir", + "showPreview": "Mostrar vista previa de robots.txt", + "hidePreview": "Ocultar vista previa de robots.txt", + "saveSettings": "Guardar ajustes de SEO" + }, + "analytics": { + "title": "Analíticas", + "umamiIntegration": "Integración de Umami Analytics", + "enableUmami": "Habilitar Umami Analytics", + "umamiUrl": "URL de Umami", + "umamiUrlHelp": "La URL de tu instancia de Umami (p. ej., https://analytics.tudominio.com)", + "websiteId": "ID del sitio web", + "websiteIdHelp": "Tu ID de sitio web de Umami (encontrado en el panel de Umami)", + "shareUrl": "URL de compartición (Opcional)", + "shareUrlHelp": "URL de compartición pública para incrustar el panel completo (crear en Umami)", + "umamiInfo": "Acerca de Umami Analytics", + "umamiInfoText": "Umami es una plataforma de análisis de código abierto y enfocada en privacidad. Realiza seguimiento de vistas de página, visitantes únicos y eventos personalizados sin usar cookies.", + "learnMore": "Obtener más información sobre Umami", + "saveAnalyticsSettings": "Guardar ajustes de analíticas", + "backendAnalytics": "Analíticas de backend", + "backendAnalyticsText": "El sistema también realiza un seguimiento de analíticas básicas del lado del servidor para supervisión de seguridad y rendimiento.", + "tracked": "Métricas rastreadas", + "galleryViews": "Vistas de página de galería", + "photoDownloads": "Descargas individuales y en masa", + "uniqueVisitors": "Visitantes únicos por IP", + "deviceTypes": "Tipos de dispositivo desde agentes de usuario", + "privacy": "Privacidad", + "privacyText": "Las direcciones IP están hasheadas para privacidad. No se almacena datos personales. Los datos de analíticas se conservan durante 90 días." + } + }, + "analytics": { + "title": "Panel de analíticas", + "titleSimple": "Analíticas", + "subtitle": "Seguimiento del rendimiento de las galerías y la interacción de los visitantes", + "detailedSubtitle": "Analíticas detalladas impulsadas por Umami", + "loadingAnalytics": "Cargando analíticas...", + "showSummaryView": "Mostrar resumen", + "fullDashboard": "Panel completo", + "refresh": "Actualizar", + "last7Days": "Últimos 7 días", + "last30Days": "Últimos 30 días", + "last90Days": "Últimos 90 días", + "pageViews": "Páginas vistas", + "uniqueVisitors": "Visitantes únicos", + "totalDownloads": "Descargas totales", + "topGallery": "Galería destacada", + "topPages": "Páginas principales", + "views": "vistas", + "visitors": "visitantes únicos", + "topDownloadsByGallery": "Descargas por galería", + "deviceBreakdown": "Distribución por dispositivo", + "desktop": "Escritorio", + "mobile": "Móvil", + "tablet": "Tableta", + "storageUsage": "Uso de almacenamiento", + "used": "Usado", + "of": "de", + "totalPhotos": "Fotos totales", + "activeEvents": "Eventos activos", + "notConfigured": "Umami no está configurado", + "configureInstructions": "Para ver datos reales, configura Umami en tus variables de entorno y en los ajustes.", + "noData": "No hay datos disponibles", + "percentChange": "{{percent}}% respecto al periodo anterior" + }, + "branding": { + "title": "Marca y temas", + "titleFull": "Marca y personalización", + "subtitle": "Personaliza la apariencia de tus galerías", + "loadingBranding": "Cargando ajustes de firma...", + "themeAndStyle": "Tema y estilo", + "companyInfo": "Información de la empresa", + "companyName": "Nombre de la empresa", + "companyNameHelp": "Se muestra en encabezados de galería y correos", + "companyTagline": "Lema de la empresa", + "companyTaglineHelp": "Una breve descripción de tu negocio", + "supportEmail": "Correo de soporte", + "supportEmailHelp": "Correo de contacto para soporte a invitados", + "footerText": "Texto del pie", + "footerTextHelp": "Se muestra al final de las galerías", + "logo": "Logo", + "currentLogo": "Logo actual", + "uploadLogo": "Subir logo", + "removeLogo": "Eliminar logo", + "logoHelp": "Tamaño recomendado: 200x60px, PNG o JPEG", + "favicon": "Favicon", + "currentFavicon": "Favicon actual", + "uploadFavicon": "Subir favicon", + "removeFavicon": "Eliminar favicon", + "faviconHelp": "Formato PNG o ICO, tamaño recomendado: 32x32px", + "watermark": "Marca de agua", + "watermarkSettings": "Ajustes de marca de agua", + "enableWatermarks": "Habilitar marcas de agua", + "watermarkHelp": "Añade el nombre de tu empresa como marca de agua en las descargas", + "watermarkLogo": "Logo de marca de agua", + "currentWatermark": "Marca de agua actual", + "uploadWatermarkLogo": "Subir logo de marca de agua", + "watermarkPosition": "Posición de la marca de agua", + "topLeft": "Arriba izquierda", + "topRight": "Arriba derecha", + "center": "Centro", + "bottomLeft": "Abajo izquierda", + "bottomRight": "Abajo derecha", + "watermarkOpacity": "Opacidad de la marca de agua", + "watermarkSize": "Tamaño de la marca de agua", + "theme": "Tema", + "galleryTheme": "Tema de la galería", + "themeCustomization": "Personalización del tema", + "selectPreset": "Seleccionar un tema predeterminado", + "colors": "Colores", + "primaryColor": "Color primario", + "secondaryColor": "Color secundario", + "accentColor": "Color de acento", + "backgroundColor": "Color de fondo", + "textColor": "Color del texto", + "colorMode": "Modo de color", + "colorModeLight": "Claro", + "colorModeDark": "Oscuro", + "colorModeAuto": "Auto", + "colorModeHelp": "Auto sigue la preferencia del sistema del visitante.", + "customCSS": "CSS personalizado", + "preview": "Vista previa", + "previewInNewTab": "Vista previa en nueva pestaña", + "reset": "Restablecer", + "saveChanges": "Guardar cambios", + "applyLivePreview": "Aplicar cambios inmediatamente (Vista previa en vivo)", + "eventSpecificThemes": "Temas específicos por evento", + "eventThemesInfo": "Puedes sobrescribir estos ajustes globales para eventos individuales al crearlos o editarlos.", + "themePresets": "Preajustes de tema", + "galleryLayout": "Diseño de galería", + "layoutDescriptions": { + "grid": "Diseño en cuadrícula clásico con tamaños uniformes", + "masonry": "Estilo Pinterest con alturas variables", + "carousel": "Presentación a pantalla completa con navegación", + "timeline": "Fotos organizadas por fecha", + "hero": "Imagen destacada con cuadrícula abajo", + "mosaic": "Diseño artístico con tamaños mixtos", + "justified": "Filas justificadas preservando proporciones", + "gallery-premium": "Tema claro elegante con hero y masonry (Beta)", + "gallery-story": "Tema oscuro cinematográfico con secciones (Beta)" + }, + "layoutSettings": "Ajustes de diseño", + "photoSpacing": "Espaciado entre fotos", + "spacing": { + "tight": "Ajustado", + "normal": "Normal", + "relaxed": "Relajado" + }, + "photoAnimation": "Animación de fotos", + "animation": { + "none": "Ninguna", + "fade": "Desvanecer", + "scale": "Escalar", + "slide": "Deslizar" + }, + "columns": "Columnas", + "mobile": "Móvil", + "tablet": "Tableta", + "desktop": "Escritorio", + "enableAutoplay": "Habilitar reproducción automática", + "autoplayInterval": "Intervalo de reproducción (segundos)", + "groupPhotosBy": "Agrupar fotos por", + "grouping": { + "day": "Día", + "week": "Semana", + "month": "Mes" + }, + "masonryMode": "Modo de layout", + "masonryModeOptions": { + "columns": "Columnas (estilo Pinterest)", + "rows": "Filas (justificado personalizado)", + "flickr": "Flickr (justificado probado)", + "justified": "Google Photos (algoritmo Knuth-Plass)" + }, + "masonryModeHint": { + "columns": "Columnas verticales estilo Pinterest con alturas variables", + "rows": "Diseño justificado basado en filas", + "flickr": "Algoritmo justificado de Flickr", + "justified": "Filas estilo Google Photos usando Knuth-Plass para cortes óptimos" + }, + "targetRowHeight": "Altura objetivo de fila", + "targetRowHeightHint": "Altura en píxeles (150-400). Las fotos se escalarán para ajustar las filas.", + "lastRowBehavior": "Alineación de la última fila", + "lastRowOptions": { + "left": "Alineada a la izquierda", + "center": "Centrada", + "justify": "Justificada (estirar)" + }, + "thumbnailScale": "Escala de miniaturas", + "thumbnailScaleOptions": { + "xs": "XS — Más fotos", + "sm": "SM — Más fotos", + "md": "MD — Predeterminado", + "lg": "LG — Fotos más grandes", + "xl": "XL — Fotos más grandes" + }, + "thumbnailScaleHint": "Ajusta el número de columnas relativo a la cuadrícula base", + "showHeroSection": "Mostrar sección Hero", + "showHeroSectionHint": "Mostrar imagen destacada encima de la galería justificada", + "heroHeight": "Altura de la sección Hero", + "heroHeightOptions": { + "small": "Pequeña (40-50%)", + "medium": "Mediana (50-70%)", + "large": "Grande (60-80%)" + }, + "heroOverlayOpacity": "Opacidad de superposición Hero", + "heroOverlayHint": "Oscurece la imagen hero para mejorar la legibilidad del texto", + "typographyAndStyle": "Tipografía y estilo", + "bodyFont": "Fuente del cuerpo", + "headingFont": "Fuente de encabezados", + "sameAsBody": "Igual que el cuerpo", + "fontSize": "Tamaño de fuente", + "fontSizes": { + "small": "Pequeño", + "normal": "Normal", + "large": "Grande" + }, + "borderRadius": "Radio de borde", + "borderRadiusOptions": { + "none": "Ninguno", + "small": "Pequeño", + "medium": "Medio", + "large": "Grande" + }, + "shadowStyle": "Estilo de sombra", + "shadowOptions": { + "none": "Ninguna", + "subtle": "Sutil", + "normal": "Normal", + "dramatic": "Dramática" + }, + "backgroundPattern": "Fondo", + "backgroundOptions": { + "none": "Ninguno", + "dots": "Puntos", + "grid": "Grid", + "waves": "Ondas" + }, + "customCSSHelp": "Avanzado: Añade CSS personalizado para ajustar aún más la apariencia", + "cssInstructions": { + "title": "Cómo usar CSS personalizado", + "variables": "Variables CSS del tema", + "variablesDesc": "Usa estas variables CSS para coincidir con tus presets de tema:", + "layouts": "Layouts personalizados de galería", + "layoutsDesc": "Apunta a elementos de la galería con estos selectores:", + "glassEffect": "Efecto Glassmorphism", + "glassEffectDesc": "Crea efectos modernos de vidrio:", + "tip": "Consejo", + "tipText": "Usa Plantillas CSS desde Ajustes > Plantillas CSS para diseños preconstruidos como Apple Liquid Glass." + }, + "resetToDefault": "Restablecer a predeterminado", + "applyTheme": "Aplicar tema", + "customTheme": "Tema personalizado", + "customizeTheme": "Personalizar tema", + "saveTheme": "Guardar tema", + "previewLayout": "Vista previa del diseño", + "livePreview": "Vista previa en vivo", + "heroPlaceholderText": "La foto hero de tu evento aparecerá aquí", + "whiteLabel": "Etiqueta blanca", + "hidePoweredBy": "Ocultar la marca \"Powered by PicPeak\"", + "hidePoweredByHelp": "Eliminar la atribución de PicPeak del pie de las galerías para una experiencia totalmente white-label", + "logoCustomization": "Personalización del logo", + "changeLogo": "Cambiar logo", + "logoSizeSmall": "Pequeño (32px)", + "logoSizeMedium": "Mediano (48px)", + "logoSizeLarge": "Grande (64px)", + "logoSizeXLarge": "Extra grande (96px)", + "logoSizeCustom": "Personalizado", + "logoMaxHeight": "Altura máxima (píxels)", + "logoMaxHeightHelp": "Establece una altura máxima personalizada para el logo (20-200 píxeles)", + "logoPosition": "Posición del logo en el encabezado", + "positionLeft": "Izquierda", + "positionCenter": "Centro", + "positionRight": "Derecha", + "logoDisplayMode": "Modo de visualización", + "logoOnly": "Solo logo", + "textOnly": "Solo nombre de la empresa", + "logoAndText": "Logo y nombre", + "showLogoInHeader": "Mostrar logo en el encabezado de la galería", + "showLogoInHeaderHelp": "Mostrar el logo en la barra de encabezado principal", + "showLogoInHero": "Mostrar logo en la sección hero", + "showLogoInHeroHelp": "Mostrar el logo en secciones hero (para layouts no grid)", + "headerStyle": "Estilo del encabezado", + "headerStyleDescription": "Elige cómo aparece el encabezado. El estilo del encabezado es independiente del diseño de fotos.", + "headerStyleOptions": { + "hero": "Hero", + "standard": "Estándar", + "banner": "Banner", + "minimal": "Minimal", + "none": "Sin encabezado" + }, + "headerStyleDescriptions": { + "hero": "Imagen de altura completa con información del evento", + "standard": "Encabezado compacto con detalles del evento", + "banner": "Encabezado estándar con banner de color arriba", + "minimal": "Encabezado compacto con info esencial", + "none": "Ocultar encabezado por completo" + }, + "heroDividerStyle": "Estilo del divisor", + "heroDividerDescription": "Elige la transición entre la imagen hero y el contenido de la galería.", + "dividerOptions": { + "wave": "Onda", + "straight": "Recto", + "angle": "Ángulo", + "curve": "Curva", + "none": "Ninguno" + }, + "controlsStyle": "Estilo de controles", + "controlsStyleDescription": "Elige cómo se muestran filtros y controles de la galería.", + "controlsStyleOptions": { + "classic": "Clásico", + "sidebar": "Barra lateral" + }, + "controlsStyleDescriptions": { + "classic": "Barra de filtros inline bajo el encabezado", + "sidebar": "Botón de menú abre barra lateral con filtros" + }, + "controlsStyleHeroWarning": "Se recomienda la barra lateral para encabezados hero para evitar controles sobre la imagen.", + "betaThumbnailWarningTitle": "Resolución de miniaturas baja detectada", + "betaThumbnailWarningText": "Tus miniaturas son {{width}}×{{height}}px. Los temas beta muestran fotos más grandes y requieren al menos {{recommended}}×{{recommended}}px para buena calidad. Aumenta las dimensiones en Ajustes > Miniaturas y regenera.", + "betaThumbnailWarningLink": "Ir a ajustes de miniaturas" + }, + "admin": { + "title": "Panel de administración", + "welcome": "Bienvenido de nuevo, {{name}}", + "recentActivity": "Actividad reciente", + "systemStatus": "Estado del sistema", + "totalEvents": "Eventos totales", + "activeGalleries": "Galerías activas", + "storageUsed": "Almacenamiento usado", + "totalPhotos": "Fotos totales", + "storagePercent": "{{percent}}% del límite {{limit}}", + "storageSoftLimitConfigured": "Límite configurado: {{limit}}", + "storageSoftLimitRecommended": "Límite sugerido: {{limit}}", + "version": "Versión", + "archivedEvents": "Eventos archivados", + "systemHealth": "Salud del sistema", + "health": { + "healthy": "Sano", + "warning": "Advertencia", + "error": "Error", + "checking": "Comprobando..." + }, + "updates": { + "available": "Actualización disponible", + "newVersion": "La versión {{version}} está disponible", + "currentVersion": "Actual: {{version}}", + "channel": "Canal: {{channel}}", + "channelStable": "Estable", + "channelBeta": "Beta", + "beta": "BETA", + "viewReleaseNotes": "Ver notas de la versión", + "updateAvailableShort": "v{{version}} disponible", + "checkForUpdates": "Buscar actualizaciones", + "upToDate": "Estás al día", + "lastChecked": "Última comprobación: {{time}}", + "updateNow": "Actualizar ahora", + "updateDialog": { + "title": "Actualizar PicPeak", + "detectedEnv": "Entorno detectado", + "beforeUpdating": "Antes de actualizar:", + "updateCommands": "Comandos de actualización:", + "afterUpdating": "Después de actualizar:", + "copyAllCommands": "Copiar todos los comandos", + "completeChecklist": "Completa la lista antes de actualizar", + "error": "Error al cargar instrucciones de actualización" + } + }, + "notifications": "Notificaciones", + "viewAllNotifications": "Ver todas las notificaciones", + "noNotifications": "No hay notificaciones nuevas", + "markAllRead": "Marcar todas como leídas", + "clearAll": "Borrar todo", + "close": "Cerrar", + "noNotificationsMessage": "No hay notificaciones", + "notificationMessages": { + "eventCreated": "Nuevo evento \"{{eventName}}\" creado", + "eventArchived": "Evento \"{{eventName}}\" archivado", + "eventUpdated": "Evento \"{{eventName}}\" actualizado", + "eventDeleted": "Evento \"{{eventName}}\" eliminado", + "photosUploaded": "{{count}} fotos subidas a \"{{eventName}}\"", + "photoDeleted": "Foto eliminada de \"{{eventName}}\"", + "photosBulkDeleted": "{{count}} fotos eliminadas de \"{{eventName}}\"", + "eventExpiring": "El evento \"{{eventName}}\" expira en {{days}} días", + "eventExpired": "El evento \"{{eventName}}\" ha expirado", + "passwordChanged": "Contraseña cambiada por {{actorName}}", + "passwordReset": "Contraseña restablecida para \"{{eventName}}\"", + "settingsUpdated": "Ajustes {{type}} actualizados", + "emailTemplateUpdated": "Plantilla de correo \"{{template}}\" actualizada", + "bulkDownload": "{{count}} fotos descargadas de \"{{eventName}}\"", + "storageWarning": "Uso de almacenamiento al {{percentage}}%", + "adminLogout": "El administrador {{actorName}} cerró sesión", + "categoryCreated": "Categoría \"{{name}}\" creada para \"{{eventName}}\"", + "categoryUpdated": "Categoría \"{{name}}\" actualizada en \"{{eventName}}\"", + "categoryDeleted": "Categoría \"{{name}}\" eliminada de \"{{eventName}}\"", + "cmsPageUpdated": "Página CMS \"{{slug}}\" actualizada", + "emailConfigUpdated": "Configuración de correo actualizada", + "faviconUploaded": "Favicon subido", + "brandingUpdated": "Ajustes de firma actualizados", + "generalSettingsUpdated": "Ajustes generales actualizados", + "securitySettingsUpdated": "Ajustes de seguridad actualizados", + "themeUpdated": "Ajustes de tema actualizados", + "archiveDownloaded": "Archivo descargado para \"{{eventName}}\"", + "archiveDeleted": "Archivo eliminado para \"{{eventName}}\"", + "archiveRestored": "Archivo restaurado para \"{{eventName}}\"", + "systemActivity": "Actividad del sistema: {{type}}", + "adminProfileUpdated": "Perfil de admin actualizado por {{actorName}}" + }, + "notificationToasts": { + "markedAllRead": "Todas las notificaciones marcadas como leídas", + "clearedAll": "Se borraron {{count}} notificaciones", + "profileUpdated": "Perfil de administrador actualizado" + }, + "markAsRead": "Marcar como leído", + "markAllAsRead": "Marcar todo como leído", + "notificationSettings": "Ajustes de notificaciones", + "changePassword": "Cambiar contraseña", + "darkMode": "Cambiar a modo oscuro", + "lightMode": "Cambiar a modo claro", + "accountSettings": { + "title": "Cuenta de administrador", + "description": "Actualiza las credenciales usadas para iniciar sesión en PicPeak.", + "username": "Usuario", + "usernamePlaceholder": "Admin", + "email": "Correo", + "emailPlaceholder": "admin@example.com", + "updateButton": "Actualizar perfil" + }, + "profileUpdateError": "No se pudo actualizar el perfil de admin. Por favor, inténtalo de nuevo.", + "loadingDashboard": "Cargando panel...", + "activeEvents": "Eventos activos", + "expiringSoon": "A punto de expirar", + "next7Days": "Próximos 7 días", + "totalViews": "Vistas totales", + "downloads": "Descargas", + "percentFromLastWeek": "{{percent}}% respecto a la semana pasada", + "dashboardSubtitle": "¡Bienvenido! Esto es lo que ocurre con tus galerías.", + "eventsExpiringSoon": "Eventos próximos a expirar", + "noEventsExpiring": "No hay eventos que expiren en los próximos 7 días", + "daysLeft": "{{count}} día restante", + "daysLeft_plural": "{{count}} días restantes", + "viewAllExpiringEvents": "Ver los {{count}} eventos próximos a expirar", + "noRecentActivity": "Sin actividad reciente", + "viewAllActivity": "Ver toda la actividad", + "quickActions": "Accesos rápidos", + "viewArchives": "Ver archivos", + "analytics": "Analíticas", + "activities": { + "event_created": "Nuevo evento creado: {{eventName}}", + "photos_uploaded": "{{count}} fotos subidas a {{eventName}}", + "event_archived": "Evento archivado: {{eventName}}", + "archive_restored": "Archivo restaurado: {{eventName}}", + "archive_deleted": "Archivo eliminado: {{eventName}}", + "archive_downloaded": "Archivo descargado: {{eventName}}", + "email_config_updated": "Configuración de correo actualizada", + "email_template_updated": "Plantilla de correo actualizada: {{template}}", + "branding_updated": "Firma actualizada", + "theme_updated": "Tema actualizado", + "bulk_download": "{{count}} fotos descargadas de {{eventName}}", + "gallery_password_entry": "Contraseña ingresada para {{eventName}}", + "expiration_warning_viewed": "Aviso de expiración visto para {{eventName}}", + "feedback_settings_updated": "Ajustes de feedback actualizados", + "feedback_moderated": "Feedback moderado", + "feedback_deleted": "Feedback eliminado", + "photo_like": "Foto marcada con me gusta en {{eventName}}", + "photo_favorite": "Foto añadida a favoritos en {{eventName}}", + "photo_rating": "Foto valorada en {{eventName}}", + "photo_comment": "Comentario en foto en {{eventName}}", + "guest_feedback_like": "Invitado marcó me gusta en {{eventName}}", + "guest_feedback_favorite": "Invitado añadió a favoritos en {{eventName}}", + "guest_feedback_rating": "Invitado valoró una foto en {{eventName}}", + "guest_feedback_comment": "Invitado comentó en una foto en {{eventName}}", + "word_filter_added": "Filtro de palabra añadido", + "external_import_completed": "Importación externa completada ({{imported}} importadas, {{skipped}} omitidas)", + "bulk_archive_completed": "Archivado masivo completado", + "event_activated": "Evento activado: {{eventName}}", + "event_deactivated": "Evento desactivado: {{eventName}}", + "photo_deleted": "Foto eliminada de {{eventName}}", + "photos_bulk_deleted": "{{count}} fotos eliminadas de {{eventName}}", + "settings_updated": "Ajustes actualizados", + "event_updated": "Evento actualizado: {{eventName}}", + "event_renamed": "Evento renombrado: {{eventName}}", + "event_deleted": "Evento eliminado: {{eventName}}", + "password_changed": "Contraseña cambiada", + "email_resent": "Correo de creación reenviado para: {{eventName}}", + "category_created": "Categoría creada: {{categoryName}}", + "category_updated": "Categoría actualizada: {{categoryName}}", + "category_deleted": "Categoría eliminada: {{categoryName}}", + "general_settings_updated": "Ajustes generales actualizados", + "favicon_uploaded": "Favicon subido", + "analytics_settings_updated": "Ajustes de analíticas actualizados", + "cms_page_updated": "Página CMS actualizada: {{page}}", + "security_settings_updated": "Ajustes de seguridad actualizados", + "password_reset": "Contraseña restablecida para: {{eventName}}", + "admin_logout": "Administrador {{actorName}} cerró sesión", + "system_activity": "Actividad del sistema: {{type}}", + "unknown": "Actividad desconocida" + }, + "userManagement": "Gestión de usuarios", + "inviteUser": "Invitar usuario", + "pendingInvitations": "Invitaciones pendientes", + "roles": { + "super_admin": "Super administrador", + "admin": "Administrador", + "editor": "Editor", + "viewer": "Espectador" + }, + "userStatus": { + "active": "Activo", + "inactive": "Inactivo" + }, + "inviteForm": { + "email": "Correo electrónico", + "role": "Rol", + "send": "Enviar invitación" + }, + "acceptInvite": { + "title": "Aceptar invitación de admin", + "username": "Elige un nombre de usuario", + "password": "Crear contraseña", + "submit": "Crear cuenta" + }, + "photos": { + "hidden": "Ocultas", + "hideSelected": "Ocultar", + "showSelected": "Mostrar", + "hiddenSuccess": "Fotos ocultas a los invitados", + "visibleSuccess": "Fotos ahora visibles para invitados" + } + }, + "permissions": { + "insufficient": "No tienes permiso para realizar esta acción", + "viewOnly": "Solo lectura" + }, + "acceptInvitation": { + "title": "Aceptar invitación", + "subtitle": "Crea tu cuenta de administrador", + "validating": "Validando invitación...", + "invalidToken": "Invitación inválida", + "invalidTokenMessage": "Este enlace de invitación es inválido o ha expirado. Por favor, contacta con tu administrador para una nueva invitación.", + "expiredToken": "Invitación expirada", + "expiredTokenMessage": "Esta invitación ha expirado. Solicita una nueva invitación a tu administrador.", + "alreadyUsed": "Invitación ya usada", + "alreadyUsedMessage": "Esta invitación ya fue usada para crear una cuenta.", + "invitedAs": "Has sido invitado como", + "expiresAt": "La invitación expira", + "usernameLabel": "Nombre de usuario", + "usernamePlaceholder": "Elige un nombre de usuario", + "usernameHelp": "3-50 caracteres; solo letras, números, guiones bajos y guiones", + "passwordLabel": "Contraseña", + "passwordPlaceholder": "Crea una contraseña segura", + "confirmPasswordLabel": "Confirmar contraseña", + "confirmPasswordPlaceholder": "Confirma tu contraseña", + "passwordStrength": "Robustez de la contraseña", + "requirements": { + "title": "Requisitos de la contraseña:", + "minLength": "Al menos 12 caracteres", + "uppercase": "Al menos una letra mayúscula", + "lowercase": "Al menos una letra minúscula", + "number": "Al menos un número", + "special": "Al menos un carácter especial" + }, + "strength": { + "weak": "Débil", + "fair": "Aceptable", + "good": "Buena", + "strong": "Fuerte" + }, + "createAccount": "Crear cuenta", + "creating": "Creando cuenta...", + "success": "¡Cuenta creada!", + "successMessage": "Tu cuenta se ha creado correctamente. Ahora puedes iniciar sesión con tus credenciales.", + "redirecting": "Redirigiendo al login en {{seconds}}...", + "goToLogin": "Ir al inicio de sesión", + "contactAdminMessage": "Por favor, contacta con tu administrador para solicitar una nueva invitación.", + "passwordsMatch": "Las contraseñas coinciden", + "alreadyHaveAccount": "¿Ya tienes una cuenta?", + "signIn": "Iniciar sesión", + "errors": { + "usernameRequired": "El nombre de usuario es obligatorio", + "usernameTooShort": "El nombre de usuario debe tener al menos 3 caracteres", + "usernameTooLong": "El nombre de usuario debe tener como máximo 50 caracteres", + "usernameInvalid": "El nombre de usuario solo puede contener letras, números, guiones bajos y guiones", + "passwordRequired": "La contraseña es obligatoria", + "passwordTooShort": "La contraseña debe tener al menos 12 caracteres", + "passwordsDoNotMatch": "Las contraseñas no coinciden", + "confirmPasswordRequired": "Por favor confirma tu contraseña", + "usernameTaken": "Este nombre de usuario ya está en uso", + "emailTaken": "Ya existe una cuenta con este correo", + "genericError": "Error al crear la cuenta. Por favor, inténtalo de nuevo." + } + }, + "errors": { + "notFound": "No encontrado", + "galleryNotFound": "Galería no encontrada", + "galleryNotFoundMessage": "Esta galería no existe o ha sido eliminada.", + "galleryArchived": "Galería archivada", + "galleryArchivedMessage": "Esta galería ha sido archivada y ya no es accesible. Contacta con el organizador si necesitas acceso.", + "unauthorized": "No autorizado", + "forbidden": "Prohibido", + "serverError": "Error del servidor", + "somethingWentWrong": "Algo salió mal", + "tryAgainLater": "Por favor, inténtalo más tarde", + "refreshPage": "Actualizar página", + "oopsSomethingWentWrong": "¡Vaya! Algo salió mal", + "unexpectedError": "Hemos encontrado un error inesperado. No te preocupes, tus datos están seguros.", + "goToHomepage": "Ir a la página principal", + "errorDetails": "Detalles del error", + "requiredFields": "Por favor rellena todos los campos obligatorios", + "enterTestEmail": "Introduce una dirección de correo de prueba", + "failedToCreateEvent": "Error al crear el evento", + "eventCreationFailed": "Error al crear el evento", + "networkError": "Error de red. Comprueba tu conexión e inténtalo de nuevo.", + "sessionExpired": "Sesión expirada. Por favor, inicia sesión de nuevo." + }, + "validation": { + "eventNameRequired": "El nombre del evento es obligatorio", + "hostEmailRequired": "El correo del cliente es obligatorio", + "hostNameRequired": "El nombre del cliente es obligatorio", + "adminEmailRequired": "El correo del administrador es obligatorio", + "invalidEmailFormat": "Formato de correo inválido", + "passwordRequired": "La contraseña es obligatoria", + "passwordMinLength": "La contraseña debe tener al menos 6 caracteres", + "passwordsDoNotMatch": "Las contraseñas no coinciden", + "passwordSecurityRequirements": "La contraseña no cumple los requisitos de seguridad", + "expirationRange": "La expiración debe ser entre 1 y 365 días" + }, + "legal": { + "impressum": "Aviso legal", + "datenschutz": "Política de privacidad", + "termsOfService": "Términos del servicio", + "cookiePolicy": "Política de cookies" + }, + "toast": { + "saveSuccess": "Cambios guardados correctamente", + "saveError": "Error al guardar cambios", + "deleteSuccess": "Eliminado correctamente", + "deleteError": "Error al eliminar", + "uploadSuccess": "Subida completada correctamente", + "uploadError": "Error en la subida", + "loginSuccess": "Inicio de sesión correcto", + "loginError": "Error al iniciar sesión", + "passwordChanged": "Contraseña cambiada con éxito", + "linkCopied": "Enlace copiado al portapapeles", + "eventCreated": "Evento creado con éxito", + "eventUpdated": "Evento actualizado con éxito", + "eventArchived": "Evento archivado con éxito", + "settingsSaved": "Ajustes guardados con éxito", + "themeUpdated": "Tema actualizado con éxito", + "brandingUpdated": "Firma actualizada con éxito", + "categoryAdded": "Categoría añadida con éxito", + "categoryDeleted": "Categoría eliminada con éxito", + "categoryUpdated": "Categoría actualizada con éxito", + "emailConfigSaved": "Configuración de correo guardada con éxito", + "testEmailSent": "Correo de prueba enviado con éxito", + "pageUpdated": "Página actualizada con éxito", + "archiveRestored": "Archivo restaurado con éxito", + "archiveDeleted": "Archivo eliminado permanentemente" + }, + "email": { + "title": "Configuración de correo", + "subtitle": "Configura los ajustes de correo para notificaciones", + "loadingSettings": "Cargando ajustes de correo...", + "smtpConfiguration": "Configuración SMTP", + "smtpHost": "Host SMTP", + "smtpHostHelp": "Hostname de tu servidor de correo", + "smtpPort": "Puerto SMTP", + "smtpPortHelp": "Normalmente 587 para TLS, 465 para SSL, 25 sin cifrado", + "smtpSecure": "Usar SSL/TLS", + "smtpSecureHelp": "Habilitar para transmisión segura", + "smtpUsername": "Usuario SMTP", + "smtpUsernameHelp": "Usuario de la cuenta de correo", + "smtpPassword": "Contraseña SMTP", + "smtpPasswordHelp": "Contraseña de la cuenta de correo", + "fromDetails": "Detalles del remitente", + "fromEmail": "Correo del remitente", + "fromEmailHelp": "Dirección que aparece como remitente", + "fromName": "Nombre del remitente", + "fromNameHelp": "Nombre que aparece como remitente", + "testConfiguration": "Probar configuración", + "testEmail": "Correo de prueba", + "testEmailHelp": "Enviar un correo de prueba para verificar ajustes", + "sendTestEmail": "Enviar correo de prueba", + "saveConfiguration": "Guardar configuración", + "emailTemplates": "Plantillas de correo", + "templateVariables": "Variables disponibles", + "previewTemplate": "Vista previa de la plantilla", + "smtpSettings": "Ajustes SMTP", + "testEmailSuccess": "Correo de prueba enviado con éxito", + "saveSmtpSettings": "Guardar ajustes SMTP", + "testEmailSection": "Correo de prueba", + "beforeTesting": "Antes de probar:", + "saveSmtpFirst": "Guarda primero los ajustes SMTP", + "ensureFirewall": "Asegúrate de que el firewall permite SMTP saliente", + "gmailAppPassword": "Para Gmail, usa una contraseña de aplicación", + "testEmailAddressLabel": "Correo de prueba", + "sendTestEmailButton": "Enviar correo de prueba", + "commonSmtpSettings": "Ajustes SMTP comunes:", + "editTemplate": "Editar plantilla", + "templateName": "Nombre de la plantilla", + "subjectLine": "Asunto", + "emailBody": "Cuerpo del correo", + "preview": "Vista previa", + "save": "Guardar", + "saveChanges": "Guardar cambios", + "templates": "Plantillas", + "variableHelp": "Usa estas variables en tu plantilla. Se reemplazarán por valores reales al enviar.", + "port": "Puerto", + "security": "Seguridad", + "username": "Usuario", + "password": "Contraseña", + "enterPassword": "Introduce la contraseña", + "required": "obligatorio", + "ignoreSslErrors": "Ignorar errores SSL/TLS", + "ignoreSslWarning": "Aviso: Desactivar verificación de certificados hace la conexión vulnerable a ataques man-in-the-middle. Habilita solo si confías en el servidor SMTP.", + "brandingTitle": "Firma de correo", + "brandingDescription": "Personaliza colores usados en plantillas de correo. Los cambios afectan barra de encabezado, botones, enlaces y fondo del pie.", + "primaryColor": "Color primario", + "primaryColorHint": "Usado para encabezado, botones y enlaces", + "secondaryColor": "Fondo del pie", + "secondaryColorHint": "Usado para el fondo del pie", + "saveEmailColors": "Guardar colores de correo", + "editor": { + "bold": "Negrita", + "italic": "Cursiva", + "bulletList": "Lista con viñetas", + "numberedList": "Lista numerada", + "blockquote": "Cita", + "link": "Agregar enlace", + "horizontalRule": "Regla horizontal", + "alignLeft": "Alinear a la izquierda", + "alignCenter": "Centrar", + "alignRight": "Alinear a la derecha", + "clearFormatting": "Quitar formato", + "undo": "Deshacer", + "redo": "Rehacer", + "insertVariable": "Insertar variable", + "sourceMode": "Fuente", + "visualMode": "Visual", + "enterUrl": "Introduce la URL...", + "addLink": "Agregar", + "cancel": "Cancelar" + }, + "copiedFromLanguage": "Contenido copiado desde {{language}}", + "noTranslation": "Sin traducción aún", + "noTranslationYet": "No existe traducción para este idioma. Copia desde otro idioma para empezar:", + "copyFrom": "Copiar desde" + }, + "cms": { + "title": "Páginas CMS", + "subtitle": "Gestiona páginas legales e informativas", + "loadingPages": "Cargando páginas...", + "pages": "Páginas", + "previewLinks": "Enlaces de vista previa", + "englishVersion": "Versión en inglés", + "germanVersion": "Versión en alemán", + "editPage": "Editar {{page}}", + "pageTitle": "Título de la página", + "pageContent": "Contenido", + "pageTitlePlaceholder": "Introduce el título...", + "saveChanges": "Guardar cambios", + "lastUpdated": "Última actualización:", + "impressum": "Aviso legal", + "datenschutz": "Política de privacidad", + "pageUpdated": "Página actualizada con éxito", + "useExternalUrl": "Usar URL externa", + "useExternalUrlHelp": "Redirige a los visitantes a una página externa en lugar de mostrar el contenido interno. El título y contenido interno se guardan como fallback.", + "externalUrl": "URL externa", + "externalUrlPlaceholder": "https://example.com/impressum", + "externalUrlInvalid": "Debe ser una URL https:// válida", + "externalUrlActive": "La URL externa está activa — el contenido interno se conserva pero no se muestra." + }, + "eventTypes": { + "title": "Tipos de evento", + "subtitle": "Personaliza tipos de evento y sus temas por defecto", + "loading": "Cargando tipos de evento...", + "loadError": "Error al cargar tipos de evento", + "createNew": "Nuevo tipo de evento", + "edit": "Editar tipo de evento", + "created": "Tipo de evento creado con éxito", + "updated": "Tipo de evento actualizado con éxito", + "deleted": "Tipo de evento eliminado con éxito", + "createError": "Error al crear el tipo de evento", + "updateError": "Error al actualizar el tipo de evento", + "deleteError": "Error al eliminar el tipo de evento", + "searchPlaceholder": "Buscar tipos de evento...", + "showInactive": "Mostrar inactivos", + "noResults": "No se encontraron tipos de evento", + "empty": "Aún no hay tipos de evento", + "system": "Sistema", + "table": { + "type": "Tipo", + "slugPrefix": "Prefijo URL", + "theme": "Tema por defecto", + "status": "Estado", + "actions": "Acciones" + }, + "form": { + "name": "Nombre visible", + "namePlaceholder": "p. ej., Sesión familiar", + "slugPrefix": "Prefijo URL", + "slugPrefixPlaceholder": "p. ej., familia", + "slugPreview": "URL de ejemplo:", + "emoji": "Icono", + "themePreset": "Tema por defecto", + "isActive": "Activo (visible al crear eventos)" + }, + "validation": { + "slugFormat": "Solo se permiten letras, números y guiones" + }, + "slugInfo": { + "title": "Info del prefijo URL:", + "description": "El prefijo se usa para generar URLs de galería. Por ejemplo, un tipo con prefijo \"family\" generará URLs como: family-smith-family-2025-01-22" + }, + "deleteConfirm": { + "title": "Eliminar tipo de evento", + "message": "¿Seguro que quieres eliminarlo?", + "warning": "Esta acción no se puede deshacer. Asegúrate de que ningún evento esté usando este tipo." + } + }, + "backup": { + "external": { + "warning": { + "title": "Medios externos excluidos", + "body": "Esta instalación referencia fotos desde /external-media. Los originales se excluyen de las copias. Se respaldan miniaturas y base de datos." + } + }, + "title": "Gestión de copias de seguridad", + "subtitle": "Gestiona copias, configura automatizaciones y restaura versiones anteriores.", + "tabs": { + "dashboard": "Panel", + "configuration": "Configuración", + "history": "Historial", + "restore": "Restaurar" + }, + "status": { + "inProgress": "Copia en progreso...", + "lastBackup": "Última copia", + "noBackups": "No se encontraron copias", + "nextBackup": "Siguiente copia", + "notScheduled": "No programada", + "enabled": "Habilitado", + "disabled": "Deshabilitado" + }, + "actions": { + "runBackupNow": "Ejecutar copia ahora", + "starting": "Iniciando...", + "running": "En ejecución...", + "testConnection": "Probar conexión", + "save": "Guardar configuración", + "delete": "Eliminar", + "view": "Ver detalles", + "download": "Descargar", + "refresh": "Actualizar" + }, + "dashboard": { + "backupHealth": "Salud de copias", + "healthStatus": { + "excellent": "Excelente", + "good": "Buena", + "warning": "Advertencia", + "critical": "Crítica" + }, + "health": { + "title": "Estado de las copias" + }, + "healthMessages": { + "noBackups": "No se encontraron copias", + "lastBackupFailed": "La última copia falló", + "upToDate": "La copia está actualizada", + "recent": "Copia reciente", + "gettingOld": "La copia empieza a envejecer", + "outdated": "Copia obsoleta", + "failed": "La última copia falló", + "old": "La copia empieza a envejecer" + }, + "stats": { + "totalBackups": "Copias totales", + "backupSize": "Tamaño de copia", + "lastDuration": "Última duración", + "backupStatus": "Estado", + "last": "Última", + "files": "archivos", + "minutes": "{{count}}m", + "active": "Activa", + "inactive": "Inactiva", + "noBackupsYet": "Aún no hay copias" + }, + "recentActivity": { + "title": "Actividad reciente de copias" + }, + "notConfigured": { + "title": "Copias no configuradas", + "message": "Configura las copias en la pestaña Configuración antes de ejecutar." + }, + "actions": { + "runBackupNow": "Ejecutar copia ahora", + "running": "En ejecución..." + }, + "coverage": { + "title": "Cobertura de copia", + "database": "Base de datos", + "photos": "Fotos", + "archives": "Archivos", + "systemFiles": "Archivos del sistema", + "included": "Incluido", + "excluded": "Excluido", + "optional": "Opcional" + }, + "storageDestination": "Destino de almacenamiento", + "nextScheduledBackup": "Próxima copia programada", + "backupType": "Copia {{type}}", + "noDestinationSet": "No se ha definido destino" + }, + "configuration": { + "enableBackup": "Habilitar copias automáticas", + "enableBackupHelp": "Crear copias automáticamente según la programación", + "destinationType": "Destino de copia", + "destinationTypes": { + "local": { + "name": "Almacenamiento local", + "description": "Guardar copias en el sistema de archivos local" + }, + "rsync": { + "name": "Servidor remoto (Rsync)", + "description": "Sincronizar copias a un servidor remoto via SSH/Rsync" + }, + "s3": { + "name": "Almacenamiento compatible S3", + "description": "Guardar copias en Amazon S3 o almacenamiento compatible" + } + }, + "fields": { + "destinationPath": "Ruta de destino", + "destinationPathHelp": "Ruta local para almacenar copias", + "destinationPathPlaceholder": "/ruta/a/copia/seguridad", + "rsyncHost": "Host remoto", + "rsyncHostHelp": "Hostname o IP SSH", + "rsyncHostPlaceholder": "backup.example.com", + "rsyncUser": "Usuario SSH", + "rsyncUserHelp": "Usuario para conexión SSH", + "rsyncUserPlaceholder": "backup-user", + "rsyncPath": "Ruta remota", + "rsyncPathHelp": "Ruta en el servidor remoto", + "rsyncPathPlaceholder": "/home/backup/photo-sharing", + "rsyncSshKey": "Clave privada SSH", + "rsyncSshKeyHelp": "Clave privada SSH para autenticación (opcional)", + "rsyncSshKeyPlaceholder": "-----BEGIN RSA PRIVATE KEY-----", + "s3Endpoint": "Endpoint S3", + "s3EndpointHelp": "Endpoint API S3 (p. ej., s3.amazonaws.com)", + "s3EndpointPlaceholder": "https://s3.amazonaws.com", + "s3Bucket": "Nombre del bucket", + "s3BucketHelp": "Bucket S3 para almacenar copias", + "s3BucketPlaceholder": "mi-bucket-backup", + "s3AccessKey": "Access Key ID", + "s3AccessKeyHelp": "ID de clave de acceso AWS/S3", + "s3AccessKeyPlaceholder": "AKIAIOSFODNN7EXAMPLE", + "s3SecretKey": "Secret Access Key", + "s3SecretKeyHelp": "Clave secreta AWS/S3", + "s3SecretKeyPlaceholder": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + "s3Region": "Región", + "s3RegionHelp": "Región S3 (p. ej., us-east-1)", + "s3RegionPlaceholder": "us-east-1" + }, + "schedule": { + "title": "Programación", + "scheduleType": "Tipo de programación", + "scheduleOptions": { + "hourly": "Cada hora", + "daily": "Diaria", + "weekly": "Semanal", + "custom": "Cron personalizado" + }, + "options": { + "hourly": "Cada hora", + "daily": "Diaria", + "weekly": "Semanal", + "custom": "Cron personalizado" + }, + "customCron": "Expresión cron", + "customCronHelp": "Introduce una expresión cron válida (p. ej., 0 3 * * *)", + "retention": "Periodo de retención", + "retentionDays": "Conservar copias por", + "retentionHelp": "días (las copias antiguas se eliminarán automáticamente)" + }, + "whatToBackup": { + "title": "Qué respaldar", + "database": "Base de datos", + "databaseHelp": "Todos los datos de eventos, ajustes y configuraciones", + "photos": "Fotos", + "photosHelp": "Todas las fotos subidas en galerías activas", + "archives": "Archivos", + "archivesHelp": "ZIPs de eventos archivados", + "thumbnails": "Miniaturas", + "thumbnailsHelp": "Miniaturas generadas (pueden recrearse)", + "tempFiles": "Archivos temporales", + "tempFilesHelp": "Archivos temporales de subida y procesamiento" + }, + "advancedOptions": { + "title": "Opciones avanzadas", + "compression": "Habilitar compresión", + "compressionHelp": "Comprimir archivos para ahorrar espacio", + "encryption": "Habilitar cifrado", + "encryptionHelp": "Encriptar copias para mayor seguridad", + "encryptionPassphrase": "Frase de cifrado", + "encryptionPassphraseHelp": "Frase fuerte para cifrar las copias", + "confirmPassphrase": "Confirmar frase", + "passphrasesDontMatch": "Las frases no coinciden" + }, + "validation": { + "requiredFields": "Por favor rellena los campos obligatorios", + "invalidCron": "Expresión cron inválida", + "connectionTestFailed": "Falló la prueba de conexión", + "connectionTestSuccess": "¡Prueba de conexión exitosa!" + }, + "messages": { + "requiredFields": "Por favor rellena los campos obligatorios", + "connectionSuccess": "¡Prueba de conexión exitosa!", + "connectionFailed": "Falló la prueba de conexión" + }, + "testingConnection": "Probando conexión...", + "saveSettings": "Guardar configuración", + "savingSettings": "Guardando..." + }, + "history": { + "searchPlaceholder": "Buscar copias...", + "allStatus": "Todos los estados", + "status": { + "completed": "Completada", + "failed": "Fallida", + "running": "En ejecución", + "partial": "Parcial" + }, + "deleteConfirm": "¿Seguro que quieres eliminar esta copia de {{date}}?", + "noBackups": "No se encontraron copias", + "tableHeaders": { + "date": "Fecha", + "type": "Tipo", + "status": "Estado", + "size": "Tamaño", + "duration": "Duración", + "actions": "Acciones" + }, + "columns": { + "status": "Estado", + "dateTime": "Fecha y hora", + "type": "Tipo", + "size": "Tamaño", + "duration": "Duración", + "actions": "Acciones" + }, + "details": { + "backupDetails": "Detalles de la copia", + "destination": "Destino", + "started": "Iniciada", + "completed": "Completada", + "contentBackedUp": "Contenido respaldado", + "errorDetails": "Detalles del error", + "manifest": "Manifiesto" + }, + "statistics": "Estadísticas", + "errors": "Errores", + "backupDetails": { + "backupId": "ID de copia", + "startTime": "Inicio", + "endTime": "Fin", + "destination": "Destino", + "filesProcessed": "Archivos procesados", + "totalSize": "Tamaño total", + "compressionRatio": "Ratio de compresión", + "errorLog": "Registro de errores", + "noErrors": "No hubo errores" + }, + "pagination": { + "showing": "Mostrando {{from}}-{{to}} de {{total}} copias", + "previous": "Anterior", + "next": "Siguiente" + }, + "filter": { + "allStatus": "Todos los estados", + "completed": "Completada", + "failed": "Fallida", + "running": "En ejecución", + "partial": "Parcial" + }, + "noBackupsFound": "No se encontraron copias", + "backupsWillAppear": "Las copias aparecerán aquí cuando se creen", + "messages": { + "deleteSuccess": "Copia eliminada con éxito" + } + }, + "restore": { + "steps": { + "selectSource": "Seleccionar origen", + "chooseBackup": "Elegir copia", + "restoreOptions": "Opciones de restauración", + "reviewConfirm": "Revisar y confirmar", + "progress": "Progreso" + }, + "source": { + "title": "Selecciona origen de copia", + "subtitle": "Elige desde dónde restaurar", + "local": { + "name": "Copia local", + "description": "Restaurar desde el filesystem local" + }, + "s3": { + "name": "Almacenamiento S3", + "description": "Restaurar desde un bucket S3" + }, + "upload": { + "name": "Subir copia", + "description": "Subir un archivo de copia", + "comingSoon": "Funcionalidad de subida próximamente" + }, + "configuration": { + "s3": "Configuración S3", + "endpoint": "URL del endpoint S3", + "bucket": "Nombre del bucket", + "accessKey": "Access Key ID", + "secretKey": "Secret Access Key" + } + }, + "backup": { + "title": "Elegir copia a restaurar", + "subtitle": "Selecciona entre las copias disponibles", + "noBackupsFound": "No se encontraron copias en el origen seleccionado", + "encrypted": "Copia cifrada", + "encryptedMessage": "Necesitarás la frase de cifrado para restaurar esta copia.", + "enterPassphrase": "Introduce la frase de cifrado", + "at": "en" + }, + "restoreTypes": { + "full": { + "name": "Restauración completa", + "description": "Restaurar todo incluyendo base de datos, fotos y archivos", + "warning": "Esto reemplazará todos los datos actuales" + }, + "database": { + "name": "Solo base de datos", + "description": "Restaurar solo la base de datos (ajustes, eventos, usuarios)", + "warning": "La base de datos actual será reemplazada" + }, + "files": { + "name": "Solo archivos", + "description": "Restaurar solo fotos y archivos", + "warning": "Los archivos existentes pueden sobrescribirse" + }, + "selective": { + "name": "Restauración selectiva", + "description": "Elegir elementos específicos para restaurar", + "warning": "Solo los elementos seleccionados se restaurarán" + } + }, + "options": { + "title": "Opciones de restauración", + "subtitle": "Elige qué restaurar", + "additionalOptions": { + "title": "Opciones adicionales", + "skipPreBackup": "Omitir copia previa", + "skipPreBackupHelp": "Por defecto se crea una copia previa antes de restaurar. Marca para omitir.", + "force": "Forzar restauración", + "forceHelp": "Omitir comprobaciones de seguridad (usar con precaución)" + } + }, + "confirmation": { + "title": "Revisar y confirmar", + "subtitle": "Revisa tu configuración de restauración", + "validation": { + "passed": "Validación superada", + "failed": "Validación fallida", + "checking": "Validando configuración..." + }, + "spaceCheck": { + "title": "Espacio de almacenamiento", + "required": "Requerido", + "available": "Disponible", + "insufficient": "Espacio insuficiente" + }, + "summary": { + "title": "Resumen de restauración", + "source": "Origen", + "backupDate": "Fecha de la copia", + "restoreType": "Tipo de restauración", + "preBackup": "Copia previa", + "enabled": "Habilitada", + "skipped": "Omitida" + }, + "warning": { + "title": "Aviso importante", + "message": "Esta restauración reemplazará los datos existentes. Asegúrate de tener una copia actual antes de proceder. Esta acción no se puede deshacer." + } + }, + "progress": { + "title": "Progreso de restauración", + "inProgress": "Restauración en curso...", + "completed": "Restauración completada", + "overallProgress": "Progreso general", + "current": "Actual", + "statusDetails": "Detalles", + "restoreLogs": "Registros de restauración", + "steps": { + "completed": "Completado", + "running": "En ejecución", + "failed": "Fallido", + "pending": "Pendiente" + }, + "success": { + "title": "Restauración completada con éxito", + "message": "Tus datos han sido restaurados. Verifica que todo funcione correctamente." + } + }, + "actions": { + "back": "Atrás", + "next": "Siguiente", + "startRestore": "Iniciar restauración", + "starting": "Iniciando...", + "validating": "Validando...", + "startNewRestore": "Iniciar nueva restauración" + }, + "messages": { + "restoreStarted": "Restauración iniciada con éxito" + } + }, + "messages": { + "backupStarted": "Copia iniciada con éxito", + "backupFailed": "Error al iniciar la copia", + "configUpdated": "Configuración de copias actualizada", + "configUpdateFailed": "Error al actualizar la configuración", + "backupDeleted": "Copia eliminada con éxito", + "deleteFailed": "Error al eliminar la copia", + "testEmailSent": "¡Prueba de conexión exitosa!", + "testEmailFailed": "Falló la prueba de conexión" + } + }, + "cssTemplates": { + "title": "Plantillas CSS personalizadas", + "template": "Plantilla", + "templateName": "Nombre de la plantilla", + "enableTemplate": "Habilitar esta plantilla", + "enableHint": "Las plantillas habilitadas pueden seleccionarse al crear eventos", + "cssContent": "Contenido CSS", + "cssHint": "Usa .gallery-page para aplicar estilos a la galería. Variables disponibles: --gallery-bg, --gallery-text, --gallery-accent", + "securityNotice": "Aviso de seguridad", + "securityText": "El CSS se sanea para evitar código malicioso. URLs externas, @import y expresiones JavaScript se bloquean.", + "resetToDefault": "Restablecer a predeterminado", + "resetConfirm": "¿Restablecer esta plantilla al predeterminado? Se perderán los cambios.", + "unsavedChanges": "Cambios sin guardar", + "saveTemplate": "Guardar plantilla", + "lastUpdated": "Última actualización", + "saved": "Plantilla guardada con éxito", + "saveFailed": "Error al guardar la plantilla", + "sanitizationWarning": "Algunos patrones CSS fueron bloqueados por seguridad", + "reset": "Plantilla restablecida al predeterminado", + "resetFailed": "Error al restablecer la plantilla" + }, + "maintenance": { + "title": "Mantenimiento del sistema", + "message": "Estamos realizando mantenimiento programado para mejorar el servicio. Volveremos pronto.", + "expectedCompletion": "Hora estimada de finalización:", + "checkBackLater": "Por favor, vuelve a comprobar más tarde", + "urgentMatters": "Para asuntos urgentes, contacta con" + }, + "passwordChange": { + "title": "Cambiar contraseña", + "currentPassword": "Contraseña actual", + "newPassword": "Nueva contraseña", + "confirmPassword": "Confirmar nueva contraseña", + "currentPasswordPlaceholder": "Introduce la contraseña actual", + "newPasswordPlaceholder": "Introduce la nueva contraseña", + "confirmPasswordPlaceholder": "Confirma la nueva contraseña", + "requirements": "Requisitos de la contraseña:", + "minLength": "Al menos 6 caracteres", + "mustDiffer": "Debe ser diferente de la contraseña actual", + "success": "Contraseña cambiada con éxito", + "failed": "Error al cambiar la contraseña", + "currentRequired": "La contraseña actual es obligatoria", + "newRequired": "La nueva contraseña es obligatoria", + "minLengthError": "La contraseña debe tener al menos 6 caracteres", + "confirmRequired": "Por favor confirma tu nueva contraseña", + "noMatch": "Las contraseñas no coinciden", + "mustBeDifferent": "La nueva contraseña debe ser diferente de la actual", + "cancel": "Cancelar" + }, + "mandatoryPasswordChange": { + "title": "Cambio de contraseña obligatorio", + "description": "Por seguridad, debes cambiar tu contraseña antes de acceder al panel.", + "success": "¡Contraseña cambiada con éxito! Ya puedes acceder al panel.", + "minLength": "Al menos 12 caracteres", + "mustContainUpperLower": "Debe contener mayúsculas y minúsculas", + "mustContainNumbers": "Debe contener números", + "mustContainSpecial": "Debe contener caracteres especiales (!@#$%^&*)", + "minLengthError": "La contraseña debe tener al menos 12 caracteres", + "mustContainLowercase": "La contraseña debe contener minúsculas", + "mustContainUppercase": "La contraseña debe contener mayúsculas", + "mustContainNumbersError": "La contraseña debe contener números", + "mustContainSpecialError": "La contraseña debe contener caracteres especiales" + }, + "offline": { + "backOnline": "De vuelta en línea", + "noConnection": "Sin conexión a internet" + }, + "download": { + "downloading": "Descargando...", + "percentComplete": "% completado" + }, + "passwordGenerator": { + "weak": "Débil", + "fair": "Aceptable", + "good": "Buena", + "strong": "Fuerte", + "generating": "Generando...", + "generatePassword": "Generar contraseña", + "showSuggestions": "Mostrar sugerencias", + "moreOptions": "Más opciones", + "suggestions": "Sugerencias de contraseña", + "characters": "caracteres", + "copyPassword": "Copiar contraseña", + "use": "Usar", + "pattern": "Patrón:", + "patternDescription": "Las contraseñas se generan usando el nombre y la fecha del evento. Ejemplo: \"Venue2024$August\" para mayor seguridad y memorización." + }, + "feedback": { + "comments": "Comentarios", + "addComment": "Añadir comentario", + "yourName": "Tu nombre", + "yourEmail": "Tu correo", + "writeComment": "Escribe un comentario...", + "submit": "Enviar", + "commentSubmitted": "Comentario enviado", + "commentError": "Error al enviar el comentario", + "rateLimited": "Espera antes de volver a comentar", + "commentRequired": "El comentario es obligatorio", + "nameRequired": "El nombre es obligatorio", + "emailRequired": "El correo es obligatorio", + "anonymous": "Anónimo", + "pendingApproval": "Pendiente de aprobación", + "noComments": "Aún no hay comentarios. ¡Sé el primero!", + "rating": "Valoración", + "ratePhoto": "Valorar esta foto", + "yourRating": "Tu valoración", + "averageRating": "Valoración media", + "totalRatings": "valoraciones", + "likes": "Me gusta", + "favorites": "Favoritos", + "likePhoto": "Marcar me gusta", + "favoritePhoto": "Añadir a favoritos", + "photoFeedback": "Feedback de la foto", + "hasFeedback": "Tiene feedback", + "hasComments": "Tiene comentarios", + "hasRating": "Tiene valoración", + "settings": { + "title": "Ajustes de feedback", + "enableFeedback": "Habilitar feedback", + "feedbackTypes": "Tipos de feedback", + "ratings": "Valoraciones (estrellas)", + "ratingsDesc": "Permitir valorar fotos (1-5 estrellas)", + "likes": "Me gusta", + "likesDesc": "Funcionalidad simple de me gusta", + "comments": "Comentarios", + "commentsDesc": "Comentarios de texto en fotos", + "favorites": "Favoritos", + "favoritesDesc": "Marcar fotos como favoritas", + "identityMode": "Modo de identidad", + "identityModeSimple": "Feedback simple", + "identityModeSimpleDesc": "Anónimo, basado en dispositivo. Visitantes del mismo dispositivo comparten estado.", + "identityModeGuest": "Por invitado", + "identityModeGuestDesc": "Cada visitante introduce su nombre. Habilita rastreo por invitado e insights admin." + } + }, + "filter": { + "feedbackFilters": "Filtros de feedback", + "clear": "Limpiar", + "rating": "Valoración", + "allPhotos": "Todas las fotos", + "anyRating": "Cualquier valoración", + "oneStarPlus": "1+ estrellas", + "twoStarsPlus": "2+ estrellas", + "threeStarsPlus": "3+ estrellas", + "fourStarsPlus": "4+ estrellas", + "fiveStarsOnly": "Solo 5 estrellas", + "hasLikes": "Con me gusta", + "hasFavorites": "Con favoritos", + "hasComments": "Con comentarios", + "showingPhotos": "Fotos totales", + "withRatings": "Con valoraciones" + }, + "adminLogin": { + "title": "Acceso admin", + "subtitle": "Inicia sesión para gestionar tus galerías", + "sessionExpired": "Tu sesión ha expirado. Por favor, inicia sesión de nuevo.", + "emailLabel": "Correo electrónico", + "emailPlaceholder": "admin@example.com", + "emailRequired": "El correo es obligatorio", + "invalidEmail": "Formato de correo inválido", + "passwordLabel": "Contraseña", + "passwordPlaceholder": "Introduce tu contraseña", + "passwordRequired": "La contraseña es obligatoria", + "passwordMinLength": "La contraseña debe tener al menos 6 caracteres", + "rememberMe": "Recordarme", + "forgotPassword": "¿Olvidaste la contraseña?", + "signIn": "Iniciar sesión", + "loginSuccess": "Inicio de sesión correcto!", + "networkError": "Error de red. Comprueba tu conexión e inténtalo de nuevo.", + "tooManyAttempts": "Demasiados intentos. Por favor, inténtalo más tarde.", + "invalidCredentials": "Correo o contraseña inválidos", + "generalError": "Ocurrió un error. Por favor, inténtalo de nuevo.", + "needHelp": "¿Necesitas ayuda? Contacta", + "poweredBy": "Desarrollado por PicPeak", + "devModeHint": "Modo desarrollo: usa admin@example.com, contraseña: admin123" + }, + "clientAccess": { + "title": "Acceso cliente", + "description": "Revisa fotos y gestiona visibilidad antes de compartir la galería con invitados.", + "pinLabel": "PIN del cliente", + "pinPlaceholder": "Introduce tu PIN", + "enterPin": "Introduce el PIN del cliente", + "invalidPin": "PIN inválido. Inténtalo de nuevo.", + "loginFailed": "Autenticación fallida. Inténtalo de nuevo.", + "loginButton": "Acceder a la galería", + "guestHint": "¿Buscas la vista de invitados?", + "guestLink": "Ir a la vista de invitados", + "banner": "Modo cliente", + "visibleCount": "{{visible}} de {{total}} fotos visibles para invitados", + "hideSelected": "Ocultar seleccionadas", + "showSelected": "Mostrar seleccionadas", + "adminTitle": "Acceso cliente", + "enableToggle": "Habilitar acceso cliente", + "enableDescription": "Permitir que clientes revisen y oculten fotos antes de compartir.", + "pinHelperText": "El cliente usará este PIN para acceder a la revisión.", + "pinUpdated": "PIN del cliente actualizado", + "setPin": "Establecer PIN", + "linkLabel": "Enlace de acceso cliente", + "regenerateToken": "Regenerar enlace", + "tokenRegenerated": "Enlace de acceso regenerado" + }, + "export": { + "button": "Exportar", + "success": "Exportación descargada con éxito", + "error": "Error en la exportación: ", + "exportSelected": "Exportar {{count}} seleccionadas", + "exportFiltered": "Exportar fotos filtradas", + "hint": "Selecciona fotos o aplica filtros para exportar" + }, + "photoSort": { + "defaultSort": "Orden por defecto", + "uploadDateNewest": "Fecha de subida (más recientes)", + "uploadDateOldest": "Fecha de subida (más antiguas)", + "captureDateNewest": "Fecha de captura (más reciente)", + "captureDateOldest": "Fecha de captura (más antigua)", + "filenameAZ": "Nombre de archivo (A-Z)", + "filenameZA": "Nombre de archivo (Z-A)", + "dateTaken": "Fecha de captura" + } +}