Files
ContainerFlow/src/client/i18n.tsx
T
2026-05-10 20:49:06 +00:00

525 lines
22 KiB
TypeScript

import { createContext, useContext, useState, useCallback, type ReactNode } from "react";
const en = {
// Header / Nav
"header.dashboard": "Dashboard",
"header.monitoring": "Monitoring",
"header.settings": "Settings",
"header.recentEvents": "Recent Events",
"header.noEvents": "No events yet",
// Footer
"footer.live": "Live",
"footer.offline": "Offline",
"footer.containers": "containers",
"footer.projects": "projects",
// Filter
"filter.projects": "Projects",
"filter.all": "All",
// Login
"login.connecting": "Connecting...",
"login.connect": "Connect",
"login.invalidToken": "Invalid token",
"login.connectionFailed": "Connection failed",
"login.establishingConnection": "Establishing secure connection...",
"login.validatingToken": "Validating AUTH_TOKEN...",
"login.tokenAccepted": "Token accepted",
"login.loadingDocker": "Loading Docker socket...",
"login.connectionEstablished": "Connection established!",
"login.errorInvalidToken": "ERROR: Invalid token",
"login.errorConnectionRefused": "Connection refused",
"login.errorConnectionFailed": "ERROR: Connection failed",
// Context menu — labels stay in English (match docker commands)
"actions.restart": "Restart",
"actions.stop": "Stop",
"actions.start": "Start",
"actions.remove": "Remove",
"actions.rebuild": "Rebuild",
"actions.recreate": "Recreate",
"actions.openLogs": "Open Logs",
"actions.open": "Open",
"actions.retry": "Retry",
// Action tooltips (hover descriptions)
"actions.start.tooltip": "Starts the container (docker start)",
"actions.stop.tooltip": "Stops the container with SIGTERM, then SIGKILL after timeout (docker stop)",
"actions.restart.tooltip": "Stops and starts the same container (docker restart). Keeps image and config.",
"actions.rebuild.tooltip": "Rebuilds the image from Dockerfile and creates a new container (docker compose up --build). Apply code changes.",
"actions.recreate.tooltip": "Recreates the container with current compose config, reusing existing image (docker compose up --force-recreate). Apply compose changes without rebuilding.",
"actions.remove.tooltip": "Stops and permanently removes the container (docker rm). For compose services, also cleans associated networks.",
"actions.retry.tooltip": "Retries starting a crashed container",
// Action error toast
"toast.actionFailed": "{action} failed",
"toast.dismiss": "Dismiss",
"toast.dismissAll": "Dismiss all",
"toast.copy": "Copy",
"toast.copied": "Copied",
"toast.expand": "Show more",
"toast.collapse": "Show less",
// Access control (ALLOWED_PATHS)
"access.viewOnly": "View-only",
"access.restricted": "Outside ALLOWED_PATHS — actions disabled",
// Edge legend
"legend.connections": "Connections",
// Service node
"node.noTag": "No Tag",
"node.cpu": "CPU",
"node.mem": "MEM",
// Detail panel
"detail.processing": "Processing...",
"detail.logs": "Logs",
"detail.info": "Info",
"detail.stats": "Stats",
"detail.env": "Env",
"detail.config": "Config",
"detail.exec": "Exec",
"detail.collapse": "Collapse",
"detail.expand": "Expand",
"detail.close": "Close",
"detail.cancel": "Cancel",
"detail.loadingLogs": "Loading logs...",
"detail.noLogs": "No logs available",
"detail.linesHidden": "lines hidden",
"detail.streaming": "streaming",
// Detail panel - Info tab
"detail.status": "Status",
"detail.image": "Image",
"detail.container": "Container",
"detail.project": "Project",
"detail.compose": "Compose",
"detail.envFile": "Env File",
"detail.envFileAutoDetect": "Auto (detect)",
"detail.envFileAuto": "Auto",
"detail.envFileTip": "Only files starting with .env are detected",
"detail.ports": "Ports",
"detail.networks": "Networks",
"detail.connectedTo": "Connected to",
// Detail panel - Config tab
"detail.restartPolicy": "Restart Policy",
"detail.resourceLimits": "Resource Limits",
"detail.memoryLimit": "Memory Limit",
"detail.cpuQuota": "CPU Quota",
"detail.unlimited": "Unlimited",
"detail.threshold": "Threshold",
"detail.thresholdTooltip": "Alert threshold — sends a Discord notification when exceeded",
"detail.limit": "Limit",
"detail.limitTooltip": "Maximum resource allocated to this container in Docker",
"detail.avg": "Avg",
"detail.healthCheck": "Health Check",
"detail.healthNotConfigured": "Not configured",
"detail.recentChecks": "Recent checks",
// Detail panel - Env tab
"detail.variables": "variables",
"detail.copyAll": "Copy all",
"detail.copied": "Copied!",
"detail.hideAll": "Hide all",
"detail.showAll": "Show all",
"detail.noEnvVars": "No environment variables available",
// Detail panel - Stats tab
"detail.cpuUsage": "CPU Usage",
"detail.memoryUsage": "Memory Usage",
"detail.memory": "Memory",
"detail.noStats": "No stats available",
"detail.cpuHistory": "CPU History",
"detail.memoryHistory": "Memory History",
"detail.noHistory": "No historical data available",
"detail.loadingHistory": "Loading history...",
// Detail panel - Warning banners
"detail.noMemoryLimit": "No memory limit configured in Docker",
"detail.noCpuLimit": "No CPU limit configured in Docker",
"detail.weakRestartPolicy": "Restart policy: none — container won't restart automatically if it stops",
// Detail panel - Notification settings
"detail.notifications": "Notifications",
"detail.notificationsEnabled": "Enabled",
"detail.cpuThreshold": "CPU threshold",
"detail.memThreshold": "Memory threshold",
"detail.global": "(global)",
"detail.custom": "(custom)",
"detail.saveSettings": "Save",
"detail.settingsSaved": "Saved",
"detail.savingSettings": "Saving...",
"detail.thresholdHint": "Drag line to set alert threshold",
// Detail panel - Actions / Confirmations
"detail.actionSuccess": "successful",
"detail.actionFailed": "Failed to",
"detail.confirmStop": "Stop this container? This will interrupt the service.",
"detail.confirmRestart": "Restart this container? This will briefly interrupt the service.",
"detail.confirmRemove": "Remove this container? This will stop and delete it.",
"detail.confirmRebuild": "Rebuild this container? This will rebuild the image and recreate the container.",
"detail.confirmRecreate": "Recreate this container? Reuses the existing image and applies current compose config.",
// Detail panel - Crash
"detail.containerCrashed": "Container crashed",
"detail.exitCode": "Exit code",
"detail.oomKilled": "OOM Killed",
"detail.restarted": "Restarted",
"detail.times": "times",
"detail.checkLogs": "Check the logs below for details",
// Detail panel - Exec
"detail.execPlaceholder": "e.g. python manage.py migrate",
"detail.run": "Run",
"detail.exitCodeLabel": "Exit code",
"detail.noOutput": "(no output)",
// Log panel
"logPanel.streaming": "streaming",
"logPanel.loadingLogs": "Loading logs...",
"logPanel.noLogs": "No logs available",
// Monitoring page
"monitoring.title": "Event History",
"monitoring.subtitle": "Docker container events in real-time",
"monitoring.noEvents": "No events yet. Events will appear here as containers start, stop, or restart.",
"monitoring.alertRules": "Alert Rules",
"monitoring.alertRulesDesc": "Configure alerting rules for container events \u2014 coming soon",
"monitoring.statsHistory": "Resource Usage History",
"monitoring.loadingHistory": "Loading historical data...",
"monitoring.noHistoryData": "No historical data available yet",
"monitoring.selectFilter": "Select a service or load all to view history",
"monitoring.loadAll": "Load all",
"monitoring.allServices": "All services",
"monitoring.allProjects": "All projects",
"monitoring.filterService": "Filter by service",
"monitoring.filterProject": "Filter by project",
// Settings page
"settings.title": "Settings",
"settings.subtitle": "Application configuration",
"settings.general": "General",
"settings.version": "Version",
"settings.mode": "Mode",
"settings.singleHost": "Single Host",
"settings.projects": "Projects",
"settings.containers": "Containers",
"settings.dockerHosts": "Docker Hosts",
"settings.dockerHostsDesc": "Multi-host management \u2014 coming soon",
"settings.dockerHostsDetail": "Connect to remote Docker daemons and manage multiple hosts from a single dashboard.",
"settings.discord": "Discord Notifications",
"settings.webhookUrl": "Webhook URL",
"settings.sending": "Sending...",
"settings.test": "Test",
"settings.webhookSuccess": "Webhook sent successfully!",
"settings.events": "Events",
"settings.containerStateChanges": "Container state changes",
"settings.containerStateChangesTooltip": "Notifies when Docker detects automatic state changes: start, stop, die, restart, or health status changes (crashes, OOM, restart policies).",
"settings.resourceAlerts": "Resource alerts",
"settings.resourceAlertsTooltip": "Monitors CPU and memory usage every 5 seconds. Sends an alert when a container exceeds the configured thresholds.",
"settings.uiActions": "UI actions",
"settings.uiActionsTooltip": "Notifies when someone performs a manual action from the ContainerFlow UI: stop, start, restart, or rebuild.",
"settings.actionErrors": "Action errors",
"settings.actionErrorsTooltip": "Notifies when an action fails, such as a rebuild that exits with an error. Includes the error details in the message.",
"settings.resourceThresholds": "Resource Thresholds",
"settings.resourceThresholdsTooltip": "Set the percentage at which CPU or memory usage triggers a Discord alert. Checked every 5 seconds during stats polling.",
"settings.cpu": "CPU",
"settings.memory": "Memory",
"settings.cooldown": "Cooldown (minutes)",
"settings.cooldownTooltip": "Minimum time between duplicate notifications for the same container and event type. Applies to state changes, resource alerts, and UI actions.",
"settings.downReminder": "Down service reminder (minutes)",
"settings.downReminderTooltip": "How often to resend a notification while a container remains down. You will keep receiving alerts at this interval until the service recovers.",
"settings.saving": "Saving...",
"settings.saved": "Saved",
"settings.save": "Save",
"settings.configSaved": "Configuration saved",
"settings.requestFailed": "Request failed",
} as const;
export type TranslationKey = keyof typeof en;
const es: Record<TranslationKey, string> = {
// Header / Nav
"header.dashboard": "Dashboard",
"header.monitoring": "Monitoreo",
"header.settings": "Configuraci\u00f3n",
"header.recentEvents": "Eventos Recientes",
"header.noEvents": "Sin eventos a\u00fan",
// Footer
"footer.live": "En vivo",
"footer.offline": "Desconectado",
"footer.containers": "contenedores",
"footer.projects": "proyectos",
// Filter
"filter.projects": "Proyectos",
"filter.all": "Todos",
// Login
"login.connecting": "Conectando...",
"login.connect": "Conectar",
"login.invalidToken": "Token inv\u00e1lido",
"login.connectionFailed": "No se pudo conectar",
"login.establishingConnection": "Estableciendo conexi\u00f3n segura...",
"login.validatingToken": "Validando AUTH_TOKEN...",
"login.tokenAccepted": "Token aceptado",
"login.loadingDocker": "Cargando socket de Docker...",
"login.connectionEstablished": "\u00a1Conexi\u00f3n establecida!",
"login.errorInvalidToken": "ERROR: Token inv\u00e1lido",
"login.errorConnectionRefused": "Conexi\u00f3n rechazada",
"login.errorConnectionFailed": "ERROR: Conexi\u00f3n fallida",
// Context menu — labels stay in English (match docker commands, evita confusion)
"actions.restart": "Restart",
"actions.stop": "Stop",
"actions.start": "Start",
"actions.remove": "Remove",
"actions.rebuild": "Rebuild",
"actions.recreate": "Recreate",
"actions.openLogs": "Ver Logs",
"actions.open": "Abrir",
"actions.retry": "Retry",
// Action tooltips (hover descriptions)
"actions.start.tooltip": "Inicia el contenedor (docker start)",
"actions.stop.tooltip": "Detiene el contenedor con SIGTERM, luego SIGKILL tras el timeout (docker stop)",
"actions.restart.tooltip": "Detiene y vuelve a iniciar el mismo contenedor (docker restart). Mantiene imagen y configuración.",
"actions.rebuild.tooltip": "Reconstruye la imagen desde el Dockerfile y crea un contenedor nuevo (docker compose up --build). Para aplicar cambios de código.",
"actions.recreate.tooltip": "Recrea el contenedor con la config actual del compose, reusando la imagen existente (docker compose up --force-recreate). Para aplicar cambios de compose sin rebuild.",
"actions.remove.tooltip": "Detiene y elimina el contenedor permanentemente (docker rm). Para servicios compose, también limpia networks asociadas.",
"actions.retry.tooltip": "Reintenta arrancar un contenedor que crasheó",
// Action error toast
"toast.actionFailed": "Error en {action}",
"toast.dismiss": "Descartar",
"toast.dismissAll": "Descartar todos",
"toast.copy": "Copiar",
"toast.copied": "Copiado",
"toast.expand": "Ver más",
"toast.collapse": "Ver menos",
// Access control (ALLOWED_PATHS)
"access.viewOnly": "Solo lectura",
"access.restricted": "Fuera de ALLOWED_PATHS — acciones deshabilitadas",
// Edge legend
"legend.connections": "Conexiones",
// Service node
"node.noTag": "Sin Tag",
"node.cpu": "CPU",
"node.mem": "MEM",
// Detail panel
"detail.processing": "Procesando...",
"detail.logs": "Logs",
"detail.info": "Info",
"detail.stats": "Stats",
"detail.env": "Env",
"detail.config": "Config",
"detail.exec": "Exec",
"detail.collapse": "Colapsar",
"detail.expand": "Expandir",
"detail.close": "Cerrar",
"detail.cancel": "Cancelar",
"detail.loadingLogs": "Cargando logs...",
"detail.noLogs": "No hay logs disponibles",
"detail.linesHidden": "l\u00edneas ocultas",
"detail.streaming": "en vivo",
// Detail panel - Info tab
"detail.status": "Estado",
"detail.image": "Imagen",
"detail.container": "Contenedor",
"detail.project": "Proyecto",
"detail.compose": "Compose",
"detail.envFile": "Env File",
"detail.envFileAutoDetect": "Auto (detectar)",
"detail.envFileAuto": "Auto",
"detail.envFileTip": "Solo se detectan archivos que comienzan con .env",
"detail.ports": "Puertos",
"detail.networks": "Redes",
"detail.connectedTo": "Conectado a",
// Detail panel - Config tab
"detail.restartPolicy": "Pol\u00edtica de Reinicio",
"detail.resourceLimits": "L\u00edmites de Recursos",
"detail.memoryLimit": "L\u00edmite de Memoria",
"detail.cpuQuota": "Cuota de CPU",
"detail.unlimited": "Sin l\u00edmite",
"detail.threshold": "Umbral",
"detail.thresholdTooltip": "Umbral de alerta \u2014 env\u00eda una notificaci\u00f3n a Discord cuando se supera",
"detail.limit": "L\u00edmite",
"detail.limitTooltip": "Recurso m\u00e1ximo asignado a este contenedor en Docker",
"detail.avg": "Prom",
"detail.healthCheck": "Health Check",
"detail.healthNotConfigured": "No configurado",
"detail.recentChecks": "Chequeos recientes",
// Detail panel - Env tab
"detail.variables": "variables",
"detail.copyAll": "Copiar todo",
"detail.copied": "\u00a1Copiado!",
"detail.hideAll": "Ocultar todo",
"detail.showAll": "Mostrar todo",
"detail.noEnvVars": "No hay variables de entorno disponibles",
// Detail panel - Stats tab
"detail.cpuUsage": "Uso de CPU",
"detail.memoryUsage": "Uso de Memoria",
"detail.memory": "Memoria",
"detail.noStats": "No hay estad\u00edsticas disponibles",
"detail.cpuHistory": "Historial de CPU",
"detail.memoryHistory": "Historial de Memoria",
"detail.noHistory": "No hay datos hist\u00f3ricos disponibles",
"detail.loadingHistory": "Cargando historial...",
// Detail panel - Warning banners
"detail.noMemoryLimit": "Sin l\u00edmite de memoria configurado en Docker",
"detail.noCpuLimit": "Sin l\u00edmite de CPU configurado en Docker",
"detail.weakRestartPolicy": "Restart policy: none \u2014 el contenedor no se reiniciar\u00e1 autom\u00e1ticamente si se detiene",
// Detail panel - Notification settings
"detail.notifications": "Notificaciones",
"detail.notificationsEnabled": "Habilitadas",
"detail.cpuThreshold": "Umbral de CPU",
"detail.memThreshold": "Umbral de memoria",
"detail.global": "(global)",
"detail.custom": "(personalizado)",
"detail.saveSettings": "Guardar",
"detail.settingsSaved": "Guardado",
"detail.savingSettings": "Guardando...",
"detail.thresholdHint": "Arrastra la l\u00ednea para configurar el umbral de alerta",
// Detail panel - Actions / Confirmations
"detail.actionSuccess": "exitoso",
"detail.actionFailed": "Error al",
"detail.confirmStop": "\u00bfDetener este contenedor? Esto interrumpir\u00e1 el servicio.",
"detail.confirmRestart": "\u00bfReiniciar este contenedor? Esto interrumpir\u00e1 brevemente el servicio.",
"detail.confirmRemove": "\u00bfEliminar este contenedor? Esto lo detendr\u00e1 y eliminar\u00e1.",
"detail.confirmRebuild": "\u00bfReconstruir este contenedor? Esto reconstruir\u00e1 la imagen y recrear\u00e1 el contenedor.",
"detail.confirmRecreate": "\u00bfRecrear este contenedor? Reusa la imagen existente y aplica la config actual del compose.",
// Detail panel - Crash
"detail.containerCrashed": "Contenedor crash\u00f3",
"detail.exitCode": "C\u00f3digo de salida",
"detail.oomKilled": "OOM Killed",
"detail.restarted": "Reiniciado",
"detail.times": "veces",
"detail.checkLogs": "Revisa los logs abajo para m\u00e1s detalles",
// Detail panel - Exec
"detail.execPlaceholder": "ej. python manage.py migrate",
"detail.run": "Ejecutar",
"detail.exitCodeLabel": "C\u00f3digo de salida",
"detail.noOutput": "(sin salida)",
// Log panel
"logPanel.streaming": "en vivo",
"logPanel.loadingLogs": "Cargando logs...",
"logPanel.noLogs": "No hay logs disponibles",
// Monitoring page
"monitoring.title": "Historial de Eventos",
"monitoring.subtitle": "Eventos de contenedores Docker en tiempo real",
"monitoring.noEvents": "Sin eventos a\u00fan. Los eventos aparecer\u00e1n aqu\u00ed cuando los contenedores inicien, se detengan o reinicien.",
"monitoring.alertRules": "Reglas de Alerta",
"monitoring.alertRulesDesc": "Configurar reglas de alerta para eventos de contenedores \u2014 pr\u00f3ximamente",
"monitoring.statsHistory": "Historial de Uso de Recursos",
"monitoring.loadingHistory": "Cargando datos hist\u00f3ricos...",
"monitoring.noHistoryData": "No hay datos hist\u00f3ricos disponibles a\u00fan",
"monitoring.selectFilter": "Selecciona un servicio o carga todos para ver el historial",
"monitoring.loadAll": "Cargar todos",
"monitoring.allServices": "Todos los servicios",
"monitoring.allProjects": "Todos los proyectos",
"monitoring.filterService": "Filtrar por servicio",
"monitoring.filterProject": "Filtrar por proyecto",
// Settings page
"settings.title": "Configuraci\u00f3n",
"settings.subtitle": "Configuraci\u00f3n de la aplicaci\u00f3n",
"settings.general": "General",
"settings.version": "Versi\u00f3n",
"settings.mode": "Modo",
"settings.singleHost": "Host \u00danico",
"settings.projects": "Proyectos",
"settings.containers": "Contenedores",
"settings.dockerHosts": "Hosts de Docker",
"settings.dockerHostsDesc": "Gesti\u00f3n multi-host \u2014 pr\u00f3ximamente",
"settings.dockerHostsDetail": "Conecta a daemons de Docker remotos y gestiona m\u00faltiples hosts desde un solo dashboard.",
"settings.discord": "Notificaciones de Discord",
"settings.webhookUrl": "URL del Webhook",
"settings.sending": "Enviando...",
"settings.test": "Probar",
"settings.webhookSuccess": "\u00a1Webhook enviado exitosamente!",
"settings.events": "Eventos",
"settings.containerStateChanges": "Cambios de estado de contenedores",
"settings.containerStateChangesTooltip": "Notifica cuando Docker detecta cambios de estado autom\u00e1ticos: inicio, parada, muerte, reinicio o cambios de salud (crashes, OOM, pol\u00edticas de reinicio).",
"settings.resourceAlerts": "Alertas de recursos",
"settings.resourceAlertsTooltip": "Monitorea el uso de CPU y memoria cada 5 segundos. Env\u00eda una alerta cuando un contenedor excede los umbrales configurados.",
"settings.uiActions": "Acciones de UI",
"settings.uiActionsTooltip": "Notifica cuando alguien realiza una acci\u00f3n manual desde la UI de ContainerFlow: detener, iniciar, reiniciar o reconstruir.",
"settings.actionErrors": "Errores de acciones",
"settings.actionErrorsTooltip": "Notifica cuando una acci\u00f3n falla, como un rebuild que termina con error. Incluye los detalles del error en el mensaje.",
"settings.resourceThresholds": "Umbrales de Recursos",
"settings.resourceThresholdsTooltip": "Configura el porcentaje en el que el uso de CPU o memoria dispara una alerta de Discord. Se revisa cada 5 segundos durante el polling de estad\u00edsticas.",
"settings.cpu": "CPU",
"settings.memory": "Memoria",
"settings.cooldown": "Cooldown (minutos)",
"settings.cooldownTooltip": "Tiempo m\u00ednimo entre notificaciones duplicadas para el mismo contenedor y tipo de evento. Aplica a cambios de estado, alertas de recursos y acciones de UI.",
"settings.downReminder": "Recordatorio de servicio ca\u00eddo (minutos)",
"settings.downReminderTooltip": "Cada cu\u00e1nto reenviar una notificaci\u00f3n mientras un contenedor siga ca\u00eddo. Seguir\u00e1s recibiendo alertas en este intervalo hasta que el servicio se recupere.",
"settings.saving": "Guardando...",
"settings.saved": "Guardado",
"settings.save": "Guardar",
"settings.configSaved": "Configuraci\u00f3n guardada",
"settings.requestFailed": "Error en la solicitud",
};
export type Lang = "en" | "es";
interface I18nContextValue {
lang: Lang;
setLang: (lang: Lang) => void;
t: (key: TranslationKey) => string;
}
const I18nContext = createContext<I18nContextValue | null>(null);
const dictionaries = { en, es } as const;
export function I18nProvider({ children }: { children: ReactNode }) {
const [lang, setLangState] = useState<Lang>(() => {
try {
const saved = localStorage.getItem("df:lang");
if (saved === "es" || saved === "en") return saved;
} catch {}
return "en";
});
const setLang = useCallback((l: Lang) => {
setLangState(l);
try { localStorage.setItem("df:lang", l); } catch {}
}, []);
const t = useCallback((key: TranslationKey): string => {
return dictionaries[lang][key] || key;
}, [lang]);
return (
<I18nContext.Provider value={{ lang, setLang, t }}>
{children}
</I18nContext.Provider>
);
}
export function useT() {
const ctx = useContext(I18nContext);
if (!ctx) throw new Error("useT must be used within I18nProvider");
return ctx;
}