diff --git a/client/i18next.config.ts b/client/i18next.config.ts index 8591dea3..b3298b95 100644 --- a/client/i18next.config.ts +++ b/client/i18next.config.ts @@ -6,6 +6,7 @@ export default defineConfig({ locales: [ Locale.en, Locale.ru, + Locale.es, ], extract: { input: 'src/**/*.{js,jsx,ts,tsx}', diff --git a/client/src/i18n/constants.ts b/client/src/i18n/constants.ts index e9fbbf63..752b869b 100644 --- a/client/src/i18n/constants.ts +++ b/client/src/i18n/constants.ts @@ -3,6 +3,7 @@ import { Locale } from './types' export const LOCALE_ISO_CODES: Record = { [Locale.en]: 'en-US', [Locale.ru]: 'ru-RU', + [Locale.es]: 'es-ES', }; export const DEFAULT_LOCALE: Locale = Locale.en; export const DEFAULT_LOCALE_ISO_CODE = LOCALE_ISO_CODES[Locale.en]; \ No newline at end of file diff --git a/client/src/i18n/locales/en/components/common/markdown.json b/client/src/i18n/locales/en/components/common/markdown.json index 88a12009..323dc398 100644 --- a/client/src/i18n/locales/en/components/common/markdown.json +++ b/client/src/i18n/locales/en/components/common/markdown.json @@ -1,15 +1,15 @@ { "mermaid": { - "title": "Mermaid Diagram", - "renderError": "Failed to render Mermaid diagram:", - "renderErrorFallback": "Error rendering diagram", - "zoomIn": "Zoom in", - "zoomOut": "Zoom out", - "resetView": "Reset view", "copyCode": "Copy Mermaid code", - "downloadSVG": "Download as SVG", "downloadPNG": "Download as PNG", + "downloadSVG": "Download as SVG", + "exitFullscreen": "Exit fullscreen", "fullscreen": "Fullscreen", - "exitFullscreen": "Exit fullscreen" + "renderError": "Failed to render Mermaid diagram:", + "renderErrorFallback": "Error rendering diagram", + "resetView": "Reset view", + "title": "Mermaid Diagram", + "zoomIn": "Zoom in", + "zoomOut": "Zoom out" } -} \ No newline at end of file +} diff --git a/client/src/i18n/locales/en/translation.json b/client/src/i18n/locales/en/translation.json index 4396580a..82e2feb7 100644 --- a/client/src/i18n/locales/en/translation.json +++ b/client/src/i18n/locales/en/translation.json @@ -23,6 +23,7 @@ "loading": "Loading...", "locale": { "en": "English", + "es": "Spanish", "ru": "Russian" }, "no": "no", @@ -47,4 +48,4 @@ "system": "System" }, "username": "Username" -} \ No newline at end of file +} diff --git a/client/src/i18n/locales/es/components/admin/common.json b/client/src/i18n/locales/es/components/admin/common.json new file mode 100644 index 00000000..f9ff8836 --- /dev/null +++ b/client/src/i18n/locales/es/components/admin/common.json @@ -0,0 +1,12 @@ +{ + "adminTable": { + "defaultEmptyMessage": "No se encontraron datos", + "defaultLoadingMessage": "Cargando..." + }, + "filterInput": { + "defaultPlaceholder": "Buscar..." + }, + "filterSelect": { + "defaultPlaceholder": "Seleccionar..." + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/admin/modals.json b/client/src/i18n/locales/es/components/admin/modals.json new file mode 100644 index 00000000..fde8f7a8 --- /dev/null +++ b/client/src/i18n/locales/es/components/admin/modals.json @@ -0,0 +1,8 @@ +{ + "snippetViewModal": { + "error": { + "failedLoad": "Error al intentar cargar el fragmento" + }, + "title": "Cargando fragmento..." + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/admin/selector.json b/client/src/i18n/locales/es/components/admin/selector.json new file mode 100644 index 00000000..d931d9c4 --- /dev/null +++ b/client/src/i18n/locales/es/components/admin/selector.json @@ -0,0 +1,7 @@ +{ + "apiKeys": "Claves API", + "dashboard": "Panel de control", + "shares": "Compartidos", + "snippets": "Fragmentos", + "users": "Usuarios" +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/admin/tabs/apiKeys.json b/client/src/i18n/locales/es/components/admin/tabs/apiKeys.json new file mode 100644 index 00000000..e12b8d9a --- /dev/null +++ b/client/src/i18n/locales/es/components/admin/tabs/apiKeys.json @@ -0,0 +1,34 @@ +{ + "apiKeyDeletedSuccessfully": "Clave API eliminada con éxito", + "columns": { + "labels": { + "actions": "Acciones", + "created": "Creado", + "lastUsed": "Último uso", + "name": "Nombre", + "owner": "Propietario", + "status": "Estado" + } + }, + "confirmationModal": { + "message": "¿Estás seguro de que quieres eliminar esta clave API? El propietario de la clave ya no podrá usarla para acceder a la API. Esta acción no se puede deshacer.", + "title": "Eliminar clave API" + }, + "entityName_one": "clave", + "entityName_many": "claves", + "entityName_other": "claves", + "error": { + "default": "Error al eliminar la clave API" + }, + "filters": { + "userId": "Filtrar por ID de Usuario" + }, + "status": { + "active": "Activo", + "inactive": "Inactivo" + }, + "table": { + "emptyMessage": "No se encontraron claves API", + "loadingMessage": "Cargando claves API..." + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/admin/tabs/dashboard.json b/client/src/i18n/locales/es/components/admin/tabs/dashboard.json new file mode 100644 index 00000000..82fb14e2 --- /dev/null +++ b/client/src/i18n/locales/es/components/admin/tabs/dashboard.json @@ -0,0 +1,31 @@ +{ + "card": { + "apiKeys": { + "title": "Claves API" + }, + "shares": { + "title": "Enlaces compartidos" + }, + "snippets": { + "apiKeys": { + "active": "Activas" + }, + "shares": { + "total": "Total" + }, + "title": "Fragmentos", + "viewType": { + "private": "Privados", + "public": "Públicos" + } + }, + "users": { + "authType": { + "internal": "Internos", + "oidc": "OIDC" + }, + "title": "Usuarios" + } + }, + "loadingMessage": "Cargando estadísticas..." +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/admin/tabs/shares.json b/client/src/i18n/locales/es/components/admin/tabs/shares.json new file mode 100644 index 00000000..21b20ed3 --- /dev/null +++ b/client/src/i18n/locales/es/components/admin/tabs/shares.json @@ -0,0 +1,50 @@ +{ + "action": { + "copyShareLink": "Copiar enlace", + "delete": "Eliminar enlace", + "viewSnippet": "Ver fragmento" + }, + "columns": { + "labels": { + "actions": "Acciones", + "auth": "Auth requerida", + "created": "Creado", + "expires": "Expira", + "id": "ID de compartición", + "owner": "Propietario", + "title": "Título" + } + }, + "confirmationModal": { + "message": "¿Estás seguro de que quieres eliminar este enlace? Cualquier persona con el enlace ya no podrá acceder al fragmento. Esta acción no se puede deshacer.", + "title": "Eliminar enlace compartido" + }, + "entityName_one": "enlace", + "entityName_many": "enlaces", + "entityName_other": "enlaces", + "error": { + "delete": { + "default": "Error al eliminar el enlace" + } + }, + "filters": { + "authType": { + "all": "Todos", + "public": "Público", + "requiresAuth": "Requiere auth" + }, + "userId": "Filtrar por User ID" + }, + "success": { + "copied": { + "default": "Enlace copiado al portapapeles" + }, + "delete": { + "default": "Enlace borrado con éxito" + } + }, + "table": { + "emptyMessage": "No se encontraron enlaces activos", + "loadingMessage": "Cargando registros..." + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/admin/tabs/snippets.json b/client/src/i18n/locales/es/components/admin/tabs/snippets.json new file mode 100644 index 00000000..d7eaea07 --- /dev/null +++ b/client/src/i18n/locales/es/components/admin/tabs/snippets.json @@ -0,0 +1,58 @@ +{ + "action": { + "delete": "Eliminar fragmento", + "makePrivate": "Hacer privado", + "makePublic": "Hacer público", + "scanForOffensiveContent": "Escanear contenido ofensivo", + "showAllSnippets": "Mostrar todos", + "viewSnippet": "Visualizar" + }, + "columns": { + "labels": { + "actions": "Acciones", + "fragments": "Archivos", + "owner": "Propietario", + "title": "Anotación", + "updated": "Actualizado", + "visibility": "Visibilidad" + } + }, + "confirmationModal": { + "message": "¿Estás seguro de que quieres eliminar permanentemente este fragmento? Esta acción no se puede deshacer.", + "title": "Eliminar código" + }, + "containsOffensiveWords": "Contiene lenguaje inapropiado: {{words}}", + "entityName_one": "fragmento", + "entityName_many": "fragmentos", + "entityName_other": "fragmentos", + "error": { + "delete": { + "default": "Fallo al remover fragmento" + }, + "update": { + "default": "Error actualizando visibilidad" + } + }, + "filters": { + "search": "Buscar fragmentos...", + "userId": "User ID", + "visibility": { + "all": "Todo", + "private": "Privado", + "public": "Público" + } + }, + "offensiveContentMessage": "Se han detectado {{total}} {{entityName}} con contenido ofensivo", + "success": { + "delete": { + "default": "Fragmento eliminado exitosamente" + }, + "update": { + "default": "Visibilidad editada" + } + }, + "table": { + "emptyMessage": "Lista completamente vacía", + "loadingMessage": "Refrescando fragmentos..." + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/admin/tabs/users.json b/client/src/i18n/locales/es/components/admin/tabs/users.json new file mode 100644 index 00000000..b0e62c09 --- /dev/null +++ b/client/src/i18n/locales/es/components/admin/tabs/users.json @@ -0,0 +1,64 @@ +{ + "action": { + "activate": "Habilitar usuario", + "deactivate": "Inhabilitar usuario", + "delete": "Borrar usuario" + }, + "columns": { + "labels": { + "actions": "Eventos", + "apiKeysCount": "Claves", + "authType": "Tipo de ingreso", + "created": "Registro", + "email": "Correo electrónico", + "lastLogin": "Último ingreso", + "snippetsCount": "Fragmentos", + "status": "Estado de la cuenta", + "username": "Usuario" + } + }, + "confirmationModal": { + "message": "¿Confirmas el borrado de esta persona? Se destruirán de forma absoluta sus fragmentos, contraseñas API y datos compartidos. Esto no se puede revertir.", + "title": "Borrado de cuenta permanentemente" + }, + "entityName_one": "usuario", + "entityName_many": "usuarios", + "entityName_other": "usuarios", + "error": { + "delete": { + "default": "No se pudo borrar al usuario" + }, + "update": { + "default": "Error afectando el estado del usuario" + } + }, + "filters": { + "authType": { + "all": "Tipos autorizados", + "internal": "Público (ByteStash)", + "oidc": "SSO (OIDC)" + }, + "search": "Buscar por email o alias...", + "status": { + "active": "Solamente cuentas activas", + "all": "Todos los perfiles", + "inactive": "Vetados / Inactivos" + } + }, + "status": { + "active": "Estable", + "inactive": "Inactivo" + }, + "success": { + "delete": { + "default": "El usuario ya no existe" + }, + "update": { + "default": "Modificación aplicada correctamente" + } + }, + "table": { + "emptyMessage": "Nadie encontrado", + "loadingMessage": "Validando sesiones de red..." + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/auth.json b/client/src/i18n/locales/es/components/auth.json new file mode 100644 index 00000000..b0101b99 --- /dev/null +++ b/client/src/i18n/locales/es/components/auth.json @@ -0,0 +1,90 @@ +{ + "apiKeysModal": { + "created": "Creado", + "createKey": "Crear clave", + "deleteApiKey": "Eliminar clave API", + "enterKeyName": "Introduce el nombre de la clave", + "lastUsed": "Último uso", + "newApiKey": "Nueva clave API (cópiala ahora, no se volverá a mostrar)", + "notApiKeys": "No se encontraron claves API", + "title": "Claves API", + "yourApiKeys": "Tus Claves API" + }, + "authProvider": { + "error": { + "failedCreateAnonymousSession": "Error al crear sesión anónima" + }, + "info": { + "logoutSuccess": "Sesión cerrada con éxito" + } + }, + "changePasswordModal": { + "confirmNewPassword": "Confirmar nueva contraseña", + "currentPassword": "Contraseña actual", + "error": { + "default": "Error al cambiar contraseña", + "newPasswordMustBeAtLeastCharacters": "La nueva contraseña debe tener al menos {{minLength}} caracteres", + "newPasswordsDoNotMatch": "Las nuevas contraseñas no coinciden" + }, + "newPassword": "Nueva contraseña", + "passwordChangedSuccessfully": "Contraseña cambiada con éxito", + "process": "Cambiando contraseña...", + "title": "Cambiar contraseña" + }, + "login": { + "account": "cuenta", + "browsePublicSnippets": "explorar fragmentos públicos", + "create": "crear una", + "error": { + "invalidUsernameOrPassword": "Usuario o contraseña inválidos" + }, + "orContinueWithPassword": "O continuar con contraseña", + "pleaseSignInToContinue": "Por favor, inicia sesión para continuar", + "signingIn": "Iniciando sesión..." + }, + "oidc": { + "error": { + "auth_failed": "Fallo de autenticación. Esto puede deberse a un intento cancelado o una sesión caducada. Por favor, inténtalo de nuevo.", + "config_error": "Hubo un error con la configuración de SSO. Por favor, contacta a tu administrador.", + "default": "Ocurrió un error inesperado durante la autenticación. Por favor, inténtalo de nuevo.", + "provider_error": "El proveedor de identidad encontró un error o no está disponible. Por favor, inténtalo de nuevo más tarde.", + "registration_disabled": "El registro de cuentas está desactivado en esta instancia. Por favor, contacta a tu administrador." + } + }, + "register": { + "browsePublicSnippets": "explorar fragmentos públicos", + "creatingAccount": "Creando cuenta...", + "disabled": { + "description": "El registro de nuevas cuentas está actualmente desactivado.", + "link": { + "text": "Volver a iniciar sesión" + }, + "title": "Registro desactivado" + }, + "error": { + "default": "Error al registrar cuenta", + "passwordsDoNotMatch": "Las contraseñas no coinciden" + }, + "firstAccountDescription": "Esta es la primera cuenta que se creará. Todos los fragmentos existentes se migrarán automáticamente a esta cuenta.", + "notAvailable": { + "description": "El registro interno está desactivado y no hay proveedores de SSO configurados. Contacta a tu administrador.", + "title": "Registro no disponible" + }, + "signInToYourAccount": "inicia sesión en tu cuenta", + "title": "Crear cuenta" + }, + "signIn": { + "completing": "Completando inicio de sesión...", + "with": "Iniciar sesión con {{displayName}}" + }, + "signOut": { + "completing": "Completando cierre de sesión..." + }, + "userDropdown": { + "adminPanel": "Panel de administración", + "apiKeys": "Claves API", + "changePassword": "Cambiar contraseña", + "signIn": "Iniciar sesión", + "signOut": "Cerrar sesión" + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/categories.json b/client/src/i18n/locales/es/components/categories.json new file mode 100644 index 00000000..9a9b50be --- /dev/null +++ b/client/src/i18n/locales/es/components/categories.json @@ -0,0 +1,12 @@ +{ + "categoryList": { + "moreCount": "{{moreCount}} más", + "noData": "Sin categorías", + "showLess": "Mostrar menos" + }, + "categorySuggestions": { + "addNewLabel": "Añadir nueva", + "placeholder": "Buscar categorías...", + "title": "Categorías" + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/common/buttons.json b/client/src/i18n/locales/es/components/common/buttons.json new file mode 100644 index 00000000..965b45bd --- /dev/null +++ b/client/src/i18n/locales/es/components/common/buttons.json @@ -0,0 +1,59 @@ +{ + "copyButton": { + "title": "Copiar al portapapeles" + }, + "downloadArchiveButton": { + "error": { + "failedDownload": "Error al descargar comprimido" + }, + "fileLabel_one": "{{count, number}} archivo", + "fileLabel_many": "{{count, number}} archivos", + "fileLabel_other": "{{count, number}} archivos", + "label": "Descargar todos", + "success": { + "downloadedAll": "Todos los fragmentos descargados" + }, + "title": "Descargar todos los archivos como ZIP" + }, + "downloadButton": { + "error": { + "failedDownload": "Error al descargar archivo" + }, + "success": { + "downloaded": "\"{{fileName}}\" descargado con éxito" + }, + "title": "Descargar {{fileName}}", + "warning": { + "nothing": "Nada que descargar" + } + }, + "fileUploadButton": { + "error": { + "unknown": "Error desconocido" + }, + "info": { + "duplicateDetected": "Archivo duplicado", + "duplicatesDetected_one": "{{count, number}} archivo duplicado", + "duplicatesDetected_many": "{{count, number}} archivos duplicados", + "duplicatesDetected_other": "{{count, number}} archivos duplicados" + }, + "label": "Subir código(s)", + "loadFromUrl": { + "contentMaxSizeError": "El contenido debe ser menor a {{max}}", + "invalidUrl": "Escribe una URL válida", + "label": "Cargar desde URL", + "title": "Cargar código desde URL externa" + }, + "success": { + "filesUploaded_one": "{{count, number}} archivo subido", + "filesUploaded_many": "{{count, number}} archivos subidos", + "filesUploaded_other": "{{count, number}} archivos subidos", + "fileUploaded": "\"{{fileName}}\" subido con éxito", + "someFilesUploaded": "{{successCount}} de {{total}} archivos subidos" + }, + "title": "Añade archivos crudos" + }, + "rawButton": { + "title": "Visualizar código original" + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/common/dropdowns.json b/client/src/i18n/locales/es/components/common/dropdowns.json new file mode 100644 index 00000000..f1b66276 --- /dev/null +++ b/client/src/i18n/locales/es/components/common/dropdowns.json @@ -0,0 +1,6 @@ +{ + "baseDropdown": { + "addNewLabel": "Añadir nuevo", + "defaultPlaceholder": "Selecciona o escribe un valor" + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/common/markdown.json b/client/src/i18n/locales/es/components/common/markdown.json new file mode 100644 index 00000000..52639bb5 --- /dev/null +++ b/client/src/i18n/locales/es/components/common/markdown.json @@ -0,0 +1,15 @@ +{ + "mermaid": { + "copyCode": "Copiar código Mermaid", + "downloadPNG": "Descargar como PNG", + "downloadSVG": "Descargar como SVG", + "exitFullscreen": "Salir de pantalla completa", + "fullscreen": "Pantalla completa", + "renderError": "Error al renderizar el diagrama Mermaid:", + "renderErrorFallback": "Error al renderizar el gráfico", + "resetView": "Restaurar vista", + "title": "Diagrama de Mermaid", + "zoomIn": "Acercar", + "zoomOut": "Alejar" + } +} diff --git a/client/src/i18n/locales/es/components/common/modals.json b/client/src/i18n/locales/es/components/common/modals.json new file mode 100644 index 00000000..426adc00 --- /dev/null +++ b/client/src/i18n/locales/es/components/common/modals.json @@ -0,0 +1,7 @@ +{ + "changelogModal": { + "error": { + "default": "Error al cargar el registro de cambios. Inténtalo más tarde." + } + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/search.json b/client/src/i18n/locales/es/components/search.json new file mode 100644 index 00000000..2dc34082 --- /dev/null +++ b/client/src/i18n/locales/es/components/search.json @@ -0,0 +1,30 @@ +{ + "action": { + "clear": "Limpiar búsqueda", + "newSnippet": "Nuevo fragmento", + "openSettings": "Abrir ajustes", + "recycleBin": "Papelera", + "showAll": "Mostrar todos", + "showFavorites": "Mostrar favoritos" + }, + "categories": { + "addNew": "Añadir nueva", + "title": "Categorías" + }, + "defaultPlaceholder": "Buscar fragmentos... (Escribe # para ver las categorías)", + "filter": { + "language": { + "all": "Todos los lenguajes" + } + }, + "sort": { + "alphaAsc": "Alfabéticamente A-Z", + "alphaDesc": "Alfabéticamente Z-A", + "newestFirst": "Recientes primero", + "oldestFirst": "Antiguos primero" + }, + "view": { + "grid": "Vista cuadrícula", + "list": "Vista lista" + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/settings.json b/client/src/i18n/locales/es/components/settings.json new file mode 100644 index 00000000..12b16172 --- /dev/null +++ b/client/src/i18n/locales/es/components/settings.json @@ -0,0 +1,95 @@ +{ + "settingsModal": { + "block": { + "category": { + "expandCategories": { + "description": "Expandir grupos de categorías automáticamente", + "label": "Expandir categorías" + }, + "showCategories": { + "description": "Mostrar etiquetas de categoría en fragmentos", + "label": "Mostrar categorías" + }, + "title": "Categoría" + }, + "dataManagement": { + "export": { + "label": "Exportar fragmentos" + }, + "import": { + "errors": { + "failed": "Fallo al importar \"{{title}}\": {{error}}", + "occurred_one": "Ocurrió {{count, number}} error", + "occurred_many": "Ocurrieron {{count, number}} errores", + "occurred_other": "Ocurrieron {{count, number}} errores" + }, + "label": "Importar fragmentos", + "progress": "Importando fragmentos..." + }, + "title": "Gestión de datos" + }, + "locale": { + "title": "Idioma" + }, + "search": { + "includeCodeInSearch": { + "description": "Buscar dentro del contenido de código, no solo títulos", + "label": "Incluir código en búsqueda" + }, + "title": "Buscar" + }, + "theme": { + "title": "Tema" + }, + "view": { + "compactView": { + "description": "Mostrar fragmentos de forma condensada", + "label": "Vista compacta" + }, + "previewLines": { + "description": "Mostrar una vista previa del código en la lista", + "label": "Mostrar vista previa" + }, + "showCodePreview": { + "description": "Número máximo de líneas en la vista previa (1-20)", + "label": "Líneas de vista previa" + }, + "showLineNumbers": { + "description": "Mostrar números de línea en bloques de código", + "label": "Mostrar números de línea" + }, + "title": "Vista" + } + }, + "export": { + "error": { + "default": "Error al exportar fragmentos", + "noSnippets": "No hay fragmentos para exportar" + }, + "markdown": { + "error": { + "default": "Error al exportar a Markdown" + }, + "success": { + "default": "Exportación a Markdown exitosa" + }, + "warning": { + "default": "No hay fragmentos para exportar" + } + }, + "success": { + "default": "{{total}} fragmentos exportados con éxito" + } + }, + "import": { + "error": { + "default": "Error al importar fragmentos" + }, + "hasFailed": "Se importaron {{succeeded}} fragmentos, {{failed}} fallaron. Revisa la consola para detalles.", + "successOnly_one": "Se ha importado {{succeeded}} fragmento. Cierra los ajustes para verlo.", + "successOnly_many": "Se han importado {{succeeded}} fragmentos. Cierra los ajustes para verlos.", + "successOnly_other": "Se han importado {{succeeded}} fragmentos. Cierra los ajustes para verlos." + }, + "title": "Ajustes genéricos" + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/snippets/edit.json b/client/src/i18n/locales/es/components/snippets/edit.json new file mode 100644 index 00000000..3bb534d8 --- /dev/null +++ b/client/src/i18n/locales/es/components/snippets/edit.json @@ -0,0 +1,57 @@ +{ + "editSnippetModal": { + "addSnippet": "Añadir fragmento", + "editSnippet": "Editar fragmento", + "error": { + "savingFailed": "Ocurrió un error al guardar el código. Por favor, inténtalo de nuevo." + }, + "form": { + "categories": { + "counter": "{{categories}}/{{max}} categorías", + "label": "Categorías (max {{max}})", + "placeholder": "Escribe una categoría y pulsa Enter" + }, + "codeFragments": { + "add": "Añadir nuevo archivo", + "label": "Archivos o módulos ({{fragments}})" + }, + "description": { + "label": "Descripción", + "placeholder": "Escribe una breve descripción para este snippet (Markdown soportado)" + }, + "isPublic": { + "description": "Los archivos públicos pueden ser vistos por cualquier persona mediante una URL abierta", + "label": "Hacer fragmento público" + }, + "title": { + "counter": "{{characters}}/{{max}} caracteres", + "label": "Título del paquete", + "placeholder": "Nombra tu fragmento (max {{max}} caracteres)" + } + }, + "fragmentRequired": "Se requiere al menos un fragmento de código", + "mustHaveFileNames": "Todos los fragmentos deben tener nombres de archivo referenciados", + "unsavedChanges": "Tienes cambios sin guardar. ¿Seguro que quieres cerrar?" + }, + "fragmentEditor": { + "action": { + "collapse": "Colapsar", + "delete": "Eliminar módulo", + "expand": "Expandir" + }, + "form": { + "fileName": { + "placeholder": "Nombre del archivo" + }, + "language": { + "placeholder": "Seleccionar lenguaje", + "sections": { + "other": "Otro", + "used": "Usado" + } + } + }, + "moveDown": "Mover abajo", + "moveUp": "Mover arriba" + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/snippets/embed.json b/client/src/i18n/locales/es/components/snippets/embed.json new file mode 100644 index 00000000..1d1587a6 --- /dev/null +++ b/client/src/i18n/locales/es/components/snippets/embed.json @@ -0,0 +1,26 @@ +{ + "embedCopyButton": { + "title": "Copiar al portapapeles" + }, + "embedModal": { + "form": { + "embedCode": "Código de Inserción", + "fragmentToShow": { + "all": "Todos los fragmentos", + "label": "Fragmento a mostrar (opcional)" + }, + "showDescription": "Mostrar descripción", + "showFileHeaders": "Mostrar cabeceras de los archivos", + "showPoweredBy": "Mostrar \"Desarrollado por ByteStash\"", + "showTitle": "Mostrar título", + "theme": "Tema" + }, + "subTitle": "Personalizar Inserción", + "title": "Insertar Fragmento" + }, + "embedView": { + "error": { + "default": "Error al cargar el fragmento" + } + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/snippets/list/snippetCard.json b/client/src/i18n/locales/es/components/snippets/list/snippetCard.json new file mode 100644 index 00000000..bec45fce --- /dev/null +++ b/client/src/i18n/locales/es/components/snippets/list/snippetCard.json @@ -0,0 +1,37 @@ +{ + "confirmationModalDelete": { + "confirmLabel": { + "isRecycleView": { + "false": "Mover a la papelera", + "true": "Eliminar permanentemente" + } + }, + "message": { + "isRecycleView": { + "false": "¿Estás seguro de que quieres enviar \"{{title}}\" a la papelera?", + "true": "¿Estás seguro de que quieres eliminar \"{{title}}\" de forma permanente? Esta acción no se puede deshacer." + } + }, + "title": { + "isRecycleView": { + "false": "Mover a la papelera", + "true": "Confirmar eliminación" + } + } + }, + "confirmationModalRestore": { + "message": "¿Estás seguro de que deseas restaurar \"{{title}}\"?", + "title": "Confirmar restauración" + }, + "date": { + "ago": "Hace {{date}}", + "expiringSoon": "A punto de caducar", + "left": "Quedan {{date}}" + }, + "defaultDescription": "No hay descripción disponible", + "defaultUpdateTime": "Desconocido", + "favorite": "Favorito", + "pinned": "Fijado", + "public": "Público", + "shared": "Compartido" +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/snippets/list/snippetCardMenu.json b/client/src/i18n/locales/es/components/snippets/list/snippetCardMenu.json new file mode 100644 index 00000000..1a5210b4 --- /dev/null +++ b/client/src/i18n/locales/es/components/snippets/list/snippetCardMenu.json @@ -0,0 +1,11 @@ +{ + "addToFavorites": "Añadir a favoritos", + "deleteSnippet": "Eliminar fragmento", + "duplicateSnippet": "Duplicar fragmento", + "editSnippet": "Editar fragmento", + "openInNewTab": "Abrir en nueva pestaña", + "pinSnippet": "Fijar fragmento", + "removeFromFavorites": "Quitar de favoritos", + "shareSnippet": "Compartir código", + "unpinSnippet": "Desfijar fragmento" +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/snippets/list/snippetList.json b/client/src/i18n/locales/es/components/snippets/list/snippetList.json new file mode 100644 index 00000000..7098f5b0 --- /dev/null +++ b/client/src/i18n/locales/es/components/snippets/list/snippetList.json @@ -0,0 +1,3 @@ +{ + "noSnippetsMatch": "Ningún fragmento coincide con tu búsqueda actual" +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/snippets/list/snippetRecycleCardMenu.json b/client/src/i18n/locales/es/components/snippets/list/snippetRecycleCardMenu.json new file mode 100644 index 00000000..6a398e4e --- /dev/null +++ b/client/src/i18n/locales/es/components/snippets/list/snippetRecycleCardMenu.json @@ -0,0 +1,4 @@ +{ + "deleteSnippet": "Destruir fragmento", + "restoreSnippet": "Restaurar a mi colección" +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/snippets/share.json b/client/src/i18n/locales/es/components/snippets/share.json new file mode 100644 index 00000000..5f258131 --- /dev/null +++ b/client/src/i18n/locales/es/components/snippets/share.json @@ -0,0 +1,48 @@ +{ + "sharedSnippetView": { + "browsePublicSnippets": "Explorar fragmentos públicos", + "loadingSnippet": "Cargando fragmento...", + "snippetExpired": "Este enlace compartido ha caducado", + "snippetNotFound": "Fragmento no encontrado" + }, + "shareMenu": { + "activeShareLinks": { + "buttons": { + "copy": "Copiar enlace", + "delete": "Borrar enlace", + "requiresAuth": { + "false": "Insertar fragmento", + "true": "Sólo los fragmentos sin autenticación pueden ser insertados" + } + }, + "date": { + "expired": "Caducado", + "neverExpires": "Nunca caduca" + }, + "noLinks": "No hay comparticiones activas", + "relativeExpiryTime": "Caduca en {{date}}", + "requiresAuth": { + "false": "Acceso público", + "true": "Protegido - Requiere autenticación" + }, + "title": "Enlaces Activos" + }, + "createButtonText": "Crear enlace", + "error": { + "created": "Error al crear la compartición", + "deleted": "Error al eliminar el enlace", + "invalidDuration": "Duración inválida. Usa 1h, 2d, 30m etc.", + "load": "Error al cargar la lista de comparticiones", + "unknownExpiryTime": "Tiempo de caducidad desconocido" + }, + "expiresIn": "Caduca en (ej. 1h, 2d, 30m)", + "expiresInPlaceholder": "Nunca", + "requiresAuth": "Requerir autenticación", + "subTitle": "Crear un nuevo enlace compartido", + "success": { + "created": "Enlace creado", + "deleted": "Enlace eliminado exitosamente" + }, + "title": "Compartir Fragmento" + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/snippets/view/all.json b/client/src/i18n/locales/es/components/snippets/view/all.json new file mode 100644 index 00000000..971b0696 --- /dev/null +++ b/client/src/i18n/locales/es/components/snippets/view/all.json @@ -0,0 +1,29 @@ +{ + "defaultUpdateTime": "Desconocido", + "fullCodeView": { + "dateTimeAgo": "Hace {{dateTime}}", + "defaultDescription": "Sin descripción disponible" + }, + "snippetModal": { + "confirmationModal": { + "confirmLabel": { + "isRecycleView": { + "false": "Enviar a Papelera", + "true": "Eliminar definitivamente" + } + }, + "message": { + "isRecycleView": { + "false": "¿Seguro que deseas enviar \"{{title}}\" a la papelera de reciclaje?", + "true": "¿Confirmas la eliminación permanente de \"{{title}}\"? Esta acción no tiene marcha atrás." + } + }, + "title": { + "isRecycleView": { + "false": "Confirmar Papelera", + "true": "Borrado irreversible" + } + } + } + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/snippets/view/common.json b/client/src/i18n/locales/es/components/snippets/view/common.json new file mode 100644 index 00000000..3a0b56eb --- /dev/null +++ b/client/src/i18n/locales/es/components/snippets/view/common.json @@ -0,0 +1,56 @@ +{ + "authAwareSnippetView": { + "error": { + "snippetLoad": "Error al cargar el fragmento", + "snippetRequireAuth": "Este fragmento requiere autenticación para ser visto" + } + }, + "baseSnippetStorage": { + "error": { + "sessionExpired": "Sesión caducada. Por favor, inicia sesión de nuevo.", + "snippetCreated": "Error al crear el fragmento", + "snippetUpdated": "Error al actualizar el fragmento" + }, + "success": { + "displayAll": "Mostrando todos los fragmentos", + "displayFavorites": "Mostrando fragmentos favoritos", + "snippetCreated": "Nuevo fragmento creado exitosamente", + "snippetUpdated": "Fragmento actualizado exitosamente" + } + }, + "browsePublicSnippets": "Explorar fragmentos públicos", + "filter": { + "filteredByCategories": "Filtrado por categorías" + }, + "loadingSnippets": "Cargando fragmentos...", + "signIn": "Iniciar sesión", + "sippetNotFound": "Fragmento no encontrado", + "snippetContentArea": { + "error": { + "duplicateSnippet": "Error al duplicar el fragmento", + "loadSnippets": "Error al cargar los fragmentos", + "moveSnippetToRecycleBin": "Error al mover el fragmento a la papelera. Por favor, inténtalo de nuevo.", + "sessionExpired": "Sesión caducada. Por favor, inicia sesión de nuevo.", + "updateFavoriteStatusAdded": "Error al actualizar estado de favorito. Por favor, inténtalo de nuevo.", + "updateFavoriteStatusDeleted": "Error al quitar de favoritos. Por favor, inténtalo de nuevo.", + "updatePinStatusAdded": "Error al actualizar estado de fijado. Por favor, inténtalo de nuevo.", + "updatePinStatusDeleted": "Error al quitar fijado. Por favor, inténtalo de nuevo." + }, + "success": { + "duplicateSnippet": "Fragmento duplicado exitosamente", + "moveSnippetToRecycleBin": "Fragmento movido a la papelera exitosamente", + "updateFavoriteStatusAdded": "Fragmento agregado a favoritos exitosamente", + "updateFavoriteStatusDeleted": "Fragmento eliminado de favoritos exitosamente", + "updatePinStatusAdded": "Fragmento fijado exitosamente", + "updatePinStatusDeleted": "Fragmento desfijado exitosamente" + } + }, + "storageHeader": { + "private": "Estás viendo tus fragmentos privados. Solo tú puedes ver y modificar estos fragmentos.", + "public": "Estás viendo fragmentos compartidos públicamente. Estos fragmentos son de solo lectura y visibles para todos." + }, + "viewSwitch": { + "private": "Privado", + "public": "Público" + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/snippets/view/public.json b/client/src/i18n/locales/es/components/snippets/view/public.json new file mode 100644 index 00000000..8eb531e7 --- /dev/null +++ b/client/src/i18n/locales/es/components/snippets/view/public.json @@ -0,0 +1,18 @@ +{ + "publicSnippetContentArea": { + "error": { + "addSnippetToCollection": "Error al agregar el fragmento a tu colección", + "failedLoadSnippets": "Error al cargar los fragmentos públicos" + }, + "filter": { + "byCategories": "Filtrado por categorías" + }, + "info": { + "requireAuth": "Por favor, inicia sesión para agregar este fragmento a tu colección" + }, + "loadingSnippets": "Cargando fragmentos...", + "success": { + "addSnippetToCollection": "Fragmento agregado a tu colección" + } + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/snippets/view/recycle.json b/client/src/i18n/locales/es/components/snippets/view/recycle.json new file mode 100644 index 00000000..1edd305b --- /dev/null +++ b/client/src/i18n/locales/es/components/snippets/view/recycle.json @@ -0,0 +1,37 @@ +{ + "recycleSnippetContentArea": { + "error": { + "deleteSnippet": "Error al eliminar el fragmento", + "loadSnippets": "Error al cargar los fragmentos", + "restoreSnippet": "Error al restaurar el fragmento", + "sessionExpired": "Sesión caducada. Por favor, inicia sesión de nuevo." + }, + "filter": { + "byCategories": "Filtrado por categorías" + }, + "loadingSnippets": "Cargando fragmentos...", + "success": { + "deleteSnippet": "Fragmento eliminado exitosamente", + "restoreSnippet": "Fragmento restaurado exitosamente" + } + }, + "recycleSnippetStorage": { + "backToSnippets": "Volver a fragmentos", + "confirmationModal": { + "message": "¿Estás seguro de que quieres eliminar permanentemente todos los fragmentos en la papelera de reciclaje? Esta acción no se puede deshacer.", + "title": "Confirmar eliminación" + }, + "description": "Los fragmentos en la papelera de reciclaje serán eliminados permanentemente después de 30 días", + "error": { + "clear": "Error al vaciar la papelera de reciclaje. Por favor, inténtalo de nuevo.", + "sessionExpired": "Sesión caducada. Por favor, inicia sesión de nuevo." + }, + "info": { + "noSnippets": "No hay fragmentos en la papelera de reciclaje para vaciar" + }, + "recycleBin": "Papelera de reciclaje", + "success": { + "clear": "Todos los fragmentos en la papelera de reciclaje han sido eliminados" + } + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/components/utils.json b/client/src/i18n/locales/es/components/utils.json new file mode 100644 index 00000000..df40d08d --- /dev/null +++ b/client/src/i18n/locales/es/components/utils.json @@ -0,0 +1,9 @@ +{ + "config": { + "caution": "Precaución", + "important": "Importante", + "note": "Nota", + "tip": "Consejo", + "warning": "Advertencia" + } +} \ No newline at end of file diff --git a/client/src/i18n/locales/es/files.txt b/client/src/i18n/locales/es/files.txt new file mode 100644 index 00000000..c28576a0 Binary files /dev/null and b/client/src/i18n/locales/es/files.txt differ diff --git a/client/src/i18n/locales/es/translation.json b/client/src/i18n/locales/es/translation.json new file mode 100644 index 00000000..e1044160 --- /dev/null +++ b/client/src/i18n/locales/es/translation.json @@ -0,0 +1,51 @@ +{ + "action": { + "addSnippet": "Añadir fragmento", + "cancel": "Cancelar", + "changePassword": "Cambiar contraseña", + "clear": "Limpiar", + "clearAll": "Limpiar todo", + "close": "Cerrar", + "confirm": "Confirmar", + "createAccount": "Crear cuenta", + "delete": "Eliminar", + "edit": "Editar", + "hidePassword": "Ocultar contraseña", + "load": "Cargar", + "maximize": "Maximizar", + "minimize": "Minimizar", + "restore": "Restaurar", + "save": "Guardar", + "showPassword": "Mostrar contraseña", + "signIn": "Iniciar sesión" + }, + "confirmPassword": "Confirmar contraseña", + "loading": "Cargando...", + "locale": { + "en": "Inglés", + "es": "Español", + "ru": "Ruso" + }, + "no": "no", + "or": "o", + "pagination": { + "next": "Siguiente", + "pageOf": "Página {{currentPage}} de {{totalPages}}", + "previous": "Anterior", + "total": "{{total}} en total", + "useSnippetPagination": { + "error": { + "failedSnippetsLoad": "Error al cargar fragmentos", + "sessionExpired": "Sesión caducada. Por favor, inicia sesión de nuevo." + } + } + }, + "password": "Contraseña", + "showing": "Mostrando", + "theme": { + "dark": "Oscuro", + "light": "Claro", + "system": "Sistema" + }, + "username": "Nombre de usuario" +} diff --git a/client/src/i18n/locales/ru/components/common/markdown.json b/client/src/i18n/locales/ru/components/common/markdown.json index bce164bc..23593a74 100644 --- a/client/src/i18n/locales/ru/components/common/markdown.json +++ b/client/src/i18n/locales/ru/components/common/markdown.json @@ -1,15 +1,15 @@ { "mermaid": { - "title": "Диаграмма Mermaid", - "renderError": "Не удалось отрисовать диаграмму Mermaid:", - "renderErrorFallback": "Ошибка отрисовки диаграммы", - "zoomIn": "Приблизить", - "zoomOut": "Отдалить", - "resetView": "Сбросить вид", "copyCode": "Копировать код Mermaid", - "downloadSVG": "Скачать как SVG", "downloadPNG": "Скачать как PNG", + "downloadSVG": "Скачать как SVG", + "exitFullscreen": "Выйти из полноэкранного режима", "fullscreen": "Полный экран", - "exitFullscreen": "Выйти из полноэкранного режима" + "renderError": "Не удалось отрисовать диаграмму Mermaid:", + "renderErrorFallback": "Ошибка отрисовки диаграммы", + "resetView": "Сбросить вид", + "title": "Диаграмма Mermaid", + "zoomIn": "Приблизить", + "zoomOut": "Отдалить" } -} \ No newline at end of file +} diff --git a/client/src/i18n/locales/ru/translation.json b/client/src/i18n/locales/ru/translation.json index 9195f199..dca7e8be 100644 --- a/client/src/i18n/locales/ru/translation.json +++ b/client/src/i18n/locales/ru/translation.json @@ -23,6 +23,7 @@ "loading": "Загрузка...", "locale": { "en": "Английский", + "es": "Испанский", "ru": "Русский" }, "no": "нет", @@ -47,4 +48,4 @@ "system": "Системная" }, "username": "Имя пользователя" -} \ No newline at end of file +} diff --git a/client/src/i18n/resources/es.ts b/client/src/i18n/resources/es.ts new file mode 100644 index 00000000..f31ec7c1 --- /dev/null +++ b/client/src/i18n/resources/es.ts @@ -0,0 +1,61 @@ +import translation from '../locales/es/translation.json' +import componentsAdminCommon from '../locales/es/components/admin/common.json' +import componentsAdminModals from '../locales/es/components/admin/modals.json' +import componentsAdminSelector from '../locales/es/components/admin/selector.json' +import componentsAdminApiKeysTab from '../locales/es/components/admin/tabs/apiKeys.json' +import componentsAdminUsersTab from '../locales/es/components/admin/tabs/users.json' +import componentsAdminSharesTab from '../locales/es/components/admin/tabs/shares.json' +import componentsAdminSnippetsTab from '../locales/es/components/admin/tabs/snippets.json' +import componentsAdminDashboardTab from '../locales/es/components/admin/tabs/dashboard.json' +import componentsAuth from '../locales/es/components/auth.json' +import componentsCategories from '../locales/es/components/categories.json' +import componentsCommonButtons from '../locales/es/components/common/buttons.json' +import componentsCommonDropdowns from '../locales/es/components/common/dropdowns.json' +import componentsCommonModals from '../locales/es/components/common/modals.json' +import componentsSearch from '../locales/es/components/search.json' +import componentsSettings from '../locales/es/components/settings.json' +import componentsSnippetsEdit from '../locales/es/components/snippets/edit.json' +import componentsSnippetsEmbed from '../locales/es/components/snippets/embed.json' +import componentsSnippetsListSnippetCard from '../locales/es/components/snippets/list/snippetCard.json' +import componentsSnippetsListSnippetCardMenu from '../locales/es/components/snippets/list/snippetCardMenu.json' +import componentsSnippetsListSnippetList from '../locales/es/components/snippets/list/snippetList.json' +import componentsSnippetsListSnippetRecycleCardMenu from '../locales/es/components/snippets/list/snippetRecycleCardMenu.json' +import componentsSnippetsShare from '../locales/es/components/snippets/share.json' +import componentsSnippetsViewAll from '../locales/es/components/snippets/view/all.json' +import componentsSnippetsViewCommon from '../locales/es/components/snippets/view/common.json' +import componentsSnippetsViewPublic from '../locales/es/components/snippets/view/public.json' +import componentsSnippetsViewRecycle from '../locales/es/components/snippets/view/recycle.json' +import componentsCommonMarkdown from '../locales/es/components/common/markdown.json' +import componentsUtils from '../locales/es/components/utils.json' + +export const resources = { + translation, + 'components/admin/common': componentsAdminCommon, + 'components/admin/modals': componentsAdminModals, + 'components/admin/selector': componentsAdminSelector, + 'components/admin/tabs/apiKeys': componentsAdminApiKeysTab, + 'components/admin/tabs/users': componentsAdminUsersTab, + 'components/admin/tabs/shares': componentsAdminSharesTab, + 'components/admin/tabs/snippets': componentsAdminSnippetsTab, + 'components/admin/tabs/dashboard': componentsAdminDashboardTab, + 'components/auth': componentsAuth, + 'components/categories': componentsCategories, + 'components/common/buttons': componentsCommonButtons, + 'components/common/dropdowns': componentsCommonDropdowns, + 'components/common/markdown': componentsCommonMarkdown, + 'components/common/modals': componentsCommonModals, + 'components/search': componentsSearch, + 'components/settings': componentsSettings, + 'components/snippets/edit': componentsSnippetsEdit, + 'components/snippets/embed': componentsSnippetsEmbed, + 'components/snippets/list/snippetCard': componentsSnippetsListSnippetCard, + 'components/snippets/list/snippetCardMenu': componentsSnippetsListSnippetCardMenu, + 'components/snippets/list/snippetList': componentsSnippetsListSnippetList, + 'components/snippets/list/snippetRecycleCardMenu': componentsSnippetsListSnippetRecycleCardMenu, + 'components/snippets/share': componentsSnippetsShare, + 'components/snippets/view/all': componentsSnippetsViewAll, + 'components/snippets/view/common': componentsSnippetsViewCommon, + 'components/snippets/view/public': componentsSnippetsViewPublic, + 'components/snippets/view/recycle': componentsSnippetsViewRecycle, + 'components/utils': componentsUtils, +}; diff --git a/client/src/i18n/resources/index.ts b/client/src/i18n/resources/index.ts index 9eae1548..12ce06e5 100644 --- a/client/src/i18n/resources/index.ts +++ b/client/src/i18n/resources/index.ts @@ -1,7 +1,9 @@ import { resources as enResources } from './en'; import { resources as ruResources } from './ru'; +import { resources as esResources } from './es'; export const resources = { en: enResources, ru: ruResources, + es: esResources, }; \ No newline at end of file diff --git a/client/src/i18n/types.ts b/client/src/i18n/types.ts index de7a8f6e..f589ee30 100644 --- a/client/src/i18n/types.ts +++ b/client/src/i18n/types.ts @@ -1,4 +1,5 @@ export enum Locale { en = 'en', ru = 'ru', + es = 'es', } \ No newline at end of file