From 8471e997c19a435f74b0eb63b969100666629d16 Mon Sep 17 00:00:00 2001 From: balzack Date: Wed, 31 Jul 2024 17:13:19 +0200 Subject: [PATCH] preparing access page --- app/client/web/src/constants/Colors.ts | 77 + app/client/web/src/constants/Strings.ts | 1254 +++++++++++++++++ .../web/src/context/SettingsContext.tsx | 13 + .../src/context/useSettingsContext.hook.ts | 229 +++ app/client/web/src/main.tsx | 13 +- 5 files changed, 1581 insertions(+), 5 deletions(-) create mode 100644 app/client/web/src/constants/Colors.ts create mode 100644 app/client/web/src/constants/Strings.ts create mode 100644 app/client/web/src/context/SettingsContext.tsx create mode 100644 app/client/web/src/context/useSettingsContext.hook.ts diff --git a/app/client/web/src/constants/Colors.ts b/app/client/web/src/constants/Colors.ts new file mode 100644 index 00000000..84465b7d --- /dev/null +++ b/app/client/web/src/constants/Colors.ts @@ -0,0 +1,77 @@ +export const LightTheme = { + remoteArea: '#080808', + localArea: '#888888', + splashArea: '#8fbea7', + baseArea: '#8fbea7', + frameArea: '#e4e4e4', + iconArea: '#ffffff', + headerArea: '#f0f0f0', + footerArea: '#f0f0f0', + modalArea: '#eeeeee', + itemArea: '#f4f4f4', + inputArea: '#ffffff', + hoverArea: '#cccccc', + noticeArea: '#8fbea7', + selectedArea: '#e8e8e8', + enabledArea: '#448866', + disabledArea: '#cccccc', + mainText: '#444444', + descriptionText: '#585858', + hintText: '#777777', + activeText: '#ffffff', + idleText: '#aaaaaa', + placeholderText: '#777777', + linkText: '#448866', + labelText: '#888888', + alertText: '#ff8888', + itemBorder: '#eeeeee', + inputBorder: '#888888', + sectionBorder: '#bbbbbb', + headerBorder: '#aaaaaa', + drawerBorder: '#cccccc', + unsaved: '#ffff00', + connected: '#44cc44', + connecting: '#dd88ff', + requested: '#4488ff', + pending: '#22aaaa', + confirmed: '#aaaaaa', +} + +export const DarkTheme = { + remoteArea: '#080808', + localArea: '#888888', + splashArea: '#4c4c4c', + baseArea: '#080808', + frameArea: '#0a0a0a', + iconArea: '#ffffff', + headerArea: '#111111', + footerArea: '#111111', + modalArea: '#444444', + itemArea: '#222222', + inputArea: '#000000', + hoverArea: '#555555', + noticeArea: '#8fbea7', + selectedArea: '#333333', + enabledArea: '#448866', + disabledArea: '#888888', + mainText: '#ffffff', + descriptionText: '#999999', + hintText: '#aaaaaa', + activeText: '#ffffff', + idleText: '#aaaaaa', + placeholderText: '#686868', + linkText: '#66aa88', + labelText: '#dddddd', + alertText: '#ff8888', + itemBorder: '#555555', + inputBorder: '#aaaaaa', + sectionBorder: '#777777', + headerBorder: '#aaaaaa', + drawerBorder: '#444444', + unsaved: '#ffff00', + connected: '#44cc44', + connecting: '#dd88ff', + requested: '#4488ff', + pending: '#22aaaa', + confirmed: '#aaaaaa', +} diff --git a/app/client/web/src/constants/Strings.ts b/app/client/web/src/constants/Strings.ts new file mode 100644 index 00000000..3d90d16a --- /dev/null +++ b/app/client/web/src/constants/Strings.ts @@ -0,0 +1,1254 @@ +export const en = { + code: 'en', + settings: 'Settings', + contacts: 'Contacts', + logout: 'Logout', + confirmLogout: 'Are you sure you want to logout?', + contactsUpdated: 'Updated contact status', + disconnected: 'Disconnected from server', + allDevices: 'Logout of all devices', + ok: 'OK', + cancel: 'Cancel', + enableNotifications: 'Push Notifications', + + new: 'New', + newMessage: 'New Message', + topics: 'Topics', + unsetSealing: 'Unset Sealing Key', + newTopic: 'New Topic', + + noContacts: 'No Contacts', + noTopics: 'No Topics', + noConnected: 'No Connected Contacts', + subjectOptional: 'Subject (optional)', + members: 'Members', + sealedTopic: 'Sealed Topic', + start: 'Start', + + communication: 'Communication for the Decentralized Web', + setupProfile: 'Setup your profile', + connectPeople: 'Connect with people', + startConversation: 'Start a conversation', + + default: 'Default', + dark: 'Dark', + light: 'Light', + + operationFailed: 'Operation Failed', + tryAgain: 'Please try again.', + + add: 'Add', + save: 'Save', + forget: 'Forget', + unlock: 'Unlock', + profile: 'Profile', + application: 'Application', + account: 'Account', + name: 'Name', + node: 'Node', + location: 'Location', + description: 'Description', + timeFormat: 'Time Format', + dateFormat: 'Date Format', + theme: 'Theme', + language: 'Language', + timeUs: '12h', + timeEu: '24h', + dateUs: 'mm/dd', + dateEu: 'dd/mm', + registry: 'Visible in Registry', + sealedTopics: 'Sealed Topics', + changeLogin: 'Change Login', + selectImage: 'Select', + profileImage: 'Profile Image', + profileDetails: 'Profile Details', + enableSealed: 'Enabled Sealed Topics', + password: 'Password', + newPassword: 'New Password', + confirmPassword: 'Confirm Password', + deleteKey: "Type 'delete' to remove key", + delete: 'delete', + remove: 'Delete', + username: 'Username', + updateProfile: 'Update Profile', + + syncError: 'Sync Error', + callTip: 'Call Contact', + messageTip: 'Message Contact', + connectedTip: 'Connected Contact', + requestedTip: 'Connection Requested by Contact', + connectingTip: 'Connection Requested', + pendingTip: 'Connection Requested by Unknown Contact', + confirmedTip: 'Disconnected Contact', + unsavedTip: 'Unknown Contact', + + actions: 'Actions', + resync: 'Resync', + connect: 'Connect', + disconnect: 'Disconnect', + disconnectContact: 'Disconnect Contact', + deleteContact: 'Delete Contact', + saveContact: 'Save Contact', + saveAccept: 'Save and Accept Connection', + saveRequest: 'Save and Request Connection', + ignoreRequest: 'Ignore Request', + acceptConnection: 'Accept Connection', + requestConnection: 'Request Connection', + cancelRequest: 'Cancel Request', + resyncContact: 'Resync Contact', + + login: 'Login', + create: 'Create', + createAccount: 'Create Account', + accountLogin: 'Account Login', + toCreate: 'Accounts are created through a link generated from the admin dashboard.', + admin: 'Admin', + loginError: 'Login Error', + loginMessage: 'Please confirm your username and password.', + createError: 'Create Account Error', + createMessage: 'Please check with your administrator.', + adminError: 'Admin Access Error', + adminMessage: 'Please confirm your password.', + + confirmDelete: 'Deleting Account', + areSure: 'Are you sure you want to delete the account?', + + mb: 'MB', + gb: 'GB', + copied: 'Copied', + accounts: 'Accounts', + accessAccount: 'Access Account', + browserLink: 'Browser Link', + mobileToken: 'Mobile Token', + createLink: 'Create Account Link', + configureServer: 'Configure Server', + reloadAccounts: 'Reload Accounts', + disableAccount: 'Disable Account', + enableAccount: 'Enable Account', + deleteAccount: 'Delete Account', + hostHint: 'domain:port/app', + federatedHost: 'Federated Host', + storageLimit: 'Storage Limit (GB) / Account', + storageHint: '0 for Unlimited', + keyType: 'Account Key Type', + accountCreation: 'Public Account Creation', + enablePush: 'Enable Push Notifications', + allowUnsealed: 'Allow Unsealed Topics', + topicContent: 'Topic Content:', + enableImage: 'Enable Image Queue', + imageHint: 'Allow images to be posted in topics', + enableAudio: 'Enable Audio Queue', + audioHint: 'Allow audio to be posted in topics', + enableVideo: 'Enable Video Queue', + videoHint: 'Allow video to be posted in topics', + enableBinary: 'Enable Binary Files', + binaryHint: 'Allow binary files to be posted in topics', + enableWeb: 'Enable WebRTC Calls', + webHint: 'Enable audio and video calls to contacts', + enableService: 'Cloudflare Service', + serviceHint: 'Enable Cloudflare Service', + serverUrl: 'WebRTC Server URL', + urlHint: 'turn:ip:port?transport=udp', + webUsername: 'WebRTC Username', + webPassword: 'WebRTC Password', + failedLoad: 'Failed to Load', + limit: 'Limit', + + deleteMessage: 'Deleting Message', + messageHint: 'Are you sure you want to delete the message?', + attachImage: 'Attach Image', + attachVideo: 'Attach Video', + attachAudio: 'Attach Audio', + attachFile: 'Attach File', + fontColor: 'Change Font Color', + fontSize: 'Change Font Size', + postMessage: 'Post Message', + + close: 'Close', + leave: 'Leave', + confirmTopic: 'Deleting Topic', + sureTopic: 'Are you sure you want to delete this topic?', + confirmLeave: 'Leaving Topic', + sureLeave: 'Are you sure you want to leave this topic?', + + details: 'Details', + host: 'Host', + guest: 'Guest', + editSubject: 'Edit Subject', + editMembership: 'Edit Membership', + deleteTopic: 'Delete Topic', + leaveTopic: 'Leave Topic', + + integrated: 'Integrated', + microphone: 'Microphone', + camera: 'Camera', + + notes: 'Notes', + + disconnecting: 'Disconnecting Contact', + confirmDisconnect: 'Are you sure you want to disconnect the contact?', + removing: 'Deleting Contact', + confirmRemove: 'Are you sure you want to delete the contact?', + message: 'Message', + securedMessage: 'Sealed Message', + + mfaTitle: 'Multi-Factor Authentication', + mfaSteps: 'Store the secret and confirm the verification code', + mfaError: 'verification code error', + mfaDisabled: 'verification temporarily disabled', + mfaConfirm: 'Confirm', + mfaEnter: 'Enter your verification code', + + enableMultifactor: 'Enable multi-factor authentication', + disableMultifactor: 'Disable multi-factor authentication', + + disable: 'Disable', + confirmDisable: 'Disabling Multi-Factor Authentication', + disablePrompt: 'Are you sure you want to disable multi-factor authentication', +} + +export const fr = { + code: 'fr', + settings: 'Paramètres', + contacts: 'Contacts', + logout: 'Déconnexion', + confirmLogout: 'Êtes-vous sûr de vouloir vous déconnecter?', + contactsUpdated: 'Vos contacts ont changer', + disconnected: 'Déconnecté du serveur', + allDevices: 'Déconnexion de tous les appareils', + ok: 'OK', + cancel: 'Annuler', + enableNotifications: 'Notifications Push', + + new: 'Nouveau', + newMessage: 'Nouveau Message', + topics: 'Sujets', + unsetSealing: 'Clé de sécurité non définie', + newTopic: 'Nouveau Sujet', + + noContacts: 'Pas de Contacts', + noTopics: 'Pas de Sujets', + noConnected: 'Pas de Contacts Connecter', + subjectOptional: 'Sujet (optionnel)', + members: 'Membres', + sealedTopic: 'Sujet Sécurisé', + start: 'Commencer', + + communication: 'Communication pour le Web Décentralisé', + setupProfile: 'Configurez votre profil', + connectPeople: 'Connectez avec les gens', + startConversation: 'Commencez une conversation', + + default: 'Défaut', + dark: 'Sombre', + light: 'Lumineux', + + operationFailed: 'Opération Échouée', + tryAgain: 'Veuillez réessayer.', + + add: 'Ajouter', + save: 'Enregistrer', + forget: 'Oublier', + unlock: 'Déverrouiller', + profile: 'Profil', + application: 'Application', + account: 'Compte', + name: 'Nom', + node: 'Serveur', + location: 'Emplacement', + description: 'Description', + timeFormat: "Format de l'heure", + dateFormat: 'Format de la date', + theme: 'Thème', + language: 'Langue', + timeUs: '12h', + timeEu: '24h', + dateUs: 'mm/jj', + dateEu: 'jj/mm', + registry: 'Visible dans le Registre', + sealedTopics: 'Sujets Sécurisé', + changeLogin: 'Changer Identifiants', + selectImage: 'Sélectionner', + profileImage: 'Image de Profil', + profileDetails: 'Détails du Profil', + enableSealed: 'Activer les Sujets Sécurisé', + password: 'Mot de Passe', + newPassword: 'Nouveau Mot de Passe', + confirmPassword: 'Confirmer le Mot de Passe', + deleteKey: "Tapez 'supprimer' pour supprimer la clé", + delete: 'supprimer', + remove: 'Supprimer', + username: "Nom d'Utilisateur", + updateProfile: 'Mettre à Jour le Profil', + + syncError: 'Erreur de Synchronisation', + callTip: 'Appeler le Contact', + messageTip: 'Envoyer un Message au Contact', + connectedTip: 'Contact Connecté', + requestedTip: 'Demande de Connexion Envoyée par le Contact', + connectingTip: 'Demande de Connexion en Cours', + pendingTip: 'Demande de Connexion Envoyée par un Contact Inconnu', + confirmedTip: 'Contact Déconnecté', + unsavedTip: 'Contact Inconnu', + + actions: 'Actions', + resync: 'Resynchroniser', + connect: 'Connecter', + disconnect: 'Déconnecter', + disconnectContact: 'Déconnecter le Contact', + deleteContact: 'Supprimer le Contact', + saveContact: 'Enregistrer le Contact', + saveAccept: 'Enregistrer et Accepter la Connexion', + saveRequest: 'Enregistrer et Demander la Connexion', + ignoreRequest: 'Ignorer la Demande', + acceptConnection: 'Accepter la Connexion', + requestConnection: 'Demander une Connexion', + cancelRequest: 'Annuler la Demande', + resyncContact: 'Resynchroniser le Contact', + + login: 'Connecter', + create: 'Créer', + createAccount: 'Créer un Compte', + accountLogin: 'Connexion au Compte', + toCreate: "Les comptes sont créés via un lien généré depuis le tableau de bord d'administration.", + admin: 'Administrateur', + loginError: 'Erreur de connexion', + loginMessage: "Veuillez confirmer votre nom d'utilisateur et votre mot de passe.", + createError: 'Erreur de création de compte', + createMessage: 'Veuillez vérifier auprès de votre administrateur.', + adminError: "Erreur d'Accès", + adminMessage: 'Veuillez confirmer votre mot de passe', + + confirmDelete: 'Suppression de Compte', + areSure: 'Êtes-vous sûr de vouloir supprimer le compte?', + + mb: 'Mo', + gb: 'Go', + copied: 'Copié', + accounts: 'Comptes', + accessAccount: 'Accéder au Compte', + browserLink: 'Lien du Navigateur', + mobileToken: 'Code Mobile', + createLink: 'Lien pour Créer un Compte', + configureServer: 'Configurer le Serveur', + reloadAccounts: 'Recharger les Comptes', + disableAccount: 'Désactiver le Compte', + enableAccount: 'Activer le Compte', + deleteAccount: 'Supprimer le Compte', + hostHint: 'domaine:port/app', + federatedHost: 'Hôte Fédéré', + storageLimit: 'Limite de Espace (Go) / Compte', + storageHint: '0 pour Illimité', + keyType: 'Type de Clé', + accountCreation: 'Création de Compte Public', + enablePush: 'Activer les Notifications Push', + allowUnsealed: 'Autoriser les Sujets non Sécurisés', + topicContent: 'Contenu du Sujet:', + enableImage: 'Activer les Images du Sujet', + imageHint: "Autoriser la publication d'images dans des sujets", + enableAudio: "Activer l'Audio du Suject", + audioHint: "Autoriser la publication d'audio dans des sujets", + enableVideo: 'Activer les Videos du Sujet', + videoHint: 'Autoriser la publication de video dans des sujets', + enableBinary: 'Activer les Fichiers Binaires du Sujet', + binaryHint: 'Autoriser la publication de fichiers binaires dans les sujets', + enableWeb: 'Activer les Appels WebRTC', + webHint: 'Autoriser les appels audio et vidéo aux contacts', + enableService: 'Service Cloudflare', + serviceHint: 'Activer le Service Cloudflare', + serverUrl: 'URL du Serveur WebRTC', + urlHint: 'turn:ip:port?transport=udp', + webUsername: "Nom d'Utilisateur WebRTC", + webPassword: 'Mot de Passe WebRTC', + failedLoad: 'Échec du Chargement', + limit: 'Limite', + + deleteMessage: 'Suppression du Message', + messageHint: 'Êtes-vous Sûr de Vouloir Supprimer le Message?', + attachImage: 'Joindre une Image', + attachVideo: 'Joindre une Vidéo', + attachAudio: 'Joindre un Audio', + attachFile: 'Joindre un Fichier', + fontColor: 'Changer la Couleur du Text', + fontSize: 'Changer la Taille du Text', + postMessage: 'Publier le Message', + + close: 'Fermer', + leave: 'Quitter', + confirmTopic: 'Suppression du Sujet', + sureTopic: 'Êtes-Vous Sûr de Vouloir Supprimer ce Sujet?', + confirmLeave: 'Quitter le Suject', + sureLeave: 'Êtes-Vous Sûr de Vouloir Quitter ce Sujet?', + + details: 'Détails', + host: 'Hôte', + guest: 'Invité', + editSubject: 'Modifier le Sujet', + editMembership: 'Modifier Membres du Suject', + deleteTopic: 'Supprimer le Sujet', + leaveTopic: 'Quitter le Suject', + + integrated: 'Intégré', + microphone: 'Microphone', + camera: 'Caméra', + + notes: 'Notes', + + disconnecting: 'Déconnexion du contact', + confirmDisconnect: 'Êtes-vous sûr de vouloir déconnecter le contact?', + removing: 'Suppression du contact', + confirmRemove: 'Êtes-vous sûr de vouloir supprimer le contact?', + + message: 'Message', + sealedMessage: 'Message Sécurisé', + + mfaTitle: 'Authentification Multi-Factor', + mfaSteps: 'Enregistrez le secret et confirmez le code de vérification', + mfaEnter: 'Entrez votre code de vérification', + mfaError: 'erreur de code de vérification', + mfaDisabled: 'vérification temporairement désactivée', + mfaConfirm: 'Confirmer', + + enableMultifactor: 'Activer l\'authentification multifacteur', + disableMultifactor: 'Désactiver l\'authentification multifacteur', + + disable: 'Désactiver', + confirmDisable: 'Désactivation de l\'authentification multi-facteurs', + disablePrompt: 'Êtes-vous sûr de vouloir désactiver l\'authentification multi-facteurs', +} + +export const sp = { + code: 'sp', + settings: 'Configuración', + contacts: 'Contactos', + logout: 'Cerrar Sesión', + confirmLogout: '¿Estás seguro de que quieres cerrar sesión?', + contactsUpdated: 'Tus contactos han cambiado', + disconnected: 'Desconectado del servidor', + allDevices: 'Cerrar sesión en todos los dispositivos', + ok: 'Aceptar', + cancel: 'Cancelar', + enableNotifications: 'Notificaciones Push', + + new: 'Nuevo', + newMessage: 'Nuevo mensaje', + topics: 'Temas', + unsetSealing: 'Clave de seguridad no definida', + newTopic: 'Nuevo tema', + + noContacts: 'Sin contactos', + noTopics: 'Sin temas', + noConnected: 'Ningún contacto conectado', + subjectOptional: 'Tema (opcional)', + members: 'Miembros', + sealedTopic: 'Tema Seguros', + start: 'Empezar', + + communication: 'Comunicación Para la Web Descentralizada', + setupProfile: 'Configura tu Perfil', + connectPeople: 'Conecta con Personas', + startConversation: 'Iniciar una conversación', + + default: 'Predeterminado', + dark: 'Oscuro', + light: 'Claro', + + operationFailed: 'Operación fallida', + tryAgain: 'Por favor, inténtalo de nuevo.', + + add: 'Agregar', + save: 'Guardar', + forget: 'Olvidar', + unlock: 'Desbloquear', + profile: 'Perfil', + application: 'Aplicación', + account: 'Cuenta', + name: 'Nombre', + node: 'Servidor', + location: 'Ubicación', + description: 'Descripción', + timeFormat: 'Formato de Hora', + dateFormat: 'Formato de Fecha', + theme: 'Tema', + language: 'Idioma', + timeUs: '12h', + timeEu: '24h', + dateUs: 'mm/dd', + dateEu: 'dd/mm', + registry: 'Visible en el Registro', + sealedTopics: 'Temas Seguros', + changeLogin: 'Cambiar Credenciales', + selectImage: 'Seleccionar', + profileImage: 'Imagen de Perfil', + profileDetails: 'Detalles del Perfil', + enableSealed: 'Activar Temas Seguros', + password: 'Contraseña', + newPassword: 'Nueva Contraseña', + confirmPassword: 'Confirmar Contraseña', + deleteKey: "Escribe 'borrar' para Eliminar la Clave", + delete: 'borrar', + remove: 'Eliminar', + username: 'Nombre de usuario', + updateProfile: 'Actualizar perfil', + + syncError: 'Error de sincronización', + callTip: 'Llamar al contacto', + messageTip: 'Enviar mensaje al contacto', + connectedTip: 'Contacto conectado', + requestedTip: 'Solicitud de conexión enviada por el contacto', + connectingTip: 'Conexión en curso', + pendingTip: 'Solicitud de conexión enviada por un contacto desconocido', + confirmedTip: 'Contacto desconectado', + unsavedTip: 'Contacto desconocido', + + actions: 'Acciones', + resync: 'Resincronizar', + connect: 'Conectar', + disconnect: 'Desconectar', + disconnectContact: 'Desconectar al Contacto', + deleteContact: 'Eliminar Contacto', + saveContact: 'Guardar Contacto', + saveAccept: 'Guardar y Aceptar la Conexión', + saveRequest: 'Guardar y solicitar la Conexión', + ignoreRequest: 'Ignorar la Solicitud', + acceptConnection: 'Aceptar Conexión', + requestConnection: 'Solicitar Conexión', + cancelRequest: 'Cancelar Solicitud', + resyncContact: 'Resincronizar Contacto', + + login: 'Iniciar sesión', + create: 'Crear', + createAccount: 'Crear cuenta', + accountLogin: 'Inicio de sesión en la cuenta', + toCreate: 'Las cuentas se crean a través de un enlace generado desde el panel de administración.', + admin: 'Administrador', + loginError: 'Error de inicio de sesión', + loginMessage: 'Por favor, confirme su nombre de usuario y contraseña.', + createError: 'Error al crear la cuenta', + createMessage: 'Por favor, consulte con su administrador.', + adminError: 'Error de acceso', + adminMessage: 'Por favor, confirme su contraseña', + + confirmDelete: 'Eliminar cuenta', + areSure: '¿Estás seguro de que quieres eliminar la cuenta?', + + mb: 'MB', + gb: 'GB', + copied: 'Copiado', + accounts: 'Cuentas', + accessAccount: 'Acceder a la Cuenta', + browserLink: 'Enlace del Navegador', + mobileToken: 'Código Móvil', + createLink: 'Enlace para Crear una Cuenta', + configureServer: 'Configurar Servidor', + reloadAccounts: 'Recargar Cuentas', + disableAccount: 'Desactivar Cuenta', + enableAccount: 'Activar Cuenta', + deleteAccount: 'Eliminar Cuenta', + hostHint: 'dominio:puerto/aplicación', + federatedHost: 'Anfitrión federado', + storageLimit: 'Límite de almacenamiento (GB) / cuenta', + storageHint: '0 para Ilimitado', + keyType: 'Tipo de Clave', + accountCreation: 'Creación de Cuenta Pública', + enablePush: 'Activar Cotificaciones push', + allowUnsealed: 'Permitir Temas no Seguros', + topicContent: 'Contenido del tema:', + enableImage: 'Activar imágenes en el tema', + imageHint: 'Permitir la publicación de imágenes en temas', + enableAudio: 'Activar audio en el tema', + audioHint: 'Permitir la publicación de audio en temas', + enableVideo: 'Activar videos en el tema', + videoHint: 'Permitir la publicación de videos en temas', + enableBinary: 'Activar archivos binarios en el tema', + binaryHint: 'Permitir que se publiquen archivos binarios en temas', + enableWeb: 'Activar llamadas WebRTC', + webHint: 'Permitir llamadas de audio y video a contactos', + enableService: 'Servicio Cloudflare', + serviceHint: 'Habilitar el Servicio Cloudflare', + serverUrl: 'URL del servidor WebRTC', + urlHint: 'turn:ip:puerto?transporte=udp', + webUsername: 'Nombre de usuario WebRTC', + webPassword: 'Contraseña WebRTC', + failedLoad: 'Error al cargar', + limit: 'Límite', + + deleteMessage: 'Eliminar mensaje', + messageHint: '¿Estás seguro de que quieres eliminar el mensaje?', + attachImage: 'Adjuntar Imagen', + attachVideo: 'Adjuntar Video', + attachAudio: 'Adjuntar Audio', + attachFile: 'Adjuntar Archivo', + fontColor: 'Cambiar el Color de la Fuente', + fontSize: 'Cambiar el Tamaño de la Fuente', + postMessage: 'Publicar Mensaje', + + close: 'Cerrar', + leave: 'Salir', + confirmTopic: 'Eliminar Rema', + sureTopic: '¿Estás seguro de que quieres eliminar este tema?', + confirmLeave: 'Salir del Rema', + sureLeave: '¿Estás seguro de que quieres salir de este tema?', + + details: 'Detalles', + host: 'Anfitrión', + guest: 'Invitado', + editSubject: 'Editar tema', + editMembership: 'Editar Miembros del Tema', + deleteTopic: 'Eliminar tema', + leaveTopic: 'Salir del tema', + + integrated: 'Integrado', + microphone: 'Micrófono', + camera: 'Cámara', + + notes: 'Notas', + + disconnecting: 'Desconexión de contacto', + confirmDisconnect: '¿Estás seguro de que quieres desconectar el contacto?', + removing: 'Eliminando contacto', + confirmRemove: '¿Estás seguro de que quieres eliminar el contacto?', + message: 'Mensaje', + sealedMessage: 'Mensaje Seguro', + + mfaTitle: 'Autenticación de Dos Factores', + mfaSteps: 'Guarde el secreto y confirme el código de verificación', + mfaEnter: 'Ingresa tu código de verificación', + mfaError: 'error de código de verificación', + mfaDisabled: 'verificación temporalmente deshabilitada', + mfaConfirm: 'Confirmar', + + enableMultifactor: 'Habilitar la autenticación multifactor', + disableMultifactor: 'Deshabilitar la autenticación multifactor', + + disable: 'Desactivar', + confirmDisable: 'Desactivación de la autenticación de dos factores', + disablePrompt: '¿Estás seguro de que quieres desactivar la autenticación de dos factores?', +} + +export const pt = { + code: 'pt', + settings: 'Configurações', + contacts: 'Contatos', + logout: 'Sair', + confirmLogout: 'Tem certeza de que deseja sair?', + contactsUpdated: 'Seus contatos foram atualizados', + disconnected: 'Desconectado do servidor', + allDevices: 'Desconectar de todos os dispositivos', + ok: 'OK', + cancel: 'Cancelar', + enableNotifications: 'Notificações Push', + + new: 'Novo', + newMessage: 'Nova mensagem', + topics: 'Tópicos', + unsetSealing: 'Chave de segurança não definida', + newTopic: 'Novo tópico', + + noContacts: 'Sem contatos', + noTopics: 'Sem tópicos', + noConnected: 'Nenhum contato conectado', + subjectOptional: 'Assunto (opcional)', + members: 'Membros', + sealedTopic: 'Tópico selado', + start: 'Iniciar', + + communication: 'Comunicação para a Web Descentralizada', + setupProfile: 'Configurar seu perfil', + connectPeople: 'Conectar-se com as pessoas', + startConversation: 'Iniciar uma conversa', + + default: 'Padrão', + dark: 'Escuro', + light: 'Claro', + + operationFailed: 'Operação falhou', + tryAgain: 'Por favor, tente novamente.', + + add: 'Adicionar', + save: 'Salvar', + forget: 'Esquecer', + unlock: 'Desbloquear', + profile: 'Perfil', + application: 'Aplicação', + account: 'Conta', + name: 'Nome', + node: 'Servidor', + location: 'Localização', + description: 'Descrição', + timeFormat: 'Formato de hora', + dateFormat: 'Formato de data', + theme: 'Tema', + language: 'Idioma', + timeUs: '12h', + timeEu: '24h', + dateUs: 'mm/dd', + dateEu: 'dd/mm', + registry: 'Visível no registro', + sealedTopics: 'Tópicos selados', + changeLogin: 'Alterar credenciais', + selectImage: 'Selecionar', + profileImage: 'Imagem do perfil', + profileDetails: 'Detalhes do perfil', + enableSealed: 'Ativar tópicos selados', + password: 'Senha', + newPassword: 'Nova senha', + confirmPassword: 'Confirmar senha', + deleteKey: "Digite 'excluir' para deletar a chave", + delete: 'excluir', + remove: 'Remover', + username: 'Nome de usuário', + updateProfile: 'Atualizar perfil', + + syncError: 'Erro de sincronização', + callTip: 'Ligar para o contato', + messageTip: 'Enviar mensagem ao contato', + connectedTip: 'Contato conectado', + requestedTip: 'Pedido de conexão enviado pelo contato', + connectingTip: 'Conexão em andamento', + pendingTip: 'Pedido de conexão enviado por um contato desconhecido', + confirmedTip: 'Contato desconectado', + unsavedTip: 'Contato desconhecido', + + actions: 'Ações', + resync: 'Resincronizar', + connect: 'Conectar', + disconnect: 'Desconectar', + disconnectContact: 'Desconectar o contato', + deleteContact: 'Excluir contato', + saveContact: 'Salvar contato', + saveAccept: 'Salvar e aceitar a conexão', + saveRequest: 'Salvar e solicitar a conexão', + ignoreRequest: 'Ignorar solicitação', + acceptConnection: 'Aceitar conexão', + requestConnection: 'Solicitar conexão', + cancelRequest: 'Cancelar solicitação', + resyncContact: 'Resincronizar contato', + + login: 'Entrar', + create: 'Criar', + createAccount: 'Criar uma conta', + accountLogin: 'Login da conta', + toCreate: 'As contas são criadas através de um link gerado no painel de administração.', + admin: 'Administrador', + loginError: 'Erro de login', + loginMessage: 'Por favor, confirme seu nome de usuário e senha.', + createError: 'Erro ao criar conta', + createMessage: 'Por favor, verifique com seu administrador.', + adminError: 'Erro de acesso', + adminMessage: 'Por favor, confirme sua senha', + + confirmDelete: 'Excluir conta', + areSure: 'Tem certeza de que deseja excluir a conta?', + + mb: 'MB', + gb: 'GB', + copied: 'Copiado', + accounts: 'Contas', + accessAccount: 'Acessar conta', + browserLink: 'Link do navegador', + mobileToken: 'Token móvel', + createLink: 'Link para criar uma conta', + configureServer: 'Configurar servidor', + reloadAccounts: 'Recarregar contas', + disableAccount: 'Desativar conta', + enableAccount: 'Ativar conta', + deleteAccount: 'Excluir conta', + hostHint: 'domínio:porta/aplicação', + federatedHost: 'Host federado', + storageLimit: 'Limite de armazenamento (GB) / conta', + storageHint: '0 para ilimitado', + keyType: 'Tipo de chave', + accountCreation: 'Criação de conta pública', + enablePush: 'Ativar notificações push', + allowUnsealed: 'Permitir tópicos não selados', + topicContent: 'Conteúdo do tópico:', + enableImage: 'Ativar imagens no tópico', + imageHint: 'Permitir a publicação de imagens em tópicos', + enableAudio: 'Ativar áudio no tópico', + audioHint: 'Permitir a publicação de áudio em tópicos', + enableVideo: 'Ativar vídeos no tópico', + videoHint: 'Permitir a publicação de vídeos em tópicos', + enableBinary: 'Ativar arquivos binários no tópico', + binaryHint: 'Permitir que arquivos binários sejam postados em tópicos', + enableWeb: 'Ativar chamadas WebRTC', + webHint: 'Permitir chamadas de áudio e vídeo para contatos', + enableService: 'Serviço Cloudflare', + serviceHint: 'Habilitar serviço Cloudflare', + serverUrl: 'URL do servidor WebRTC', + urlHint: 'turn:ip:port?transport=udp', + webUsername: 'Nome de usuário WebRTC', + webPassword: 'Senha WebRTC', + failedLoad: 'Falha ao carregar', + limit: 'Limite', + + deleteMessage: 'Excluir mensagem', + messageHint: 'Tem certeza de que deseja excluir a mensagem?', + attachImage: 'Anexar imagem', + attachVideo: 'Anexar vídeo', + attachAudio: 'Anexar áudio', + attachFile: 'Anexar arquivo', + fontColor: 'Alterar cor da fonte', + fontSize: 'Alterar tamanho da fonte', + postMessage: 'Publicar mensagem', + + close: 'Fechar', + leave: 'Sair', + confirmTopic: 'Excluir tópico', + sureTopic: 'Tem certeza de que deseja excluir este tópico?', + confirmLeave: 'Sair do tópico', + sureLeave: 'Tem certeza de que deseja sair deste tópico?', + + details: 'Detalhes', + host: 'Anfitrião', + guest: 'Convidado', + editSubject: 'Editar tópico', + editMembership: 'Editar membros do tópico', + deleteTopic: 'Excluir tópico', + leaveTopic: 'Sair do tópico', + + integrated: 'Integrado', + microphone: 'Microfone', + camera: 'Câmera', + + notes: 'Notas', + + disconnecting: 'Desconectando Contato', + confirmDisconnect: 'Tem certeza de que deseja desconectar o contato?', + removing: 'Removendo Contato', + confirmRemove: 'Tem certeza de que deseja remover o contato?', + message: 'Mensagem', + sealedMessage: 'Mensagem Segura', + + mfaTitle: 'Autenticação de Dois Fatores', + mfaSteps: 'Salve o segredo e confirme o código de verificação', + mfaEnter: 'Digite seu código de verificação', + mfaError: 'erro de código de verificação', + mfaDisabled: 'verificação temporariamente desativada', + mfaConfirm: 'Confirmar', + + enableMultifactor: 'Habilitar autenticação multifator', + disableMultifactor: 'Desativar autenticação multifator', + + disable: 'Desativar', + confirmDisable: 'Desativando Autenticação de Dois Fatores', + disablePrompt: 'Tem certeza de que deseja desativar a autenticação de dois fatores?', +} + +export const de = { + code: 'de', + settings: 'Einstellungen', + contacts: 'Kontakte', + logout: 'Ausloggen', + confirmLogout: 'Sind Sie sicher, dass Sie sich abmelden möchten?', + contactsUpdated: 'Ihre Kontakte wurden aktualisiert', + disconnected: 'Vom Server getrennt', + allDevices: 'Alle Geräte abmelden', + ok: 'OK', + cancel: 'Abbrechen', + enableNotifications: 'Mitteilungen', + + new: 'Neu', + newMessage: 'Neue Nachricht', + topics: 'Themen', + unsetSealing: 'Sicherheitsschlüssel nicht festgelegt', + newTopic: 'Neues Thema', + + noContacts: 'Keine Kontakte', + noTopics: 'Keine Themen', + noConnected: 'Keine verbundenen Kontakte', + subjectOptional: 'Betreff (optional)', + members: 'Mitglieder', + sealedTopic: 'Versiegeltes Thema', + start: 'Starten', + + communication: 'Kommunikation für das dezentrale Web', + setupProfile: 'Profil einrichten', + connectPeople: 'Mit Menschen verbinden', + startConversation: 'Gespräch beginnen', + + default: 'Standard', + dark: 'Dunkel', + light: 'Hell', + + operationFailed: 'Vorgang fehlgeschlagen', + tryAgain: 'Bitte versuchen Sie es erneut.', + + add: 'Hinzufügen', + save: 'Speichern', + forget: 'Vergessen', + unlock: 'Entsperren', + profile: 'Profil', + application: 'Anwendung', + account: 'Konto', + name: 'Name', + node: 'Server', + location: 'Standort', + description: 'Beschreibung', + timeFormat: 'Zeitformat', + dateFormat: 'Datumsformat', + theme: 'Thema', + language: 'Sprache', + timeUs: '12 Stunden', + timeEu: '24 Stunden', + dateUs: 'MM/TT', + dateEu: 'TT/MM', + registry: 'Im Register sichtbar', + sealedTopics: 'Versiegelte Themen', + changeLogin: 'Anmeldeinformationen ändern', + selectImage: 'Auswählen', + profileImage: 'Profilbild', + profileDetails: 'Profildetails', + enableSealed: 'Versiegelte Themen aktivieren', + password: 'Passwort', + newPassword: 'Neues Passwort', + confirmPassword: 'Passwort bestätigen', + deleteKey: "'löschen' eingeben, um den Schlüssel zu löschen", + delete: 'löschen', + remove: 'Entfernen', + username: 'Benutzername', + updateProfile: 'Profil aktualisieren', + + syncError: 'Synchronisierungsfehler', + callTip: 'Kontakt anrufen', + messageTip: 'Nachricht an den Kontakt senden', + connectedTip: 'Verbundener Kontakt', + requestedTip: 'Verbindungsanfrage vom Kontakt gesendet', + connectingTip: 'Verbindungsanfrage läuft', + pendingTip: 'Verbindungsanfrage von einem unbekannten Kontakt gesendet', + confirmedTip: 'Kontakt getrennt', + unsavedTip: 'Unbekannter Kontakt', + + actions: 'Aktionen', + resync: 'Neusynchronisieren', + connect: 'Verbinden', + disconnect: 'Trennen', + disconnectContact: 'Kontakt trennen', + deleteContact: 'Kontakt löschen', + saveContact: 'Kontakt speichern', + saveAccept: 'Speichern und Verbindung akzeptieren', + saveRequest: 'Speichern und Verbindung anfordern', + ignoreRequest: 'Anfrage ignorieren', + acceptConnection: 'Verbindung akzeptieren', + requestConnection: 'Verbindung anfordern', + cancelRequest: 'Anfrage abbrechen', + resyncContact: 'Kontakt neu synchronisieren', + + login: 'Einloggen', + create: 'Erstellen', + createAccount: 'Konto erstellen', + accountLogin: 'Kontoanmeldung', + toCreate: 'Konten werden über einen Link erstellt, der im Administrations-Dashboard generiert wird.', + admin: 'Administrator', + loginError: 'Anmeldefehler', + loginMessage: 'Bitte bestätigen Sie Ihren Benutzernamen und Ihr Passwort.', + createError: 'Fehler beim Erstellen des Kontos', + createMessage: 'Bitte überprüfen Sie bei Ihrem Administrator nach.', + adminError: 'Zugriffsfehler', + adminMessage: 'Bitte bestätigen Sie Ihr Passwort', + + confirmDelete: 'Konto löschen', + areSure: 'Sind Sie sicher, dass Sie das Konto löschen möchten?', + + mb: 'MB', + gb: 'GB', + copied: 'Kopiert', + accounts: 'Konten', + accessAccount: 'Kontozugriff', + browserLink: 'Browser-Link', + mobileToken: 'Mobilcode', + createLink: 'Link zum Erstellen eines Kontos', + configureServer: 'Server konfigurieren', + reloadAccounts: 'Konten neu laden', + disableAccount: 'Konto deaktivieren', + enableAccount: 'Konto aktivieren', + deleteAccount: 'Konto löschen', + hostHint: 'Domäne:Port/Anwendung', + federatedHost: 'Föderierter Host', + storageLimit: 'Speicherbegrenzung (GB) / Konto', + storageHint: '0 für unbegrenzt', + keyType: 'Schlüsseltyp', + accountCreation: 'Öffentliche Kontenerstellung', + enablePush: 'Push-Benachrichtigungen aktivieren', + allowUnsealed: 'Nicht versiegelte Themen zulassen', + topicContent: 'Themeninhalt:', + enableImage: 'Bilder im Thema aktivieren', + imageHint: 'Veröffentlichung von Bildern in Themen ermöglichen', + enableAudio: 'Audio im Thema aktivieren', + audioHint: 'Veröffentlichung von Audio in Themen ermöglichen', + enableVideo: 'Videos im Thema aktivieren', + videoHint: 'Veröffentlichung von Videos in Themen ermöglichen', + enableBinary: 'Binärdateien im Betreff aktivieren', + binaryHint: 'Erlauben Sie die Veröffentlichung von Binärdateien in Themen', + enableWeb: 'WebRTC-Anrufe aktivieren', + webHint: 'Audio- und Videoanrufe an Kontakte zulassen', + enableService: 'Cloudflare-Dienst', + serviceHint: 'Aktivieren Sie den Cloudflare-Dienst', + serverUrl: 'URL des WebRTC-Servers', + urlHint: 'turn:ip:port?transport=udp', + webUsername: 'WebRTC-Benutzername', + webPassword: 'WebRTC-Passwort', + failedLoad: 'Laden fehlgeschlagen', + limit: 'Limit', + + deleteMessage: 'Nachricht löschen', + messageHint: 'Sind Sie sicher, dass Sie die Nachricht löschen möchten?', + attachImage: 'Bild anhängen', + attachVideo: 'Video anhängen', + attachAudio: 'Audio anhängen', + attachFile: 'Datei anhängen', + fontColor: 'Schriftfarbe ändern', + fontSize: 'Schriftgröße ändern', + postMessage: 'Nachricht veröffentlichen', + + close: 'Schließen', + leave: 'Verlassen', + confirmTopic: 'Thema löschen', + sureTopic: 'Sind Sie sicher, dass Sie dieses Thema löschen möchten?', + confirmLeave: 'Thema verlassen', + sureLeave: 'Sind Sie sicher, dass Sie dieses Thema verlassen möchten?', + + details: 'Details', + host: 'Host', + guest: 'Gast', + editSubject: 'Thema bearbeiten', + editMembership: 'Mitgliedschaft im Thema bearbeiten', + deleteTopic: 'Thema löschen', + leaveTopic: 'Thema verlassen', + + integrated: 'Integriert', + microphone: 'Mikrofon', + camera: 'Kamera', + + notes: 'Notizen', + + disconnecting: 'Kontakt wird getrennt', + confirmDisconnect: 'Sind Sie sicher, dass Sie den Kontakt trennen möchten?', + removing: 'Kontakt wird gelöscht', + confirmRemove: 'Sind Sie sicher, dass Sie den Kontakt löschen möchten?', + message: 'Nachricht', + sealedMessage: 'Gesicherte Nachricht', + + mfaTitle: 'Zwei-Faktor-Authentifizierung', + mfaSteps: 'Speichern Sie das Geheimnis und bestätigen Sie den Bestätigungscode', + mfaEnter: 'Geben Sie Ihren Bestätigungs-Code ein', + mfaError: 'Verifizierungscodefehler', + mfaDisabled: 'Verifizierung vorübergehend deaktiviert', + mfaConfirm: 'Bestätigen', + + enableMultifactor: 'Aktivieren Sie die Multi-Faktor-Authentifizierung', + disableMultifactor: 'Deaktivieren Sie die Multi-Faktor-Authentifizierung', + + disable: 'Deaktivieren', + confirmDisable: 'Deaktivierung der Zwei-Faktor-Authentifizierung', + disablePrompt: 'Sind Sie sicher, dass Sie die Zwei-Faktor-Authentifizierung deaktivieren möchten?', +} + +export const ru = { + code: 'ru', + settings: 'Настройки', + contacts: 'Контакты', + logout: 'Выйти', + confirmLogout: 'Вы уверены, что хотите выйти?', + contactsUpdated: 'Ваши контакты обновлены', + disconnected: 'Отключено от сервера', + allDevices: 'Выйти со всех устройств', + ok: 'OK', + cancel: 'Отмена', + enableNotifications: 'Уведомления', + + new: 'Новый', + newMessage: 'Новое сообщение', + topics: 'Темы', + unsetSealing: 'Ключ безопасности не установлен', + newTopic: 'Новая тема', + + noContacts: 'Нет контактов', + noTopics: 'Нет тем', + noConnected: 'Нет подключенных контактов', + subjectOptional: 'Тема (необязательно)', + members: 'Участники', + sealedTopic: 'Запечатанная тема', + start: 'Начать', + + communication: 'Коммуникация для децентрализованной сети', + setupProfile: 'Настройка профиля', + connectPeople: 'Подключиться к людям', + startConversation: 'Начать разговор', + + default: 'По умолчанию', + dark: 'Темная', + light: 'Светлая', + + operationFailed: 'Операция не удалась', + tryAgain: 'Пожалуйста, попробуйте снова.', + + add: 'Добавить', + save: 'Сохранить', + forget: 'Забыть', + unlock: 'Разблокировать', + profile: 'Профиль', + application: 'Приложение', + account: 'Аккаунт', + name: 'Имя', + node: 'Сервер', + location: 'Местоположение', + description: 'Описание', + timeFormat: 'Формат времени', + dateFormat: 'Формат даты', + theme: 'Тема', + language: 'Язык', + timeUs: '12 часов', + timeEu: '24 часа', + dateUs: 'ММ/ДД', + dateEu: 'ДД/ММ', + registry: 'Видимо в реестре', + sealedTopics: 'Запечатанные темы', + changeLogin: 'Изменить логин', + selectImage: 'Выбрать', + profileImage: 'Изображение профиля', + profileDetails: 'Детали профиля', + enableSealed: 'Включить запечатанные темы', + password: 'Пароль', + newPassword: 'Новый пароль', + confirmPassword: 'Подтвердите пароль', + deleteKey: "Введите 'удалить', чтобы удалить ключ", + delete: 'удалить', + remove: 'Удалить', + username: 'Имя пользователя', + updateProfile: 'Обновить профиль', + + syncError: 'Ошибка синхронизации', + callTip: 'Позвонить контакту', + messageTip: 'Отправить сообщение контакту', + connectedTip: 'Подключенный контакт', + requestedTip: 'Запрос на подключение отправлен контактом', + connectingTip: 'Запрос на подключение выполняется', + pendingTip: 'Запрос на подключение отправлен неизвестным контактом', + confirmedTip: 'Контакт отключен', + unsavedTip: 'Неизвестный контакт', + + actions: 'Действия', + resync: 'Пересинхронизация', + connect: 'Подключиться', + disconnect: 'Отключиться', + disconnectContact: 'Отключить контакт', + deleteContact: 'Удалить контакт', + saveContact: 'Сохранить контакт', + saveAccept: 'Сохранить и принять подключение', + saveRequest: 'Сохранить и запросить подключение', + ignoreRequest: 'Игнорировать запрос', + acceptConnection: 'Принять подключение', + requestConnection: 'Запросить подключение', + cancelRequest: 'Отменить запрос', + resyncContact: 'Пересинхронизировать контакт', + + login: 'Войти', + create: 'Создать', + createAccount: 'Создать аккаунт', + accountLogin: 'Вход в аккаунт', + toCreate: 'Учетные записи создаются через ссылку, сгенерированную в панели администратора.', + admin: 'Администратор', + loginError: 'Ошибка входа', + loginMessage: 'Пожалуйста, подтвердите свое имя пользователя и пароль.', + createError: 'Ошибка создания учетной записи', + createMessage: 'Пожалуйста, проверьте с вашим администратором.', + adminError: 'Ошибка доступа', + adminMessage: 'Пожалуйста, подтвердите ваш пароль', + + confirmDelete: 'Удаление аккаунта', + areSure: 'Вы уверены, что хотите удалить аккаунт?', + + mb: 'МБ', + gb: 'ГБ', + copied: 'Скопировано', + accounts: 'Аккаунты', + accessAccount: 'Доступ к аккаунту', + browserLink: 'Ссылка на браузер', + mobileToken: 'Мобильный токен', + createLink: 'Создать ссылку на аккаунт', + configureServer: 'Настроить сервер', + reloadAccounts: 'Перезагрузить аккаунты', + disableAccount: 'От ключить аккаунт', + enableAccount: 'Включить аккаунт', + deleteAccount: 'Удалить аккаунт', + hostHint: 'домен:порт/приложение', + federatedHost: 'Федеративный хост', + storageLimit: 'Лимит хранения (ГБ) / аккаунт', + storageHint: '0 для неограниченного', + keyType: 'Тип ключа', + accountCreation: 'Создание открытого аккаунта', + enablePush: 'Включить уведомления Push', + allowUnsealed: 'Разрешить незапечатанные темы', + topicContent: 'Содержимое темы:', + enableImage: 'Включить изображения тем', + imageHint: 'Разрешить публикацию изображений в темах', + enableAudio: 'Включить аудио тем', + audioHint: 'Разрешить публикацию аудио в темах', + enableVideo: 'Включить видео тем', + videoHint: 'Разрешить публикацию видео в темах', + enableBinary: 'Включить двоичные файлы', + binaryHint: 'Разрешить публикацию двоичных файлов в темах', + enableWeb: 'Включить WebRTC-звонки', + webHint: 'Разрешить аудио- и видеозвонки контактам', + enableService: 'Сервис Cloudflare', + serviceHint: 'Включить службу Cloudflare', + serverUrl: 'URL сервера WebRTC', + urlHint: 'turn:ip:port?transport=udp', + webUsername: 'Имя пользователя WebRTC', + webPassword: 'Пароль WebRTC', + failedLoad: 'Загрузка не удалась', + limit: 'Лимит', + + deleteMessage: 'Удаление сообщения', + messageHint: 'Вы уверены, что хотите удалить сообщение?', + attachImage: 'Прикрепить изображение', + attachVideo: 'Прикрепить видео', + attachAudio: 'Прикрепить аудио', + attachFile: 'Прикрепить файл', + fontColor: 'Изменить цвет текста', + fontSize: 'Изменить размер текста', + postMessage: 'Опубликовать сообщение', + + close: 'Закрыть', + leave: 'Покинуть', + confirmTopic: 'Удаление темы', + sureTopic: 'Вы уверены, что хотите удалить эту тему?', + confirmLeave: 'Покинуть тему', + sureLeave: 'Вы уверены, что хотите покинуть эту тему?', + + details: 'Детали', + host: 'Хост', + guest: 'Гость', + editSubject: 'Редактировать тему', + editMembership: 'Редактировать участников темы', + deleteTopic: 'Удалить тему', + leaveTopic: 'Покинуть тему', + + integrated: 'Интегрированный', + microphone: 'Микрофон', + camera: 'Камера', + + notes: 'Заметки', + + disconnecting: 'Отключение контакта', + confirmDisconnect: 'Вы уверены, что хотите отключить контакт?', + removing: 'Удаление контакта', + confirmRemove: 'Вы уверены, что хотите удалить контакт?', + message: 'Cообщение', + sealedMessage: 'Защищенное Cообщение', + + mfaTitle: 'Двухфакторная аутентификация', + mfaSteps: 'Сохраните секрет и подтвердите код подтверждения', + mfaEnter: 'Введите Ваш верификационный код', + mfaError: 'ошибка проверочного кода', + mfaDisabled: 'проверка временно отключена', + mfaConfirm: 'Подтвердить', + + enableMultifactor: 'Включить многофакторную аутентификацию', + disableMultifactor: 'Отключить многофакторную аутентификацию', + + disable: 'Отключить', + confirmDisable: 'Отключение двухфакторной аутентификации', + disablePrompt: 'Вы уверены, что хотите отключить двухфакторную аутентификацию?', +} diff --git a/app/client/web/src/context/SettingsContext.tsx b/app/client/web/src/context/SettingsContext.tsx new file mode 100644 index 00000000..19eb2679 --- /dev/null +++ b/app/client/web/src/context/SettingsContext.tsx @@ -0,0 +1,13 @@ +import { ReactNode, createContext } from 'react' +import { useSettingsContext } from './useSettingsContext.hook' + +export const SettingsContext = createContext({}) + +export function SettingsContextProvider ({ children }: { children: ReactNode }) { + const { state, actions } = useSettingsContext() + return ( + + {children} + + ) +} diff --git a/app/client/web/src/context/useSettingsContext.hook.ts b/app/client/web/src/context/useSettingsContext.hook.ts new file mode 100644 index 00000000..7bf92102 --- /dev/null +++ b/app/client/web/src/context/useSettingsContext.hook.ts @@ -0,0 +1,229 @@ +import { useEffect, useState } from 'react' +import { LightTheme, DarkTheme } from '../constants/Colors' +import { en, fr, sp, pt, de, ru } from '../constants/Strings' + +export function useSettingsContext () { + const [state, setState] = useState({ + display: null, + width: null, + height: null, + themes: [{ value: 'dark', label: 'Dark' }, { value: 'light', label: 'Light' }], + theme: null, + scheme: null, + colors: {}, + menuStyle: {}, + languages: [{ value: 'en', label: 'English' }, { value: 'fr', label: 'Français' }, { value: 'sp', label: 'Español' }, { value: 'pt', label: 'Português' }, { value: 'de', label: 'Deutsch' }, { value: 'ru', label: 'Русский' }], + language: null, + strings: en, + dateFormat: 'mm/dd', + timeFormat: '12h', + audioId: null, + audioInputs: [], + videoId: null, + videoInputs: [], + }) + + const SMALL_MEDIUM = 650 + const MEDIUM_LARGE = 1000 + const LARGE_XLARGE = 1600 + + const updateState = (value: any) => { + setState((s) => ({ ...s, ...value })) + } + + const handleResize = () => { + if (window.innerWidth < SMALL_MEDIUM) { + updateState({ display: 'small', width: window.innerWidth, height: window.innerHeight }) + } else if (window.innerWidth < MEDIUM_LARGE) { + updateState({ display: 'medium', width: window.innerWidth, height: window.innerHeight }) + } else if (window.innerWidth < LARGE_XLARGE) { + updateState({ display: 'large', width: window.innerWidth, height: window.innerHeight }) + } else { + updateState({ display: 'xlarge', width: window.innerWidth, height: window.innerHeight }) + } + } + + const getDevices = async (type: string) => { + if (!navigator || !navigator.mediaDevices) { + return [] + } + + const filtered = new Map() + const devices = await navigator.mediaDevices.enumerateDevices() + + devices.filter(item => item.kind === type + 'input').forEach(item => { + if (item) { + const label = item.label ? item.label : state.strings.integrated + const entry = filtered.get(item.groupId) + if (entry) { + if (item.label && label.length < entry.label.length) { + filtered.set(item.groupId, { value: item.deviceId, label }) + } + } else { + filtered.set(item.groupId, { value: item.deviceId, label }) + } + } + }) + return Array.from(filtered.values()) + } + + useEffect(() => { + getDevices('audio').then(audio => { + updateState({ audioInputs: audio }) + }) + getDevices('video').then(video => { + updateState({ videoInputs: video }) + }) + // eslint-disable-next-line + }, [state.strings]); + + useEffect(() => { + for (let i = 0; i < 10; i++) { + setTimeout(handleResize, 100 * i) // cludge for my mobile browser + } + handleResize() + window.addEventListener('resize', handleResize) + window.addEventListener('orientationchange', handleResize) + + const scheme = localStorage.getItem('color_scheme') + if (scheme === 'dark') { + updateState({ theme: scheme, scheme: 'dark', colors: DarkTheme, menuStyle: { backgroundColor: DarkTheme.modalArea, color: DarkTheme.mainText } }) + } else if (scheme === 'light') { + updateState({ theme: scheme, scheme: 'light', colors: LightTheme, menuStyle: { backgroundColor: LightTheme.modalArea, color: LightTheme.mainText } }) + } else { + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + updateState({ theme: null, scheme: 'dark', colors: DarkTheme, menuStyle: { backgroundColor: DarkTheme.modalArea, color: DarkTheme.mainText } }) + } else { + updateState({ theme: null, scheme: 'light', colors: LightTheme, menuStyle: { backgroundColor: LightTheme.modalArea, color: LightTheme.mainText } }) + } + } + + const timeFormat = localStorage.getItem('time_format') + if (timeFormat === '24h') { + updateState({ timeFormat }) + } else { + updateState({ timeFormat: '12h' }) + } + + const dateFormat = localStorage.getItem('date_format') + if (dateFormat === 'dd/mm') { + updateState({ dateFormat }) + } else { + updateState({ dateFormat: 'mm/dd' }) + } + + const language = localStorage.getItem('language') + if (language && language.startsWith('fr')) { + updateState({ language: 'fr', strings: fr, themes: [{ value: 'dark', label: fr.dark }, { value: 'light', label: fr.light }] }) + } else if (language && language.startsWith('sp')) { + updateState({ language: 'sp', strings: sp, themes: [{ value: 'dark', label: sp.dark }, { value: 'light', label: sp.light }] }) + } else if (language && language.startsWith('en')) { + updateState({ language: 'en', strings: en, themes: [{ value: 'dark', label: en.dark }, { value: 'light', label: en.light }] }) + } else if (language && language.startsWith('pt')) { + updateState({ language: 'pt', strings: pt, themes: [{ value: 'dark', label: pt.dark }, { value: 'light', label: pt.light }] }) + } else if (language && language.startsWith('de')) { + updateState({ language: 'de', strings: de, themes: [{ value: 'dark', label: de.dark }, { value: 'light', label: de.light }] }) + } else if (language && language.startsWith('ru')) { + updateState({ language: 'ru', strings: ru, themes: [{ value: 'dark', label: ru.dark }, { value: 'light', label: ru.light }] }) + } else { + const browser = navigator.language + if (browser && browser.startsWith('fr')) { + updateState({ language: null, strings: fr, themes: [{ value: 'dark', label: fr.dark }, { value: 'light', label: fr.light }] }) + } else if (browser && browser.startsWith('sp')) { + updateState({ language: null, strings: sp, themes: [{ value: 'dark', label: sp.dark }, { value: 'light', label: sp.light }] }) + } else if (browser && browser.startsWith('pt')) { + updateState({ language: null, strings: pt, themes: [{ value: 'dark', label: pt.dark }, { value: 'light', label: pt.light }] }) + } else if (browser && browser.startsWith('de')) { + updateState({ language: null, strings: de, themes: [{ value: 'dark', label: de.dark }, { value: 'light', label: de.light }] }) + } else if (browser && browser.startsWith('ru')) { + updateState({ language: null, strings: ru, themes: [{ value: 'dark', label: ru.dark }, { value: 'light', label: ru.light }] }) + } else { + updateState({ language: null, strings: en, themes: [{ value: 'dark', label: en.dark }, { value: 'light', label: en.light }] }) + } + } + + const audioId = localStorage.getItem('audio_input') + const videoId = localStorage.getItem('video_input') + updateState({ audioId, videoId }) + + return () => { + window.removeEventListener('resize', handleResize) + window.removeEventListener('orientationchange', handleResize) + } + // eslint-disable-next-line + }, []); + + const actions = { + setTheme: (theme: string) => { + if (theme === 'dark') { + localStorage.setItem('color_scheme', 'dark') + updateState({ theme: 'dark', scheme: 'dark', colors: DarkTheme, menuStyle: { backgroundColor: DarkTheme.modalArea, color: DarkTheme.mainText } }) + } else if (theme === 'light') { + localStorage.setItem('color_scheme', 'light') + updateState({ theme: 'light', scheme: 'light', colors: LightTheme, menuStyle: { backgroundColor: LightTheme.modalArea, color: LightTheme.mainText } }) + } else { + localStorage.removeItem('color_scheme') + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + updateState({ theme: null, scheme: 'dark', colors: DarkTheme, menuStyle: { backgroundColor: DarkTheme.modalArea, color: DarkTheme.mainText } }) + } else { + updateState({ theme: null, scheme: 'ligth', colors: LightTheme, menuStyle: { backgroundColor: LightTheme.modalArea, color: LightTheme.mainText } }) + } + } + }, + setLanguage: (code: string) => { + if (code && code.startsWith('fr')) { + localStorage.setItem('language', 'fr') + updateState({ language: 'fr', strings: fr, themes: [{ value: 'dark', label: fr.dark }, { value: 'light', label: fr.light }] }) + } else if (code && code.startsWith('sp')) { + localStorage.setItem('language', 'sp') + updateState({ language: 'sp', strings: sp, themes: [{ value: 'dark', label: sp.dark }, { value: 'light', label: sp.light }] }) + } else if (code && code.startsWith('en')) { + localStorage.setItem('language', 'en') + updateState({ language: 'en', strings: en, themes: [{ value: 'dark', label: en.dark }, { value: 'light', label: en.light }] }) + } else if (code && code.startsWith('pt')) { + localStorage.setItem('language', 'pt') + updateState({ language: 'pt', strings: pt, themes: [{ value: 'dark', label: pt.dark }, { value: 'light', label: pt.light }] }) + } else if (code && code.startsWith('de')) { + localStorage.setItem('language', 'de') + updateState({ language: 'de', strings: de, themes: [{ value: 'dark', label: de.dark }, { value: 'light', label: de.light }] }) + } else if (code && code.startsWith('ru')) { + localStorage.setItem('language', 'ru') + updateState({ language: 'ru', strings: ru, themes: [{ value: 'dark', label: ru.dark }, { value: 'light', label: ru.light }] }) + } else { + localStorage.removeItem('language') + const browser = navigator.language + if (browser && browser.startsWith('fr')) { + updateState({ language: null, strings: fr, themes: [{ value: 'dark', label: fr.dark }, { value: 'light', label: fr.light }] }) + } else if (browser && browser.startsWith('sp')) { + updateState({ language: null, strings: sp, themes: [{ value: 'dark', label: sp.dark }, { value: 'light', label: sp.light }] }) + } else if (browser && browser.startsWith('pt')) { + updateState({ language: null, strings: pt, themes: [{ value: 'dark', label: pt.dark }, { value: 'light', label: pt.light }] }) + } else if (browser && browser.startsWith('de')) { + updateState({ language: null, strings: de, themes: [{ value: 'dark', label: de.dark }, { value: 'light', label: de.light }] }) + } else if (browser && browser.startsWith('ru')) { + updateState({ language: null, strings: ru, themes: [{ value: 'dark', label: ru.dark }, { value: 'light', label: ru.light }] }) + } else { + updateState({ language: null, strings: en, themes: [{ value: 'dark', label: en.dark }, { value: 'light', label: en.light }] }) + } + } + }, + setDateFormat: (dateFormat: string) => { + localStorage.setItem('date_format', dateFormat) + updateState({ dateFormat }) + }, + setTimeFormat: (timeFormat: string) => { + localStorage.setItem('time_format', timeFormat) + updateState({ timeFormat }) + }, + setAudioInput: (audioId: string) => { + localStorage.setItem('audio_input', audioId) + updateState({ audioId }) + }, + setVideoInput: (videoId: string) => { + localStorage.setItem('video_input', videoId) + updateState({ videoId }) + }, + } + + return { state, actions } +} diff --git a/app/client/web/src/main.tsx b/app/client/web/src/main.tsx index eb083580..19deacad 100644 --- a/app/client/web/src/main.tsx +++ b/app/client/web/src/main.tsx @@ -3,6 +3,7 @@ import { createRoot } from 'react-dom/client' import { App } from '@/App' import './index.css' import { AppContextProvider } from './context/AppContext' +import { SettingsContextProvider } from './context/SettingsContext' const rootElement = document.querySelector('[data-js="root"]') @@ -12,9 +13,11 @@ if (!rootElement) { const root = createRoot(rootElement) root.render( - - - - - , + + + + + + + , )