/** * Internationalization utility for backend error messages and system messages */ const SUPPORTED_LANGUAGES = ['en', 'sv']; const DEFAULT_LANGUAGE = 'en'; // Error message translations const ERROR_MESSAGES = { en: { // Authentication errors TOKEN_EXPIRED: 'Your session has expired. Please log in again.', INVALID_TOKEN: 'Invalid authentication token. Please log in again.', TOKEN_NOT_ACTIVE: 'Authentication token is not yet active.', USER_NOT_FOUND: 'User account not found. Please contact support.', ACCOUNT_DEACTIVATED: 'Your account has been deactivated. Please contact support.', AUTH_REQUIRED: 'Authentication is required to access this resource.', NO_TOKEN: 'No authentication token provided.', // Authorization errors INSUFFICIENT_PERMISSIONS: 'Access denied. This action requires {requiredRoles} permissions, but you have {userRole} permissions.', PERMISSION_DENIED: 'You do not have permission to perform this action.', INSUFFICIENT_MANAGEMENT_PRIVILEGES: 'Insufficient management privileges to access this resource.', // Management errors AUTHENTICATION_REQUIRED: 'Management authentication required.', INSUFFICIENT_PRIVILEGES: 'Insufficient management privileges.', INVALID_MANAGEMENT_CREDENTIALS: 'Invalid management credentials.', MANAGEMENT_AUTH_ERROR: 'Management authentication error.', // Tenant management TENANT_NOT_FOUND: 'Tenant not found.', TENANT_CREATION_FAILED: 'Failed to create tenant.', TENANT_UPDATE_FAILED: 'Failed to update tenant.', TENANT_DELETE_FAILED: 'Failed to delete tenant.', TENANT_SLUG_EXISTS: 'Tenant slug already exists.', DOMAIN_EXISTS: 'Domain already exists for another tenant.', CANNOT_DELETE_DEFAULT: 'Cannot delete default tenant.', NAME_SLUG_REQUIRED: 'Name and slug are required.', TENANT_CREATED: 'Tenant created successfully.', TENANT_UPDATED: 'Tenant updated successfully.', TENANT_DELETED: 'Tenant deleted successfully.', FETCH_TENANTS_FAILED: 'Failed to fetch tenants.', FETCH_TENANT_FAILED: 'Failed to fetch tenant.', // System errors SYSTEM_INFO_FAILED: 'Failed to fetch system information.', DOCKER_ACCESS_ERROR: 'This could mean Docker is not running, no containers are active, or the monitoring system needs Docker access.', MANAGEMENT_AUTH_REQUIRED: 'Management authentication is required to access this resource.', INVALID_MANAGEMENT_TOKEN: 'Invalid management authentication token.', // General errors AUTHENTICATION_FAILED: 'Authentication failed. Please try again.', ACCESS_DENIED: 'Access denied.', VALIDATION_ERROR: 'Validation error occurred.', INTERNAL_ERROR: 'An internal error occurred. Please try again later.' }, sv: { // Authentication errors TOKEN_EXPIRED: 'Din session har löpt ut. Vänligen logga in igen.', INVALID_TOKEN: 'Ogiltig autentiseringstoken. Vänligen logga in igen.', TOKEN_NOT_ACTIVE: 'Autentiseringstoken är inte aktiv ännu.', USER_NOT_FOUND: 'Användarkonto hittades inte. Vänligen kontakta support.', ACCOUNT_DEACTIVATED: 'Ditt konto har inaktiverats. Vänligen kontakta support.', AUTH_REQUIRED: 'Autentisering krävs för att komma åt denna resurs.', NO_TOKEN: 'Ingen autentiseringstoken tillhandahållen.', // Authorization errors INSUFFICIENT_PERMISSIONS: 'Åtkomst nekad. Denna åtgärd kräver {requiredRoles} behörigheter, men du har {userRole} behörigheter.', PERMISSION_DENIED: 'Du har inte behörighet att utföra denna åtgärd.', INSUFFICIENT_MANAGEMENT_PRIVILEGES: 'Otillräckliga förvaltningsprivilegier för att komma åt denna resurs.', // Management errors MANAGEMENT_AUTH_REQUIRED: 'Förvaltningsautentisering krävs för att komma åt denna resurs.', INVALID_MANAGEMENT_TOKEN: 'Ogiltig förvaltningsautentiseringstoken.', AUTHENTICATION_REQUIRED: 'Förvaltningsautentisering krävs.', INSUFFICIENT_PRIVILEGES: 'Otillräckliga förvaltningsprivilegier.', INVALID_MANAGEMENT_CREDENTIALS: 'Ogiltiga förvaltningsuppgifter.', MANAGEMENT_AUTH_ERROR: 'Förvaltningsautentiseringsfel.', // Tenant management TENANT_NOT_FOUND: 'Hyresgäst hittades inte.', TENANT_CREATION_FAILED: 'Misslyckades att skapa hyresgäst.', TENANT_UPDATE_FAILED: 'Misslyckades att uppdatera hyresgäst.', TENANT_DELETE_FAILED: 'Misslyckades att ta bort hyresgäst.', TENANT_SLUG_EXISTS: 'Hyresgästslug finns redan.', DOMAIN_EXISTS: 'Domän finns redan för en annan hyresgäst.', CANNOT_DELETE_DEFAULT: 'Kan inte ta bort standardhyresgäst.', NAME_SLUG_REQUIRED: 'Namn och slug krävs.', TENANT_CREATED: 'Hyresgäst skapad framgångsrikt.', TENANT_UPDATED: 'Hyresgäst uppdaterad framgångsrikt.', TENANT_DELETED: 'Hyresgäst borttagen framgångsrikt.', FETCH_TENANTS_FAILED: 'Misslyckades att hämta hyresgäster.', FETCH_TENANT_FAILED: 'Misslyckades att hämta hyresgäst.', // System errors SYSTEM_INFO_FAILED: 'Misslyckades att hämta systeminformation.', DOCKER_ACCESS_ERROR: 'Detta kan betyda att Docker inte körs, inga behållare är aktiva eller att övervakningssystemet behöver Docker-åtkomst.', // General errors AUTHENTICATION_FAILED: 'Autentisering misslyckades. Vänligen försök igen.', ACCESS_DENIED: 'Åtkomst nekad.', VALIDATION_ERROR: 'Valideringsfel inträffade.', INTERNAL_ERROR: 'Ett internt fel inträffade. Vänligen försök igen senare.' } }; // System message translations (for alerts, notifications, etc.) const SYSTEM_MESSAGES = { en: { // Alert messages CRITICAL_THREAT_DETECTED: 'CRITICAL THREAT: {droneType} DETECTED - IMMEDIATE RESPONSE REQUIRED', HIGH_THREAT_DETECTED: 'HIGH THREAT: {droneType} detected at {distance}m', MEDIUM_THREAT_DETECTED: 'MEDIUM THREAT: {droneType} detected in vicinity', LOW_THREAT_DETECTED: 'LOW THREAT: {droneType} detected at distance', MONITORING_DETECTED: 'MONITORING: {droneType} detected at long range', MILITARY_THREAT_ENHANCED: 'MILITARY THREAT: {droneType} DETECTED - ENHANCED RESPONSE REQUIRED', // Device messages DEVICE_OFFLINE: 'Device {deviceName} is offline', DEVICE_ONLINE: 'Device {deviceName} is back online', HEARTBEAT_RECEIVED: 'Heartbeat received from {deviceName}', // General system messages OPERATION_SUCCESSFUL: 'Operation completed successfully', OPERATION_FAILED: 'Operation failed', DATA_SAVED: 'Data saved successfully', DATA_DELETED: 'Data deleted successfully' }, sv: { // Alert messages CRITICAL_THREAT_DETECTED: 'KRITISKT HOT: {droneType} UPPTÄCKT - OMEDELBAR RESPONS KRÄVS', HIGH_THREAT_DETECTED: 'HÖGT HOT: {droneType} upptäckt på {distance}m', MEDIUM_THREAT_DETECTED: 'MEDELHÖGT HOT: {droneType} upptäckt i närheten', LOW_THREAT_DETECTED: 'LÅGT HOT: {droneType} upptäckt på avstånd', MONITORING_DETECTED: 'ÖVERVAKNING: {droneType} upptäckt på långt avstånd', MILITARY_THREAT_ENHANCED: 'MILITÄRT HOT: {droneType} UPPTÄCKT - FÖRSTÄRKT RESPONS KRÄVS', // Device messages DEVICE_OFFLINE: 'Enhet {deviceName} är offline', DEVICE_ONLINE: 'Enhet {deviceName} är online igen', HEARTBEAT_RECEIVED: 'Heartbeat mottagen från {deviceName}', // General system messages OPERATION_SUCCESSFUL: 'Operationen slutfördes framgångsrikt', OPERATION_FAILED: 'Operationen misslyckades', DATA_SAVED: 'Data sparad framgångsrikt', DATA_DELETED: 'Data raderad framgångsrikt' } }; // Drone type translations const DRONE_TYPES = { en: { 0: 'Unknown Drone', 1: 'DJI Mavic', 2: 'Orlan', 3: 'Racing Drone', 4: 'DJI Phantom', 5: 'Zala Lancet', 6: 'Eleron', 7: 'Commercial Drone', 8: 'Professional Drone' }, sv: { 0: 'Okänd Drönare', 1: 'DJI Mavic', 2: 'Orlan', 3: 'Racing Drönare', 4: 'DJI Phantom', 5: 'Zala Lancet', 6: 'Eleron', 7: 'Kommersiell Drönare', 8: 'Professionell Drönare' } }; /** * Get user's preferred language from request headers */ function getLanguageFromRequest(req) { // Check for explicit language header const explicitLang = req.headers['accept-language-override'] || req.headers['x-language']; if (explicitLang && SUPPORTED_LANGUAGES.includes(explicitLang)) { return explicitLang; } // Parse Accept-Language header const acceptLanguage = req.headers['accept-language']; if (acceptLanguage) { const languages = acceptLanguage .split(',') .map(lang => lang.split(';')[0].trim().toLowerCase()) .map(lang => lang.split('-')[0]); // Get just the language part (sv from sv-SE) for (const lang of languages) { if (SUPPORTED_LANGUAGES.includes(lang)) { return lang; } } } return DEFAULT_LANGUAGE; } /** * Get localized error message */ function getErrorMessage(messageKey, language = DEFAULT_LANGUAGE, params = {}) { const lang = SUPPORTED_LANGUAGES.includes(language) ? language : DEFAULT_LANGUAGE; let message = ERROR_MESSAGES[lang]?.[messageKey] || ERROR_MESSAGES[DEFAULT_LANGUAGE]?.[messageKey] || messageKey; // Replace parameters in message Object.keys(params).forEach(key => { message = message.replace(new RegExp(`{${key}}`, 'g'), params[key]); }); return message; } /** * Get localized system message */ function getSystemMessage(messageKey, language = DEFAULT_LANGUAGE, params = {}) { const lang = SUPPORTED_LANGUAGES.includes(language) ? language : DEFAULT_LANGUAGE; let message = SYSTEM_MESSAGES[lang]?.[messageKey] || SYSTEM_MESSAGES[DEFAULT_LANGUAGE]?.[messageKey] || messageKey; // Replace parameters in message Object.keys(params).forEach(key => { message = message.replace(new RegExp(`{${key}}`, 'g'), params[key]); }); return message; } /** * Get localized drone type name */ function getDroneTypeName(droneType, language = DEFAULT_LANGUAGE) { const lang = SUPPORTED_LANGUAGES.includes(language) ? language : DEFAULT_LANGUAGE; return DRONE_TYPES[lang]?.[droneType] || DRONE_TYPES[DEFAULT_LANGUAGE]?.[droneType] || 'Unknown'; } /** * Create localized error response */ function createErrorResponse(req, status, errorCode, params = {}) { const language = getLanguageFromRequest(req); const message = getErrorMessage(errorCode, language, params); return { status, json: { success: false, message, error: errorCode, errorCode, language, ...params } }; } module.exports = { getLanguageFromRequest, getErrorMessage, getSystemMessage, getDroneTypeName, createErrorResponse, SUPPORTED_LANGUAGES, DEFAULT_LANGUAGE };