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

@@ -1875,5 +1875,125 @@
"enterRegionName": "Nombre de región (ej. auckland)",
"noRegionsFound": "No se encontraron regiones en este repetidor.",
"regionScopeSet": "Ámbito de región establecido a {name}",
"regionScopeCleared": "Ámbito de región limpiado"
"regionScopeCleared": "Ámbito de región limpiado",
"simpleMode": "Modo simple",
"simpleModeDescription": "Solo chat y mapa con contactos favoritos",
"simpleModeRequiresLockedDestination": "Activa primero «Bloquear destino» con un canal para usar el modo simple",
"noFavouriteContacts": "Sin contactos favoritos",
"selectDestinationFirst": "Selecciona primero un canal, contacto o sala",
"recipientNoLongerAvailable": "El destinatario ya no está disponible",
"failedToSendMessage": "Error al enviar: {error}",
"tictactoeDirectOnly": "El tres en raya solo funciona en mensajes directos. Elige primero un contacto.",
"deviceKeyUnavailable": "La clave del dispositivo no está disponible",
"imageCompressionFailed": "Error al comprimir la imagen",
"imageFragmentationFailed": "Error al fragmentar la imagen",
"failedToAnnounceImage": "Error al anunciar la imagen",
"noRecipientSelected": "Ningún destinatario seleccionado",
"imageSendFailed": "Error al enviar la imagen",
"microphonePermissionRequiredForVoice": "Se necesita permiso de micrófono para los mensajes de voz",
"recordingDiscarded": "Grabación descartada",
"publicChannelMediaWarning": "Estás a punto de enviar {mediaType} al canal público. No es recomendable, porque cualquiera en la red mesh podría recibirlo. Elige un canal privado o etiquetado, salvo que sea lo que quieres.",
"selectPrivateChannelToShareLocation": "Selecciona un canal privado para compartir tu ubicación.",
"stopSharingMyLocation": "Dejar de compartir mi ubicación",
"shareMyLocation": "Compartir mi ubicación",
"stopRecording": "Detener grabación",
"recordVoice": "Grabar voz",
"moreActions": "Más acciones",
"noMessagesInFilter": "No hay mensajes en este filtro",
"noMatchesInFilter": "No hay coincidencias en este filtro",
"syncFailed": "Error de sincronización: {error}",
"invalidLink": "Enlace no válido",
"cannotOpenLink": "No se puede abrir el enlace",
"failedToOpenLink": "Error al abrir el enlace",
"failedToResendMessage": "Error al reenviar el mensaje",
"retryFailed": "Error al reintentar: {error}",
"deleteDrawingAndMessage": "Eliminar dibujo y mensaje",
"lastEchoRelay": "Último eco: repetidor",
"lastEchoPath": "Último eco: ruta",
"lastEchoBytesReport": "Último eco: informe de bytes",
"noGpsCoordinatesAvailable": "No hay coordenadas GPS disponibles",
"notASarMarker": "No es un marcador SAR",
"failedToSaveTemplate": "Error al guardar la plantilla: {error}",
"drawingNotFound": "Dibujo no encontrado",
"unknownDrawingType": "Tipo de dibujo desconocido",
"customMapMarker": "Marcador de mapa personalizado",
"customMapPointLabel": "Punto: {point}",
"mapIdLabel": "ID del mapa: {id}",
"cannotFetchVoice": "No se puede obtener la voz",
"senderContactUnknown": "El contacto del remitente es desconocido. Sincroniza primero los contactos.",
"senderRouteUnknown": "La ruta del remitente es desconocida. Sincroniza primero los contactos/la ruta.",
"messageTooFar": "El mensaje está demasiado lejos ({hops} saltos, máx. {maxHops}).",
"senderRouteNoPathResponse": "La ruta del remitente no respondió a la comprobación de ruta. Sincroniza los contactos/la ruta e inténtalo de nuevo.",
"senderRouteNoRawResponse": "La ruta del remitente no respondió en la ruta de transporte raw.",
"voiceFetchOverHops": "Obtener la voz a través de {hops} saltos puede tardar un poco.",
"voiceReceiveCanceled": "Recepción de voz cancelada",
"fetchingMissingVoiceFragments": "Obteniendo fragmentos de voz que faltan",
"receivingVoice": "Recibiendo voz",
"cannotFetchImage": "No se puede obtener la imagen",
"imageFetchOverHops": "Obtener la imagen a través de {hops} saltos puede tardar un poco.",
"imageAlreadyBeingReceived": "La imagen ya se está recibiendo",
"loadImage": "Cargar imagen",
"imageFetchFailedToSendRequest": "No se pudo enviar la solicitud de imagen",
"imageUnavailable": "Imagen no disponible por ahora",
"imageFetchTimedOut": "La obtención de la imagen agotó el tiempo de espera",
"imageReceiveCanceled": "Recepción de imagen cancelada",
"closeImagePreview": "Cerrar vista previa de imagen",
"recordingReleaseToSend": "Grabando... suelta para enviar la voz",
"sendingVoice": "Enviando voz...",
"sendLongPressToRecordVoice": "Enviar (mantén pulsado para grabar voz)",
"discardRecording": "Descartar grabación",
"hopCount": "{count, plural, =1{1 salto} other{{count} saltos}}",
"channelsSectionDescription": "Canales de difusión para el tráfico mesh cercano",
"roomsSectionDescription": "Espacios compartidos para la coordinación del equipo",
"contactsSectionDescription": "Personas y dispositivos a tu alcance directo",
"noRecentChatters": "Sin participantes recientes",
"public": "Público",
"failedToSendTicTacToeMove": "No se pudo enviar el movimiento de tres en raya",
"youWon": "Has ganado",
"opponentWon": "Ha ganado el rival",
"gameDraw": "Empate",
"yourTurn": "Te toca",
"opponentTurn": "Le toca al rival",
"pleaseEnterValidCoordinates": "Introduce coordenadas válidas",
"locationNotAvailable": "Ubicación no disponible",
"customCaveMapPoint": "Punto de mapa de cueva personalizado",
"mapPoint": "Punto del mapa",
"fetchingMissingImageFragments": "Obteniendo fragmentos que faltan…",
"loadingImage": "Cargando…",
"receivingImage": "Recibiendo…",
"tapToLoad": "Toca para cargar",
"connectDevice": "Conectar dispositivo",
"chooseTransportSubtitle": "Elige el transporte Bluetooth, WiFi o Serie",
"ble": "BLE",
"network": "Red",
"serial": "Serie",
"scan": "Buscar",
"pressScanToSearchForDevices": "Pulsa Buscar para encontrar dispositivos cercanos",
"failedToConnectToDevice": "Error al conectar con {name}",
"unknownDevice": "Dispositivo desconocido",
"showingCachedResultsTapRefresh": "Mostrando resultados en caché. Toca actualizar para volver a buscar.",
"scanningLocalNetworkOnPort": "Buscando dispositivos MeshCore WiFi en la red local en el puerto {port}",
"cancelScanAndAddServer": "Cancelar búsqueda y añadir servidor",
"addServer": "Añadir servidor",
"scanningProgressIps": "Buscando... {scanned}/{total} IP",
"cancelAndEnterManually": "Cancelar e introducir manualmente",
"noRecentOrDiscoveredServers": "Aún no hay servidores recientes ni descubiertos",
"recentlyUsed": "Usados recientemente",
"connecting": "Conectando...",
"discoveredOnThisNetwork": "Descubiertos en esta red",
"serverNoLongerAvailable": "El servidor en {host}:{port} ya no está disponible. Vuelve a buscar para encontrar servidores activos.",
"failedToConnectToHost": "Error al conectar con {host}:{port}",
"enterValidIpAddress": "Introduce una dirección IP válida",
"enterValidTcpPort": "Introduce un puerto TCP válido",
"ipAddress": "Dirección IP",
"tcpPort": "Puerto TCP",
"customServerPort": "Puerto de servidor personalizado",
"chat": "Chat",
"restoringPreviousLink": "Restaurando el enlace anterior",
"noDeviceConnected": "Ningún dispositivo conectado",
"sendAdvert": "Enviar mi contacto",
"advertMode": "Enviar mi contacto",
"advertModeSubtitle": "Elige hasta dónde debe llegar tu información de contacto.",
"floodAdvertSent": "Contacto enviado a través de la red mesh",
"directAdvertSent": "Contacto enviado a dispositivos cercanos"
}