mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-26 05:26:37 +10:00
Added Line Of Sight Feature for repeater placement, Added app wide Units Setting (#198)
* feat: add LOS workflow, global units, l10n cleanup, and mobile UI overflow fixes Squashes prior PR commits into one changeset including: LOS map/service/tests, global metric/imperial unit system adoption, notification/BLE safety fixes, app-wide localization backfill/mojibake cleanup, and responsive UI title/overflow hardening. * l10n: revert unrelated locale churn for LOS feature * feat: keep LOS with app-wide unit settings * fix: resolve post-merge app bar/import analyzer errors * style: format screen files for CI
This commit is contained in:
+116
-1
@@ -1599,5 +1599,120 @@
|
|||||||
"chat_ShowAllPaths": "Покажи всички пътища",
|
"chat_ShowAllPaths": "Покажи всички пътища",
|
||||||
"settings_clientRepeatSubtitle": "Позволете на това устройство да предава пакети към мрежата за други устройства.",
|
"settings_clientRepeatSubtitle": "Позволете на това устройство да предава пакети към мрежата за други устройства.",
|
||||||
"settings_clientRepeatFreqWarning": "За повторение извън мрежата са необходими честоти от 433, 869 или 918 MHz.",
|
"settings_clientRepeatFreqWarning": "За повторение извън мрежата са необходими честоти от 433, 869 или 918 MHz.",
|
||||||
"settings_clientRepeat": "Без електричество – повторение"
|
"settings_clientRepeat": "Без електричество – повторение",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "Данни за надморска височина на LOS: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "единици",
|
||||||
|
"appSettings_unitsMetric": "Метрика (m / km)",
|
||||||
|
"appSettings_unitsImperial": "Имперска (ft / mi)",
|
||||||
|
"map_lineOfSight": "Линия на видимост",
|
||||||
|
"map_losScreenTitle": "Линия на видимост",
|
||||||
|
"losSelectStartEnd": "Изберете начални и крайни възли за LOS.",
|
||||||
|
"losRunFailed": "Проверката на пряката видимост е неуспешна: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Изчистете всички точки",
|
||||||
|
"losRunToViewElevationProfile": "Стартирайте LOS, за да видите профила на надморската височина",
|
||||||
|
"losMenuTitle": "LOS меню",
|
||||||
|
"losMenuSubtitle": "Докоснете възли или натиснете продължително карта за персонализирани точки",
|
||||||
|
"losShowDisplayNodes": "Показване на възли на дисплея",
|
||||||
|
"losCustomPoints": "Персонализирани точки",
|
||||||
|
"losCustomPointLabel": "Персонализирано {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Точка А",
|
||||||
|
"losPointB": "Точка Б",
|
||||||
|
"losAntennaA": "Антена A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Антена B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Стартирайте LOS",
|
||||||
|
"losNoElevationData": "Няма данни за надморска височина",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, чист LOS, минимално разстояние {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, блокиран от {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: проверка...",
|
||||||
|
"losStatusNoData": "LOS: няма данни",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} ясно, {blocked} блокирано, {unknown} неизвестно",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Няма налични данни за надморска височина за една или повече проби.",
|
||||||
|
"losErrorInvalidInput": "Невалидни данни за точки/надморска височина за изчисляване на LOS.",
|
||||||
|
"losRenameCustomPoint": "Преименувайте персонализирана точка",
|
||||||
|
"losPointName": "Име на точката",
|
||||||
|
"losShowPanelTooltip": "Показване на LOS панел",
|
||||||
|
"losHidePanelTooltip": "Скриване на LOS панела",
|
||||||
|
"losElevationAttribution": "Данни за надморска височина: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-1
@@ -1627,5 +1627,120 @@
|
|||||||
"chat_ShowAllPaths": "Alle Pfade anzeigen",
|
"chat_ShowAllPaths": "Alle Pfade anzeigen",
|
||||||
"settings_clientRepeat": "Wiederholung, ohne Stromanschluss",
|
"settings_clientRepeat": "Wiederholung, ohne Stromanschluss",
|
||||||
"settings_clientRepeatFreqWarning": "Die Kommunikation ohne Stromversorgung erfordert Frequenzen von 433, 869 oder 918 MHz.",
|
"settings_clientRepeatFreqWarning": "Die Kommunikation ohne Stromversorgung erfordert Frequenzen von 433, 869 oder 918 MHz.",
|
||||||
"settings_clientRepeatSubtitle": "Ermöglichen Sie diesem Gerät, Mesh-Pakete für andere zu wiederholen."
|
"settings_clientRepeatSubtitle": "Ermöglichen Sie diesem Gerät, Mesh-Pakete für andere zu wiederholen.",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "LOS-Höhendaten: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "Einheiten",
|
||||||
|
"appSettings_unitsMetric": "Metrisch (m/km)",
|
||||||
|
"appSettings_unitsImperial": "Imperial (ft/mi)",
|
||||||
|
"map_lineOfSight": "Sichtlinie",
|
||||||
|
"map_losScreenTitle": "Sichtlinie",
|
||||||
|
"losSelectStartEnd": "Wählen Sie Start- und Endknoten für LOS aus.",
|
||||||
|
"losRunFailed": "Sichtlinienprüfung fehlgeschlagen: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Löschen Sie alle Punkte",
|
||||||
|
"losRunToViewElevationProfile": "Führen Sie LOS aus, um das Höhenprofil anzuzeigen",
|
||||||
|
"losMenuTitle": "LOS-Menü",
|
||||||
|
"losMenuSubtitle": "Tippen Sie auf Knoten oder drücken Sie lange auf die Karte, um benutzerdefinierte Punkte anzuzeigen",
|
||||||
|
"losShowDisplayNodes": "Anzeigeknoten anzeigen",
|
||||||
|
"losCustomPoints": "Benutzerdefinierte Punkte",
|
||||||
|
"losCustomPointLabel": "Benutzerdefiniert {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Punkt A",
|
||||||
|
"losPointB": "Punkt B",
|
||||||
|
"losAntennaA": "Antenne A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Antenne B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Führen Sie LOS aus",
|
||||||
|
"losNoElevationData": "Keine Höhendaten",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, freie Sichtlinie, Mindestabstand {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, blockiert durch {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: Überprüfen...",
|
||||||
|
"losStatusNoData": "LOS: keine Daten",
|
||||||
|
"losStatusSummary": "Sichtlinie: {clear}/{total} frei, {blocked} blockiert, {unknown} unbekannt",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Für eine oder mehrere Proben sind keine Höhendaten verfügbar.",
|
||||||
|
"losErrorInvalidInput": "Ungültige Punkte/Höhendaten für die LOS-Berechnung.",
|
||||||
|
"losRenameCustomPoint": "Benennen Sie den benutzerdefinierten Punkt um",
|
||||||
|
"losPointName": "Punktname",
|
||||||
|
"losShowPanelTooltip": "LOS-Panel anzeigen",
|
||||||
|
"losHidePanelTooltip": "LOS-Panel ausblenden",
|
||||||
|
"losElevationAttribution": "Höhendaten: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,7 @@
|
|||||||
},
|
},
|
||||||
"settings_aboutLegalese": "2026 MeshCore Open Source Project",
|
"settings_aboutLegalese": "2026 MeshCore Open Source Project",
|
||||||
"settings_aboutDescription": "An open-source Flutter client for MeshCore LoRa mesh networking devices.",
|
"settings_aboutDescription": "An open-source Flutter client for MeshCore LoRa mesh networking devices.",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "LOS elevation data: Open-Meteo (CC BY 4.0)",
|
||||||
"settings_infoName": "Name",
|
"settings_infoName": "Name",
|
||||||
"settings_infoId": "ID",
|
"settings_infoId": "ID",
|
||||||
"settings_infoStatus": "Status",
|
"settings_infoStatus": "Status",
|
||||||
@@ -242,6 +243,9 @@
|
|||||||
"appSettings_last24Hours": "Last 24 hours",
|
"appSettings_last24Hours": "Last 24 hours",
|
||||||
"appSettings_lastWeek": "Last week",
|
"appSettings_lastWeek": "Last week",
|
||||||
"appSettings_offlineMapCache": "Offline Map Cache",
|
"appSettings_offlineMapCache": "Offline Map Cache",
|
||||||
|
"appSettings_unitsTitle": "Units",
|
||||||
|
"appSettings_unitsMetric": "Metric (m / km)",
|
||||||
|
"appSettings_unitsImperial": "Imperial (ft / mi)",
|
||||||
"appSettings_noAreaSelected": "No area selected",
|
"appSettings_noAreaSelected": "No area selected",
|
||||||
"appSettings_areaSelectedZoom": "Area selected (zoom {minZoom}-{maxZoom})",
|
"appSettings_areaSelectedZoom": "Area selected (zoom {minZoom}-{maxZoom})",
|
||||||
"@appSettings_areaSelectedZoom": {
|
"@appSettings_areaSelectedZoom": {
|
||||||
@@ -639,6 +643,8 @@
|
|||||||
},
|
},
|
||||||
"chat_invalidLink": "Invalid link format",
|
"chat_invalidLink": "Invalid link format",
|
||||||
"map_title": "Node Map",
|
"map_title": "Node Map",
|
||||||
|
"map_lineOfSight": "Line of Sight",
|
||||||
|
"map_losScreenTitle": "Line of Sight",
|
||||||
"map_noNodesWithLocation": "No nodes with location data",
|
"map_noNodesWithLocation": "No nodes with location data",
|
||||||
"map_nodesNeedGps": "Nodes need to share their GPS coordinates\nto appear on the map",
|
"map_nodesNeedGps": "Nodes need to share their GPS coordinates\nto appear on the map",
|
||||||
"map_nodesCount": "Nodes: {count}",
|
"map_nodesCount": "Nodes: {count}",
|
||||||
@@ -1548,6 +1554,115 @@
|
|||||||
"pathTrace_refreshTooltip": "Refresh Path Trace.",
|
"pathTrace_refreshTooltip": "Refresh Path Trace.",
|
||||||
"pathTrace_someHopsNoLocation": "One or more of the hops is missing a location!",
|
"pathTrace_someHopsNoLocation": "One or more of the hops is missing a location!",
|
||||||
"pathTrace_clearTooltip": "Clear path.",
|
"pathTrace_clearTooltip": "Clear path.",
|
||||||
|
"losSelectStartEnd": "Select start and end nodes for LOS.",
|
||||||
|
"losRunFailed": "Line-of-sight check failed: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Clear all points",
|
||||||
|
"losRunToViewElevationProfile": "Run LOS to view elevation profile",
|
||||||
|
"losMenuTitle": "LOS Menu",
|
||||||
|
"losMenuSubtitle": "Tap nodes or long-press map for custom points",
|
||||||
|
"losShowDisplayNodes": "Show display nodes",
|
||||||
|
"losCustomPoints": "Custom points",
|
||||||
|
"losCustomPointLabel": "Custom {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Point A",
|
||||||
|
"losPointB": "Point B",
|
||||||
|
"losAntennaA": "Antenna A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Antenna B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Run LOS",
|
||||||
|
"losNoElevationData": "No elevation data",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, clear LOS, min clearance {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, blocked by {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: checking...",
|
||||||
|
"losStatusNoData": "LOS: no data",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} clear, {blocked} blocked, {unknown} unknown",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Elevation data unavailable for one or more samples.",
|
||||||
|
"losErrorInvalidInput": "Invalid points/elevation data for LOS calculation.",
|
||||||
|
"losRenameCustomPoint": "Rename custom point",
|
||||||
|
"losPointName": "Point name",
|
||||||
|
"losShowPanelTooltip": "Show LOS panel",
|
||||||
|
"losHidePanelTooltip": "Hide LOS panel",
|
||||||
|
"losElevationAttribution": "Elevation data: Open-Meteo (CC BY 4.0)",
|
||||||
"contacts_pathTrace": "Path Trace",
|
"contacts_pathTrace": "Path Trace",
|
||||||
"contacts_ping": "Ping",
|
"contacts_ping": "Ping",
|
||||||
"contacts_repeaterPathTrace": "Path trace to repeater",
|
"contacts_repeaterPathTrace": "Path trace to repeater",
|
||||||
|
|||||||
+116
-1
@@ -1627,5 +1627,120 @@
|
|||||||
"chat_ShowAllPaths": "Mostrar todos los caminos",
|
"chat_ShowAllPaths": "Mostrar todos los caminos",
|
||||||
"settings_clientRepeatFreqWarning": "Para la comunicación fuera de la red, se requiere una frecuencia de 433, 869 o 918 MHz.",
|
"settings_clientRepeatFreqWarning": "Para la comunicación fuera de la red, se requiere una frecuencia de 433, 869 o 918 MHz.",
|
||||||
"settings_clientRepeat": "Repetir sin conexión",
|
"settings_clientRepeat": "Repetir sin conexión",
|
||||||
"settings_clientRepeatSubtitle": "Permita que este dispositivo repita los paquetes de red para otros usuarios."
|
"settings_clientRepeatSubtitle": "Permita que este dispositivo repita los paquetes de red para otros usuarios.",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "Datos de elevación LOS: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "Unidades",
|
||||||
|
"appSettings_unitsMetric": "Métrico (m/km)",
|
||||||
|
"appSettings_unitsImperial": "Imperial (pies/millas)",
|
||||||
|
"map_lineOfSight": "Línea de visión",
|
||||||
|
"map_losScreenTitle": "Línea de visión",
|
||||||
|
"losSelectStartEnd": "Seleccione los nodos de inicio y fin para LOS.",
|
||||||
|
"losRunFailed": "Error en la comprobación de la línea de visión: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Borrar todos los puntos",
|
||||||
|
"losRunToViewElevationProfile": "Ejecute LOS para ver el perfil de elevación",
|
||||||
|
"losMenuTitle": "Menú LOS",
|
||||||
|
"losMenuSubtitle": "Toque nodos o mantenga presionado el mapa para puntos personalizados",
|
||||||
|
"losShowDisplayNodes": "Mostrar nodos de visualización",
|
||||||
|
"losCustomPoints": "Puntos personalizados",
|
||||||
|
"losCustomPointLabel": "Personalizado {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Punto A",
|
||||||
|
"losPointB": "Punto B",
|
||||||
|
"losAntennaA": "Antena A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Antena B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Ejecutar LOS",
|
||||||
|
"losNoElevationData": "Sin datos de elevación",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, despejar LOS, autorización mínima {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, bloqueado por {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: comprobando...",
|
||||||
|
"losStatusNoData": "LOS: sin datos",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} claro, {blocked} bloqueado, {unknown} desconocido",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Datos de elevación no disponibles para una o más muestras.",
|
||||||
|
"losErrorInvalidInput": "Datos de puntos/elevación no válidos para el cálculo de LOS.",
|
||||||
|
"losRenameCustomPoint": "Cambiar el nombre del punto personalizado",
|
||||||
|
"losPointName": "Nombre del punto",
|
||||||
|
"losShowPanelTooltip": "Mostrar panel LOS",
|
||||||
|
"losHidePanelTooltip": "Ocultar panel LOS",
|
||||||
|
"losElevationAttribution": "Datos de elevación: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-1
@@ -1599,5 +1599,120 @@
|
|||||||
"chat_ShowAllPaths": "Afficher tous les chemins",
|
"chat_ShowAllPaths": "Afficher tous les chemins",
|
||||||
"settings_clientRepeatFreqWarning": "Pour les transmissions hors réseau, il est nécessaire d'utiliser les fréquences de 433, 869 ou 918 MHz.",
|
"settings_clientRepeatFreqWarning": "Pour les transmissions hors réseau, il est nécessaire d'utiliser les fréquences de 433, 869 ou 918 MHz.",
|
||||||
"settings_clientRepeatSubtitle": "Permettez à cet appareil de répéter les paquets de données pour les autres.",
|
"settings_clientRepeatSubtitle": "Permettez à cet appareil de répéter les paquets de données pour les autres.",
|
||||||
"settings_clientRepeat": "Répétition hors réseau"
|
"settings_clientRepeat": "Répétition hors réseau",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "Données d'élévation LOS : Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "Unités",
|
||||||
|
"appSettings_unitsMetric": "Métrique (m/km)",
|
||||||
|
"appSettings_unitsImperial": "Impérial (ft / mi)",
|
||||||
|
"map_lineOfSight": "Ligne de vue",
|
||||||
|
"map_losScreenTitle": "Ligne de vue",
|
||||||
|
"losSelectStartEnd": "Sélectionnez les nœuds de début et de fin pour LOS.",
|
||||||
|
"losRunFailed": "Échec de la vérification de la ligne de vue : {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Effacer tous les points",
|
||||||
|
"losRunToViewElevationProfile": "Exécutez LOS pour afficher le profil d'altitude",
|
||||||
|
"losMenuTitle": "Menu LOS",
|
||||||
|
"losMenuSubtitle": "Appuyez sur les nœuds ou appuyez longuement sur la carte pour des points personnalisés",
|
||||||
|
"losShowDisplayNodes": "Afficher les nœuds d'affichage",
|
||||||
|
"losCustomPoints": "Points personnalisés",
|
||||||
|
"losCustomPointLabel": "Personnalisé {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Point A",
|
||||||
|
"losPointB": "Point B",
|
||||||
|
"losAntennaA": "Antenne A : {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Antenne B : {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Exécuter la LOS",
|
||||||
|
"losNoElevationData": "Aucune donnée d'altitude",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, LOS clair, clairance minimale {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, bloqué par {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS : vérification...",
|
||||||
|
"losStatusNoData": "LOS : aucune donnée",
|
||||||
|
"losStatusSummary": "LOS : {clear}/{total} clair, {blocked} bloqué, {unknown} inconnu",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Données d'altitude indisponibles pour un ou plusieurs échantillons.",
|
||||||
|
"losErrorInvalidInput": "Données de points/d'altitude non valides pour le calcul de la LOS.",
|
||||||
|
"losRenameCustomPoint": "Renommer le point personnalisé",
|
||||||
|
"losPointName": "Nom du point",
|
||||||
|
"losShowPanelTooltip": "Afficher le panneau LOS",
|
||||||
|
"losHidePanelTooltip": "Masquer le panneau LOS",
|
||||||
|
"losElevationAttribution": "Données d'altitude : Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-1
@@ -1599,5 +1599,120 @@
|
|||||||
"chat_ShowAllPaths": "Mostra tutti i percorsi",
|
"chat_ShowAllPaths": "Mostra tutti i percorsi",
|
||||||
"settings_clientRepeat": "Ripetizione \"fuori dalla rete\"",
|
"settings_clientRepeat": "Ripetizione \"fuori dalla rete\"",
|
||||||
"settings_clientRepeatFreqWarning": "Per la comunicazione fuori rete, è necessario utilizzare frequenze di 433, 869 o 918 MHz.",
|
"settings_clientRepeatFreqWarning": "Per la comunicazione fuori rete, è necessario utilizzare frequenze di 433, 869 o 918 MHz.",
|
||||||
"settings_clientRepeatSubtitle": "Permetti a questo dispositivo di ripetere i pacchetti di rete per gli altri."
|
"settings_clientRepeatSubtitle": "Permetti a questo dispositivo di ripetere i pacchetti di rete per gli altri.",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "Dati di elevazione LOS: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "Unità",
|
||||||
|
"appSettings_unitsMetric": "Metrico (m/km)",
|
||||||
|
"appSettings_unitsImperial": "Imperiale (ft / mi)",
|
||||||
|
"map_lineOfSight": "Linea di vista",
|
||||||
|
"map_losScreenTitle": "Linea di vista",
|
||||||
|
"losSelectStartEnd": "Seleziona i nodi iniziali e finali per la LOS.",
|
||||||
|
"losRunFailed": "Controllo della linea di vista fallito: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Cancella tutti i punti",
|
||||||
|
"losRunToViewElevationProfile": "Eseguire LOS per visualizzare il profilo altimetrico",
|
||||||
|
"losMenuTitle": "Menù LOS",
|
||||||
|
"losMenuSubtitle": "Tocca i nodi o premi a lungo la mappa per punti personalizzati",
|
||||||
|
"losShowDisplayNodes": "Mostra i nodi di visualizzazione",
|
||||||
|
"losCustomPoints": "Punti personalizzati",
|
||||||
|
"losCustomPointLabel": "Personalizzato {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Punto A",
|
||||||
|
"losPointB": "Punto B",
|
||||||
|
"losAntennaA": "Antenna A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Antenna B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Esegui LOS",
|
||||||
|
"losNoElevationData": "Nessun dato di elevazione",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, libera LOS, distanza minima {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, bloccato da {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: controllo...",
|
||||||
|
"losStatusNoData": "LOS: nessun dato",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} libera, {blocked} bloccato, {unknown} sconosciuto",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Dati di elevazione non disponibili per uno o più campioni.",
|
||||||
|
"losErrorInvalidInput": "Dati punti/elevazione non validi per il calcolo della LOS.",
|
||||||
|
"losRenameCustomPoint": "Rinomina punto personalizzato",
|
||||||
|
"losPointName": "Nome del punto",
|
||||||
|
"losShowPanelTooltip": "Mostra il pannello LOS",
|
||||||
|
"losHidePanelTooltip": "Nascondi il pannello LOS",
|
||||||
|
"losElevationAttribution": "Dati di elevazione: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -700,6 +700,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'An open-source Flutter client for MeshCore LoRa mesh networking devices.'**
|
/// **'An open-source Flutter client for MeshCore LoRa mesh networking devices.'**
|
||||||
String get settings_aboutDescription;
|
String get settings_aboutDescription;
|
||||||
|
|
||||||
|
/// No description provided for @settings_aboutOpenMeteoAttribution.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'LOS elevation data: Open-Meteo (CC BY 4.0)'**
|
||||||
|
String get settings_aboutOpenMeteoAttribution;
|
||||||
|
|
||||||
/// No description provided for @settings_infoName.
|
/// No description provided for @settings_infoName.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -1240,6 +1246,24 @@ abstract class AppLocalizations {
|
|||||||
/// **'Offline Map Cache'**
|
/// **'Offline Map Cache'**
|
||||||
String get appSettings_offlineMapCache;
|
String get appSettings_offlineMapCache;
|
||||||
|
|
||||||
|
/// No description provided for @appSettings_unitsTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Units'**
|
||||||
|
String get appSettings_unitsTitle;
|
||||||
|
|
||||||
|
/// No description provided for @appSettings_unitsMetric.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Metric (m / km)'**
|
||||||
|
String get appSettings_unitsMetric;
|
||||||
|
|
||||||
|
/// No description provided for @appSettings_unitsImperial.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Imperial (ft / mi)'**
|
||||||
|
String get appSettings_unitsImperial;
|
||||||
|
|
||||||
/// No description provided for @appSettings_noAreaSelected.
|
/// No description provided for @appSettings_noAreaSelected.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -2290,6 +2314,18 @@ abstract class AppLocalizations {
|
|||||||
/// **'Node Map'**
|
/// **'Node Map'**
|
||||||
String get map_title;
|
String get map_title;
|
||||||
|
|
||||||
|
/// No description provided for @map_lineOfSight.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Line of Sight'**
|
||||||
|
String get map_lineOfSight;
|
||||||
|
|
||||||
|
/// No description provided for @map_losScreenTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Line of Sight'**
|
||||||
|
String get map_losScreenTitle;
|
||||||
|
|
||||||
/// No description provided for @map_noNodesWithLocation.
|
/// No description provided for @map_noNodesWithLocation.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -4772,6 +4808,178 @@ abstract class AppLocalizations {
|
|||||||
/// **'Clear path.'**
|
/// **'Clear path.'**
|
||||||
String get pathTrace_clearTooltip;
|
String get pathTrace_clearTooltip;
|
||||||
|
|
||||||
|
/// No description provided for @losSelectStartEnd.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Select start and end nodes for LOS.'**
|
||||||
|
String get losSelectStartEnd;
|
||||||
|
|
||||||
|
/// No description provided for @losRunFailed.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Line-of-sight check failed: {error}'**
|
||||||
|
String losRunFailed(String error);
|
||||||
|
|
||||||
|
/// No description provided for @losClearAllPoints.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Clear all points'**
|
||||||
|
String get losClearAllPoints;
|
||||||
|
|
||||||
|
/// No description provided for @losRunToViewElevationProfile.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Run LOS to view elevation profile'**
|
||||||
|
String get losRunToViewElevationProfile;
|
||||||
|
|
||||||
|
/// No description provided for @losMenuTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'LOS Menu'**
|
||||||
|
String get losMenuTitle;
|
||||||
|
|
||||||
|
/// No description provided for @losMenuSubtitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Tap nodes or long-press map for custom points'**
|
||||||
|
String get losMenuSubtitle;
|
||||||
|
|
||||||
|
/// No description provided for @losShowDisplayNodes.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Show display nodes'**
|
||||||
|
String get losShowDisplayNodes;
|
||||||
|
|
||||||
|
/// No description provided for @losCustomPoints.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Custom points'**
|
||||||
|
String get losCustomPoints;
|
||||||
|
|
||||||
|
/// No description provided for @losCustomPointLabel.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Custom {index}'**
|
||||||
|
String losCustomPointLabel(int index);
|
||||||
|
|
||||||
|
/// No description provided for @losPointA.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Point A'**
|
||||||
|
String get losPointA;
|
||||||
|
|
||||||
|
/// No description provided for @losPointB.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Point B'**
|
||||||
|
String get losPointB;
|
||||||
|
|
||||||
|
/// No description provided for @losAntennaA.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Antenna A: {value} {unit}'**
|
||||||
|
String losAntennaA(String value, String unit);
|
||||||
|
|
||||||
|
/// No description provided for @losAntennaB.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Antenna B: {value} {unit}'**
|
||||||
|
String losAntennaB(String value, String unit);
|
||||||
|
|
||||||
|
/// No description provided for @losRun.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Run LOS'**
|
||||||
|
String get losRun;
|
||||||
|
|
||||||
|
/// No description provided for @losNoElevationData.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'No elevation data'**
|
||||||
|
String get losNoElevationData;
|
||||||
|
|
||||||
|
/// No description provided for @losProfileClear.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{distance} {distanceUnit}, clear LOS, min clearance {clearance} {heightUnit}'**
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// No description provided for @losProfileBlocked.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{distance} {distanceUnit}, blocked by {obstruction} {heightUnit}'**
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// No description provided for @losStatusChecking.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'LOS: checking...'**
|
||||||
|
String get losStatusChecking;
|
||||||
|
|
||||||
|
/// No description provided for @losStatusNoData.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'LOS: no data'**
|
||||||
|
String get losStatusNoData;
|
||||||
|
|
||||||
|
/// No description provided for @losStatusSummary.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'LOS: {clear}/{total} clear, {blocked} blocked, {unknown} unknown'**
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown);
|
||||||
|
|
||||||
|
/// No description provided for @losErrorElevationUnavailable.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Elevation data unavailable for one or more samples.'**
|
||||||
|
String get losErrorElevationUnavailable;
|
||||||
|
|
||||||
|
/// No description provided for @losErrorInvalidInput.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Invalid points/elevation data for LOS calculation.'**
|
||||||
|
String get losErrorInvalidInput;
|
||||||
|
|
||||||
|
/// No description provided for @losRenameCustomPoint.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Rename custom point'**
|
||||||
|
String get losRenameCustomPoint;
|
||||||
|
|
||||||
|
/// No description provided for @losPointName.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Point name'**
|
||||||
|
String get losPointName;
|
||||||
|
|
||||||
|
/// No description provided for @losShowPanelTooltip.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Show LOS panel'**
|
||||||
|
String get losShowPanelTooltip;
|
||||||
|
|
||||||
|
/// No description provided for @losHidePanelTooltip.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Hide LOS panel'**
|
||||||
|
String get losHidePanelTooltip;
|
||||||
|
|
||||||
|
/// No description provided for @losElevationAttribution.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Elevation data: Open-Meteo (CC BY 4.0)'**
|
||||||
|
String get losElevationAttribution;
|
||||||
|
|
||||||
/// No description provided for @contacts_pathTrace.
|
/// No description provided for @contacts_pathTrace.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -326,6 +326,10 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Отворен софтуер за Flutter клиент за MeshCore LoRa мрежови устройства.';
|
'Отворен софтуер за Flutter клиент за MeshCore LoRa мрежови устройства.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'Данни за надморска височина на LOS: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Име';
|
String get settings_infoName => 'Име';
|
||||||
|
|
||||||
@@ -622,6 +626,15 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Кеш на офлайн карти';
|
String get appSettings_offlineMapCache => 'Кеш на офлайн карти';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'единици';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Метрика (m / km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Имперска (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Няма избрана област';
|
String get appSettings_noAreaSelected => 'Няма избрана област';
|
||||||
|
|
||||||
@@ -1243,6 +1256,12 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Карта на възлите';
|
String get map_title => 'Карта на възлите';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Линия на видимост';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Линия на видимост';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation => 'Няма възли с данни за местоположение.';
|
String get map_noNodesWithLocation => 'Няма възли с данни за местоположение.';
|
||||||
|
|
||||||
@@ -2724,6 +2743,116 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Изчисти пътя';
|
String get pathTrace_clearTooltip => 'Изчисти пътя';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd => 'Изберете начални и крайни възли за LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Проверката на пряката видимост е неуспешна: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Изчистете всички точки';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Стартирайте LOS, за да видите профила на надморската височина';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'LOS меню';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Докоснете възли или натиснете продължително карта за персонализирани точки';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Показване на възли на дисплея';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Персонализирани точки';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Персонализирано $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Точка А';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Точка Б';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Антена A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Антена B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Стартирайте LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Няма данни за надморска височина';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, чист LOS, минимално разстояние $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, блокиран от $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: проверка...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: няма данни';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total ясно, $blocked блокирано, $unknown неизвестно';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Няма налични данни за надморска височина за една или повече проби.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Невалидни данни за точки/надморска височина за изчисляване на LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Преименувайте персонализирана точка';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Име на точката';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Показване на LOS панел';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Скриване на LOS панела';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Данни за надморска височина: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Пътен проследяване';
|
String get contacts_pathTrace => 'Пътен проследяване';
|
||||||
|
|
||||||
|
|||||||
@@ -320,6 +320,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Ein Open-Source-Flutter-Client für MeshCore LoRa-Meshnetzwerkgeräte.';
|
'Ein Open-Source-Flutter-Client für MeshCore LoRa-Meshnetzwerkgeräte.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'LOS-Höhendaten: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Name';
|
String get settings_infoName => 'Name';
|
||||||
|
|
||||||
@@ -619,6 +623,15 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Offline-Karten-Cache';
|
String get appSettings_offlineMapCache => 'Offline-Karten-Cache';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Einheiten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Metrisch (m/km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Imperial (ft/mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Kein Bereich ausgewählt';
|
String get appSettings_noAreaSelected => 'Kein Bereich ausgewählt';
|
||||||
|
|
||||||
@@ -1242,6 +1255,12 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Karte';
|
String get map_title => 'Karte';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Sichtlinie';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Sichtlinie';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation => 'Keine Knoten mit Standortdaten';
|
String get map_noNodesWithLocation => 'Keine Knoten mit Standortdaten';
|
||||||
|
|
||||||
@@ -2729,6 +2748,117 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Pfad löschen';
|
String get pathTrace_clearTooltip => 'Pfad löschen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd =>
|
||||||
|
'Wählen Sie Start- und Endknoten für LOS aus.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Sichtlinienprüfung fehlgeschlagen: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Löschen Sie alle Punkte';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Führen Sie LOS aus, um das Höhenprofil anzuzeigen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'LOS-Menü';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Tippen Sie auf Knoten oder drücken Sie lange auf die Karte, um benutzerdefinierte Punkte anzuzeigen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Anzeigeknoten anzeigen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Benutzerdefinierte Punkte';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Benutzerdefiniert $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Punkt A';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Punkt B';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Antenne A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Antenne B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Führen Sie LOS aus';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Keine Höhendaten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, freie Sichtlinie, Mindestabstand $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, blockiert durch $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: Überprüfen...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: keine Daten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'Sichtlinie: $clear/$total frei, $blocked blockiert, $unknown unbekannt';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Für eine oder mehrere Proben sind keine Höhendaten verfügbar.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Ungültige Punkte/Höhendaten für die LOS-Berechnung.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint =>
|
||||||
|
'Benennen Sie den benutzerdefinierten Punkt um';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Punktname';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'LOS-Panel anzeigen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'LOS-Panel ausblenden';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution => 'Höhendaten: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Pfadverfolgung';
|
String get contacts_pathTrace => 'Pfadverfolgung';
|
||||||
|
|
||||||
|
|||||||
@@ -318,6 +318,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'An open-source Flutter client for MeshCore LoRa mesh networking devices.';
|
'An open-source Flutter client for MeshCore LoRa mesh networking devices.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'LOS elevation data: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Name';
|
String get settings_infoName => 'Name';
|
||||||
|
|
||||||
@@ -614,6 +618,15 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Offline Map Cache';
|
String get appSettings_offlineMapCache => 'Offline Map Cache';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Units';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Metric (m / km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Imperial (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'No area selected';
|
String get appSettings_noAreaSelected => 'No area selected';
|
||||||
|
|
||||||
@@ -1222,6 +1235,12 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Node Map';
|
String get map_title => 'Node Map';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Line of Sight';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Line of Sight';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation => 'No nodes with location data';
|
String get map_noNodesWithLocation => 'No nodes with location data';
|
||||||
|
|
||||||
@@ -2683,6 +2702,115 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Clear path.';
|
String get pathTrace_clearTooltip => 'Clear path.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd => 'Select start and end nodes for LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Line-of-sight check failed: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Clear all points';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Run LOS to view elevation profile';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'LOS Menu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle => 'Tap nodes or long-press map for custom points';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Show display nodes';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Custom points';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Custom $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Point A';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Point B';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Antenna A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Antenna B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Run LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'No elevation data';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, clear LOS, min clearance $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, blocked by $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: checking...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: no data';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total clear, $blocked blocked, $unknown unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Elevation data unavailable for one or more samples.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Invalid points/elevation data for LOS calculation.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Rename custom point';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Point name';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Show LOS panel';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Hide LOS panel';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Elevation data: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Path Trace';
|
String get contacts_pathTrace => 'Path Trace';
|
||||||
|
|
||||||
|
|||||||
@@ -323,6 +323,10 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Un cliente de código abierto de Flutter para dispositivos de red mesh LoRa de MeshCore.';
|
'Un cliente de código abierto de Flutter para dispositivos de red mesh LoRa de MeshCore.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'Datos de elevación LOS: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Nombre';
|
String get settings_infoName => 'Nombre';
|
||||||
|
|
||||||
@@ -620,6 +624,15 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Caché de Mapa Offline';
|
String get appSettings_offlineMapCache => 'Caché de Mapa Offline';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Unidades';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Métrico (m/km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Imperial (pies/millas)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'No se ha seleccionado ningún área';
|
String get appSettings_noAreaSelected => 'No se ha seleccionado ningún área';
|
||||||
|
|
||||||
@@ -1240,6 +1253,12 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Mapa de Nodos';
|
String get map_title => 'Mapa de Nodos';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Línea de visión';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Línea de visión';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation => 'No hay nodos con datos de ubicación';
|
String get map_noNodesWithLocation => 'No hay nodos con datos de ubicación';
|
||||||
|
|
||||||
@@ -2722,6 +2741,118 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Borrar ruta';
|
String get pathTrace_clearTooltip => 'Borrar ruta';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd =>
|
||||||
|
'Seleccione los nodos de inicio y fin para LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Error en la comprobación de la línea de visión: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Borrar todos los puntos';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Ejecute LOS para ver el perfil de elevación';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'Menú LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Toque nodos o mantenga presionado el mapa para puntos personalizados';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Mostrar nodos de visualización';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Puntos personalizados';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Personalizado $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Punto A';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Punto B';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Antena A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Antena B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Ejecutar LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Sin datos de elevación';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, despejar LOS, autorización mínima $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, bloqueado por $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: comprobando...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: sin datos';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total claro, $blocked bloqueado, $unknown desconocido';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Datos de elevación no disponibles para una o más muestras.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Datos de puntos/elevación no válidos para el cálculo de LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint =>
|
||||||
|
'Cambiar el nombre del punto personalizado';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Nombre del punto';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Mostrar panel LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Ocultar panel LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Datos de elevación: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Rastreo de caminos';
|
String get contacts_pathTrace => 'Rastreo de caminos';
|
||||||
|
|
||||||
|
|||||||
@@ -324,6 +324,10 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Un client Flutter open source pour les appareils de réseau mesh MeshCore LoRa.';
|
'Un client Flutter open source pour les appareils de réseau mesh MeshCore LoRa.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'Données d\'élévation LOS : Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Nom';
|
String get settings_infoName => 'Nom';
|
||||||
|
|
||||||
@@ -622,6 +626,15 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Cache de Carte Hors Ligne';
|
String get appSettings_offlineMapCache => 'Cache de Carte Hors Ligne';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Unités';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Métrique (m/km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Impérial (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Aucune zone sélectionnée';
|
String get appSettings_noAreaSelected => 'Aucune zone sélectionnée';
|
||||||
|
|
||||||
@@ -1246,6 +1259,12 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Carte des nœuds';
|
String get map_title => 'Carte des nœuds';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Ligne de vue';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Ligne de vue';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation =>
|
String get map_noNodesWithLocation =>
|
||||||
'Aucun nœud avec des données de localisation';
|
'Aucun nœud avec des données de localisation';
|
||||||
@@ -2738,6 +2757,117 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Effacer le chemin';
|
String get pathTrace_clearTooltip => 'Effacer le chemin';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd =>
|
||||||
|
'Sélectionnez les nœuds de début et de fin pour LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Échec de la vérification de la ligne de vue : $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Effacer tous les points';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Exécutez LOS pour afficher le profil d\'altitude';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'Menu LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Appuyez sur les nœuds ou appuyez longuement sur la carte pour des points personnalisés';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Afficher les nœuds d\'affichage';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Points personnalisés';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Personnalisé $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Point A';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Point B';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Antenne A : $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Antenne B : $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Exécuter la LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Aucune donnée d\'altitude';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, LOS clair, clairance minimale $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, bloqué par $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS : vérification...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS : aucune donnée';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS : $clear/$total clair, $blocked bloqué, $unknown inconnu';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Données d\'altitude indisponibles pour un ou plusieurs échantillons.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Données de points/d\'altitude non valides pour le calcul de la LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Renommer le point personnalisé';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Nom du point';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Afficher le panneau LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Masquer le panneau LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Données d\'altitude : Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Traçage de chemin';
|
String get contacts_pathTrace => 'Traçage de chemin';
|
||||||
|
|
||||||
|
|||||||
@@ -322,6 +322,10 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Un client Flutter open-source per i dispositivi di rete mesh LoRa Core di MeshCore.';
|
'Un client Flutter open-source per i dispositivi di rete mesh LoRa Core di MeshCore.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'Dati di elevazione LOS: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Nome';
|
String get settings_infoName => 'Nome';
|
||||||
|
|
||||||
@@ -619,6 +623,15 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Cache Mappa Offline';
|
String get appSettings_offlineMapCache => 'Cache Mappa Offline';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Unità';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Metrico (m/km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Imperiale (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Nessun\'area selezionata';
|
String get appSettings_noAreaSelected => 'Nessun\'area selezionata';
|
||||||
|
|
||||||
@@ -1239,6 +1252,12 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Mappa Nodi';
|
String get map_title => 'Mappa Nodi';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Linea di vista';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Linea di vista';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation => 'Nessun nodo con dati di posizione';
|
String get map_noNodesWithLocation => 'Nessun nodo con dati di posizione';
|
||||||
|
|
||||||
@@ -2723,6 +2742,117 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Pulisci percorso';
|
String get pathTrace_clearTooltip => 'Pulisci percorso';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd =>
|
||||||
|
'Seleziona i nodi iniziali e finali per la LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Controllo della linea di vista fallito: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Cancella tutti i punti';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Eseguire LOS per visualizzare il profilo altimetrico';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'Menù LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Tocca i nodi o premi a lungo la mappa per punti personalizzati';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Mostra i nodi di visualizzazione';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Punti personalizzati';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Personalizzato $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Punto A';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Punto B';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Antenna A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Antenna B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Esegui LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Nessun dato di elevazione';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, libera LOS, distanza minima $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, bloccato da $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: controllo...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: nessun dato';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total libera, $blocked bloccato, $unknown sconosciuto';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Dati di elevazione non disponibili per uno o più campioni.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Dati punti/elevazione non validi per il calcolo della LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Rinomina punto personalizzato';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Nome del punto';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Mostra il pannello LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Nascondi il pannello LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Dati di elevazione: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Traccia Percorso';
|
String get contacts_pathTrace => 'Traccia Percorso';
|
||||||
|
|
||||||
|
|||||||
@@ -320,6 +320,10 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Een open-source Flutter client voor MeshCore LoRa mesh netwerkapparaten.';
|
'Een open-source Flutter client voor MeshCore LoRa mesh netwerkapparaten.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'LOS-hoogtegegevens: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Naam';
|
String get settings_infoName => 'Naam';
|
||||||
|
|
||||||
@@ -617,6 +621,15 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Offline Kaarten Cache';
|
String get appSettings_offlineMapCache => 'Offline Kaarten Cache';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Eenheden';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Metrisch (m / km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Imperiaal (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Geen gebied geselecteerd';
|
String get appSettings_noAreaSelected => 'Geen gebied geselecteerd';
|
||||||
|
|
||||||
@@ -1235,6 +1248,12 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Node Map';
|
String get map_title => 'Node Map';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Zichtlijn';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Zichtlijn';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation => 'Geen nodes met locatiegegevens';
|
String get map_noNodesWithLocation => 'Geen nodes met locatiegegevens';
|
||||||
|
|
||||||
@@ -2714,6 +2733,117 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Weg wissen';
|
String get pathTrace_clearTooltip => 'Weg wissen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd =>
|
||||||
|
'Selecteer begin- en eindknooppunten voor LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Zichtlijncontrole mislukt: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Wis alle punten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Voer LOS uit om het hoogteprofiel te bekijken';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'LOS-menu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Tik op knooppunten of druk lang op de kaart voor aangepaste punten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Toon weergaveknooppunten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Aangepaste punten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Aangepast $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Punt A';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Punt B';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Antenne A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Antenne B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Voer LOS uit';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Geen hoogtegegevens';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, vrije LOS, min. vrije ruimte $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, geblokkeerd door $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: controleren...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: geen gegevens';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total gewist, $blocked geblokkeerd, $unknown onbekend';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Hoogtegegevens niet beschikbaar voor een of meer monsters.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Ongeldige punten/hoogtegegevens voor LOS-berekening.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Hernoem aangepast punt';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Puntnaam';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Toon LOS-paneel';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'LOS-paneel verbergen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Hoogtegegevens: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Pad Traceren';
|
String get contacts_pathTrace => 'Pad Traceren';
|
||||||
|
|
||||||
|
|||||||
@@ -323,6 +323,10 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Otwarty kod źródłowy klient Flutter dla urządzeń do sieci mesh LoRa MeshCore.';
|
'Otwarty kod źródłowy klient Flutter dla urządzeń do sieci mesh LoRa MeshCore.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'Dane wysokościowe LOS: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Imię';
|
String get settings_infoName => 'Imię';
|
||||||
|
|
||||||
@@ -621,6 +625,15 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Bufor Map Offline';
|
String get appSettings_offlineMapCache => 'Bufor Map Offline';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Jednostki';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Metryczne (m / km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Imperialne (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Nie zaznaczono żadnej powierzchni.';
|
String get appSettings_noAreaSelected => 'Nie zaznaczono żadnej powierzchni.';
|
||||||
|
|
||||||
@@ -1241,6 +1254,12 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Mapa węzłów';
|
String get map_title => 'Mapa węzłów';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Linia wzroku';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Linia wzroku';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation => 'Brak węzłów z danymi lokalizacyjnymi';
|
String get map_noNodesWithLocation => 'Brak węzłów z danymi lokalizacyjnymi';
|
||||||
|
|
||||||
@@ -2721,6 +2740,116 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Wyczyść ścieżkę';
|
String get pathTrace_clearTooltip => 'Wyczyść ścieżkę';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd => 'Wybierz węzły początkowe i końcowe dla LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Sprawdzenie pola widzenia nie powiodło się: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Wyczyść wszystkie punkty';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Uruchom LOS, aby wyświetlić profil wysokości';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'Menu LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Stuknij węzły lub naciśnij i przytrzymaj mapę, aby uzyskać niestandardowe punkty';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Pokaż węzły wyświetlające';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Punkty niestandardowe';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Niestandardowe $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Punkt A';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Punkt B';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Antena A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Antena B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Uruchom LOS-a';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Brak danych o wysokości';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, czysty LOS, minimalny prześwit $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, zablokowane przez $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: sprawdzam...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: brak danych';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total jasne, $blocked zablokowane, $unknown nieznane';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Dane dotyczące wysokości są niedostępne dla jednej lub większej liczby próbek.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Nieprawidłowe dane punktów/wysokości do obliczenia LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Zmień nazwę punktu niestandardowego';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Nazwa punktu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Pokaż panel LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Ukryj panel LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Dane dotyczące wysokości: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Śledzenie Ścieżek';
|
String get contacts_pathTrace => 'Śledzenie Ścieżek';
|
||||||
|
|
||||||
|
|||||||
@@ -324,6 +324,10 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Um cliente Flutter de código aberto para dispositivos de rede mesh LoRa Core da MeshCore.';
|
'Um cliente Flutter de código aberto para dispositivos de rede mesh LoRa Core da MeshCore.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'Dados de elevação LOS: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Nome';
|
String get settings_infoName => 'Nome';
|
||||||
|
|
||||||
@@ -620,6 +624,15 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Cache de Mapa Offline';
|
String get appSettings_offlineMapCache => 'Cache de Mapa Offline';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Unidades';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Métrico (m/km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Imperial (ft/mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Nenhuma área selecionada';
|
String get appSettings_noAreaSelected => 'Nenhuma área selecionada';
|
||||||
|
|
||||||
@@ -1240,6 +1253,12 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Mapa de Nós';
|
String get map_title => 'Mapa de Nós';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Linha de visão';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Linha de visão';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation =>
|
String get map_noNodesWithLocation =>
|
||||||
'Não existem nós com dados de localização.';
|
'Não existem nós com dados de localização.';
|
||||||
@@ -2723,6 +2742,116 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Limpar caminho';
|
String get pathTrace_clearTooltip => 'Limpar caminho';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd => 'Selecione nós iniciais e finais para LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Falha na verificação da linha de visão: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Limpe todos os pontos';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Execute o LOS para visualizar o perfil de elevação';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'Menu LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Toque nos nós ou mantenha pressionado o mapa para obter pontos personalizados';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Mostrar nós de exibição';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Pontos personalizados';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return '$index personalizado';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Ponto A';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Ponto B';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Antena A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Antena B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Executar LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Sem dados de elevação';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, limpar LOS, liberação mínima $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, bloqueado por $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: verificando...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: sem dados';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total limpo, $blocked bloqueado, $unknown desconhecido';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Dados de elevação indisponíveis para uma ou mais amostras.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Dados de pontos/elevação inválidos para cálculo de LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Renomear ponto personalizado';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Nome do ponto';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Mostrar painel LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Ocultar painel LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Dados de elevação: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Traçado de Caminho';
|
String get contacts_pathTrace => 'Traçado de Caminho';
|
||||||
|
|
||||||
|
|||||||
@@ -321,6 +321,10 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Открытое клиентское приложение на Flutter для устройств MeshCore с LoRa-сетями.';
|
'Открытое клиентское приложение на Flutter для устройств MeshCore с LoRa-сетями.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'Данные о высоте LOS: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Имя';
|
String get settings_infoName => 'Имя';
|
||||||
|
|
||||||
@@ -620,6 +624,15 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Кэш офлайн-карты';
|
String get appSettings_offlineMapCache => 'Кэш офлайн-карты';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Единицы';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Метрическая (м/км)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Имперская (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Область не выбрана';
|
String get appSettings_noAreaSelected => 'Область не выбрана';
|
||||||
|
|
||||||
@@ -1242,6 +1255,12 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Карта нод';
|
String get map_title => 'Карта нод';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Линия видимости';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Линия видимости';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation => 'Нет нод с данными о местоположении';
|
String get map_noNodesWithLocation => 'Нет нод с данными о местоположении';
|
||||||
|
|
||||||
@@ -2726,6 +2745,116 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Очистить путь';
|
String get pathTrace_clearTooltip => 'Очистить путь';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd => 'Выберите начальный и конечный узлы для LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Проверка прямой видимости не удалась: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Очистить все точки';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Запустите LOS, чтобы просмотреть профиль высот.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'ЛОС Меню';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Коснитесь узлов или нажмите и удерживайте карту для выбора пользовательских точек.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Показать узлы отображения';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Пользовательские точки';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Пользовательский $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Точка А';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Точка Б';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Антенна А: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Антенна Б: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Запустить ЛОС';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Нет данных о высоте';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, свободная зона видимости, минимальный зазор $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, заблокирован $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'ЛОС: проверяю...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'ЛОС: нет данных';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total очищено, $blocked заблокировано, $unknown неизвестно.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Данные о высоте недоступны для одного или нескольких образцов.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Неверные данные о точках/высоте для расчета LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Переименовать пользовательскую точку';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Имя точки';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Показать панель LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Скрыть панель LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Данные о высоте: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Трассировка пути';
|
String get contacts_pathTrace => 'Трассировка пути';
|
||||||
|
|
||||||
|
|||||||
@@ -320,6 +320,10 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Otvorený zdrojový Flutter klient pre MeshCore LoRa sieťové zariadenia.';
|
'Otvorený zdrojový Flutter klient pre MeshCore LoRa sieťové zariadenia.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'Údaje o nadmorskej výške LOS: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Meno';
|
String get settings_infoName => 'Meno';
|
||||||
|
|
||||||
@@ -614,6 +618,15 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Offline Mapa Pamäť';
|
String get appSettings_offlineMapCache => 'Offline Mapa Pamäť';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Jednotky';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Metrické (m / km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Imperiálne (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Neoznačila sa žiadna oblasť';
|
String get appSettings_noAreaSelected => 'Neoznačila sa žiadna oblasť';
|
||||||
|
|
||||||
@@ -1236,6 +1249,12 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Mapa uzlov';
|
String get map_title => 'Mapa uzlov';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Line of Sight';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Line of Sight';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation => 'Žiadne uzly s údajmi o polohe';
|
String get map_noNodesWithLocation => 'Žiadne uzly s údajmi o polohe';
|
||||||
|
|
||||||
@@ -2709,6 +2728,116 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Zmazať cestu';
|
String get pathTrace_clearTooltip => 'Zmazať cestu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd => 'Vyberte počiatočný a koncový uzol pre LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Kontrola priamej viditeľnosti zlyhala: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Vymazať všetky body';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Ak chcete zobraziť výškový profil, spustite LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'Menu LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Klepnutím na uzly alebo dlhým stlačením mapy získate vlastné body';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Zobraziť uzly zobrazenia';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Vlastné body';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Vlastné $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Bod A';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Bod B';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Anténa A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Anténa B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Spustite LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Žiadne údaje o nadmorskej výške';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, vymazať LOS, min. vôľa $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, blokovaný $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: kontrolujem...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: žiadne údaje';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total vymazané, $blocked blokované, $unknown neznáme';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Údaje o nadmorskej výške nie sú k dispozícii pre jednu alebo viacero vzoriek.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Neplatné body/údaje o nadmorskej výške pre výpočet LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Premenovať vlastný bod';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Názov bodu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Zobraziť panel LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Skryť panel LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Údaje o nadmorskej výške: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Sledovanie lúčov';
|
String get contacts_pathTrace => 'Sledovanie lúčov';
|
||||||
|
|
||||||
|
|||||||
@@ -319,6 +319,10 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Odprtokodni Flutter klient za naprave za LoRa omrežje MeshCore.';
|
'Odprtokodni Flutter klient za naprave za LoRa omrežje MeshCore.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'Podatki o višini LOS: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Ime';
|
String get settings_infoName => 'Ime';
|
||||||
|
|
||||||
@@ -615,6 +619,15 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Shramba zemljevidov brez povezave';
|
String get appSettings_offlineMapCache => 'Shramba zemljevidov brez povezave';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Enote';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Metrična (m/km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Imperialno (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Območje ni izbrano';
|
String get appSettings_noAreaSelected => 'Območje ni izbrano';
|
||||||
|
|
||||||
@@ -1231,6 +1244,12 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Mapa omrežja';
|
String get map_title => 'Mapa omrežja';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Linija vida';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Linija vida';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation =>
|
String get map_noNodesWithLocation =>
|
||||||
'Nihče od notranjih elementov nima podatkov o lokaciji.';
|
'Nihče od notranjih elementov nima podatkov o lokaciji.';
|
||||||
@@ -2712,6 +2731,116 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Počisti pot';
|
String get pathTrace_clearTooltip => 'Počisti pot';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd => 'Izberite začetno in končno vozlišče za LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Preverjanje vidnega polja ni uspelo: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Počisti vse točke';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Zaženite LOS za ogled višinskega profila';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'LOS meni';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Tapnite vozlišča ali dolgo pritisnite na zemljevid za točke po meri';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Pokaži prikazna vozlišča';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Točke po meri';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Po meri $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Točka A';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Točka B';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Antena A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Antena B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Zaženi LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Ni podatkov o višini';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, čisti LOS, najmanjša razdalja $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, blokiral $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: preverjam ...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: ni podatkov';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total jasno, $blocked blokirano, $unknown neznano';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Podatki o nadmorski višini niso na voljo za enega ali več vzorcev.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Neveljavni podatki o točkah/višini za izračun LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Preimenujte točko po meri';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Ime točke';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Pokaži ploščo LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Skrij ploščo LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Podatki o višini: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Sledenje poti';
|
String get contacts_pathTrace => 'Sledenje poti';
|
||||||
|
|
||||||
|
|||||||
@@ -317,6 +317,10 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'En öppen källkods Flutter-klient för MeshCore LoRa meshnätverksenheter.';
|
'En öppen källkods Flutter-klient för MeshCore LoRa meshnätverksenheter.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'LOS-höjddata: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Namn';
|
String get settings_infoName => 'Namn';
|
||||||
|
|
||||||
@@ -610,6 +614,15 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Offline Kartcache';
|
String get appSettings_offlineMapCache => 'Offline Kartcache';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'Enheter';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Metriskt (m/km)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Imperialt (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Ingen area markerad';
|
String get appSettings_noAreaSelected => 'Ingen area markerad';
|
||||||
|
|
||||||
@@ -1228,6 +1241,12 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Nodkarta';
|
String get map_title => 'Nodkarta';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Synlinje';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Synlinje';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation => 'Inga noder med platsinformation';
|
String get map_noNodesWithLocation => 'Inga noder med platsinformation';
|
||||||
|
|
||||||
@@ -2697,6 +2716,114 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Rensa väg';
|
String get pathTrace_clearTooltip => 'Rensa väg';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd => 'Välj start- och slutnoder för LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Synlinjekontroll misslyckades: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Rensa alla punkter';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile => 'Kör LOS för att se höjdprofil';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'LOS-menyn';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Tryck på noder eller tryck länge på kartan för anpassade punkter';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Visa displaynoder';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Anpassade poäng';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Anpassad $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Punkt A';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Punkt B';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Antenn A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Antenn B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Kör LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Inga höjddata';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, rensa LOS, min clearance $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, blockerad av $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: kollar...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: inga data';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total rensa, $blocked blockerad, $unknown okänd';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Höjddata är inte tillgänglig för ett eller flera prover.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Ogiltiga poäng/höjddata för LOS-beräkning.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Byt namn på anpassad punkt';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Punktnamn';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Visa LOS-panelen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Dölj LOS-panelen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution => 'Höjddata: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Path Trace';
|
String get contacts_pathTrace => 'Path Trace';
|
||||||
|
|
||||||
|
|||||||
@@ -322,6 +322,10 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'Клієнт Flutter з відкритим вихідним кодом для пристроїв мережі MeshCore LoRa.';
|
'Клієнт Flutter з відкритим вихідним кодом для пристроїв мережі MeshCore LoRa.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'Дані про висоту LOS: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => 'Ім\'я';
|
String get settings_infoName => 'Ім\'я';
|
||||||
|
|
||||||
@@ -618,6 +622,15 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => 'Офлайн-кеш карти';
|
String get appSettings_offlineMapCache => 'Офлайн-кеш карти';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => 'одиниці';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => 'Метричний (м / км)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => 'Імперська (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => 'Область не вибрано';
|
String get appSettings_noAreaSelected => 'Область не вибрано';
|
||||||
|
|
||||||
@@ -1240,6 +1253,12 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => 'Карта вузлів';
|
String get map_title => 'Карта вузлів';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => 'Пряма видимість';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => 'Пряма видимість';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation =>
|
String get map_noNodesWithLocation =>
|
||||||
'Немає вузлів з даними про розташування';
|
'Немає вузлів з даними про розташування';
|
||||||
@@ -2733,6 +2752,117 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => 'Очистити шлях';
|
String get pathTrace_clearTooltip => 'Очистити шлях';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd =>
|
||||||
|
'Виберіть початковий і кінцевий вузли для LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return 'Помилка перевірки прямої видимості: $error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => 'Очистити всі пункти';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile =>
|
||||||
|
'Запустіть LOS, щоб переглянути профіль висоти';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => 'Меню LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle =>
|
||||||
|
'Торкніться вузлів або утримуйте карту, щоб отримати власні точки';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => 'Показати вузли відображення';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => 'Користувальницькі точки';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return 'Спеціальний $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'Точка А';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'Точка Б';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return 'Антена A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return 'Антена B: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => 'Запустіть LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => 'Немає даних про висоту';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, чистий LOS, мінімальний зазор $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit, заблоковано $obstruction $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => 'LOS: перевірка...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS: немає даних';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS: $clear/$total очищено, $blocked заблоковано, $unknown невідомо';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable =>
|
||||||
|
'Дані про висоту недоступні для одного чи кількох зразків.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput =>
|
||||||
|
'Недійсні дані про точки/висоту для розрахунку LOS.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => 'Перейменуйте спеціальну точку';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => 'Назва точки';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => 'Показати панель LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => 'Приховати панель LOS';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution =>
|
||||||
|
'Дані про висоту: Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => 'Трасування шляхів';
|
String get contacts_pathTrace => 'Трасування шляхів';
|
||||||
|
|
||||||
|
|||||||
@@ -307,6 +307,10 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
String get settings_aboutDescription =>
|
String get settings_aboutDescription =>
|
||||||
'一个开源的 Flutter 客户端,用于 MeshCore LoRa 无线网络设备。';
|
'一个开源的 Flutter 客户端,用于 MeshCore LoRa 无线网络设备。';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settings_aboutOpenMeteoAttribution =>
|
||||||
|
'LOS 高程数据:Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settings_infoName => '姓名';
|
String get settings_infoName => '姓名';
|
||||||
|
|
||||||
@@ -585,6 +589,15 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get appSettings_offlineMapCache => '离线地图缓存';
|
String get appSettings_offlineMapCache => '离线地图缓存';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsTitle => '单位';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsMetric => '公制(米/公里)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appSettings_unitsImperial => '英制 (ft / mi)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appSettings_noAreaSelected => '未选择任何区域';
|
String get appSettings_noAreaSelected => '未选择任何区域';
|
||||||
|
|
||||||
@@ -1182,6 +1195,12 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get map_title => '节点图';
|
String get map_title => '节点图';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_lineOfSight => '视线';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get map_losScreenTitle => '视线';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get map_noNodesWithLocation => '没有包含位置信息的节点';
|
String get map_noNodesWithLocation => '没有包含位置信息的节点';
|
||||||
|
|
||||||
@@ -2579,6 +2598,111 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get pathTrace_clearTooltip => '清除路径';
|
String get pathTrace_clearTooltip => '清除路径';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losSelectStartEnd => '选择 LOS 的起始节点和结束节点。';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losRunFailed(String error) {
|
||||||
|
return '视线检查失败:$error';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losClearAllPoints => '清除所有点';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRunToViewElevationProfile => '运行 LOS 查看高程剖面';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuTitle => '服务水平菜单';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losMenuSubtitle => '点击节点或长按地图以获取自定义点';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowDisplayNodes => '显示显示节点';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losCustomPoints => '自定义积分';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losCustomPointLabel(int index) {
|
||||||
|
return '自定义 $index';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointA => 'A点';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointB => 'B点';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaA(String value, String unit) {
|
||||||
|
return '天线 A: $value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losAntennaB(String value, String unit) {
|
||||||
|
return '天线 B:$value $unit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRun => '运行视距';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losNoElevationData => '无海拔数据';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileClear(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String clearance,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit,清除 LOS,最小间隙 $clearance $heightUnit';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losProfileBlocked(
|
||||||
|
String distance,
|
||||||
|
String distanceUnit,
|
||||||
|
String obstruction,
|
||||||
|
String heightUnit,
|
||||||
|
) {
|
||||||
|
return '$distance $distanceUnit,被 $obstruction $heightUnit 阻止';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusChecking => '洛斯:正在检查...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losStatusNoData => 'LOS:无数据';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String losStatusSummary(int clear, int total, int blocked, int unknown) {
|
||||||
|
return 'LOS:$clear/$total 清除,$blocked 阻塞,$unknown 未知';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorElevationUnavailable => '一个或多个样本的海拔数据不可用。';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losErrorInvalidInput => '用于 LOS 计算的点/高程数据无效。';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losRenameCustomPoint => '重命名自定义点';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losPointName => '点名称';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losShowPanelTooltip => '显示 LOS 面板';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losHidePanelTooltip => '隐藏 LOS 面板';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get losElevationAttribution => '高程数据:Open-Meteo (CC BY 4.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contacts_pathTrace => '路径追踪';
|
String get contacts_pathTrace => '路径追踪';
|
||||||
|
|
||||||
|
|||||||
+116
-1
@@ -1599,5 +1599,120 @@
|
|||||||
"chat_ShowAllPaths": "Toon alle paden",
|
"chat_ShowAllPaths": "Toon alle paden",
|
||||||
"settings_clientRepeat": "Herhalen: Afgekoppeld",
|
"settings_clientRepeat": "Herhalen: Afgekoppeld",
|
||||||
"settings_clientRepeatSubtitle": "Laat dit apparaat de mesh-pakketten opnieuw verzenden voor andere apparaten.",
|
"settings_clientRepeatSubtitle": "Laat dit apparaat de mesh-pakketten opnieuw verzenden voor andere apparaten.",
|
||||||
"settings_clientRepeatFreqWarning": "Om een signaal buiten het netwerk te versturen, zijn frequenties van 433, 869 of 918 MHz vereist."
|
"settings_clientRepeatFreqWarning": "Om een signaal buiten het netwerk te versturen, zijn frequenties van 433, 869 of 918 MHz vereist.",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "LOS-hoogtegegevens: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "Eenheden",
|
||||||
|
"appSettings_unitsMetric": "Metrisch (m / km)",
|
||||||
|
"appSettings_unitsImperial": "Imperiaal (ft / mi)",
|
||||||
|
"map_lineOfSight": "Zichtlijn",
|
||||||
|
"map_losScreenTitle": "Zichtlijn",
|
||||||
|
"losSelectStartEnd": "Selecteer begin- en eindknooppunten voor LOS.",
|
||||||
|
"losRunFailed": "Zichtlijncontrole mislukt: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Wis alle punten",
|
||||||
|
"losRunToViewElevationProfile": "Voer LOS uit om het hoogteprofiel te bekijken",
|
||||||
|
"losMenuTitle": "LOS-menu",
|
||||||
|
"losMenuSubtitle": "Tik op knooppunten of druk lang op de kaart voor aangepaste punten",
|
||||||
|
"losShowDisplayNodes": "Toon weergaveknooppunten",
|
||||||
|
"losCustomPoints": "Aangepaste punten",
|
||||||
|
"losCustomPointLabel": "Aangepast {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Punt A",
|
||||||
|
"losPointB": "Punt B",
|
||||||
|
"losAntennaA": "Antenne A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Antenne B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Voer LOS uit",
|
||||||
|
"losNoElevationData": "Geen hoogtegegevens",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, vrije LOS, min. vrije ruimte {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, geblokkeerd door {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: controleren...",
|
||||||
|
"losStatusNoData": "LOS: geen gegevens",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} gewist, {blocked} geblokkeerd, {unknown} onbekend",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Hoogtegegevens niet beschikbaar voor een of meer monsters.",
|
||||||
|
"losErrorInvalidInput": "Ongeldige punten/hoogtegegevens voor LOS-berekening.",
|
||||||
|
"losRenameCustomPoint": "Hernoem aangepast punt",
|
||||||
|
"losPointName": "Puntnaam",
|
||||||
|
"losShowPanelTooltip": "Toon LOS-paneel",
|
||||||
|
"losHidePanelTooltip": "LOS-paneel verbergen",
|
||||||
|
"losElevationAttribution": "Hoogtegegevens: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-1
@@ -1599,5 +1599,120 @@
|
|||||||
"chat_ShowAllPaths": "Pokaż wszystkie ścieżki",
|
"chat_ShowAllPaths": "Pokaż wszystkie ścieżki",
|
||||||
"settings_clientRepeatSubtitle": "Pozwól temu urządzeniu powtarzać pakiety danych dla innych urządzeń.",
|
"settings_clientRepeatSubtitle": "Pozwól temu urządzeniu powtarzać pakiety danych dla innych urządzeń.",
|
||||||
"settings_clientRepeat": "Powtórzenie: Niezależne od sieci",
|
"settings_clientRepeat": "Powtórzenie: Niezależne od sieci",
|
||||||
"settings_clientRepeatFreqWarning": "Powtórka poza siecią wymaga częstotliwości 433, 869 lub 918 MHz."
|
"settings_clientRepeatFreqWarning": "Powtórka poza siecią wymaga częstotliwości 433, 869 lub 918 MHz.",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "Dane wysokościowe LOS: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "Jednostki",
|
||||||
|
"appSettings_unitsMetric": "Metryczne (m / km)",
|
||||||
|
"appSettings_unitsImperial": "Imperialne (ft / mi)",
|
||||||
|
"map_lineOfSight": "Linia wzroku",
|
||||||
|
"map_losScreenTitle": "Linia wzroku",
|
||||||
|
"losSelectStartEnd": "Wybierz węzły początkowe i końcowe dla LOS.",
|
||||||
|
"losRunFailed": "Sprawdzenie pola widzenia nie powiodło się: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Wyczyść wszystkie punkty",
|
||||||
|
"losRunToViewElevationProfile": "Uruchom LOS, aby wyświetlić profil wysokości",
|
||||||
|
"losMenuTitle": "Menu LOS",
|
||||||
|
"losMenuSubtitle": "Stuknij węzły lub naciśnij i przytrzymaj mapę, aby uzyskać niestandardowe punkty",
|
||||||
|
"losShowDisplayNodes": "Pokaż węzły wyświetlające",
|
||||||
|
"losCustomPoints": "Punkty niestandardowe",
|
||||||
|
"losCustomPointLabel": "Niestandardowe {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Punkt A",
|
||||||
|
"losPointB": "Punkt B",
|
||||||
|
"losAntennaA": "Antena A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Antena B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Uruchom LOS-a",
|
||||||
|
"losNoElevationData": "Brak danych o wysokości",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, czysty LOS, minimalny prześwit {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, zablokowane przez {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: sprawdzam...",
|
||||||
|
"losStatusNoData": "LOS: brak danych",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} jasne, {blocked} zablokowane, {unknown} nieznane",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Dane dotyczące wysokości są niedostępne dla jednej lub większej liczby próbek.",
|
||||||
|
"losErrorInvalidInput": "Nieprawidłowe dane punktów/wysokości do obliczenia LOS.",
|
||||||
|
"losRenameCustomPoint": "Zmień nazwę punktu niestandardowego",
|
||||||
|
"losPointName": "Nazwa punktu",
|
||||||
|
"losShowPanelTooltip": "Pokaż panel LOS",
|
||||||
|
"losHidePanelTooltip": "Ukryj panel LOS",
|
||||||
|
"losElevationAttribution": "Dane dotyczące wysokości: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-1
@@ -1599,5 +1599,120 @@
|
|||||||
"chat_ShowAllPaths": "Mostrar todos os caminhos",
|
"chat_ShowAllPaths": "Mostrar todos os caminhos",
|
||||||
"settings_clientRepeatFreqWarning": "A repetição fora da rede requer frequências de 433, 869 ou 918 MHz.",
|
"settings_clientRepeatFreqWarning": "A repetição fora da rede requer frequências de 433, 869 ou 918 MHz.",
|
||||||
"settings_clientRepeat": "Repetição sem rede",
|
"settings_clientRepeat": "Repetição sem rede",
|
||||||
"settings_clientRepeatSubtitle": "Permita que este dispositivo repita pacotes de rede para outros dispositivos."
|
"settings_clientRepeatSubtitle": "Permita que este dispositivo repita pacotes de rede para outros dispositivos.",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "Dados de elevação LOS: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "Unidades",
|
||||||
|
"appSettings_unitsMetric": "Métrico (m/km)",
|
||||||
|
"appSettings_unitsImperial": "Imperial (ft/mi)",
|
||||||
|
"map_lineOfSight": "Linha de visão",
|
||||||
|
"map_losScreenTitle": "Linha de visão",
|
||||||
|
"losSelectStartEnd": "Selecione nós iniciais e finais para LOS.",
|
||||||
|
"losRunFailed": "Falha na verificação da linha de visão: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Limpe todos os pontos",
|
||||||
|
"losRunToViewElevationProfile": "Execute o LOS para visualizar o perfil de elevação",
|
||||||
|
"losMenuTitle": "Menu LOS",
|
||||||
|
"losMenuSubtitle": "Toque nos nós ou mantenha pressionado o mapa para obter pontos personalizados",
|
||||||
|
"losShowDisplayNodes": "Mostrar nós de exibição",
|
||||||
|
"losCustomPoints": "Pontos personalizados",
|
||||||
|
"losCustomPointLabel": "{index} personalizado",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Ponto A",
|
||||||
|
"losPointB": "Ponto B",
|
||||||
|
"losAntennaA": "Antena A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Antena B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Executar LOS",
|
||||||
|
"losNoElevationData": "Sem dados de elevação",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, limpar LOS, liberação mínima {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, bloqueado por {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: verificando...",
|
||||||
|
"losStatusNoData": "LOS: sem dados",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} limpo, {blocked} bloqueado, {unknown} desconhecido",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Dados de elevação indisponíveis para uma ou mais amostras.",
|
||||||
|
"losErrorInvalidInput": "Dados de pontos/elevação inválidos para cálculo de LOS.",
|
||||||
|
"losRenameCustomPoint": "Renomear ponto personalizado",
|
||||||
|
"losPointName": "Nome do ponto",
|
||||||
|
"losShowPanelTooltip": "Mostrar painel LOS",
|
||||||
|
"losHidePanelTooltip": "Ocultar painel LOS",
|
||||||
|
"losElevationAttribution": "Dados de elevação: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-1
@@ -839,5 +839,120 @@
|
|||||||
"chat_ShowAllPaths": "Показать все пути",
|
"chat_ShowAllPaths": "Показать все пути",
|
||||||
"settings_clientRepeatFreqWarning": "Для работы в режиме \"без подключения к сети\" требуется частота 433, 869 или 918 МГц.",
|
"settings_clientRepeatFreqWarning": "Для работы в режиме \"без подключения к сети\" требуется частота 433, 869 или 918 МГц.",
|
||||||
"settings_clientRepeatSubtitle": "Позвольте этому устройству повторять пакеты данных для других устройств.",
|
"settings_clientRepeatSubtitle": "Позвольте этому устройству повторять пакеты данных для других устройств.",
|
||||||
"settings_clientRepeat": "Повторение \"вне сети\""
|
"settings_clientRepeat": "Повторение \"вне сети\"",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "Данные о высоте LOS: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "Единицы",
|
||||||
|
"appSettings_unitsMetric": "Метрическая (м/км)",
|
||||||
|
"appSettings_unitsImperial": "Имперская (ft / mi)",
|
||||||
|
"map_lineOfSight": "Линия видимости",
|
||||||
|
"map_losScreenTitle": "Линия видимости",
|
||||||
|
"losSelectStartEnd": "Выберите начальный и конечный узлы для LOS.",
|
||||||
|
"losRunFailed": "Проверка прямой видимости не удалась: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Очистить все точки",
|
||||||
|
"losRunToViewElevationProfile": "Запустите LOS, чтобы просмотреть профиль высот.",
|
||||||
|
"losMenuTitle": "ЛОС Меню",
|
||||||
|
"losMenuSubtitle": "Коснитесь узлов или нажмите и удерживайте карту для выбора пользовательских точек.",
|
||||||
|
"losShowDisplayNodes": "Показать узлы отображения",
|
||||||
|
"losCustomPoints": "Пользовательские точки",
|
||||||
|
"losCustomPointLabel": "Пользовательский {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Точка А",
|
||||||
|
"losPointB": "Точка Б",
|
||||||
|
"losAntennaA": "Антенна А: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Антенна Б: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Запустить ЛОС",
|
||||||
|
"losNoElevationData": "Нет данных о высоте",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, свободная зона видимости, минимальный зазор {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, заблокирован {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "ЛОС: проверяю...",
|
||||||
|
"losStatusNoData": "ЛОС: нет данных",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} очищено, {blocked} заблокировано, {unknown} неизвестно.",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Данные о высоте недоступны для одного или нескольких образцов.",
|
||||||
|
"losErrorInvalidInput": "Неверные данные о точках/высоте для расчета LOS.",
|
||||||
|
"losRenameCustomPoint": "Переименовать пользовательскую точку",
|
||||||
|
"losPointName": "Имя точки",
|
||||||
|
"losShowPanelTooltip": "Показать панель LOS",
|
||||||
|
"losHidePanelTooltip": "Скрыть панель LOS",
|
||||||
|
"losElevationAttribution": "Данные о высоте: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-1
@@ -1599,5 +1599,120 @@
|
|||||||
"chat_ShowAllPaths": "Zobraziť všetky cesty",
|
"chat_ShowAllPaths": "Zobraziť všetky cesty",
|
||||||
"settings_clientRepeat": "Opätovné použitie bez elektrickej siete",
|
"settings_clientRepeat": "Opätovné použitie bez elektrickej siete",
|
||||||
"settings_clientRepeatFreqWarning": "Použitie off-grid systému vyžaduje frekvencie 433, 869 alebo 918 MHz.",
|
"settings_clientRepeatFreqWarning": "Použitie off-grid systému vyžaduje frekvencie 433, 869 alebo 918 MHz.",
|
||||||
"settings_clientRepeatSubtitle": "Umožnite, aby toto zariadenie opakovávalo siete pre ostatných."
|
"settings_clientRepeatSubtitle": "Umožnite, aby toto zariadenie opakovávalo siete pre ostatných.",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "Údaje o nadmorskej výške LOS: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "Jednotky",
|
||||||
|
"appSettings_unitsMetric": "Metrické (m / km)",
|
||||||
|
"appSettings_unitsImperial": "Imperiálne (ft / mi)",
|
||||||
|
"map_lineOfSight": "Line of Sight",
|
||||||
|
"map_losScreenTitle": "Line of Sight",
|
||||||
|
"losSelectStartEnd": "Vyberte počiatočný a koncový uzol pre LOS.",
|
||||||
|
"losRunFailed": "Kontrola priamej viditeľnosti zlyhala: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Vymazať všetky body",
|
||||||
|
"losRunToViewElevationProfile": "Ak chcete zobraziť výškový profil, spustite LOS",
|
||||||
|
"losMenuTitle": "Menu LOS",
|
||||||
|
"losMenuSubtitle": "Klepnutím na uzly alebo dlhým stlačením mapy získate vlastné body",
|
||||||
|
"losShowDisplayNodes": "Zobraziť uzly zobrazenia",
|
||||||
|
"losCustomPoints": "Vlastné body",
|
||||||
|
"losCustomPointLabel": "Vlastné {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Bod A",
|
||||||
|
"losPointB": "Bod B",
|
||||||
|
"losAntennaA": "Anténa A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Anténa B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Spustite LOS",
|
||||||
|
"losNoElevationData": "Žiadne údaje o nadmorskej výške",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, vymazať LOS, min. vôľa {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, blokovaný {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: kontrolujem...",
|
||||||
|
"losStatusNoData": "LOS: žiadne údaje",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} vymazané, {blocked} blokované, {unknown} neznáme",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Údaje o nadmorskej výške nie sú k dispozícii pre jednu alebo viacero vzoriek.",
|
||||||
|
"losErrorInvalidInput": "Neplatné body/údaje o nadmorskej výške pre výpočet LOS.",
|
||||||
|
"losRenameCustomPoint": "Premenovať vlastný bod",
|
||||||
|
"losPointName": "Názov bodu",
|
||||||
|
"losShowPanelTooltip": "Zobraziť panel LOS",
|
||||||
|
"losHidePanelTooltip": "Skryť panel LOS",
|
||||||
|
"losElevationAttribution": "Údaje o nadmorskej výške: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-1
@@ -1599,5 +1599,120 @@
|
|||||||
"chat_ShowAllPaths": "Prikaži vse poti",
|
"chat_ShowAllPaths": "Prikaži vse poti",
|
||||||
"settings_clientRepeatFreqWarning": "Za ponovni prenos na brezžični način so potrebne frekvence 433, 869 ali 918 MHz.",
|
"settings_clientRepeatFreqWarning": "Za ponovni prenos na brezžični način so potrebne frekvence 433, 869 ali 918 MHz.",
|
||||||
"settings_clientRepeatSubtitle": "Omogočite temu naprave, da ponavlja paketne sporočila za druge.",
|
"settings_clientRepeatSubtitle": "Omogočite temu naprave, da ponavlja paketne sporočila za druge.",
|
||||||
"settings_clientRepeat": "Neovadno ponavljanje"
|
"settings_clientRepeat": "Neovadno ponavljanje",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "Podatki o višini LOS: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "Enote",
|
||||||
|
"appSettings_unitsMetric": "Metrična (m/km)",
|
||||||
|
"appSettings_unitsImperial": "Imperialno (ft / mi)",
|
||||||
|
"map_lineOfSight": "Linija vida",
|
||||||
|
"map_losScreenTitle": "Linija vida",
|
||||||
|
"losSelectStartEnd": "Izberite začetno in končno vozlišče za LOS.",
|
||||||
|
"losRunFailed": "Preverjanje vidnega polja ni uspelo: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Počisti vse točke",
|
||||||
|
"losRunToViewElevationProfile": "Zaženite LOS za ogled višinskega profila",
|
||||||
|
"losMenuTitle": "LOS meni",
|
||||||
|
"losMenuSubtitle": "Tapnite vozlišča ali dolgo pritisnite na zemljevid za točke po meri",
|
||||||
|
"losShowDisplayNodes": "Pokaži prikazna vozlišča",
|
||||||
|
"losCustomPoints": "Točke po meri",
|
||||||
|
"losCustomPointLabel": "Po meri {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Točka A",
|
||||||
|
"losPointB": "Točka B",
|
||||||
|
"losAntennaA": "Antena A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Antena B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Zaženi LOS",
|
||||||
|
"losNoElevationData": "Ni podatkov o višini",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, čisti LOS, najmanjša razdalja {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, blokiral {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: preverjam ...",
|
||||||
|
"losStatusNoData": "LOS: ni podatkov",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} jasno, {blocked} blokirano, {unknown} neznano",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Podatki o nadmorski višini niso na voljo za enega ali več vzorcev.",
|
||||||
|
"losErrorInvalidInput": "Neveljavni podatki o točkah/višini za izračun LOS.",
|
||||||
|
"losRenameCustomPoint": "Preimenujte točko po meri",
|
||||||
|
"losPointName": "Ime točke",
|
||||||
|
"losShowPanelTooltip": "Pokaži ploščo LOS",
|
||||||
|
"losHidePanelTooltip": "Skrij ploščo LOS",
|
||||||
|
"losElevationAttribution": "Podatki o višini: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-1
@@ -1599,5 +1599,120 @@
|
|||||||
"chat_ShowAllPaths": "Visa alla vägar",
|
"chat_ShowAllPaths": "Visa alla vägar",
|
||||||
"settings_clientRepeatSubtitle": "Låt enheten repetera nätpaket för andra användare.",
|
"settings_clientRepeatSubtitle": "Låt enheten repetera nätpaket för andra användare.",
|
||||||
"settings_clientRepeat": "Upprepa utan elnät",
|
"settings_clientRepeat": "Upprepa utan elnät",
|
||||||
"settings_clientRepeatFreqWarning": "För att kunna kommunicera utanför elnätet krävs frekvenserna 433, 869 eller 918 MHz."
|
"settings_clientRepeatFreqWarning": "För att kunna kommunicera utanför elnätet krävs frekvenserna 433, 869 eller 918 MHz.",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "LOS-höjddata: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "Enheter",
|
||||||
|
"appSettings_unitsMetric": "Metriskt (m/km)",
|
||||||
|
"appSettings_unitsImperial": "Imperialt (ft / mi)",
|
||||||
|
"map_lineOfSight": "Synlinje",
|
||||||
|
"map_losScreenTitle": "Synlinje",
|
||||||
|
"losSelectStartEnd": "Välj start- och slutnoder för LOS.",
|
||||||
|
"losRunFailed": "Synlinjekontroll misslyckades: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Rensa alla punkter",
|
||||||
|
"losRunToViewElevationProfile": "Kör LOS för att se höjdprofil",
|
||||||
|
"losMenuTitle": "LOS-menyn",
|
||||||
|
"losMenuSubtitle": "Tryck på noder eller tryck länge på kartan för anpassade punkter",
|
||||||
|
"losShowDisplayNodes": "Visa displaynoder",
|
||||||
|
"losCustomPoints": "Anpassade poäng",
|
||||||
|
"losCustomPointLabel": "Anpassad {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Punkt A",
|
||||||
|
"losPointB": "Punkt B",
|
||||||
|
"losAntennaA": "Antenn A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Antenn B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Kör LOS",
|
||||||
|
"losNoElevationData": "Inga höjddata",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, rensa LOS, min clearance {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, blockerad av {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: kollar...",
|
||||||
|
"losStatusNoData": "LOS: inga data",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} rensa, {blocked} blockerad, {unknown} okänd",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Höjddata är inte tillgänglig för ett eller flera prover.",
|
||||||
|
"losErrorInvalidInput": "Ogiltiga poäng/höjddata för LOS-beräkning.",
|
||||||
|
"losRenameCustomPoint": "Byt namn på anpassad punkt",
|
||||||
|
"losPointName": "Punktnamn",
|
||||||
|
"losShowPanelTooltip": "Visa LOS-panelen",
|
||||||
|
"losHidePanelTooltip": "Dölj LOS-panelen",
|
||||||
|
"losElevationAttribution": "Höjddata: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-1
@@ -1599,5 +1599,120 @@
|
|||||||
"chat_ShowAllPaths": "Показати всі шляхи",
|
"chat_ShowAllPaths": "Показати всі шляхи",
|
||||||
"settings_clientRepeatFreqWarning": "Повтор без підключення до мережі вимагає частоти 433, 869 або 918 МГц.",
|
"settings_clientRepeatFreqWarning": "Повтор без підключення до мережі вимагає частоти 433, 869 або 918 МГц.",
|
||||||
"settings_clientRepeatSubtitle": "Дозвольте цьому пристрою повторювати пакети даних для інших пристроїв.",
|
"settings_clientRepeatSubtitle": "Дозвольте цьому пристрою повторювати пакети даних для інших пристроїв.",
|
||||||
"settings_clientRepeat": "Автономна система"
|
"settings_clientRepeat": "Автономна система",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "Дані про висоту LOS: Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "одиниці",
|
||||||
|
"appSettings_unitsMetric": "Метричний (м / км)",
|
||||||
|
"appSettings_unitsImperial": "Імперська (ft / mi)",
|
||||||
|
"map_lineOfSight": "Пряма видимість",
|
||||||
|
"map_losScreenTitle": "Пряма видимість",
|
||||||
|
"losSelectStartEnd": "Виберіть початковий і кінцевий вузли для LOS.",
|
||||||
|
"losRunFailed": "Помилка перевірки прямої видимості: {error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "Очистити всі пункти",
|
||||||
|
"losRunToViewElevationProfile": "Запустіть LOS, щоб переглянути профіль висоти",
|
||||||
|
"losMenuTitle": "Меню LOS",
|
||||||
|
"losMenuSubtitle": "Торкніться вузлів або утримуйте карту, щоб отримати власні точки",
|
||||||
|
"losShowDisplayNodes": "Показати вузли відображення",
|
||||||
|
"losCustomPoints": "Користувальницькі точки",
|
||||||
|
"losCustomPointLabel": "Спеціальний {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "Точка А",
|
||||||
|
"losPointB": "Точка Б",
|
||||||
|
"losAntennaA": "Антена A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "Антена B: {value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "Запустіть LOS",
|
||||||
|
"losNoElevationData": "Немає даних про висоту",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit}, чистий LOS, мінімальний зазор {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit}, заблоковано {obstruction} {heightUnit}",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "LOS: перевірка...",
|
||||||
|
"losStatusNoData": "LOS: немає даних",
|
||||||
|
"losStatusSummary": "LOS: {clear}/{total} очищено, {blocked} заблоковано, {unknown} невідомо",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "Дані про висоту недоступні для одного чи кількох зразків.",
|
||||||
|
"losErrorInvalidInput": "Недійсні дані про точки/висоту для розрахунку LOS.",
|
||||||
|
"losRenameCustomPoint": "Перейменуйте спеціальну точку",
|
||||||
|
"losPointName": "Назва точки",
|
||||||
|
"losShowPanelTooltip": "Показати панель LOS",
|
||||||
|
"losHidePanelTooltip": "Приховати панель LOS",
|
||||||
|
"losElevationAttribution": "Дані про висоту: Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-1
@@ -1599,5 +1599,120 @@
|
|||||||
"chat_ShowAllPaths": "显示所有路径",
|
"chat_ShowAllPaths": "显示所有路径",
|
||||||
"settings_clientRepeat": "离网重复",
|
"settings_clientRepeat": "离网重复",
|
||||||
"settings_clientRepeatSubtitle": "允许此设备重复发送网状数据包给其他设备",
|
"settings_clientRepeatSubtitle": "允许此设备重复发送网状数据包给其他设备",
|
||||||
"settings_clientRepeatFreqWarning": "离网重复通信需要使用 433、869 或 918 兆赫兹的频率。"
|
"settings_clientRepeatFreqWarning": "离网重复通信需要使用 433、869 或 918 兆赫兹的频率。",
|
||||||
|
"settings_aboutOpenMeteoAttribution": "LOS 高程数据:Open-Meteo (CC BY 4.0)",
|
||||||
|
"appSettings_unitsTitle": "单位",
|
||||||
|
"appSettings_unitsMetric": "公制(米/公里)",
|
||||||
|
"appSettings_unitsImperial": "英制 (ft / mi)",
|
||||||
|
"map_lineOfSight": "视线",
|
||||||
|
"map_losScreenTitle": "视线",
|
||||||
|
"losSelectStartEnd": "选择 LOS 的起始节点和结束节点。",
|
||||||
|
"losRunFailed": "视线检查失败:{error}",
|
||||||
|
"@losRunFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"error": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losClearAllPoints": "清除所有点",
|
||||||
|
"losRunToViewElevationProfile": "运行 LOS 查看高程剖面",
|
||||||
|
"losMenuTitle": "服务水平菜单",
|
||||||
|
"losMenuSubtitle": "点击节点或长按地图以获取自定义点",
|
||||||
|
"losShowDisplayNodes": "显示显示节点",
|
||||||
|
"losCustomPoints": "自定义积分",
|
||||||
|
"losCustomPointLabel": "自定义 {index}",
|
||||||
|
"@losCustomPointLabel": {
|
||||||
|
"placeholders": {
|
||||||
|
"index": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losPointA": "A点",
|
||||||
|
"losPointB": "B点",
|
||||||
|
"losAntennaA": "天线 A: {value} {unit}",
|
||||||
|
"@losAntennaA": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losAntennaB": "天线 B:{value} {unit}",
|
||||||
|
"@losAntennaB": {
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losRun": "运行视距",
|
||||||
|
"losNoElevationData": "无海拔数据",
|
||||||
|
"losProfileClear": "{distance} {distanceUnit},清除 LOS,最小间隙 {clearance} {heightUnit}",
|
||||||
|
"@losProfileClear": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"clearance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losProfileBlocked": "{distance} {distanceUnit},被 {obstruction} {heightUnit} 阻止",
|
||||||
|
"@losProfileBlocked": {
|
||||||
|
"placeholders": {
|
||||||
|
"distance": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"distanceUnit": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"obstruction": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"heightUnit": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losStatusChecking": "洛斯:正在检查...",
|
||||||
|
"losStatusNoData": "LOS:无数据",
|
||||||
|
"losStatusSummary": "LOS:{clear}/{total} 清除,{blocked} 阻塞,{unknown} 未知",
|
||||||
|
"@losStatusSummary": {
|
||||||
|
"placeholders": {
|
||||||
|
"clear": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"blocked": {
|
||||||
|
"type": "int"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"losErrorElevationUnavailable": "一个或多个样本的海拔数据不可用。",
|
||||||
|
"losErrorInvalidInput": "用于 LOS 计算的点/高程数据无效。",
|
||||||
|
"losRenameCustomPoint": "重命名自定义点",
|
||||||
|
"losPointName": "点名称",
|
||||||
|
"losShowPanelTooltip": "显示 LOS 面板",
|
||||||
|
"losHidePanelTooltip": "隐藏 LOS 面板",
|
||||||
|
"losElevationAttribution": "高程数据:Open-Meteo (CC BY 4.0)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'l10n/app_localizations.dart';
|
import 'l10n/app_localizations.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
@@ -47,6 +48,7 @@ void main() async {
|
|||||||
final notificationService = NotificationService();
|
final notificationService = NotificationService();
|
||||||
await notificationService.initialize();
|
await notificationService.initialize();
|
||||||
await backgroundService.initialize();
|
await backgroundService.initialize();
|
||||||
|
_registerThirdPartyLicenses();
|
||||||
|
|
||||||
// Wire up connector with services
|
// Wire up connector with services
|
||||||
connector.initialize(
|
connector.initialize(
|
||||||
@@ -80,6 +82,27 @@ void main() async {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _registerThirdPartyLicenses() {
|
||||||
|
LicenseRegistry.addLicense(() async* {
|
||||||
|
yield const LicenseEntryWithLineBreaks(
|
||||||
|
<String>['Open-Meteo Elevation API Data'],
|
||||||
|
'''
|
||||||
|
Data used by LOS elevation lookups is provided by Open-Meteo.
|
||||||
|
|
||||||
|
Open-Meteo terms and attribution:
|
||||||
|
https://open-meteo.com/en/terms
|
||||||
|
|
||||||
|
Elevation API:
|
||||||
|
https://open-meteo.com/en/docs/elevation-api
|
||||||
|
|
||||||
|
Attribution license reference:
|
||||||
|
Creative Commons Attribution 4.0 International (CC BY 4.0)
|
||||||
|
https://creativecommons.org/licenses/by/4.0/
|
||||||
|
''',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class MeshCoreApp extends StatelessWidget {
|
class MeshCoreApp extends StatelessWidget {
|
||||||
final MeshCoreConnector connector;
|
final MeshCoreConnector connector;
|
||||||
final MessageRetryService retryService;
|
final MessageRetryService retryService;
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
enum UnitSystem { metric, imperial }
|
||||||
|
|
||||||
|
extension UnitSystemValue on UnitSystem {
|
||||||
|
String get value {
|
||||||
|
switch (this) {
|
||||||
|
case UnitSystem.imperial:
|
||||||
|
return 'imperial';
|
||||||
|
case UnitSystem.metric:
|
||||||
|
return 'metric';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AppSettings {
|
class AppSettings {
|
||||||
static const Object _unset = Object();
|
static const Object _unset = Object();
|
||||||
|
|
||||||
@@ -21,6 +34,7 @@ class AppSettings {
|
|||||||
final String? languageOverride; // null = system default
|
final String? languageOverride; // null = system default
|
||||||
final bool appDebugLogEnabled;
|
final bool appDebugLogEnabled;
|
||||||
final Map<String, String> batteryChemistryByDeviceId;
|
final Map<String, String> batteryChemistryByDeviceId;
|
||||||
|
final UnitSystem unitSystem;
|
||||||
|
|
||||||
AppSettings({
|
AppSettings({
|
||||||
this.clearPathOnMaxRetry = false,
|
this.clearPathOnMaxRetry = false,
|
||||||
@@ -43,6 +57,7 @@ class AppSettings {
|
|||||||
this.languageOverride,
|
this.languageOverride,
|
||||||
this.appDebugLogEnabled = false,
|
this.appDebugLogEnabled = false,
|
||||||
Map<String, String>? batteryChemistryByDeviceId,
|
Map<String, String>? batteryChemistryByDeviceId,
|
||||||
|
this.unitSystem = UnitSystem.metric,
|
||||||
}) : batteryChemistryByDeviceId = batteryChemistryByDeviceId ?? {};
|
}) : batteryChemistryByDeviceId = batteryChemistryByDeviceId ?? {};
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
@@ -67,10 +82,18 @@ class AppSettings {
|
|||||||
'language_override': languageOverride,
|
'language_override': languageOverride,
|
||||||
'app_debug_log_enabled': appDebugLogEnabled,
|
'app_debug_log_enabled': appDebugLogEnabled,
|
||||||
'battery_chemistry_by_device_id': batteryChemistryByDeviceId,
|
'battery_chemistry_by_device_id': batteryChemistryByDeviceId,
|
||||||
|
'unit_system': unitSystem.value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
factory AppSettings.fromJson(Map<String, dynamic> json) {
|
factory AppSettings.fromJson(Map<String, dynamic> json) {
|
||||||
|
UnitSystem parseUnitSystem(dynamic value) {
|
||||||
|
if (value is String && value.toLowerCase() == 'imperial') {
|
||||||
|
return UnitSystem.imperial;
|
||||||
|
}
|
||||||
|
return UnitSystem.metric;
|
||||||
|
}
|
||||||
|
|
||||||
return AppSettings(
|
return AppSettings(
|
||||||
clearPathOnMaxRetry: json['clear_path_on_max_retry'] as bool? ?? false,
|
clearPathOnMaxRetry: json['clear_path_on_max_retry'] as bool? ?? false,
|
||||||
mapShowRepeaters: json['map_show_repeaters'] as bool? ?? true,
|
mapShowRepeaters: json['map_show_repeaters'] as bool? ?? true,
|
||||||
@@ -101,6 +124,9 @@ class AppSettings {
|
|||||||
(key, value) => MapEntry(key.toString(), value.toString()),
|
(key, value) => MapEntry(key.toString(), value.toString()),
|
||||||
) ??
|
) ??
|
||||||
{},
|
{},
|
||||||
|
unitSystem: parseUnitSystem(
|
||||||
|
json['unit_system'] ?? json['los_unit_system'],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +151,7 @@ class AppSettings {
|
|||||||
Object? languageOverride = _unset,
|
Object? languageOverride = _unset,
|
||||||
bool? appDebugLogEnabled,
|
bool? appDebugLogEnabled,
|
||||||
Map<String, String>? batteryChemistryByDeviceId,
|
Map<String, String>? batteryChemistryByDeviceId,
|
||||||
|
UnitSystem? unitSystem,
|
||||||
}) {
|
}) {
|
||||||
return AppSettings(
|
return AppSettings(
|
||||||
clearPathOnMaxRetry: clearPathOnMaxRetry ?? this.clearPathOnMaxRetry,
|
clearPathOnMaxRetry: clearPathOnMaxRetry ?? this.clearPathOnMaxRetry,
|
||||||
@@ -154,6 +181,7 @@ class AppSettings {
|
|||||||
appDebugLogEnabled: appDebugLogEnabled ?? this.appDebugLogEnabled,
|
appDebugLogEnabled: appDebugLogEnabled ?? this.appDebugLogEnabled,
|
||||||
batteryChemistryByDeviceId:
|
batteryChemistryByDeviceId:
|
||||||
batteryChemistryByDeviceId ?? this.batteryChemistryByDeviceId,
|
batteryChemistryByDeviceId ?? this.batteryChemistryByDeviceId,
|
||||||
|
unitSystem: unitSystem ?? this.unitSystem,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
|
|||||||
|
|
||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
import '../services/app_debug_log_service.dart';
|
import '../services/app_debug_log_service.dart';
|
||||||
|
import '../widgets/adaptive_app_bar_title.dart';
|
||||||
|
|
||||||
class AppDebugLogScreen extends StatelessWidget {
|
class AppDebugLogScreen extends StatelessWidget {
|
||||||
const AppDebugLogScreen({super.key});
|
const AppDebugLogScreen({super.key});
|
||||||
@@ -17,7 +18,7 @@ class AppDebugLogScreen extends StatelessWidget {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.l10n.debugLog_appTitle),
|
title: AdaptiveAppBarTitle(context.l10n.debugLog_appTitle),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import 'package:provider/provider.dart';
|
|||||||
|
|
||||||
import '../connector/meshcore_connector.dart';
|
import '../connector/meshcore_connector.dart';
|
||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
|
import '../models/app_settings.dart';
|
||||||
import '../services/app_settings_service.dart';
|
import '../services/app_settings_service.dart';
|
||||||
import '../services/notification_service.dart';
|
import '../services/notification_service.dart';
|
||||||
|
import '../widgets/adaptive_app_bar_title.dart';
|
||||||
import 'map_cache_screen.dart';
|
import 'map_cache_screen.dart';
|
||||||
|
|
||||||
class AppSettingsScreen extends StatelessWidget {
|
class AppSettingsScreen extends StatelessWidget {
|
||||||
@@ -14,7 +16,7 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.l10n.appSettings_title),
|
title: AdaptiveAppBarTitle(context.l10n.appSettings_title),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
@@ -360,6 +362,18 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
onTap: () => _showTimeFilterDialog(context, settingsService),
|
onTap: () => _showTimeFilterDialog(context, settingsService),
|
||||||
),
|
),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.straighten),
|
||||||
|
title: Text(context.l10n.appSettings_unitsTitle),
|
||||||
|
subtitle: Text(
|
||||||
|
settingsService.settings.unitSystem == UnitSystem.imperial
|
||||||
|
? context.l10n.appSettings_unitsImperial
|
||||||
|
: context.l10n.appSettings_unitsMetric,
|
||||||
|
),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () => _showUnitsDialog(context, settingsService),
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.download_outlined),
|
leading: const Icon(Icons.download_outlined),
|
||||||
title: Text(context.l10n.appSettings_offlineMapCache),
|
title: Text(context.l10n.appSettings_offlineMapCache),
|
||||||
@@ -706,6 +720,46 @@ class AppSettingsScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showUnitsDialog(
|
||||||
|
BuildContext context,
|
||||||
|
AppSettingsService settingsService,
|
||||||
|
) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text(context.l10n.appSettings_unitsTitle),
|
||||||
|
content: RadioGroup<UnitSystem>(
|
||||||
|
groupValue: settingsService.settings.unitSystem,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
settingsService.setUnitSystem(value);
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text(context.l10n.appSettings_unitsMetric),
|
||||||
|
leading: const Radio<UnitSystem>(value: UnitSystem.metric),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(context.l10n.appSettings_unitsImperial),
|
||||||
|
leading: const Radio<UnitSystem>(value: UnitSystem.imperial),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: Text(context.l10n.common_close),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildDebugCard(
|
Widget _buildDebugCard(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
AppSettingsService settingsService,
|
AppSettingsService settingsService,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
|
|||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
import '../services/ble_debug_log_service.dart';
|
import '../services/ble_debug_log_service.dart';
|
||||||
import '../connector/meshcore_protocol.dart';
|
import '../connector/meshcore_protocol.dart';
|
||||||
|
import '../widgets/adaptive_app_bar_title.dart';
|
||||||
|
|
||||||
enum _BleLogView { frames, rawLogRx }
|
enum _BleLogView { frames, rawLogRx }
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ class _BleDebugLogScreenState extends State<BleDebugLogScreen> {
|
|||||||
: rawEntries.isNotEmpty;
|
: rawEntries.isNotEmpty;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.l10n.debugLog_bleTitle),
|
title: AdaptiveAppBarTitle(context.l10n.debugLog_bleTitle),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: context.l10n.debugLog_copyLog,
|
tooltip: context.l10n.debugLog_copyLog,
|
||||||
|
|||||||
@@ -9,11 +9,14 @@ import 'package:provider/provider.dart';
|
|||||||
|
|
||||||
import '../connector/meshcore_connector.dart';
|
import '../connector/meshcore_connector.dart';
|
||||||
import '../services/map_tile_cache_service.dart';
|
import '../services/map_tile_cache_service.dart';
|
||||||
|
import '../services/app_settings_service.dart';
|
||||||
import '../connector/meshcore_protocol.dart';
|
import '../connector/meshcore_protocol.dart';
|
||||||
import '../l10n/app_localizations.dart';
|
import '../l10n/app_localizations.dart';
|
||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
import '../models/channel_message.dart';
|
import '../models/channel_message.dart';
|
||||||
|
import '../models/app_settings.dart';
|
||||||
import '../models/contact.dart';
|
import '../models/contact.dart';
|
||||||
|
import '../widgets/adaptive_app_bar_title.dart';
|
||||||
|
|
||||||
class ChannelMessagePathScreen extends StatelessWidget {
|
class ChannelMessagePathScreen extends StatelessWidget {
|
||||||
final ChannelMessage message;
|
final ChannelMessage message;
|
||||||
@@ -48,7 +51,7 @@ class ChannelMessagePathScreen extends StatelessWidget {
|
|||||||
final extraPaths = _otherPaths(primaryPath, message.pathVariants);
|
final extraPaths = _otherPaths(primaryPath, message.pathVariants);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(l10n.channelPath_title),
|
title: AdaptiveAppBarTitle(l10n.channelPath_title),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.radar_outlined),
|
icon: const Icon(Icons.radar_outlined),
|
||||||
@@ -297,8 +300,12 @@ class ChannelMessagePathMapScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class _ChannelMessagePathMapScreenState
|
class _ChannelMessagePathMapScreenState
|
||||||
extends State<ChannelMessagePathMapScreen> {
|
extends State<ChannelMessagePathMapScreen> {
|
||||||
|
static const double _labelZoomThreshold = 8.5;
|
||||||
|
|
||||||
Uint8List? _selectedPath;
|
Uint8List? _selectedPath;
|
||||||
double _pathDistance = 0.0;
|
double _pathDistance = 0.0;
|
||||||
|
bool _showNodeLabels = true;
|
||||||
|
bool _didReceivePositionUpdate = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -333,6 +340,8 @@ class _ChannelMessagePathMapScreenState
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<MeshCoreConnector>(
|
return Consumer<MeshCoreConnector>(
|
||||||
builder: (context, connector, _) {
|
builder: (context, connector, _) {
|
||||||
|
final settings = context.watch<AppSettingsService>().settings;
|
||||||
|
final isImperial = settings.unitSystem == UnitSystem.imperial;
|
||||||
final tileCache = context.read<MapTileCacheService>();
|
final tileCache = context.read<MapTileCacheService>();
|
||||||
final primaryPath = _selectPrimaryPath(
|
final primaryPath = _selectPrimaryPath(
|
||||||
widget.message.pathBytes,
|
widget.message.pathBytes,
|
||||||
@@ -393,6 +402,9 @@ class _ChannelMessagePathMapScreenState
|
|||||||
? points.first
|
? points.first
|
||||||
: const LatLng(0, 0);
|
: const LatLng(0, 0);
|
||||||
final initialZoom = points.isNotEmpty ? 13.0 : 2.0;
|
final initialZoom = points.isNotEmpty ? 13.0 : 2.0;
|
||||||
|
if (!_didReceivePositionUpdate) {
|
||||||
|
_showNodeLabels = initialZoom >= _labelZoomThreshold;
|
||||||
|
}
|
||||||
final bounds = points.length > 1
|
final bounds = points.length > 1
|
||||||
? LatLngBounds.fromPoints(points)
|
? LatLngBounds.fromPoints(points)
|
||||||
: null;
|
: null;
|
||||||
@@ -402,7 +414,9 @@ class _ChannelMessagePathMapScreenState
|
|||||||
_pathDistance = _getPathDistance(points);
|
_pathDistance = _getPathDistance(points);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(context.l10n.channelPath_mapTitle)),
|
appBar: AppBar(
|
||||||
|
title: AdaptiveAppBarTitle(context.l10n.channelPath_mapTitle),
|
||||||
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@@ -424,6 +438,17 @@ class _ChannelMessagePathMapScreenState
|
|||||||
interactionOptions: InteractionOptions(
|
interactionOptions: InteractionOptions(
|
||||||
flags: ~InteractiveFlag.rotate,
|
flags: ~InteractiveFlag.rotate,
|
||||||
),
|
),
|
||||||
|
onPositionChanged: (camera, hasGesture) {
|
||||||
|
final shouldShow = camera.zoom >= _labelZoomThreshold;
|
||||||
|
if (!_didReceivePositionUpdate ||
|
||||||
|
shouldShow != _showNodeLabels) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_didReceivePositionUpdate = true;
|
||||||
|
_showNodeLabels = shouldShow;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
TileLayer(
|
TileLayer(
|
||||||
@@ -435,7 +460,12 @@ class _ChannelMessagePathMapScreenState
|
|||||||
),
|
),
|
||||||
if (polylines.isNotEmpty)
|
if (polylines.isNotEmpty)
|
||||||
PolylineLayer(polylines: polylines),
|
PolylineLayer(polylines: polylines),
|
||||||
MarkerLayer(markers: _buildHopMarkers(hops)),
|
MarkerLayer(
|
||||||
|
markers: _buildHopMarkers(
|
||||||
|
hops,
|
||||||
|
showLabels: _showNodeLabels,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (observedPaths.length > 1)
|
if (observedPaths.length > 1)
|
||||||
@@ -458,7 +488,7 @@ class _ChannelMessagePathMapScreenState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildLegendCard(context, hops),
|
_buildLegendCard(context, hops, isImperial),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -530,45 +560,61 @@ class _ChannelMessagePathMapScreenState
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Marker> _buildHopMarkers(List<_PathHop> hops) {
|
List<Marker> _buildHopMarkers(
|
||||||
return [
|
List<_PathHop> hops, {
|
||||||
for (final hop in hops)
|
required bool showLabels,
|
||||||
if (hop.hasLocation)
|
}) {
|
||||||
Marker(
|
final markers = <Marker>[];
|
||||||
point: hop.position!,
|
for (final hop in hops) {
|
||||||
width: 35,
|
if (!hop.hasLocation) continue;
|
||||||
height: 35,
|
final point = hop.position!;
|
||||||
child: Container(
|
markers.add(
|
||||||
decoration: BoxDecoration(
|
Marker(
|
||||||
color: Colors.green,
|
point: point,
|
||||||
shape: BoxShape.circle,
|
width: 35,
|
||||||
border: Border.all(color: Colors.white, width: 2),
|
height: 35,
|
||||||
boxShadow: [
|
child: Container(
|
||||||
BoxShadow(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black.withValues(alpha: 0.3),
|
color: Colors.green,
|
||||||
blurRadius: 4,
|
shape: BoxShape.circle,
|
||||||
offset: const Offset(0, 2),
|
border: Border.all(color: Colors.white, width: 2),
|
||||||
),
|
boxShadow: [
|
||||||
],
|
BoxShadow(
|
||||||
),
|
color: Colors.black.withValues(alpha: 0.3),
|
||||||
alignment: Alignment.center,
|
blurRadius: 4,
|
||||||
child: Text(
|
offset: const Offset(0, 2),
|
||||||
hop.index.toString(),
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
hop.index.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (context.read<MeshCoreConnector>().selfLatitude != null &&
|
),
|
||||||
context.read<MeshCoreConnector>().selfLongitude != null)
|
);
|
||||||
Marker(
|
if (showLabels) {
|
||||||
point: LatLng(
|
markers.add(
|
||||||
context.read<MeshCoreConnector>().selfLatitude!,
|
_buildNodeLabelMarker(
|
||||||
context.read<MeshCoreConnector>().selfLongitude!,
|
point: point,
|
||||||
|
label: hop.contact?.name ?? _formatPrefix(hop.prefix),
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final selfLat = context.read<MeshCoreConnector>().selfLatitude;
|
||||||
|
final selfLon = context.read<MeshCoreConnector>().selfLongitude;
|
||||||
|
if (selfLat != null && selfLon != null) {
|
||||||
|
final selfPoint = LatLng(selfLat, selfLon);
|
||||||
|
markers.add(
|
||||||
|
Marker(
|
||||||
|
point: selfPoint,
|
||||||
width: 35,
|
width: 35,
|
||||||
height: 35,
|
height: 35,
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -595,10 +641,63 @@ class _ChannelMessagePathMapScreenState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
);
|
||||||
|
if (showLabels) {
|
||||||
|
markers.add(
|
||||||
|
_buildNodeLabelMarker(
|
||||||
|
point: selfPoint,
|
||||||
|
label: context.l10n.pathTrace_you,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return markers;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLegendCard(BuildContext context, List<_PathHop> hops) {
|
Marker _buildNodeLabelMarker({required LatLng point, required String label}) {
|
||||||
|
return Marker(
|
||||||
|
point: point,
|
||||||
|
width: 120,
|
||||||
|
height: 24,
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
child: IgnorePointer(
|
||||||
|
child: Transform.translate(
|
||||||
|
offset: const Offset(0, -26),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black54,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 96,
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLegendCard(
|
||||||
|
BuildContext context,
|
||||||
|
List<_PathHop> hops,
|
||||||
|
bool isImperial,
|
||||||
|
) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final maxHeight = MediaQuery.of(context).size.height * 0.35;
|
final maxHeight = MediaQuery.of(context).size.height * 0.35;
|
||||||
final estimatedHeight = 72.0 + (hops.length * 56.0);
|
final estimatedHeight = 72.0 + (hops.length * 56.0);
|
||||||
@@ -617,7 +716,7 @@ class _ChannelMessagePathMapScreenState
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
child: Text(
|
child: Text(
|
||||||
'${l10n.channelPath_repeaterHops} (${(_pathDistance / 1609.34).toStringAsFixed(2)} Miles / ${(_pathDistance / 1000).toStringAsFixed(2)} Km)',
|
'${l10n.channelPath_repeaterHops} ${formatDistance(_pathDistance, isImperial: isImperial)}',
|
||||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import '../connector/meshcore_connector.dart';
|
|||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
import '../models/community.dart';
|
import '../models/community.dart';
|
||||||
import '../storage/community_store.dart';
|
import '../storage/community_store.dart';
|
||||||
|
import '../widgets/adaptive_app_bar_title.dart';
|
||||||
import '../widgets/qr_scanner_widget.dart';
|
import '../widgets/qr_scanner_widget.dart';
|
||||||
|
|
||||||
/// Screen for scanning community QR codes to join communities.
|
/// Screen for scanning community QR codes to join communities.
|
||||||
@@ -29,7 +30,7 @@ class _CommunityQrScannerScreenState extends State<CommunityQrScannerScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.l10n.community_scanQr),
|
title: AdaptiveAppBarTitle(context.l10n.community_scanQr),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
body: _isProcessing
|
body: _isProcessing
|
||||||
|
|||||||
@@ -1170,12 +1170,17 @@ class _ContactTile extends StatelessWidget {
|
|||||||
backgroundColor: _getTypeColor(contact.type),
|
backgroundColor: _getTypeColor(contact.type),
|
||||||
child: _buildContactAvatar(contact),
|
child: _buildContactAvatar(contact),
|
||||||
),
|
),
|
||||||
title: Text(contact.name),
|
title: Text(contact.name, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||||
subtitle: Column(
|
subtitle: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(contact.pathLabel),
|
Text(contact.pathLabel, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||||
Text(contact.shortPubKeyHex, style: TextStyle(fontSize: 12)),
|
Text(
|
||||||
|
contact.shortPubKeyHex,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// Clamp text scaling in trailing section to prevent overflow while
|
// Clamp text scaling in trailing section to prevent overflow while
|
||||||
@@ -1186,26 +1191,32 @@ class _ContactTile extends StatelessWidget {
|
|||||||
MediaQuery.textScalerOf(context).scale(1.0).clamp(1.0, 1.3),
|
MediaQuery.textScalerOf(context).scale(1.0).clamp(1.0, 1.3),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: SizedBox(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
width: 120,
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
child: Column(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
if (unreadCount > 0) ...[
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
UnreadBadge(count: unreadCount),
|
children: [
|
||||||
const SizedBox(height: 4),
|
if (unreadCount > 0) ...[
|
||||||
],
|
UnreadBadge(count: unreadCount),
|
||||||
Text(
|
const SizedBox(height: 4),
|
||||||
_formatLastSeen(context, lastSeen),
|
|
||||||
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
if (contact.hasLocation)
|
|
||||||
Icon(Icons.location_on, size: 14, color: Colors.grey[400]),
|
|
||||||
],
|
],
|
||||||
),
|
Text(
|
||||||
],
|
_formatLastSeen(context, lastSeen),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (contact.hasLocation)
|
||||||
|
Icon(Icons.location_on, size: 14, color: Colors.grey[400]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ import '../l10n/app_localizations.dart';
|
|||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
import '../services/app_settings_service.dart';
|
import '../services/app_settings_service.dart';
|
||||||
import '../services/map_tile_cache_service.dart';
|
import '../services/map_tile_cache_service.dart';
|
||||||
|
import '../widgets/adaptive_app_bar_title.dart';
|
||||||
|
|
||||||
class MapCacheScreen extends StatefulWidget {
|
class MapCacheScreen extends StatefulWidget {
|
||||||
const MapCacheScreen({super.key});
|
const MapCacheScreen({super.key});
|
||||||
@@ -224,7 +225,10 @@ class _MapCacheScreenState extends State<MapCacheScreen> {
|
|||||||
: (_completedTiles / _estimatedTiles).clamp(0.0, 1.0).toDouble();
|
: (_completedTiles / _estimatedTiles).clamp(0.0, 1.0).toDouble();
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(l10n.mapCache_title), centerTitle: true),
|
appBar: AppBar(
|
||||||
|
title: AdaptiveAppBarTitle(l10n.mapCache_title),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|||||||
+129
-5
@@ -11,6 +11,7 @@ import 'package:provider/provider.dart';
|
|||||||
import '../connector/meshcore_connector.dart';
|
import '../connector/meshcore_connector.dart';
|
||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
import '../connector/meshcore_protocol.dart';
|
import '../connector/meshcore_protocol.dart';
|
||||||
|
import '../models/app_settings.dart';
|
||||||
import '../models/channel.dart';
|
import '../models/channel.dart';
|
||||||
import '../models/contact.dart';
|
import '../models/contact.dart';
|
||||||
import '../services/app_settings_service.dart';
|
import '../services/app_settings_service.dart';
|
||||||
@@ -26,6 +27,7 @@ import '../widgets/repeater_login_dialog.dart';
|
|||||||
import '../widgets/room_login_dialog.dart';
|
import '../widgets/room_login_dialog.dart';
|
||||||
import 'repeater_hub_screen.dart';
|
import 'repeater_hub_screen.dart';
|
||||||
import 'settings_screen.dart';
|
import 'settings_screen.dart';
|
||||||
|
import 'line_of_sight_map_screen.dart';
|
||||||
|
|
||||||
class MapScreen extends StatefulWidget {
|
class MapScreen extends StatefulWidget {
|
||||||
final LatLng? highlightPosition;
|
final LatLng? highlightPosition;
|
||||||
@@ -46,6 +48,8 @@ class MapScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MapScreenState extends State<MapScreen> {
|
class _MapScreenState extends State<MapScreen> {
|
||||||
|
static const double _labelZoomThreshold = 8.5;
|
||||||
|
|
||||||
final MapController _mapController = MapController();
|
final MapController _mapController = MapController();
|
||||||
final MapMarkerService _markerService = MapMarkerService();
|
final MapMarkerService _markerService = MapMarkerService();
|
||||||
final Set<String> _hiddenMarkerIds = {};
|
final Set<String> _hiddenMarkerIds = {};
|
||||||
@@ -58,6 +62,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
final List<LatLng> _points = [];
|
final List<LatLng> _points = [];
|
||||||
final List<Polyline> _polylines = [];
|
final List<Polyline> _polylines = [];
|
||||||
bool _legendExpanded = false;
|
bool _legendExpanded = false;
|
||||||
|
bool _showNodeLabels = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -247,6 +252,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
// Re center map after removed markers have loaded
|
// Re center map after removed markers have loaded
|
||||||
if (!_hasInitializedMap && _removedMarkersLoaded) {
|
if (!_hasInitializedMap && _removedMarkersLoaded) {
|
||||||
_hasInitializedMap = true;
|
_hasInitializedMap = true;
|
||||||
|
_showNodeLabels = initialZoom >= _labelZoomThreshold;
|
||||||
if (hasMapContent) {
|
if (hasMapContent) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@@ -272,6 +278,47 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
onPressed: () => _startPath(),
|
onPressed: () => _startPath(),
|
||||||
tooltip: context.l10n.contacts_pathTrace,
|
tooltip: context.l10n.contacts_pathTrace,
|
||||||
),
|
),
|
||||||
|
if (!_isBuildingPathTrace)
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.visibility),
|
||||||
|
onPressed: () {
|
||||||
|
final candidates = <LineOfSightEndpoint>[];
|
||||||
|
if (connector.selfLatitude != null &&
|
||||||
|
connector.selfLongitude != null) {
|
||||||
|
candidates.add(
|
||||||
|
LineOfSightEndpoint(
|
||||||
|
label: context.l10n.pathTrace_you,
|
||||||
|
point: LatLng(
|
||||||
|
connector.selfLatitude!,
|
||||||
|
connector.selfLongitude!,
|
||||||
|
),
|
||||||
|
color: Colors.teal,
|
||||||
|
icon: Icons.person_pin_circle,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (final c in contactsWithLocation) {
|
||||||
|
candidates.add(
|
||||||
|
LineOfSightEndpoint(
|
||||||
|
label: c.name,
|
||||||
|
point: LatLng(c.latitude!, c.longitude!),
|
||||||
|
color: _getNodeColor(c.type),
|
||||||
|
icon: _getNodeIcon(c.type),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => LineOfSightMapScreen(
|
||||||
|
title: context.l10n.map_losScreenTitle,
|
||||||
|
candidates: candidates,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
tooltip: context.l10n.map_lineOfSight,
|
||||||
|
),
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
@@ -350,6 +397,14 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
position: latLng,
|
position: latLng,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
onPositionChanged: (camera, hasGesture) {
|
||||||
|
final shouldShow = camera.zoom >= _labelZoomThreshold;
|
||||||
|
if (shouldShow != _showNodeLabels && mounted) {
|
||||||
|
setState(() {
|
||||||
|
_showNodeLabels = shouldShow;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
TileLayer(
|
TileLayer(
|
||||||
@@ -374,7 +429,11 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
size: 34,
|
size: 34,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
..._buildMarkers(contactsWithLocation, settings),
|
..._buildMarkers(
|
||||||
|
contactsWithLocation,
|
||||||
|
settings,
|
||||||
|
showLabels: _showNodeLabels,
|
||||||
|
),
|
||||||
...sharedMarkers.map(_buildSharedMarker),
|
...sharedMarkers.map(_buildSharedMarker),
|
||||||
if (connector.selfLatitude != null &&
|
if (connector.selfLatitude != null &&
|
||||||
connector.selfLongitude != null)
|
connector.selfLongitude != null)
|
||||||
@@ -413,6 +472,16 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (_showNodeLabels &&
|
||||||
|
connector.selfLatitude != null &&
|
||||||
|
connector.selfLongitude != null)
|
||||||
|
_buildNodeLabelMarker(
|
||||||
|
point: LatLng(
|
||||||
|
connector.selfLatitude!,
|
||||||
|
connector.selfLongitude!,
|
||||||
|
),
|
||||||
|
label: connector.deviceDisplayName,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -444,7 +513,11 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Marker> _buildMarkers(List<Contact> contacts, settings) {
|
List<Marker> _buildMarkers(
|
||||||
|
List<Contact> contacts,
|
||||||
|
settings, {
|
||||||
|
required bool showLabels,
|
||||||
|
}) {
|
||||||
final markers = <Marker>[];
|
final markers = <Marker>[];
|
||||||
|
|
||||||
for (final contact in contacts) {
|
for (final contact in contacts) {
|
||||||
@@ -499,11 +572,57 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
markers.add(marker);
|
markers.add(marker);
|
||||||
|
if (showLabels) {
|
||||||
|
markers.add(
|
||||||
|
_buildNodeLabelMarker(
|
||||||
|
point: LatLng(contact.latitude!, contact.longitude!),
|
||||||
|
label: contact.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return markers;
|
return markers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Marker _buildNodeLabelMarker({required LatLng point, required String label}) {
|
||||||
|
return Marker(
|
||||||
|
point: point,
|
||||||
|
width: 120,
|
||||||
|
height: 24,
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
child: IgnorePointer(
|
||||||
|
child: Transform.translate(
|
||||||
|
offset: const Offset(0, -26),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black54,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 96,
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Color _getNodeColor(int type) {
|
Color _getNodeColor(int type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case advTypeChat:
|
case advTypeChat:
|
||||||
@@ -1519,6 +1638,9 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
|
|
||||||
Widget _buildPathTraceOverlay() {
|
Widget _buildPathTraceOverlay() {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
|
final isImperial =
|
||||||
|
context.read<AppSettingsService>().settings.unitSystem ==
|
||||||
|
UnitSystem.imperial;
|
||||||
return Positioned(
|
return Positioned(
|
||||||
top: 16,
|
top: 16,
|
||||||
left: 16,
|
left: 16,
|
||||||
@@ -1539,7 +1661,7 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
if (_pathTrace.isNotEmpty)
|
if (_pathTrace.isNotEmpty)
|
||||||
Text(
|
Text(
|
||||||
"${l10n.path_currentPathLabel} ${formatDistance(getPathDistanceMeters(_points))}",
|
"${l10n.path_currentPathLabel} ${formatDistance(getPathDistanceMeters(_points), isImperial: isImperial)}",
|
||||||
style: TextStyle(fontSize: 12, color: Colors.grey[700]),
|
style: TextStyle(fontSize: 12, color: Colors.grey[700]),
|
||||||
),
|
),
|
||||||
SelectableText(
|
SelectableText(
|
||||||
@@ -1549,8 +1671,10 @@ class _MapScreenState extends State<MapScreen> {
|
|||||||
style: TextStyle(fontSize: 18),
|
style: TextStyle(fontSize: 18),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Row(
|
Wrap(
|
||||||
mainAxisSize: MainAxisSize.min,
|
alignment: WrapAlignment.center,
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 8,
|
||||||
children: [
|
children: [
|
||||||
if (_pathTrace.isNotEmpty)
|
if (_pathTrace.isNotEmpty)
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
|
|||||||
+137
-54
@@ -8,7 +8,9 @@ import 'package:latlong2/latlong.dart';
|
|||||||
import 'package:meshcore_open/connector/meshcore_connector.dart';
|
import 'package:meshcore_open/connector/meshcore_connector.dart';
|
||||||
import 'package:meshcore_open/connector/meshcore_protocol.dart';
|
import 'package:meshcore_open/connector/meshcore_protocol.dart';
|
||||||
import 'package:meshcore_open/l10n/l10n.dart';
|
import 'package:meshcore_open/l10n/l10n.dart';
|
||||||
|
import 'package:meshcore_open/models/app_settings.dart';
|
||||||
import 'package:meshcore_open/models/contact.dart';
|
import 'package:meshcore_open/models/contact.dart';
|
||||||
|
import 'package:meshcore_open/services/app_settings_service.dart';
|
||||||
import 'package:meshcore_open/services/map_tile_cache_service.dart';
|
import 'package:meshcore_open/services/map_tile_cache_service.dart';
|
||||||
import 'package:meshcore_open/utils/app_logger.dart';
|
import 'package:meshcore_open/utils/app_logger.dart';
|
||||||
import 'package:meshcore_open/widgets/snr_indicator.dart';
|
import 'package:meshcore_open/widgets/snr_indicator.dart';
|
||||||
@@ -27,8 +29,11 @@ double getPathDistanceMeters(List<LatLng> points) {
|
|||||||
return distanceMeters;
|
return distanceMeters;
|
||||||
}
|
}
|
||||||
|
|
||||||
String formatDistance(double distanceMeters) {
|
String formatDistance(double distanceMeters, {required bool isImperial}) {
|
||||||
return '(${(distanceMeters / 1609.34).toStringAsFixed(2)} Miles / ${(distanceMeters / 1000).toStringAsFixed(2)} Km)';
|
if (isImperial) {
|
||||||
|
return '(${(distanceMeters / 1609.34).toStringAsFixed(2)} mi)';
|
||||||
|
}
|
||||||
|
return '(${(distanceMeters / 1000).toStringAsFixed(2)} km)';
|
||||||
}
|
}
|
||||||
|
|
||||||
class PathTraceData {
|
class PathTraceData {
|
||||||
@@ -64,6 +69,8 @@ class PathTraceMapScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
||||||
|
static const double _labelZoomThreshold = 8.5;
|
||||||
|
|
||||||
StreamSubscription<Uint8List>? _frameSubscription;
|
StreamSubscription<Uint8List>? _frameSubscription;
|
||||||
Timer? _timeoutTimer;
|
Timer? _timeoutTimer;
|
||||||
|
|
||||||
@@ -78,6 +85,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
LatLngBounds? _bounds;
|
LatLngBounds? _bounds;
|
||||||
ValueKey<String> _mapKey = const ValueKey('initial');
|
ValueKey<String> _mapKey = const ValueKey('initial');
|
||||||
double _pathDistanceMeters = 0.0;
|
double _pathDistanceMeters = 0.0;
|
||||||
|
bool _showNodeLabels = true;
|
||||||
|
|
||||||
String _formatPathPrefixes(Uint8List pathBytes) {
|
String _formatPathPrefixes(Uint8List pathBytes) {
|
||||||
return pathBytes
|
return pathBytes
|
||||||
@@ -291,6 +299,8 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<MeshCoreConnector>(
|
return Consumer<MeshCoreConnector>(
|
||||||
builder: (context, connector, _) {
|
builder: (context, connector, _) {
|
||||||
|
final settings = context.watch<AppSettingsService>().settings;
|
||||||
|
final isImperial = settings.unitSystem == UnitSystem.imperial;
|
||||||
final tileCache = context.read<MapTileCacheService>();
|
final tileCache = context.read<MapTileCacheService>();
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -355,7 +365,8 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (_hasData) _buildLegendCard(context, _traceData!),
|
if (_hasData)
|
||||||
|
_buildLegendCard(context, _traceData!, isImperial),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -364,55 +375,61 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Marker> _buildHopMarkers(List<int> pathData) {
|
List<Marker> _buildHopMarkers(
|
||||||
return [
|
List<int> pathData, {
|
||||||
for (final hop in pathData)
|
required bool showLabels,
|
||||||
if (_traceData!.pathContacts[hop] != null &&
|
}) {
|
||||||
_traceData!.pathContacts[hop]!.hasLocation)
|
final markers = <Marker>[];
|
||||||
Marker(
|
for (final hop in pathData) {
|
||||||
point: LatLng(
|
final contact = _traceData!.pathContacts[hop];
|
||||||
_traceData!.pathContacts[hop]!.latitude!,
|
if (contact == null || !contact.hasLocation) continue;
|
||||||
_traceData!.pathContacts[hop]!.longitude!,
|
final point = LatLng(contact.latitude!, contact.longitude!);
|
||||||
),
|
markers.add(
|
||||||
width: 35,
|
|
||||||
height: 35,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(4),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.green,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(color: Colors.white, width: 2),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withValues(alpha: 0.3),
|
|
||||||
blurRadius: 4,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Text(
|
|
||||||
_traceData!.pathContacts[hop]!.publicKey
|
|
||||||
.sublist(0, 1)
|
|
||||||
.map(
|
|
||||||
(b) => b.toRadixString(16).padLeft(2, '0').toUpperCase(),
|
|
||||||
)
|
|
||||||
.join(),
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (context.read<MeshCoreConnector>().selfLatitude != null &&
|
|
||||||
context.read<MeshCoreConnector>().selfLongitude != null)
|
|
||||||
Marker(
|
Marker(
|
||||||
point: LatLng(
|
point: point,
|
||||||
context.read<MeshCoreConnector>().selfLatitude!,
|
width: 35,
|
||||||
context.read<MeshCoreConnector>().selfLongitude!,
|
height: 35,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.green,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(color: Colors.white, width: 2),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.3),
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
contact.publicKey
|
||||||
|
.sublist(0, 1)
|
||||||
|
.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase())
|
||||||
|
.join(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (showLabels) {
|
||||||
|
markers.add(_buildNodeLabelMarker(point: point, label: contact.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final selfLat = context.read<MeshCoreConnector>().selfLatitude;
|
||||||
|
final selfLon = context.read<MeshCoreConnector>().selfLongitude;
|
||||||
|
if (selfLat != null && selfLon != null) {
|
||||||
|
final selfPoint = LatLng(selfLat, selfLon);
|
||||||
|
markers.add(
|
||||||
|
Marker(
|
||||||
|
point: selfPoint,
|
||||||
width: 35,
|
width: 35,
|
||||||
height: 35,
|
height: 35,
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -440,7 +457,56 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
);
|
||||||
|
if (showLabels) {
|
||||||
|
markers.add(
|
||||||
|
_buildNodeLabelMarker(
|
||||||
|
point: selfPoint,
|
||||||
|
label: context.l10n.pathTrace_you,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return markers;
|
||||||
|
}
|
||||||
|
|
||||||
|
Marker _buildNodeLabelMarker({required LatLng point, required String label}) {
|
||||||
|
return Marker(
|
||||||
|
point: point,
|
||||||
|
width: 120,
|
||||||
|
height: 24,
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
child: IgnorePointer(
|
||||||
|
child: Transform.translate(
|
||||||
|
offset: const Offset(0, -26),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black54,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 96,
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String formatDirectionText(PathTraceData pathTraceData, int index) {
|
String formatDirectionText(PathTraceData pathTraceData, int index) {
|
||||||
@@ -520,6 +586,14 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
),
|
),
|
||||||
minZoom: 2.0,
|
minZoom: 2.0,
|
||||||
maxZoom: 18.0,
|
maxZoom: 18.0,
|
||||||
|
onPositionChanged: (camera, hasGesture) {
|
||||||
|
final shouldShow = camera.zoom >= _labelZoomThreshold;
|
||||||
|
if (shouldShow != _showNodeLabels && mounted) {
|
||||||
|
setState(() {
|
||||||
|
_showNodeLabels = shouldShow;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
TileLayer(
|
TileLayer(
|
||||||
@@ -530,12 +604,21 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
),
|
),
|
||||||
if (_polylines.isNotEmpty) PolylineLayer(polylines: _polylines),
|
if (_polylines.isNotEmpty) PolylineLayer(polylines: _polylines),
|
||||||
if (_traceData!.pathData.isNotEmpty)
|
if (_traceData!.pathData.isNotEmpty)
|
||||||
MarkerLayer(markers: _buildHopMarkers(_traceData!.pathData)),
|
MarkerLayer(
|
||||||
|
markers: _buildHopMarkers(
|
||||||
|
_traceData!.pathData,
|
||||||
|
showLabels: _showNodeLabels,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLegendCard(BuildContext context, PathTraceData pathTraceData) {
|
Widget _buildLegendCard(
|
||||||
|
BuildContext context,
|
||||||
|
PathTraceData pathTraceData,
|
||||||
|
bool isImperial,
|
||||||
|
) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final maxHeight = MediaQuery.of(context).size.height * 0.35;
|
final maxHeight = MediaQuery.of(context).size.height * 0.35;
|
||||||
final estimatedHeight = 72.0 + (pathTraceData.pathData.length * 56.0);
|
final estimatedHeight = 72.0 + (pathTraceData.pathData.length * 56.0);
|
||||||
@@ -554,7 +637,7 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
child: Text(
|
child: Text(
|
||||||
'${l10n.channelPath_repeaterHops} ${formatDistance(_pathDistanceMeters)}',
|
'${l10n.channelPath_repeaterHops} ${formatDistance(_pathDistanceMeters, isImperial: isImperial)}',
|
||||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
|
|||||||
|
|
||||||
import '../connector/meshcore_connector.dart';
|
import '../connector/meshcore_connector.dart';
|
||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
|
import '../widgets/adaptive_app_bar_title.dart';
|
||||||
import '../widgets/device_tile.dart';
|
import '../widgets/device_tile.dart';
|
||||||
import 'contacts_screen.dart';
|
import 'contacts_screen.dart';
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ class _ScannerScreenState extends State<ScannerScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.l10n.scanner_title),
|
title: AdaptiveAppBarTitle(context.l10n.scanner_title),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import '../connector/meshcore_connector.dart';
|
|||||||
import '../connector/meshcore_protocol.dart';
|
import '../connector/meshcore_protocol.dart';
|
||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
import '../models/radio_settings.dart';
|
import '../models/radio_settings.dart';
|
||||||
|
import '../widgets/adaptive_app_bar_title.dart';
|
||||||
import 'app_settings_screen.dart';
|
import 'app_settings_screen.dart';
|
||||||
import 'app_debug_log_screen.dart';
|
import 'app_debug_log_screen.dart';
|
||||||
import 'ble_debug_log_screen.dart';
|
import 'ble_debug_log_screen.dart';
|
||||||
@@ -41,7 +42,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(l10n.settings_title), centerTitle: true),
|
appBar: AppBar(
|
||||||
|
title: AdaptiveAppBarTitle(l10n.settings_title),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
child: Consumer<MeshCoreConnector>(
|
child: Consumer<MeshCoreConnector>(
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import 'package:provider/provider.dart';
|
|||||||
import '../l10n/l10n.dart';
|
import '../l10n/l10n.dart';
|
||||||
import '../models/contact.dart';
|
import '../models/contact.dart';
|
||||||
import '../models/path_selection.dart';
|
import '../models/path_selection.dart';
|
||||||
|
import '../models/app_settings.dart';
|
||||||
import '../connector/meshcore_connector.dart';
|
import '../connector/meshcore_connector.dart';
|
||||||
import '../connector/meshcore_protocol.dart';
|
import '../connector/meshcore_protocol.dart';
|
||||||
|
import '../services/app_settings_service.dart';
|
||||||
import '../services/repeater_command_service.dart';
|
import '../services/repeater_command_service.dart';
|
||||||
import '../widgets/path_management_dialog.dart';
|
import '../widgets/path_management_dialog.dart';
|
||||||
import '../helpers/cayenne_lpp.dart';
|
import '../helpers/cayenne_lpp.dart';
|
||||||
@@ -181,6 +183,8 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final connector = context.watch<MeshCoreConnector>();
|
final connector = context.watch<MeshCoreConnector>();
|
||||||
|
final settings = context.watch<AppSettingsService>().settings;
|
||||||
|
final isImperialUnits = settings.unitSystem == UnitSystem.imperial;
|
||||||
final repeater = _resolveRepeater(connector);
|
final repeater = _resolveRepeater(connector);
|
||||||
final isFloodMode = repeater.pathOverride == -1;
|
final isFloodMode = repeater.pathOverride == -1;
|
||||||
|
|
||||||
@@ -307,6 +311,7 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
|
|||||||
entry['values'],
|
entry['values'],
|
||||||
l10n.telemetry_channelTitle(entry['channel']),
|
l10n.telemetry_channelTitle(entry['channel']),
|
||||||
entry['channel'],
|
entry['channel'],
|
||||||
|
isImperialUnits,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -319,6 +324,7 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
|
|||||||
Map<String, dynamic> channelData,
|
Map<String, dynamic> channelData,
|
||||||
String title,
|
String title,
|
||||||
int channel,
|
int channel,
|
||||||
|
bool isImperialUnits,
|
||||||
) {
|
) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
return Card(
|
return Card(
|
||||||
@@ -358,12 +364,12 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
|
|||||||
else if (entry.key == 'temperature' && channel == 1)
|
else if (entry.key == 'temperature' && channel == 1)
|
||||||
_buildInfoRow(
|
_buildInfoRow(
|
||||||
l10n.telemetry_mcuTemperatureLabel,
|
l10n.telemetry_mcuTemperatureLabel,
|
||||||
_temperatureText(entry.value),
|
_temperatureText(entry.value, isImperialUnits),
|
||||||
)
|
)
|
||||||
else if (entry.key == 'temperature')
|
else if (entry.key == 'temperature')
|
||||||
_buildInfoRow(
|
_buildInfoRow(
|
||||||
l10n.telemetry_temperatureLabel,
|
l10n.telemetry_temperatureLabel,
|
||||||
_temperatureText(entry.value),
|
_temperatureText(entry.value, isImperialUnits),
|
||||||
)
|
)
|
||||||
else if (entry.key == 'current' && channel == 1)
|
else if (entry.key == 'current' && channel == 1)
|
||||||
_buildInfoRow(
|
_buildInfoRow(
|
||||||
@@ -421,13 +427,13 @@ class _TelemetryScreenState extends State<TelemetryScreen> {
|
|||||||
return (((millivolts - minMv) * 100) / (maxMv - minMv)).round();
|
return (((millivolts - minMv) * 100) / (maxMv - minMv)).round();
|
||||||
}
|
}
|
||||||
|
|
||||||
String _temperatureText(double? tempC) {
|
String _temperatureText(double? tempC, bool isImperialUnits) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
if (tempC == null) return l10n.common_notAvailable;
|
if (tempC == null) return l10n.common_notAvailable;
|
||||||
final tempF = (tempC * 9 / 5) + 32;
|
final tempF = (tempC * 9 / 5) + 32;
|
||||||
return l10n.telemetry_temperatureValue(
|
if (isImperialUnits) {
|
||||||
tempC.toStringAsFixed(1),
|
return '${tempF.toStringAsFixed(1)}°F';
|
||||||
tempF.toStringAsFixed(1),
|
}
|
||||||
);
|
return '${tempC.toStringAsFixed(1)}°C';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,4 +132,14 @@ class AppSettingsService extends ChangeNotifier {
|
|||||||
_settings.copyWith(batteryChemistryByDeviceId: updated),
|
_settings.copyWith(batteryChemistryByDeviceId: updated),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> setUnitSystem(UnitSystem value) async {
|
||||||
|
await updateSettings(_settings.copyWith(unitSystem: value));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setLosUnitSystem(String value) async {
|
||||||
|
await setUnitSystem(
|
||||||
|
value == 'imperial' ? UnitSystem.imperial : UnitSystem.metric,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
import '../connector/meshcore_protocol.dart';
|
import '../connector/meshcore_protocol.dart';
|
||||||
|
|
||||||
class BleDebugLogEntry {
|
class BleDebugLogEntry {
|
||||||
@@ -44,6 +45,7 @@ class BleDebugLogService extends ChangeNotifier {
|
|||||||
static const int maxEntries = 500;
|
static const int maxEntries = 500;
|
||||||
final List<BleDebugLogEntry> _entries = [];
|
final List<BleDebugLogEntry> _entries = [];
|
||||||
final List<BleRawLogRxEntry> _rawLogRxEntries = [];
|
final List<BleRawLogRxEntry> _rawLogRxEntries = [];
|
||||||
|
bool _notifyScheduled = false;
|
||||||
|
|
||||||
List<BleDebugLogEntry> get entries => List.unmodifiable(_entries);
|
List<BleDebugLogEntry> get entries => List.unmodifiable(_entries);
|
||||||
List<BleRawLogRxEntry> get rawLogRxEntries =>
|
List<BleRawLogRxEntry> get rawLogRxEntries =>
|
||||||
@@ -78,13 +80,31 @@ class BleDebugLogService extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
_notifyListenersSafely();
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
_entries.clear();
|
_entries.clear();
|
||||||
_rawLogRxEntries.clear();
|
_rawLogRxEntries.clear();
|
||||||
notifyListeners();
|
_notifyListenersSafely();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _notifyListenersSafely() {
|
||||||
|
final phase = SchedulerBinding.instance.schedulerPhase;
|
||||||
|
final canNotifyNow =
|
||||||
|
phase == SchedulerPhase.idle ||
|
||||||
|
phase == SchedulerPhase.postFrameCallbacks;
|
||||||
|
if (canNotifyNow) {
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_notifyScheduled) return;
|
||||||
|
_notifyScheduled = true;
|
||||||
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_notifyScheduled = false;
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
String _describeFrame(
|
String _describeFrame(
|
||||||
|
|||||||
@@ -0,0 +1,406 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:latlong2/latlong.dart';
|
||||||
|
|
||||||
|
typedef ElevationDataSource =
|
||||||
|
Future<List<double?>> Function(List<LatLng> points);
|
||||||
|
|
||||||
|
class LineOfSightSample {
|
||||||
|
final double distanceMeters;
|
||||||
|
final double terrainMeters;
|
||||||
|
final double lineHeightMeters;
|
||||||
|
final double clearanceMeters;
|
||||||
|
|
||||||
|
const LineOfSightSample({
|
||||||
|
required this.distanceMeters,
|
||||||
|
required this.terrainMeters,
|
||||||
|
required this.lineHeightMeters,
|
||||||
|
required this.clearanceMeters,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class LineOfSightResult {
|
||||||
|
final bool hasData;
|
||||||
|
final bool isClear;
|
||||||
|
final double totalDistanceMeters;
|
||||||
|
final double maxObstructionMeters;
|
||||||
|
final double? firstObstructionDistanceMeters;
|
||||||
|
final List<LineOfSightSample> samples;
|
||||||
|
final String? errorMessage;
|
||||||
|
|
||||||
|
const LineOfSightResult({
|
||||||
|
required this.hasData,
|
||||||
|
required this.isClear,
|
||||||
|
required this.totalDistanceMeters,
|
||||||
|
required this.maxObstructionMeters,
|
||||||
|
required this.firstObstructionDistanceMeters,
|
||||||
|
required this.samples,
|
||||||
|
this.errorMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
const LineOfSightResult.error({
|
||||||
|
required this.totalDistanceMeters,
|
||||||
|
required this.errorMessage,
|
||||||
|
}) : hasData = false,
|
||||||
|
isClear = false,
|
||||||
|
maxObstructionMeters = 0,
|
||||||
|
firstObstructionDistanceMeters = null,
|
||||||
|
samples = const [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class LineOfSightPathSegment {
|
||||||
|
final int index;
|
||||||
|
final LatLng start;
|
||||||
|
final LatLng end;
|
||||||
|
final LineOfSightResult result;
|
||||||
|
|
||||||
|
const LineOfSightPathSegment({
|
||||||
|
required this.index,
|
||||||
|
required this.start,
|
||||||
|
required this.end,
|
||||||
|
required this.result,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class LineOfSightPathResult {
|
||||||
|
final List<LineOfSightPathSegment> segments;
|
||||||
|
final int clearSegments;
|
||||||
|
final int blockedSegments;
|
||||||
|
final int unknownSegments;
|
||||||
|
|
||||||
|
const LineOfSightPathResult({
|
||||||
|
required this.segments,
|
||||||
|
required this.clearSegments,
|
||||||
|
required this.blockedSegments,
|
||||||
|
required this.unknownSegments,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class LineOfSightService {
|
||||||
|
static const String errorElevationUnavailable =
|
||||||
|
'los_error_elevation_unavailable';
|
||||||
|
static const String errorInvalidInput = 'los_error_invalid_input';
|
||||||
|
|
||||||
|
static const double _earthRadiusMeters = 6371000.0;
|
||||||
|
static const Distance _distance = Distance();
|
||||||
|
static const Duration _cacheTtl = Duration(hours: 24);
|
||||||
|
static const int _maxFetchAttempts = 4; // initial try + 3 retries
|
||||||
|
static const Duration _initialBackoff = Duration(milliseconds: 300);
|
||||||
|
|
||||||
|
final http.Client _httpClient;
|
||||||
|
final bool _ownsHttpClient;
|
||||||
|
final ElevationDataSource? _elevationDataSource;
|
||||||
|
final Map<String, _CachedElevation> _elevationCache = {};
|
||||||
|
|
||||||
|
LineOfSightService({
|
||||||
|
http.Client? httpClient,
|
||||||
|
ElevationDataSource? elevationDataSource,
|
||||||
|
}) : _httpClient = httpClient ?? http.Client(),
|
||||||
|
_ownsHttpClient = httpClient == null,
|
||||||
|
_elevationDataSource = elevationDataSource;
|
||||||
|
|
||||||
|
Future<LineOfSightPathResult> analyzePath(
|
||||||
|
List<LatLng> points, {
|
||||||
|
double startAntennaHeightMeters = 1.5,
|
||||||
|
double endAntennaHeightMeters = 1.5,
|
||||||
|
double kFactor = 4.0 / 3.0,
|
||||||
|
double obstructionToleranceMeters = 0.0,
|
||||||
|
}) async {
|
||||||
|
if (points.length < 2) {
|
||||||
|
return const LineOfSightPathResult(
|
||||||
|
segments: [],
|
||||||
|
clearSegments: 0,
|
||||||
|
blockedSegments: 0,
|
||||||
|
unknownSegments: 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final segments = <LineOfSightPathSegment>[];
|
||||||
|
var clearSegments = 0;
|
||||||
|
var blockedSegments = 0;
|
||||||
|
var unknownSegments = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < points.length - 1; i++) {
|
||||||
|
final result = await analyzeLink(
|
||||||
|
points[i],
|
||||||
|
points[i + 1],
|
||||||
|
startAntennaHeightMeters: startAntennaHeightMeters,
|
||||||
|
endAntennaHeightMeters: endAntennaHeightMeters,
|
||||||
|
kFactor: kFactor,
|
||||||
|
obstructionToleranceMeters: obstructionToleranceMeters,
|
||||||
|
);
|
||||||
|
segments.add(
|
||||||
|
LineOfSightPathSegment(
|
||||||
|
index: i,
|
||||||
|
start: points[i],
|
||||||
|
end: points[i + 1],
|
||||||
|
result: result,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!result.hasData) {
|
||||||
|
unknownSegments++;
|
||||||
|
} else if (result.isClear) {
|
||||||
|
clearSegments++;
|
||||||
|
} else {
|
||||||
|
blockedSegments++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return LineOfSightPathResult(
|
||||||
|
segments: segments,
|
||||||
|
clearSegments: clearSegments,
|
||||||
|
blockedSegments: blockedSegments,
|
||||||
|
unknownSegments: unknownSegments,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<LineOfSightResult> analyzeLink(
|
||||||
|
LatLng start,
|
||||||
|
LatLng end, {
|
||||||
|
double startAntennaHeightMeters = 1.5,
|
||||||
|
double endAntennaHeightMeters = 1.5,
|
||||||
|
double kFactor = 4.0 / 3.0,
|
||||||
|
double obstructionToleranceMeters = 0.0,
|
||||||
|
}) async {
|
||||||
|
final totalDistanceMeters = _distance.as(LengthUnit.Meter, start, end);
|
||||||
|
if (totalDistanceMeters <= 1) {
|
||||||
|
return LineOfSightResult(
|
||||||
|
hasData: true,
|
||||||
|
isClear: true,
|
||||||
|
totalDistanceMeters: totalDistanceMeters,
|
||||||
|
maxObstructionMeters: 0,
|
||||||
|
firstObstructionDistanceMeters: null,
|
||||||
|
samples: const [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final samplePoints = _buildSamplePoints(start, end, totalDistanceMeters);
|
||||||
|
final elevations = await _getElevations(samplePoints);
|
||||||
|
|
||||||
|
if (elevations.any((e) => e == null)) {
|
||||||
|
return LineOfSightResult.error(
|
||||||
|
totalDistanceMeters: totalDistanceMeters,
|
||||||
|
errorMessage: errorElevationUnavailable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return computeFromElevations(
|
||||||
|
points: samplePoints,
|
||||||
|
elevations: elevations.cast<double>(),
|
||||||
|
startAntennaHeightMeters: startAntennaHeightMeters,
|
||||||
|
endAntennaHeightMeters: endAntennaHeightMeters,
|
||||||
|
kFactor: kFactor,
|
||||||
|
obstructionToleranceMeters: obstructionToleranceMeters,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LineOfSightResult computeFromElevations({
|
||||||
|
required List<LatLng> points,
|
||||||
|
required List<double> elevations,
|
||||||
|
double startAntennaHeightMeters = 1.5,
|
||||||
|
double endAntennaHeightMeters = 1.5,
|
||||||
|
double kFactor = 4.0 / 3.0,
|
||||||
|
double obstructionToleranceMeters = 0.0,
|
||||||
|
}) {
|
||||||
|
if (points.length < 2 || elevations.length != points.length) {
|
||||||
|
return const LineOfSightResult.error(
|
||||||
|
totalDistanceMeters: 0,
|
||||||
|
errorMessage: errorInvalidInput,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final totalDistanceMeters = _distance.as(
|
||||||
|
LengthUnit.Meter,
|
||||||
|
points.first,
|
||||||
|
points.last,
|
||||||
|
);
|
||||||
|
final effectiveEarthRadius = _earthRadiusMeters * kFactor;
|
||||||
|
final startLineHeight = elevations.first + startAntennaHeightMeters;
|
||||||
|
final endLineHeight = elevations.last + endAntennaHeightMeters;
|
||||||
|
|
||||||
|
var maxObstructionMeters = 0.0;
|
||||||
|
double? firstObstructionDistanceMeters;
|
||||||
|
final samples = <LineOfSightSample>[];
|
||||||
|
var isClear = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < points.length; i++) {
|
||||||
|
final fraction = points.length == 1 ? 0.0 : i / (points.length - 1);
|
||||||
|
final distanceFromStart = totalDistanceMeters * fraction;
|
||||||
|
final lineHeight =
|
||||||
|
startLineHeight + (endLineHeight - startLineHeight) * fraction;
|
||||||
|
|
||||||
|
final earthBulge =
|
||||||
|
(distanceFromStart * (totalDistanceMeters - distanceFromStart)) /
|
||||||
|
(2 * effectiveEarthRadius);
|
||||||
|
final terrainHeight = elevations[i] + earthBulge;
|
||||||
|
final clearance = lineHeight - terrainHeight;
|
||||||
|
|
||||||
|
if (clearance < -obstructionToleranceMeters) {
|
||||||
|
isClear = false;
|
||||||
|
final obstruction = -clearance;
|
||||||
|
if (obstruction > maxObstructionMeters) {
|
||||||
|
maxObstructionMeters = obstruction;
|
||||||
|
}
|
||||||
|
firstObstructionDistanceMeters ??= distanceFromStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
samples.add(
|
||||||
|
LineOfSightSample(
|
||||||
|
distanceMeters: distanceFromStart,
|
||||||
|
terrainMeters: terrainHeight,
|
||||||
|
lineHeightMeters: lineHeight,
|
||||||
|
clearanceMeters: clearance,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LineOfSightResult(
|
||||||
|
hasData: true,
|
||||||
|
isClear: isClear,
|
||||||
|
totalDistanceMeters: totalDistanceMeters,
|
||||||
|
maxObstructionMeters: maxObstructionMeters,
|
||||||
|
firstObstructionDistanceMeters: firstObstructionDistanceMeters,
|
||||||
|
samples: samples,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LatLng> _buildSamplePoints(
|
||||||
|
LatLng start,
|
||||||
|
LatLng end,
|
||||||
|
double distanceMeters,
|
||||||
|
) {
|
||||||
|
final sampleCount = distanceMeters < 2000
|
||||||
|
? 21
|
||||||
|
: distanceMeters < 10000
|
||||||
|
? 41
|
||||||
|
: 81;
|
||||||
|
|
||||||
|
final points = <LatLng>[];
|
||||||
|
for (int i = 0; i < sampleCount; i++) {
|
||||||
|
final t = i / (sampleCount - 1);
|
||||||
|
points.add(
|
||||||
|
LatLng(
|
||||||
|
start.latitude + (end.latitude - start.latitude) * t,
|
||||||
|
start.longitude + (end.longitude - start.longitude) * t,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<double?>> _getElevations(List<LatLng> points) async {
|
||||||
|
final dataSource = _elevationDataSource;
|
||||||
|
if (dataSource != null) {
|
||||||
|
return dataSource(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
final uncached = <int, LatLng>{};
|
||||||
|
final values = List<double?>.filled(points.length, null);
|
||||||
|
for (int i = 0; i < points.length; i++) {
|
||||||
|
final key = _cacheKey(points[i]);
|
||||||
|
final cached = _readCachedValue(key);
|
||||||
|
if (cached != null) {
|
||||||
|
values[i] = cached;
|
||||||
|
} else {
|
||||||
|
uncached[i] = points[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uncached.isEmpty) return values;
|
||||||
|
|
||||||
|
final latCsv = uncached.values
|
||||||
|
.map((p) => p.latitude.toStringAsFixed(6))
|
||||||
|
.join(',');
|
||||||
|
final lonCsv = uncached.values
|
||||||
|
.map((p) => p.longitude.toStringAsFixed(6))
|
||||||
|
.join(',');
|
||||||
|
|
||||||
|
final uri = Uri.parse(
|
||||||
|
'https://api.open-meteo.com/v1/elevation?latitude=$latCsv&longitude=$lonCsv',
|
||||||
|
);
|
||||||
|
|
||||||
|
final response = await _getWithBackoff(uri);
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
final decoded = jsonDecode(response.body);
|
||||||
|
if (decoded is! Map<String, dynamic>) {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
final elevations = decoded['elevation'];
|
||||||
|
if (elevations is! List) {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
final indices = uncached.keys.toList();
|
||||||
|
for (int i = 0; i < min(indices.length, elevations.length); i++) {
|
||||||
|
final value = elevations[i];
|
||||||
|
if (value is! num) continue;
|
||||||
|
final index = indices[i];
|
||||||
|
final elevation = value.toDouble();
|
||||||
|
values[index] = elevation;
|
||||||
|
_elevationCache[_cacheKey(points[index])] = _CachedElevation(
|
||||||
|
value: elevation,
|
||||||
|
expiresAt: DateTime.now().add(_cacheTtl),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<http.Response> _getWithBackoff(Uri uri) async {
|
||||||
|
var attempt = 0;
|
||||||
|
Duration backoff = _initialBackoff;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
attempt++;
|
||||||
|
try {
|
||||||
|
final response = await _httpClient.get(uri);
|
||||||
|
if (!_shouldRetryStatus(response.statusCode) ||
|
||||||
|
attempt >= _maxFetchAttempts) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
if (attempt >= _maxFetchAttempts) rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Future.delayed(backoff);
|
||||||
|
backoff *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _shouldRetryStatus(int statusCode) {
|
||||||
|
return statusCode == 429 || statusCode >= 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
double? _readCachedValue(String key) {
|
||||||
|
final cached = _elevationCache[key];
|
||||||
|
if (cached == null) return null;
|
||||||
|
if (DateTime.now().isAfter(cached.expiresAt)) {
|
||||||
|
_elevationCache.remove(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return cached.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _cacheKey(LatLng point) {
|
||||||
|
return '${point.latitude.toStringAsFixed(5)},${point.longitude.toStringAsFixed(5)}';
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
if (_ownsHttpClient) {
|
||||||
|
_httpClient.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CachedElevation {
|
||||||
|
final double value;
|
||||||
|
final DateTime expiresAt;
|
||||||
|
|
||||||
|
const _CachedElevation({required this.value, required this.expiresAt});
|
||||||
|
}
|
||||||
@@ -58,11 +58,17 @@ class NotificationService {
|
|||||||
requestBadgePermission: true,
|
requestBadgePermission: true,
|
||||||
requestSoundPermission: true,
|
requestSoundPermission: true,
|
||||||
);
|
);
|
||||||
|
const windowsSettings = WindowsInitializationSettings(
|
||||||
|
appName: 'MeshCore Open',
|
||||||
|
appUserModelId: 'org.meshcore.open.app',
|
||||||
|
guid: 'e7ea8f85-72f5-4f36-91f6-038f740ccf86',
|
||||||
|
);
|
||||||
|
|
||||||
const initSettings = InitializationSettings(
|
const initSettings = InitializationSettings(
|
||||||
android: androidSettings,
|
android: androidSettings,
|
||||||
iOS: iosSettings,
|
iOS: iosSettings,
|
||||||
macOS: macSettings,
|
macOS: macSettings,
|
||||||
|
windows: windowsSettings,
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -76,6 +82,13 @@ class NotificationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> _ensureInitialized() async {
|
||||||
|
if (!_isInitialized) {
|
||||||
|
await initialize();
|
||||||
|
}
|
||||||
|
return _isInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> requestPermissions() async {
|
Future<bool> requestPermissions() async {
|
||||||
if (!_isInitialized) {
|
if (!_isInitialized) {
|
||||||
await initialize();
|
await initialize();
|
||||||
@@ -114,9 +127,7 @@ class NotificationService {
|
|||||||
String? contactId,
|
String? contactId,
|
||||||
int? badgeCount,
|
int? badgeCount,
|
||||||
}) async {
|
}) async {
|
||||||
if (!_isInitialized) {
|
if (!await _ensureInitialized()) return;
|
||||||
await initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
final androidDetails = AndroidNotificationDetails(
|
final androidDetails = AndroidNotificationDetails(
|
||||||
'messages',
|
'messages',
|
||||||
@@ -148,13 +159,17 @@ class NotificationService {
|
|||||||
macOS: macDetails,
|
macOS: macDetails,
|
||||||
);
|
);
|
||||||
|
|
||||||
await _notifications.show(
|
try {
|
||||||
id: contactId?.hashCode ?? 0,
|
await _notifications.show(
|
||||||
title: contactName,
|
id: contactId?.hashCode ?? 0,
|
||||||
body: message,
|
title: contactName,
|
||||||
notificationDetails: notificationDetails,
|
body: message,
|
||||||
payload: 'message:$contactId',
|
notificationDetails: notificationDetails,
|
||||||
);
|
payload: 'message:$contactId',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to show message notification: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showAdvertNotificationImpl({
|
Future<void> _showAdvertNotificationImpl({
|
||||||
@@ -162,9 +177,7 @@ class NotificationService {
|
|||||||
required String contactType,
|
required String contactType,
|
||||||
String? contactId,
|
String? contactId,
|
||||||
}) async {
|
}) async {
|
||||||
if (!_isInitialized) {
|
if (!await _ensureInitialized()) return;
|
||||||
await initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
const androidDetails = AndroidNotificationDetails(
|
const androidDetails = AndroidNotificationDetails(
|
||||||
'adverts',
|
'adverts',
|
||||||
@@ -193,13 +206,17 @@ class NotificationService {
|
|||||||
macOS: macDetails,
|
macOS: macDetails,
|
||||||
);
|
);
|
||||||
|
|
||||||
await _notifications.show(
|
try {
|
||||||
id: contactId?.hashCode ?? DateTime.now().millisecondsSinceEpoch,
|
await _notifications.show(
|
||||||
title: _l10n.notification_newTypeDiscovered(contactType),
|
id: contactId?.hashCode ?? DateTime.now().millisecondsSinceEpoch,
|
||||||
body: contactName,
|
title: _l10n.notification_newTypeDiscovered(contactType),
|
||||||
notificationDetails: notificationDetails,
|
body: contactName,
|
||||||
payload: 'advert:$contactId',
|
notificationDetails: notificationDetails,
|
||||||
);
|
payload: 'advert:$contactId',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to show advert notification: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showChannelMessageNotificationImpl({
|
Future<void> _showChannelMessageNotificationImpl({
|
||||||
@@ -208,9 +225,7 @@ class NotificationService {
|
|||||||
int? channelIndex,
|
int? channelIndex,
|
||||||
int? badgeCount,
|
int? badgeCount,
|
||||||
}) async {
|
}) async {
|
||||||
if (!_isInitialized) {
|
if (!await _ensureInitialized()) return;
|
||||||
await initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
final androidDetails = AndroidNotificationDetails(
|
final androidDetails = AndroidNotificationDetails(
|
||||||
'channel_messages',
|
'channel_messages',
|
||||||
@@ -247,13 +262,17 @@ class NotificationService {
|
|||||||
? _l10n.notification_receivedNewMessage
|
? _l10n.notification_receivedNewMessage
|
||||||
: preview;
|
: preview;
|
||||||
|
|
||||||
await _notifications.show(
|
try {
|
||||||
id: channelIndex?.hashCode ?? DateTime.now().millisecondsSinceEpoch,
|
await _notifications.show(
|
||||||
title: channelName,
|
id: channelIndex?.hashCode ?? DateTime.now().millisecondsSinceEpoch,
|
||||||
body: body,
|
title: channelName,
|
||||||
notificationDetails: notificationDetails,
|
body: body,
|
||||||
payload: 'channel:$channelIndex',
|
notificationDetails: notificationDetails,
|
||||||
);
|
payload: 'channel:$channelIndex',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to show channel notification: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a privacy-safe identifier for debug logging.
|
/// Returns a privacy-safe identifier for debug logging.
|
||||||
@@ -396,35 +415,39 @@ class NotificationService {
|
|||||||
Future<void> _showNotificationImmediately(
|
Future<void> _showNotificationImmediately(
|
||||||
_PendingNotification notification,
|
_PendingNotification notification,
|
||||||
) async {
|
) async {
|
||||||
switch (notification.type) {
|
try {
|
||||||
case _NotificationType.message:
|
switch (notification.type) {
|
||||||
await _showMessageNotificationImpl(
|
case _NotificationType.message:
|
||||||
contactName: notification.title,
|
await _showMessageNotificationImpl(
|
||||||
message: notification.body,
|
contactName: notification.title,
|
||||||
contactId: notification.id,
|
message: notification.body,
|
||||||
badgeCount: notification.badgeCount,
|
contactId: notification.id,
|
||||||
);
|
badgeCount: notification.badgeCount,
|
||||||
break;
|
);
|
||||||
case _NotificationType.advert:
|
break;
|
||||||
await _showAdvertNotificationImpl(
|
case _NotificationType.advert:
|
||||||
contactName: notification.body,
|
await _showAdvertNotificationImpl(
|
||||||
contactType: notification.title,
|
contactName: notification.body,
|
||||||
contactId: notification.id,
|
contactType: notification.title,
|
||||||
);
|
contactId: notification.id,
|
||||||
break;
|
);
|
||||||
case _NotificationType.channelMessage:
|
break;
|
||||||
await _showChannelMessageNotificationImpl(
|
case _NotificationType.channelMessage:
|
||||||
channelName: notification.title,
|
await _showChannelMessageNotificationImpl(
|
||||||
message: notification.body,
|
channelName: notification.title,
|
||||||
channelIndex: int.tryParse(notification.id ?? ''),
|
message: notification.body,
|
||||||
badgeCount: notification.badgeCount,
|
channelIndex: int.tryParse(notification.id ?? ''),
|
||||||
);
|
badgeCount: notification.badgeCount,
|
||||||
break;
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to show immediate notification: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showBatchSummary(List<_PendingNotification> batch) async {
|
Future<void> _showBatchSummary(List<_PendingNotification> batch) async {
|
||||||
if (!_isInitialized) await initialize();
|
if (!await _ensureInitialized()) return;
|
||||||
|
|
||||||
// Group by type
|
// Group by type
|
||||||
final messages = batch
|
final messages = batch
|
||||||
@@ -468,13 +491,17 @@ class NotificationService {
|
|||||||
|
|
||||||
const notificationDetails = NotificationDetails(android: androidDetails);
|
const notificationDetails = NotificationDetails(android: androidDetails);
|
||||||
|
|
||||||
await _notifications.show(
|
try {
|
||||||
id: 'batch_summary'.hashCode,
|
await _notifications.show(
|
||||||
title: _l10n.notification_activityTitle,
|
id: 'batch_summary'.hashCode,
|
||||||
body: parts.join(', '),
|
title: _l10n.notification_activityTitle,
|
||||||
notificationDetails: notificationDetails,
|
body: parts.join(', '),
|
||||||
payload: 'batch',
|
notificationDetails: notificationDetails,
|
||||||
);
|
payload: 'batch',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to show batch summary notification: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AdaptiveAppBarTitle extends StatelessWidget {
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
const AdaptiveAppBarTitle(this.text, {super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, constraints) => SizedBox(
|
||||||
|
width: constraints.maxWidth,
|
||||||
|
child: FittedBox(fit: BoxFit.scaleDown, child: Text(text, maxLines: 1)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:latlong2/latlong.dart';
|
||||||
|
import 'package:meshcore_open/services/line_of_sight_service.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
List<LatLng> makePoints(int count) {
|
||||||
|
return List<LatLng>.generate(count, (i) => LatLng(0, i * 0.00001));
|
||||||
|
}
|
||||||
|
|
||||||
|
test('computeFromElevations reports clear LOS on flat terrain', () {
|
||||||
|
final points = makePoints(21);
|
||||||
|
final elevations = List<double>.filled(points.length, 100);
|
||||||
|
|
||||||
|
final result = LineOfSightService.computeFromElevations(
|
||||||
|
points: points,
|
||||||
|
elevations: elevations,
|
||||||
|
startAntennaHeightMeters: 2,
|
||||||
|
endAntennaHeightMeters: 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.hasData, isTrue);
|
||||||
|
expect(result.isClear, isTrue);
|
||||||
|
expect(result.maxObstructionMeters, equals(0));
|
||||||
|
expect(result.firstObstructionDistanceMeters, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'computeFromElevations reports blocked LOS with central obstruction',
|
||||||
|
() {
|
||||||
|
final points = makePoints(21);
|
||||||
|
final elevations = List<double>.filled(points.length, 100);
|
||||||
|
elevations[10] = 300;
|
||||||
|
|
||||||
|
final result = LineOfSightService.computeFromElevations(
|
||||||
|
points: points,
|
||||||
|
elevations: elevations,
|
||||||
|
startAntennaHeightMeters: 1.5,
|
||||||
|
endAntennaHeightMeters: 1.5,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.hasData, isTrue);
|
||||||
|
expect(result.isClear, isFalse);
|
||||||
|
expect(result.maxObstructionMeters, greaterThan(0));
|
||||||
|
expect(result.firstObstructionDistanceMeters, isNotNull);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test('analyzePath summarizes clear and blocked segments', () async {
|
||||||
|
final service = LineOfSightService(
|
||||||
|
elevationDataSource: (points) async {
|
||||||
|
final elevations = List<double?>.filled(points.length, 100);
|
||||||
|
if (points.first.longitude > 0.00005) {
|
||||||
|
elevations[elevations.length ~/ 2] = 300;
|
||||||
|
}
|
||||||
|
return elevations;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final path = [
|
||||||
|
const LatLng(0, 0),
|
||||||
|
const LatLng(0, 0.0001),
|
||||||
|
const LatLng(0, 0.0002),
|
||||||
|
];
|
||||||
|
|
||||||
|
final result = await service.analyzePath(path);
|
||||||
|
|
||||||
|
expect(result.segments.length, 2);
|
||||||
|
expect(result.clearSegments, 1);
|
||||||
|
expect(result.blockedSegments, 1);
|
||||||
|
expect(result.unknownSegments, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user