diff --git a/frontend/src/i18n/index.tsx b/frontend/src/i18n/index.tsx index 7320bdd..b4525d9 100644 --- a/frontend/src/i18n/index.tsx +++ b/frontend/src/i18n/index.tsx @@ -3,8 +3,9 @@ import { createContext, useContext, useState, useCallback, type ReactNode } from 'react'; import en from './translations/en.json'; import zhCN from './translations/zh-CN.json'; +import fr from './translations/fr.json'; -export type Locale = 'en' | 'zh-CN'; +export type Locale = 'en' | 'zh-CN' | 'fr'; /** * Registry of available locales for the UI language toggle. @@ -27,9 +28,10 @@ export type Locale = 'en' | 'zh-CN'; export const LOCALES: ReadonlyArray<{ code: Locale; label: string }> = [ { code: 'en', label: 'English' }, { code: 'zh-CN', label: '中文 (简体)' }, + { code: 'fr', label: 'Français' }, ]; -const translations: Record>> = { en, 'zh-CN': zhCN }; +const translations: Record>> = { en, 'zh-CN': zhCN, fr }; function isLocale(value: unknown): value is Locale { return typeof value === 'string' && LOCALES.some((entry) => entry.code === value); diff --git a/frontend/src/i18n/translations/fr.json b/frontend/src/i18n/translations/fr.json new file mode 100644 index 0000000..951eb72 --- /dev/null +++ b/frontend/src/i18n/translations/fr.json @@ -0,0 +1,246 @@ +{ + "brand": { + "title": "S H A D O W B R O K E R", + "subtitle": "INTERCEPTION MONDIALE DES MENACES", + "systemMetrics": "OPTIC VIS:113 SRC:180 DENS:1.42 0.8ms" + }, + "nav": { + "layers": "COUCHES", + "intel": "RENSEIGNEMENTS", + "markets": "MARCHÉS", + "dataLayers": "COUCHES DE DONNÉES", + "prioritizingMapFeeds": "PRIORISATION DES FLUX CARTE", + "restoreUi": "RESTAURER L'INTERFACE" + }, + "controls": { + "updates": "MISES À JOUR", + "checking": "VÉRIFICATION...", + "upToDate": "À JOUR", + "checkFailed": "VÉRIFICATION ÉCHOUÉE", + "node": "NŒUD", + "terminal": "TERMINAL", + "coordinates": "COORDONNÉES", + "location": "LOCALISATION", + "style": "STYLE", + "solar": "SOLAIRE", + "hoverMap": "Survolez la carte...", + "na": "N/A" + }, + "update": { + "downloadInstaller": "TÉLÉCHARGER L'INSTALLATEUR", + "installUpdate": "INSTALLER LA MISE À JOUR", + "autoUpdate": "MISE À JOUR AUTO", + "viewRelease": "VOIR LA VERSION", + "manualDownload": "TÉLÉCHARGEMENT MANUEL", + "cancel": "ANNULER", + "tryAgain": "RÉESSAYER", + "downloadingUpdate": "TÉLÉCHARGEMENT DE LA MISE À JOUR...", + "restarting": "REDÉMARRAGE...", + "updateFailed": "MISE À JOUR ÉCHOUÉE", + "dockerUpdate": "MISE À JOUR DOCKER", + "dockerUpdateDetail": "Les conteneurs Docker doivent être mis à jour en tirant de nouvelles images.\n Exécutez ceci sur votre machine hôte :" + }, + "node": { + "activateNode": "ACTIVER LE NŒUD", + "activatingNode": "ACTIVATION DU NŒUD", + "nodeActivated": "NŒUD ACTIVÉ", + "stipulations": "CONDITIONS", + "yes": "OUI", + "no": "NON", + "agree": "ACCEPTER", + "disagree": "REFUSER", + "turnOff": "DÉSACTIVER", + "keepOn": "GARDER ACTIVÉ", + "turningOff": "DÉSACTIVATION...", + "activating": "ACTIVATION...", + "nodeOnline": "NŒUD EN LIGNE", + "generatingIdentity": "Génération de l'identité...", + "identityReady": "Identité prête", + "preparingTransport": "Préparation du transport onion...", + "findingPeers": "Recherche de pairs d'amorçage...", + "peersReady": "Pairs d'amorçage prêts", + "syncingChain": "Synchronisation de la chaîne...", + "soloNodeReady": "Nœud solo prêt", + "synced": "Synchronisé", + "events": "événements", + "peers": "pairs", + "close": "FERMER", + "activatePrompt": "Voulez-vous activer un nœud sur cette installation ?", + "activateDetail": "Cela active votre nœud participant local et synchronise l'Infonet uniquement via les pairs onion/RNS Wormhole disponibles. L'amorçage par clearnet est désactivé par défaut.", + "keepSyncing": "Votre nœud continue de se synchroniser tant que le backend est en cours d'exécution — vous pouvez fermer cet onglet de navigateur. Pour exécuter un nœud headless sans le tableau de bord, utilisez", + "termsTitle": "EN CONTINUANT VOUS ACCEPTEZ :", + "term1": "Cette installation peut conserver une copie locale de la chaîne Infonet publique.", + "term2": "Les nouvelles installations n'utilisent pas de seed Infonet clearnet.", + "term3": "La synchronisation du nœud participant nécessite un pair onion/RNS via Wormhole.", + "term4": "Votre backend peut se synchroniser avec des pairs d'amorçage privés configurés en arrière-plan.", + "term5": "Wormhole maintient l'Infonet, les gates, le Dead Drop et le trafic DM sur la voie obfusquée.", + "syncTakingLong": "La synchronisation prend plus de temps que prévu. Votre nœud est actif et continuera à se synchroniser en arrière-plan." + }, + "terminal": { + "infonetTerminal": "TERMINAL INFONET", + "privateLaneReady": "VOIE PRIVÉE PRÊTE", + "privateLaneStarting": "VOIE PRIVÉE DÉMARRAGE", + "privateLaneOffline": "VOIE PRIVÉE HORS LIGNE", + "enterTerminal": "Entrer dans le terminal Wormhole et se synchroniser avec les communs obfusqués de l'Infonet ?", + "terminalDetail": "Le terminal fonctionne via Wormhole pour les gates obfusqués, la boîte de réception et les communications expérimentales.", + "identityReady": "Votre identité obfusquée est déjà provisionnée. Entrer maintenant maintient la voie obfusquée séparée du chemin de synchronisation du nœud public.", + "identityNotReady": "Cela active Wormhole et ouvre la voie obfusquée. Si vous avez déjà une identité Wormhole, elle sera réutilisée. Si vous n'en avez pas, une sera amorcée une fois puis conservée.", + "beforeYouEnter": "AVANT D'ENTRER :", + "termTerminal1": "Le terminal est destiné aux gates Wormhole (voie privée transitoire) et au Dead Drop / DM (voie privée renforcée).", + "termTerminal2": "Votre nœud participant peut rester actif séparément sans modifier cette voie d'identité obfusquée.", + "termTerminal3": "Mesh reste le périmètre public. Wormhole est le commun obfusqué.", + "wormholeCleanup": "NETTOYAGE WORMHOLE :", + "cleanupDetail": "Fermer le terminal Infonet arrêtera Wormhole automatiquement. Si vous forcez la fermeture du navigateur ou si l'arrêt échoue, Wormhole peut continuer à tourner en arrière-plan. Exécutez", + "cleanupFromRoot": "depuis la racine du projet pour vous assurer qu'il est complètement arrêté.", + "enterWormhole": "ENTRER DANS WORMHOLE", + "activateWormhole": "ACTIVER WORMHOLE", + "entering": "ENTRÉE...", + "goToMesh": "ALLER AU MESH" + }, + "status": { + "off": "ARRÊTÉ", + "solo": "SOLO", + "connected": "CONNECTÉ", + "syncing": "SYNCHRONISATION", + "forkStop": "ARRÊT FOURCHE", + "syncIssue": "PROBLÈME DE SYNC", + "active": "ACTIF", + "participant": "participant", + "nodeOff": "nœud • arrêté", + "bootstrapWarning": "nœud • avertissement de démarrage" + }, + "backend": { + "offline": "BACKEND HORS LIGNE — Impossible de joindre le serveur backend. Vérifiez que le conteneur backend est en cours d'exécution et que BACKEND_URL est correct." + }, + "settings": { + "title": "Paramètres", + "close": "Fermer", + "general": "Général", + "feeds": "Flux", + "shodan": "Shodan", + "sar": "SAR", + "infonet": "Infonet", + "about": "À propos" + }, + "legend": { + "title": "Légende", + "close": "Fermer" + }, + "onboarding": { + "welcome": "Bienvenue dans ShadowBroker", + "getStarted": "Commencer" + }, + "news": { + "title": "Renseignement Actualités", + "noResults": "Aucun résultat", + "searchPlaceholder": "Rechercher des actualités..." + }, + "filters": { + "title": "Filtres de données", + "clear": "Effacer", + "all": "Tout" + }, + "map": { + "findLocate": "Rechercher / Localiser", + "searchPlaceholder": "Rechercher coordonnées, lieu ou indicatif...", + "measure": "Mesurer", + "clearMeasure": "Effacer la mesure" + }, + "layers": { + "aircraft": "Aéronefs", + "commercialFlights": "Vols commerciaux", + "privateAircraft": "Aéronefs privés", + "privateJets": "Jets privés", + "militaryFlights": "Vols militaires", + "trackedAircraft": "Aéronefs suivis", + "gpsJamming": "Brouillage GPS", + "maritime": "Maritime", + "militaryVessels": "Navires militaires", + "cargoShips": "Navires de charge", + "civilianShips": "Navires civils", + "passengerShips": "Navires à passagers", + "trackedYachts": "Yachts suivis", + "fishingActivity": "Activité de pêche", + "space": "Espace", + "satellites": "Satellites", + "gibsImagery": "Imagerie GIBS", + "highresSatellite": "Satellite haute résolution", + "sentinelHub": "Sentinel Hub", + "viirsNightlights": "Éclairage nocturne VIIRS", + "hazards": "Dangers", + "earthquakes": "Séismes", + "fires": "Incendies", + "ukraineAlerts": "Alertes Ukraine", + "weatherAlerts": "Alertes météo", + "volcanoes": "Volcans", + "airQuality": "Qualité de l'air", + "infrastructure": "Infrastructure", + "cctv": "CCTV", + "datacenters": "Centres de données", + "internetOutages": "Pannes Internet", + "powerPlants": "Centrales électriques", + "militaryBases": "Bases militaires", + "trains": "Trains", + "sigint": "SIGINT", + "kiwisdr": "KiwiSDR", + "pskReporter": "PSK Reporter", + "satnogs": "SatNOGS", + "tinygs": "TinyGS", + "scanners": "Scanners", + "meshtastic": "Meshtastic", + "aprs": "APRS", + "overlays": "Couches superposées", + "ukraineFrontline": "Ligne de front Ukraine", + "globalIncidents": "Incidents mondiaux", + "dayNight": "Jour/Nuit", + "correlations": "Corrélations", + "contradictions": "Contradictions", + "uapSightings": "Observations de PAN", + "biosurveillance": "Biosurveillance", + "wastewater": "Eaux usées", + "crowdThreat": "CrowdThreat", + "shodanOverlay": "Couche Shodan", + "aiIntel": "Infos IA", + "sar": "SAR" + }, + "shodan": { + "title": "Connecteur Shodan", + "searchPlaceholder": "Rechercher des appareils...", + "apiKeyRequired": "Clé API requise", + "results": "résultats" + }, + "ai": { + "title": "Panneau d'infos IA", + "connected": "Connecté", + "disconnected": "Déconnecté" + }, + "meshChat": { + "title": "Chat Mesh", + "infonet": "Infonet", + "meshtastic": "Meshtastic", + "deadDrop": "Dead Drop", + "sendMessage": "Envoyer un message", + "placeholder": "Tapez un message..." + }, + "watchlist": { + "title": "Liste de surveillance", + "empty": "Aucun élément surveillé", + "clear": "Effacer" + }, + "timeline": { + "title": "Chronologie des événements", + "noEvents": "Aucun événement" + }, + "sar": { + "title": "Détection de changements au sol SAR", + "modeA": "Mode Catalogue", + "modeB": "Mode Anomalie", + "aoiEditor": "Éditeur AOI", + "addAoi": "Ajouter AOI", + "groundDeformation": "Déformation du sol", + "waterChange": "Changement hydrique", + "vegetation": "Perturbation végétale", + "damage": "Évaluation des dégâts", + "coherence": "Changement de cohérence" + } +}