feat: UX polish, localization, advert relocation, and map filter fixes

- i18n: translate connection screen + chat tab keys into 13 locales;
  add "Send my contact" (advert) strings in all locales
- feat: move self-advert from home header into composer "+" action menu
  (new lib/utils/advert_helper.dart, always shows Flood/Direct sheet)
- fix: hide-repeaters map toggle was bypassed in simple mode
- fix: simple mode map now shows only favourite chat contacts
- fix: wrap ListTiles in Material (contact_tile secondary actions,
  inferred contact group card) to satisfy Flutter ink assertions
- device-authoritative contact/channel sync and UX review fixes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Janez T
2026-06-12 10:01:10 +02:00
parent 3e1113035d
commit ba27843166
62 changed files with 12546 additions and 2521 deletions

View File

@@ -1878,5 +1878,125 @@
"enterRegionName": "Nom de région (ex. auckland)",
"noRegionsFound": "Aucune région trouvée sur ce relais.",
"regionScopeSet": "Portée régionale définie sur {name}",
"regionScopeCleared": "Portée régionale effacée"
"regionScopeCleared": "Portée régionale effacée",
"simpleMode": "Mode simple",
"simpleModeDescription": "Uniquement le chat et la carte avec les contacts favoris",
"simpleModeRequiresLockedDestination": "Activez d'abord « Verrouiller la destination » avec un canal pour utiliser le mode simple",
"noFavouriteContacts": "Aucun contact favori",
"selectDestinationFirst": "Sélectionnez d'abord un canal, un contact ou un salon",
"recipientNoLongerAvailable": "Le destinataire n'est plus disponible",
"failedToSendMessage": "Échec de l'envoi : {error}",
"tictactoeDirectOnly": "Le morpion ne fonctionne qu'en messages directs. Choisissez d'abord un contact.",
"deviceKeyUnavailable": "La clé de l'appareil n'est pas disponible",
"imageCompressionFailed": "Échec de la compression de l'image",
"imageFragmentationFailed": "Échec de la fragmentation de l'image",
"failedToAnnounceImage": "Échec de l'annonce de l'image",
"noRecipientSelected": "Aucun destinataire sélectionné",
"imageSendFailed": "Échec de l'envoi de l'image",
"microphonePermissionRequiredForVoice": "L'autorisation du microphone est requise pour la voix",
"recordingDiscarded": "Enregistrement abandonné",
"publicChannelMediaWarning": "Vous êtes sur le point d'envoyer {mediaType} sur le canal public. Ce n'est pas conseillé, car tout le monde sur le réseau mesh peut le recevoir. Choisissez un canal privé ou étiqueté, sauf si c'est bien votre intention.",
"selectPrivateChannelToShareLocation": "Sélectionnez un canal privé pour partager votre position.",
"stopSharingMyLocation": "Arrêter de partager ma position",
"shareMyLocation": "Partager ma position",
"stopRecording": "Arrêter l'enregistrement",
"recordVoice": "Enregistrer un message vocal",
"moreActions": "Plus d'actions",
"noMessagesInFilter": "Aucun message dans ce filtre",
"noMatchesInFilter": "Aucun résultat dans ce filtre",
"syncFailed": "Échec de la synchronisation : {error}",
"invalidLink": "Lien invalide",
"cannotOpenLink": "Impossible d'ouvrir le lien",
"failedToOpenLink": "Échec de l'ouverture du lien",
"failedToResendMessage": "Échec du renvoi du message",
"retryFailed": "Échec de la nouvelle tentative : {error}",
"deleteDrawingAndMessage": "Supprimer le dessin et le message",
"lastEchoRelay": "Dernier écho : relais",
"lastEchoPath": "Dernier écho : chemin",
"lastEchoBytesReport": "Dernier écho : rapport d'octets",
"noGpsCoordinatesAvailable": "Aucune coordonnée GPS disponible",
"notASarMarker": "Ce n'est pas un marqueur SAR",
"failedToSaveTemplate": "Échec de l'enregistrement du modèle : {error}",
"drawingNotFound": "Dessin introuvable",
"unknownDrawingType": "Type de dessin inconnu",
"customMapMarker": "Marqueur de carte personnalisé",
"customMapPointLabel": "Point : {point}",
"mapIdLabel": "ID de carte : {id}",
"cannotFetchVoice": "Impossible de récupérer la voix",
"senderContactUnknown": "Le contact de l'expéditeur est inconnu. Synchronisez d'abord les contacts.",
"senderRouteUnknown": "La route de l'expéditeur est inconnue. Synchronisez d'abord les contacts/le chemin.",
"messageTooFar": "Le message est trop loin ({hops} sauts, max. {maxHops}).",
"senderRouteNoPathResponse": "La route de l'expéditeur n'a pas répondu à la vérification du chemin. Synchronisez les contacts/le chemin et réessayez.",
"senderRouteNoRawResponse": "La route de l'expéditeur n'a pas répondu sur le chemin de transport brut.",
"voiceFetchOverHops": "La récupération de la voix sur {hops} sauts peut prendre un moment.",
"voiceReceiveCanceled": "Réception vocale annulée",
"fetchingMissingVoiceFragments": "Récupération des fragments vocaux manquants",
"receivingVoice": "Réception de la voix",
"cannotFetchImage": "Impossible de récupérer l'image",
"imageFetchOverHops": "La récupération de l'image sur {hops} sauts peut prendre un moment.",
"imageAlreadyBeingReceived": "L'image est déjà en cours de réception",
"loadImage": "Charger l'image",
"imageFetchFailedToSendRequest": "Échec de l'envoi de la demande d'image",
"imageUnavailable": "Image indisponible pour le moment",
"imageFetchTimedOut": "Délai de récupération de l'image dépassé",
"imageReceiveCanceled": "Réception de l'image annulée",
"closeImagePreview": "Fermer l'aperçu de l'image",
"recordingReleaseToSend": "Enregistrement... relâchez pour envoyer",
"sendingVoice": "Envoi du message vocal...",
"sendLongPressToRecordVoice": "Envoyer (appui long pour enregistrer la voix)",
"discardRecording": "Abandonner l'enregistrement",
"hopCount": "{count, plural, one{{count} saut} other{{count} sauts}}",
"channelsSectionDescription": "Canaux de diffusion pour le trafic mesh à proximité",
"roomsSectionDescription": "Espaces partagés pour la coordination d'équipe",
"contactsSectionDescription": "Personnes et appareils joignables directement",
"noRecentChatters": "Aucun participant récent",
"public": "Public",
"failedToSendTicTacToeMove": "Échec de l'envoi du coup de morpion",
"youWon": "Vous avez gagné",
"opponentWon": "L'adversaire a gagné",
"gameDraw": "Match nul",
"yourTurn": "À vous de jouer",
"opponentTurn": "Au tour de l'adversaire",
"pleaseEnterValidCoordinates": "Veuillez saisir des coordonnées valides",
"locationNotAvailable": "Position non disponible",
"customCaveMapPoint": "Point de carte de grotte personnalisé",
"mapPoint": "Point de carte",
"fetchingMissingImageFragments": "Récupération des fragments manquants…",
"loadingImage": "Chargement…",
"receivingImage": "Réception…",
"tapToLoad": "Touchez pour charger",
"connectDevice": "Connecter l'appareil",
"chooseTransportSubtitle": "Choisissez le transport Bluetooth, WiFi ou Série",
"ble": "BLE",
"network": "Réseau",
"serial": "Série",
"scan": "Rechercher",
"pressScanToSearchForDevices": "Appuyez sur Rechercher pour trouver les appareils à proximité",
"failedToConnectToDevice": "Échec de la connexion à {name}",
"unknownDevice": "Appareil inconnu",
"showingCachedResultsTapRefresh": "Affichage des résultats en cache. Touchez Actualiser pour relancer la recherche.",
"scanningLocalNetworkOnPort": "Recherche d'appareils MeshCore WiFi sur le réseau local, port {port}",
"cancelScanAndAddServer": "Annuler la recherche et ajouter un serveur",
"addServer": "Ajouter un serveur",
"scanningProgressIps": "Recherche... {scanned}/{total} IP",
"cancelAndEnterManually": "Annuler et saisir manuellement",
"noRecentOrDiscoveredServers": "Aucun serveur récent ou découvert pour le moment",
"recentlyUsed": "Utilisés récemment",
"connecting": "Connexion...",
"discoveredOnThisNetwork": "Découverts sur ce réseau",
"serverNoLongerAvailable": "Le serveur {host}:{port} n'est plus disponible. Veuillez relancer la recherche pour trouver des serveurs actifs.",
"failedToConnectToHost": "Échec de la connexion à {host}:{port}",
"enterValidIpAddress": "Saisissez une adresse IP valide",
"enterValidTcpPort": "Saisissez un port TCP valide",
"ipAddress": "Adresse IP",
"tcpPort": "Port TCP",
"customServerPort": "Port de serveur personnalisé",
"chat": "Chat",
"restoringPreviousLink": "Restauration de la liaison précédente",
"noDeviceConnected": "Aucun appareil connecté",
"sendAdvert": "Envoyer mon contact",
"advertMode": "Envoyer mon contact",
"advertModeSubtitle": "Choisissez jusqu'où vos informations de contact doivent être diffusées.",
"floodAdvertSent": "Contact envoyé à travers le réseau mesh",
"directAdvertSent": "Contact envoyé aux appareils à proximité"
}