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

@@ -908,5 +908,125 @@
"enterRegionName": "Название региона (напр. auckland)",
"noRegionsFound": "Регионы не найдены на этом ретрансляторе.",
"regionScopeSet": "Область региона установлена на {name}",
"regionScopeCleared": "Область региона очищена"
"regionScopeCleared": "Область региона очищена",
"simpleMode": "Простой режим",
"simpleModeDescription": "Только чат и карта с избранными контактами",
"simpleModeRequiresLockedDestination": "Сначала включите «Зафиксировать назначение» с каналом, чтобы использовать простой режим",
"noFavouriteContacts": "Нет избранных контактов",
"selectDestinationFirst": "Сначала выберите канал, контакт или комнату",
"recipientNoLongerAvailable": "Получатель больше не доступен",
"failedToSendMessage": "Не удалось отправить: {error}",
"tictactoeDirectOnly": "Крестики-нолики работают только в личных сообщениях. Сначала выберите контакт.",
"deviceKeyUnavailable": "Ключ устройства недоступен",
"imageCompressionFailed": "Не удалось сжать изображение",
"imageFragmentationFailed": "Не удалось разделить изображение на фрагменты",
"failedToAnnounceImage": "Не удалось анонсировать изображение",
"noRecipientSelected": "Получатель не выбран",
"imageSendFailed": "Не удалось отправить изображение",
"microphonePermissionRequiredForVoice": "Для голосовых сообщений требуется разрешение на микрофон",
"recordingDiscarded": "Запись отменена",
"publicChannelMediaWarning": "Вы собираетесь отправить {mediaType} в публичный канал. Это не рекомендуется: получить это может любой в mesh-сети. Выберите приватный или тегированный канал, если только это не то, что вы хотите.",
"selectPrivateChannelToShareLocation": "Выберите приватный канал, чтобы поделиться местоположением.",
"stopSharingMyLocation": "Прекратить делиться местоположением",
"shareMyLocation": "Поделиться местоположением",
"stopRecording": "Остановить запись",
"recordVoice": "Записать голосовое сообщение",
"moreActions": "Другие действия",
"noMessagesInFilter": "В этом фильтре нет сообщений",
"noMatchesInFilter": "В этом фильтре нет совпадений",
"syncFailed": "Ошибка синхронизации: {error}",
"invalidLink": "Недействительная ссылка",
"cannotOpenLink": "Не удаётся открыть ссылку",
"failedToOpenLink": "Не удалось открыть ссылку",
"failedToResendMessage": "Не удалось повторно отправить сообщение",
"retryFailed": "Повторная попытка не удалась: {error}",
"deleteDrawingAndMessage": "Удалить рисунок и сообщение",
"lastEchoRelay": "Последнее эхо: ретранслятор",
"lastEchoPath": "Последнее эхо: путь",
"lastEchoBytesReport": "Последнее эхо: отчёт о байтах",
"noGpsCoordinatesAvailable": "Координаты GPS недоступны",
"notASarMarker": "Это не маркер SAR",
"failedToSaveTemplate": "Не удалось сохранить шаблон: {error}",
"drawingNotFound": "Рисунок не найден",
"unknownDrawingType": "Неизвестный тип рисунка",
"customMapMarker": "Пользовательский маркер карты",
"customMapPointLabel": "Точка: {point}",
"mapIdLabel": "ID карты: {id}",
"cannotFetchVoice": "Не удаётся получить голосовое сообщение",
"senderContactUnknown": "Контакт отправителя неизвестен. Сначала синхронизируйте контакты.",
"senderRouteUnknown": "Маршрут отправителя неизвестен. Сначала синхронизируйте контакты/путь.",
"messageTooFar": "Сообщение слишком далеко ({hops} хопов, макс. {maxHops}).",
"senderRouteNoPathResponse": "Маршрут отправителя не ответил на проверку пути. Синхронизируйте контакты/путь и попробуйте снова.",
"senderRouteNoRawResponse": "Маршрут отправителя не ответил на raw-транспортном пути.",
"voiceFetchOverHops": "Получение голоса через {hops} хопов может занять время.",
"voiceReceiveCanceled": "Приём голосового сообщения отменён",
"fetchingMissingVoiceFragments": "Получение недостающих фрагментов голоса",
"receivingVoice": "Приём голоса",
"cannotFetchImage": "Не удаётся получить изображение",
"imageFetchOverHops": "Получение изображения через {hops} хопов может занять время.",
"imageAlreadyBeingReceived": "Изображение уже принимается",
"loadImage": "Загрузить изображение",
"imageFetchFailedToSendRequest": "Не удалось отправить запрос на изображение",
"imageUnavailable": "Изображение сейчас недоступно",
"imageFetchTimedOut": "Время ожидания изображения истекло",
"imageReceiveCanceled": "Приём изображения отменён",
"closeImagePreview": "Закрыть просмотр изображения",
"recordingReleaseToSend": "Запись... отпустите, чтобы отправить",
"sendingVoice": "Отправка голоса...",
"sendLongPressToRecordVoice": "Отправить (удерживайте для записи голоса)",
"discardRecording": "Удалить запись",
"hopCount": "{count, plural, one{{count} хоп} few{{count} хопа} many{{count} хопов} other{{count} хопа}}",
"channelsSectionDescription": "Широковещательные каналы для mesh-трафика поблизости",
"roomsSectionDescription": "Общие пространства для постоянной координации команды",
"contactsSectionDescription": "Люди и устройства в прямой досягаемости",
"noRecentChatters": "Нет недавних собеседников",
"public": "Публичный",
"failedToSendTicTacToeMove": "Не удалось отправить ход в крестики-нолики",
"youWon": "Вы победили",
"opponentWon": "Победил соперник",
"gameDraw": "Ничья",
"yourTurn": "Ваш ход",
"opponentTurn": "Ход соперника",
"pleaseEnterValidCoordinates": "Введите корректные координаты",
"locationNotAvailable": "Местоположение недоступно",
"customCaveMapPoint": "Пользовательская точка карты пещеры",
"mapPoint": "Точка карты",
"fetchingMissingImageFragments": "Получение недостающих фрагментов…",
"loadingImage": "Загрузка…",
"receivingImage": "Приём…",
"tapToLoad": "Нажмите, чтобы загрузить",
"connectDevice": "Подключить устройство",
"chooseTransportSubtitle": "Выберите транспорт: Bluetooth, WiFi или последовательный порт",
"ble": "BLE",
"network": "Сеть",
"serial": "Последовательный порт",
"scan": "Сканировать",
"pressScanToSearchForDevices": "Нажмите «Сканировать», чтобы найти устройства поблизости",
"failedToConnectToDevice": "Не удалось подключиться к {name}",
"unknownDevice": "Неизвестное устройство",
"showingCachedResultsTapRefresh": "Показаны кэшированные результаты. Нажмите «Обновить», чтобы пересканировать.",
"scanningLocalNetworkOnPort": "Сканирование локальной сети на наличие WiFi-устройств MeshCore на порту {port}",
"cancelScanAndAddServer": "Отменить сканирование и добавить сервер",
"addServer": "Добавить сервер",
"scanningProgressIps": "Сканирование... {scanned}/{total} IP-адресов",
"cancelAndEnterManually": "Отменить и ввести вручную",
"noRecentOrDiscoveredServers": "Пока нет недавних или обнаруженных серверов",
"recentlyUsed": "Недавно использованные",
"connecting": "Подключение...",
"discoveredOnThisNetwork": "Обнаружены в этой сети",
"serverNoLongerAvailable": "Сервер {host}:{port} больше недоступен. Выполните сканирование снова, чтобы найти активные серверы.",
"failedToConnectToHost": "Не удалось подключиться к {host}:{port}",
"enterValidIpAddress": "Введите корректный IP-адрес",
"enterValidTcpPort": "Введите корректный TCP-порт",
"ipAddress": "IP-адрес",
"tcpPort": "TCP-порт",
"customServerPort": "Пользовательский порт сервера",
"chat": "Чат",
"restoringPreviousLink": "Восстановление предыдущего подключения",
"noDeviceConnected": "Нет подключённого устройства",
"sendAdvert": "Отправить мой контакт",
"advertMode": "Отправить мой контакт",
"advertModeSubtitle": "Выберите, как далеко должны распространиться ваши контактные данные.",
"floodAdvertSent": "Контакт отправлен по всей mesh-сети",
"directAdvertSent": "Контакт отправлен ближайшим устройствам"
}