diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 6fdfdd7f..d6927e7f 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -11,6 +11,12 @@ "state" : "translated", "value" : "\t%@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "\t%@" + } } } }, @@ -21,6 +27,12 @@ "state" : "translated", "value" : "%@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : " %@" + } } } }, @@ -31,6 +43,12 @@ "state" : "translated", "value" : "%@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : " %@" + } } } }, @@ -41,6 +59,12 @@ "state" : "translated", "value" : "Да ли желите да користите режим INPUT_PULLUP за GPIO пин. Применљиво само ако плоча користи pull-up отпорнике на пиновима" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "是否为 GPIO 引脚使用输入上拉模式。仅适用于电路板在引脚上使用上拉电阻的情况" + } } } }, @@ -51,6 +75,12 @@ "state" : "translated", "value" : ": %@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : ": %@" + } } } }, @@ -61,6 +91,12 @@ "state" : "translated", "value" : ": %d" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : ": %d" + } } } }, @@ -71,6 +107,12 @@ "state" : "translated", "value" : "(Поново)дефинишите PIN_GPS_EN за своју плочу." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "为你的电路板重新定义 PIN_GPS_EN" + } } } }, @@ -81,6 +123,12 @@ "state" : "translated", "value" : "%@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } } } }, @@ -97,6 +145,12 @@ "state" : "translated", "value" : "%1$@ - %2$@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ - %2$@" + } } } }, @@ -113,6 +167,12 @@ "state" : "translated", "value" : "%1$@ - %2$@ - %3$@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ - %2$@ - %3$@" + } } } }, @@ -129,6 +189,46 @@ "state" : "translated", "value" : "%1$@ - %2$@ Одлазних скокова %3$@ Долазних скокова" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ - %2$@ Towards %3$@ Back" + } + } + } + }, + "%@ - 1 Hop" : { + "extractionState" : "stale", + "localizations" : { + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ - 1 Скок" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ - 1 跳" + } + } + } + }, + "%@ - Direct" : { + "extractionState" : "stale", + "localizations" : { + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ - Директно" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ - 直接" + } } } }, @@ -145,6 +245,12 @@ "state" : "translated", "value" : "%@ - Нема одговора" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ - 没有响应" + } } } }, @@ -161,6 +267,12 @@ "state" : "translated", "value" : "%@ - Није послато" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ - 未发送" + } } } }, @@ -177,6 +289,12 @@ "state" : "translated", "value" : "%1$@ (%2$@)" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ (%2$@)" + } } } }, @@ -193,6 +311,12 @@ "state" : "translated", "value" : "%1$@ %2$@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@" + } } } }, @@ -209,6 +333,12 @@ "state" : "translated", "value" : "%1$@ %2$lld" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$lld" + } } } }, @@ -225,6 +355,12 @@ "state" : "translated", "value" : "%@ удаљено" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ 离开" + } } } }, @@ -241,6 +377,12 @@ "state" : "translated", "value" : "%1$@ може имати до %2$@ бајтова." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ 的长度可达 %2$@ 字节" + } } } }, @@ -251,6 +393,12 @@ "state" : "translated", "value" : "%@ Канали?" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ 频道?" + } } } }, @@ -261,6 +409,12 @@ "state" : "translated", "value" : "%@ конфигурациони подаци су затражени преко административног канала, али никакав одговор није враћен са удаљеног чвора." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已通过管理频道请求 %@ 配置数据,但远程节点未返回任何响应。" + } } } }, @@ -271,6 +425,29 @@ "state" : "translated", "value" : "%@ dB" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ dB" + } + } + } + }, + "%@ hPa" : { + "extractionState" : "stale", + "localizations" : { + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ hPa" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ hPa" + } } } }, @@ -287,6 +464,12 @@ "state" : "translated", "value" : "%1$@, %2$@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@" + } } } }, @@ -303,6 +486,12 @@ "state" : "translated", "value" : "%1$@: %2$lld / %3$lld" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@: %2$lld / %3$lld" + } } } }, @@ -313,6 +502,12 @@ "state" : "translated", "value" : "%@%%" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@%%" + } } } }, @@ -323,6 +518,12 @@ "state" : "translated", "value" : "%@°F" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@°F" + } } } }, @@ -333,6 +534,12 @@ "state" : "translated", "value" : "%d" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d" + } } } }, @@ -391,6 +598,24 @@ } } } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "new", + "value" : "%d Hops" + } + }, + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "Direct" + } + } + } + } } } }, @@ -401,6 +626,12 @@ "state" : "translated", "value" : "%d%%" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d%%" + } } } }, @@ -411,6 +642,12 @@ "state" : "translated", "value" : "%lf" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lf" + } } } }, @@ -421,6 +658,12 @@ "state" : "translated", "value" : "%lld" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" + } } } }, @@ -467,6 +710,12 @@ "state" : "translated", "value" : "%lld%%" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld%%" + } } } }, @@ -483,6 +732,12 @@ "state" : "translated", "value" : "%llddb снага преноса" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "发射功率 %llddb" + } } } }, @@ -509,6 +764,12 @@ "state" : "translated", "value" : "< 1%" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "< 1%" + } } } }, @@ -529,6 +790,12 @@ "state" : "translated", "value" : "1 byte" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 byte" + } } } }, @@ -539,6 +806,12 @@ "state" : "translated", "value" : "1 hop away" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "1 跳" + } } } }, @@ -566,6 +839,12 @@ "state" : "translated", "value" : "7" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "7" + } } } }, @@ -576,6 +855,12 @@ "state" : "translated", "value" : "8" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "8" + } } } }, @@ -586,6 +871,12 @@ "state" : "translated", "value" : "25" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "25" + } } } }, @@ -596,6 +887,12 @@ "state" : "translated", "value" : "50" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "50" + } } } }, @@ -606,6 +903,12 @@ "state" : "translated", "value" : "75" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "75" + } } } }, @@ -616,6 +919,12 @@ "state" : "translated", "value" : "100" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "100" + } } } }, @@ -626,6 +935,12 @@ "state" : "translated", "value" : "128 bit" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "128 bit" + } } } }, @@ -636,6 +951,12 @@ "state" : "translated", "value" : "256 bit" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "256 bit" + } } } }, @@ -978,6 +1299,12 @@ "state" : "translated", "value" : "Додатна помоћ" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "其他帮助" + } } } }, @@ -988,6 +1315,12 @@ "state" : "translated", "value" : "Адреса" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "地址" + } } } }, @@ -1058,11 +1391,23 @@ }, "Admin & Direct Message Keys" : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schlüssel für Administrator und Direktnachrichten" + } + }, "sr" : { "stringUnit" : { "state" : "translated", "value" : "Админ и кључеви директних порука" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "管理员 & 私信密钥" + } } } }, @@ -1139,6 +1484,12 @@ "state" : "translated", "value" : "Администрација" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "管理员" + } } } }, @@ -1149,6 +1500,12 @@ "state" : "translated", "value" : "Напредно" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "高级" + } } } }, @@ -1159,6 +1516,12 @@ "state" : "translated", "value" : "Напредне поставке GPS уређаја" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "高级设备 GPS" + } } } }, @@ -1169,6 +1532,12 @@ "state" : "translated", "value" : "Напредне GPIO опције" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "高级 GPIO 选项" + } } } }, @@ -1179,6 +1548,12 @@ "state" : "translated", "value" : "Напредне поставке позиционих заставица" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "高级位置标志" + } } } }, @@ -1319,6 +1694,12 @@ "state" : "translated", "value" : "Време емитовања" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "广播时间" + } } } }, @@ -1329,6 +1710,12 @@ "state" : "translated", "value" : "Време емитовања %@%%" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "广播时间 %@%%" + } } } }, @@ -1349,6 +1736,12 @@ "state" : "translated", "value" : "Упозорите GPIO зујалицу када примите звоно" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "收到铃声时发出警报 GPIO 蜂鸣器" + } } } }, @@ -1369,6 +1762,12 @@ "state" : "translated", "value" : "Упозорите GPIO вибра мотор када примите звоно" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "收到铃声时提醒 GPIO 振动电机" + } } } }, @@ -1389,6 +1788,12 @@ "state" : "translated", "value" : "Упозори када примиш звоно" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "收到铃声时发出警报" + } } } }, @@ -1415,6 +1820,12 @@ "state" : "translated", "value" : "Сви" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "全部" + } } } }, @@ -1425,11 +1836,23 @@ "state" : "translated", "value" : "Сви подаци о уређају и апликацији ће бити избрисани." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "所有设备以及 App 数据都会被删除。" + } } } }, "Allow incoming device control over the insecure legacy admin channel." : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erlaubt die eingehende Gerätesteuerung über den unsicheren Legacy-Admin-Kanal." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -1732,6 +2155,12 @@ "state" : "translated", "value" : "Отворена, off-grid, децентрализована, меш мрежа која ради на приступачним радио уређајима мале снаге." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "这是一个开源、离网、分布式 Mesh 网络,可在价格低廉的低功率无线电设备上运行。" + } } } }, @@ -1742,6 +2171,12 @@ "state" : "translated", "value" : "Све пропуштене поруке ће бити поново испоручене." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "任何错过的信息都会再次发送。" + } } } }, @@ -1752,6 +2187,12 @@ "state" : "translated", "value" : "Подаци апликације" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "App 数据" + } } } }, @@ -1762,6 +2203,12 @@ "state" : "translated", "value" : "Фајлови апликације" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "App 文件" + } } } }, @@ -1772,6 +2219,12 @@ "state" : "translated", "value" : "Подешавања апликације" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "App 设置" + } } } }, @@ -1782,6 +2235,12 @@ "state" : "translated", "value" : "Епл апликације" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Apps" + } } } }, @@ -1854,7 +2313,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "通用设置" + "value" : "App 设置" } }, "zh-Hant-TW" : { @@ -2067,6 +2526,12 @@ "state" : "translated", "value" : "Да ли си сигуран да желиш да обришеш ову поруку?" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "你确定删除这条消息么?" + } } } }, @@ -2083,6 +2548,12 @@ "state" : "translated", "value" : "Да ли си стигуран да желиш да вратиш овај чвор на фабричка подешавања?" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "你确定要初始化这个节点么?" + } } } }, @@ -2304,6 +2775,12 @@ "state" : "translated", "value" : "Аутоматски се пребацује на следећу страницу на екрану као карусел, на основу наведеног интервала." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "根据指定的时间间隔,像旋转木马一样自动切换到屏幕上的下一页。" + } } } }, @@ -2314,6 +2791,12 @@ "state" : "translated", "value" : "Доступна унапред подешена подешавања модема, подразумевана је Long Fast." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "可用的调制解调器预置,默认为 “Long Fast”。" + } } } }, @@ -2388,6 +2871,29 @@ "state" : "translated", "value" : "Резервна база података" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "备份数据库" + } + } + } + }, + "Bad" : { + "extractionState" : "stale", + "localizations" : { + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Лош" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "坏" + } } } }, @@ -2404,6 +2910,12 @@ "state" : "translated", "value" : "Проток" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "带宽" + } } } }, @@ -2414,6 +2926,12 @@ "state" : "translated", "value" : "Bar" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bar" + } } } }, @@ -2424,6 +2942,12 @@ "state" : "translated", "value" : "Bar серија" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bar Series" + } } } }, @@ -2434,6 +2958,12 @@ "state" : "translated", "value" : "Барометарски притисак" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "气压" + } } } }, @@ -2444,6 +2974,12 @@ "state" : "translated", "value" : "Ниво батерије у %" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "电池电量 %" + } } } }, @@ -2518,6 +3054,12 @@ "state" : "translated", "value" : "Baud" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "波特率" + } } } }, @@ -2528,6 +3070,12 @@ "state" : "translated", "value" : "BLE RSSI: %lld" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "BLE RSSI: %lld" + } } } }, @@ -2538,6 +3086,12 @@ "state" : "translated", "value" : "BLE: %@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "BLE: %@" + } } } }, @@ -2595,7 +3149,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "尝试连接%d失败,你可能需要在系统设置的蓝牙选项中忽略该电台。" + "value" : "尝试连接%d失败,你可能需要在系统设置的蓝牙选项中忽略该设备。" } }, "zh-Hant-TW" : { @@ -2620,6 +3174,12 @@ "state" : "translated", "value" : "Веза је неочекивано истекла." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接意外超时。" + } } } }, @@ -2677,7 +3237,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "%@ 如果在首选电台的旁边,App 将会自动重连。" + "value" : "%@ 如果在默认电台的旁边,App 将会自动重连。" } }, "zh-Hant-TW" : { @@ -2702,6 +3262,12 @@ "state" : "translated", "value" : "Радио уређај је уклонио информације о упаривању." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "同伴删除了配对信息。" + } } } }, @@ -3420,6 +3986,12 @@ "state" : "translated", "value" : "Интервал емитовања" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "广播间隔" + } } } }, @@ -3430,6 +4002,12 @@ "state" : "translated", "value" : "Дугме GPIO" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "按钮 GPIO" + } } } }, @@ -3440,6 +4018,12 @@ "state" : "translated", "value" : "Купите готове радио уређаје" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "购买完整的电台" + } } } }, @@ -3450,6 +4034,12 @@ "state" : "translated", "value" : "Звучни сигнал GPIO" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "蜂鸣器 GPIO" + } } } }, @@ -3525,6 +4115,12 @@ "state" : "translated", "value" : "Позивни знак" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "呼号" + } } } }, @@ -3535,6 +4131,12 @@ "state" : "translated", "value" : "Позивни знак не може бити празан" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "呼号不能为空" + } } } }, @@ -3615,6 +4217,12 @@ "state" : "translated", "value" : "Откажи" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "取消" + } } } }, @@ -3800,7 +4408,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "M5Stack 卡片键盘 / RAK 键盘" + "value" : "M5 Stack 卡片键盘 / RAK 键盘" } }, "zh-Hant-TW" : { @@ -3930,7 +4538,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "RAK 旋转编码器" + "value" : "RAK 编码器" } }, "zh-Hant-TW" : { @@ -3964,6 +4572,12 @@ "state" : "translated", "value" : "Категорије" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "分类" + } } } }, @@ -4044,6 +4658,12 @@ "state" : "translated", "value" : "Канал" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "频道" + } } } }, @@ -4054,6 +4674,12 @@ "state" : "translated", "value" : "Канал 0 укључен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "包含频道 0" + } } } }, @@ -4064,6 +4690,12 @@ "state" : "translated", "value" : "Канал 1 укључен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "包含频道 1" + } } } }, @@ -4074,6 +4706,12 @@ "state" : "translated", "value" : "Канал 2 укључен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "包含频道 2" + } } } }, @@ -4084,6 +4722,12 @@ "state" : "translated", "value" : "Канал 3 укључен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "包含频道 3" + } } } }, @@ -4094,6 +4738,12 @@ "state" : "translated", "value" : "Канал 4 укључен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "包含频道 4" + } } } }, @@ -4104,6 +4754,12 @@ "state" : "translated", "value" : "Канал 5 укључен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "包含频道 5" + } } } }, @@ -4114,6 +4770,12 @@ "state" : "translated", "value" : "Канал 6 укључен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "包含频道 6" + } } } }, @@ -4124,6 +4786,12 @@ "state" : "translated", "value" : "Канал 7 укључен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "包含频道 7" + } } } }, @@ -4134,6 +4802,12 @@ "state" : "translated", "value" : "детаљи канала" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "频道详情" + } } } }, @@ -4144,6 +4818,12 @@ "state" : "translated", "value" : "Назив канала" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "频道名称" + } } } }, @@ -4154,6 +4834,12 @@ "state" : "translated", "value" : "Број канала мора бити између 0 и 7." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "频道编号必须介于 0 和 7 之间。" + } } } }, @@ -4164,6 +4850,12 @@ "state" : "translated", "value" : "Улога канала" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "频道角色" + } } } }, @@ -4174,6 +4866,12 @@ "state" : "translated", "value" : "Искоришћеност канала %@%%" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "频道利用率 %@%% " + } } } }, @@ -4507,6 +5205,12 @@ "state" : "translated", "value" : "Канали који се додају из КР кода нису сачувани. Приликом додавања канала имена морају бити јединствена." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "通过二维码添加的频道无法保存。添加频道时,名称必须唯一。" + } } } }, @@ -4517,6 +5221,12 @@ "state" : "translated", "value" : "Графукон" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "图表" + } } } }, @@ -4544,6 +5254,12 @@ "state" : "translated", "value" : "Кина" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "CHG" + } } } }, @@ -4554,6 +5270,12 @@ "state" : "translated", "value" : "Очисти" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "清除" + } } } }, @@ -4692,6 +5414,12 @@ "state" : "translated", "value" : "Клијент" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端" + } } } }, @@ -4702,6 +5430,12 @@ "state" : "translated", "value" : "Историја клијената" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端历史" + } } } }, @@ -4712,6 +5446,12 @@ "state" : "translated", "value" : "Захтев за историју клијента је послат" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已发送客户端历史记录请求" + } } } }, @@ -4722,6 +5462,12 @@ "state" : "translated", "value" : "Опције клијента" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端选项" + } } } }, @@ -4732,6 +5478,12 @@ "state" : "translated", "value" : "Ротациони догађај у смеру казаљке на сату" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "顺时针旋转活动" + } } } }, @@ -4806,6 +5558,12 @@ "state" : "translated", "value" : "Стопа кодирања" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "编码率" + } } } }, @@ -4822,6 +5580,12 @@ "state" : "translated", "value" : "Боја" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "颜色" + } } } }, @@ -4878,7 +5642,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "与电台进行通讯中..." + "value" : "与设备进行通讯中..." } }, "zh-Hant-TW" : { @@ -4896,6 +5660,12 @@ "state" : "translated", "value" : "Конфигурација" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "配置" + } } } }, @@ -5233,12 +6003,6 @@ "value" : "Мултипликатор" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "ADC 放大" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -5291,12 +6055,6 @@ "value" : "Преписивање ADC-а" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "ADC 修改" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -5510,7 +6268,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "尽可能将所有部件都置于休眠状态,对于跟踪器和传感器功能,这还包括 LoRa 无线电。如果您要使用手机应用程序或者使用没有用户按钮的设备,请不要使用这个设置。" + "value" : "尽可能让所有设备处于睡眠状态,对于跟踪器和传感器来说,这也包括 LoRa 无线电。如果您想将电台与手机 App 一起使用,或使用没有用户按钮的电台,请不要使用此设置。" } }, "zh-Hant-TW" : { @@ -5740,12 +6498,6 @@ "value" : "Након" } }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "After" - } - }, "zh-Hant-TW" : { "stringUnit" : { "state" : "translated", @@ -6203,7 +6955,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "电台将会在配置保存后重启。" + "value" : "节点将会在保存配置后重启。" } }, "zh-Hant-TW" : { @@ -6216,6 +6968,12 @@ }, "Configuration for: %@" : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfiguration für: %@" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -6231,6 +6989,12 @@ "state" : "translated", "value" : "Унапред подешене конфигурације" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "配置预设" + } } } }, @@ -6319,7 +7083,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "已连接到电台" + "value" : "蓝牙已连接" } }, "zh-Hant-TW" : { @@ -6399,7 +7163,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "已连接的电台" + "value" : "电台已连接" } }, "zh-Hant-TW" : { @@ -6487,6 +7251,12 @@ "state" : "translated", "value" : "Покушај повезивања %lld од 10" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接尝试 %lld,共 10 次" + } } } }, @@ -6627,6 +7397,12 @@ "state" : "translated", "value" : "Тип контроле" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "控制类型" + } } } }, @@ -6637,6 +7413,12 @@ "state" : "translated", "value" : "Контролише трептајући ЛЕД на уређају. За већину уређаја ово ће контролисати један од до максималних 4 ЛЕД, ЛЕД пуњења и ГПС ЛЕД диоде се не могу контролисати." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "控制设备上闪烁的 LED。 对大多数设备而言,这将控制最多 4 个 LED 中的一个,充电指示灯和 GPS 状态灯无法控制。" + } } } }, @@ -6691,6 +7473,12 @@ "state" : "translated", "value" : "Координате %1$@, %2$@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "坐标 %1$@, %2$@" + } } } }, @@ -6787,6 +7575,12 @@ "state" : "translated", "value" : "Није могуће наћи чвор" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法找到节点" + } } } }, @@ -6845,6 +7639,12 @@ "state" : "translated", "value" : "Тренутна верзија фирмвера: %@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "当前固件版本号:%@" + } } } }, @@ -6867,6 +7667,12 @@ "state" : "translated", "value" : "Тренутна верзија фирмвера: %1$@, најновија верзија фирмвера: %2$@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "当前固件版本号:%1$@,最新固件版本号:%2$@" + } } } }, @@ -6893,6 +7699,12 @@ "state" : "translated", "value" : "Тренутно препоручени начин за ажурирање ЕСП32 уређаја је коришћење веб флешера на десктоп рачунару из прегледача заснованог на хрому. Не ради на мобилним уређајима или преко BLE-а." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "目前,更新 ESP32 设备的推荐方法是在电脑上使用基于 Chrome 浏览器的 Web Flasher。该方法不适用于移动设备或通过 BLE 进行更新。" + } } } }, @@ -6919,11 +7731,23 @@ "state" : "translated", "value" : "Дебагуј" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Debug" + } } } }, "Debug Logs" : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fehlersuchprotokolle" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -7037,6 +7861,12 @@ "state" : "translated", "value" : "Подразумевани изглед екрана 128x64" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "默认 128x64 屏幕布局" + } } } }, @@ -7111,6 +7941,12 @@ "state" : "translated", "value" : "Желите ли да избришете све показатеље окружења?" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "删除所有环境指标?" + } } } }, @@ -7121,6 +7957,12 @@ "state" : "translated", "value" : "Избрисати све плочице мапе?" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "删除所有瓦片地图?" + } } } }, @@ -7203,6 +8045,12 @@ "state" : "translated", "value" : "Опис мора бити испод 100 бајтова" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "描述必须少于 100 字节" + } } } }, @@ -7243,6 +8091,12 @@ "state" : "translated", "value" : "Поруке сензора за откривање се примају као текстуалне поруке. Ако омогућите обавештења, добићете обавештење за сваку примљену поруку за откривање и одговарајућу значку непрочитане поруке." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "检测传感器信息以文本信息的形式接收。如果启用通知功能,则每收到一条检测信息都会收到一条通知,并显示相应的未读信息。" + } } } }, @@ -7347,6 +8201,12 @@ "state" : "translated", "value" : "Подешавања ензора откривања" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "检测传感器配置" + } } } }, @@ -7387,6 +8247,12 @@ "state" : "translated", "value" : "Логови сензора откривања" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "检测传感器日志" + } } } }, @@ -7397,6 +8263,12 @@ "state" : "translated", "value" : "Програмери" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "开发者" + } } } }, @@ -7453,7 +8325,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "电台" + "value" : "设备" } }, "zh-Hant-TW" : { @@ -7477,6 +8349,12 @@ "state" : "translated", "value" : "GPS уређај" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备 GPS" + } } } }, @@ -7487,6 +8365,12 @@ "state" : "translated", "value" : "Уређајем управља администратор мреже, корисник не може да приступи ниједном подешавању уређаја." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备由 Mesh 管理员管理,用户无法访问任何设备设置。" + } } } }, @@ -7497,6 +8381,12 @@ "state" : "translated", "value" : "Метрика уређаја" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备指标" + } } } }, @@ -7507,6 +8397,12 @@ "state" : "translated", "value" : "Логови метрике уређаја" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备指标日志" + } } } }, @@ -7523,6 +8419,12 @@ "state" : "translated", "value" : "Модел уређаја: %@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备模型:%@" + } } } }, @@ -7533,6 +8435,12 @@ "state" : "translated", "value" : "Улога уређаја" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备角色" + } } } }, @@ -7543,6 +8451,12 @@ "state" : "translated", "value" : "Екран уређаја" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备屏幕" + } } } }, @@ -7599,7 +8513,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "电台配置" + "value" : "设备配置" } }, "zh-Hant-TW" : { @@ -7715,7 +8629,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "删除所有电台指标?" + "value" : "删除所有设备指标?" } }, "zh-Hant-TW" : { @@ -7779,7 +8693,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "电台指标日志" + "value" : "设备指标日志" } }, "zh-Hant-TW" : { @@ -7844,7 +8758,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "标准模式 - App 可以连接到电台进行收发操作,并且会自动转发 Mesh 网络中其他节点的消息。" + "value" : "连接 App 或独立的消息发送设备。" } }, "zh-Hant-TW" : { @@ -7861,7 +8775,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : " Used for nodes that \"only speak when spoken to\" Turns all of the routine broadcasts but allows for ad-hoc communication. Still rebroadcasts, but with local only rebroadcast mode (known meshes only). Can be used for private operation or to dramatically reduce airtime / power consumption." + "value" : "Gerät, das nur bei Bedarf sendet, um nicht entdeckt zu werden oder Strom zu sparen." } }, "en" : { @@ -7909,7 +8823,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "用于\"只有在被请求时才发言\"的节点。关闭所有常规广播,但允许临时通信。仍然会进行转发,但采用本地转发模式(仅限已知的网络)。可用于私密操作或大幅减少空中时间/功耗。" + "value" : "只在需要时才广播的设备,以达到隐蔽或省电的目的。" } }, "zh-Hant-TW" : { @@ -7926,7 +8840,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Dasselbe wie Client, außer dass die Pakete nicht über diesen Knoten weitergeleitet werden. Nimmt nicht am Mesh-Routing teil." + "value" : "Gerät, das keine Pakete von anderen Geräten weiterleitet." } }, "en" : { @@ -7974,7 +8888,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "静默模式 - 与标准模式类似,App 可以连接到电台进行收发操作,但不会转发 Mesh 网络中其他节点的消息。" + "value" : "不转发其他设备数据包的设备。" } }, "zh-Hant-TW" : { @@ -7991,7 +8905,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Broadcasts location as message to default channel regularly for to assist with device recovery." + "value" : "Sendet den Standort regelmäßig als Nachricht an den Standardkanal, um die Suche nach dem Gerät zu unterstützen." } }, "en" : { @@ -8039,7 +8953,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "定期向默认通道广播位置信息,以帮助寻回设备。" + "value" : "定期向默认信道发送位置信息,以协助设备恢复。" } }, "zh-Hant-TW" : { @@ -8053,6 +8967,12 @@ "device.role.name.client" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8064,12 +8984,24 @@ "state" : "translated", "value" : "Клијент" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端" + } } } }, "device.role.name.clientHidden" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client - Versteckt" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8081,6 +9013,12 @@ "state" : "translated", "value" : "Скривени клијент" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端隐藏" + } } } }, @@ -8098,12 +9036,24 @@ "state" : "translated", "value" : "Клијент мутиран" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端静默" + } } } }, "device.role.name.lostAndFound" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tracker" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8115,12 +9065,24 @@ "state" : "translated", "value" : "Изгубљено и нађено" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "失物招领" + } } } }, "device.role.name.repeater" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repeater" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8132,12 +9094,24 @@ "state" : "translated", "value" : "Поновљач" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "中继" + } } } }, "device.role.name.router" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Router" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8149,12 +9123,24 @@ "state" : "translated", "value" : "Рутер" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "路由" + } } } }, "device.role.name.routerClient" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Router & Client" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8166,12 +9152,24 @@ "state" : "translated", "value" : "Рутер и клијент" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "路由 & 客户端" + } } } }, "device.role.name.routerlate" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Router mit Verzögerung" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8183,6 +9181,12 @@ "device.role.name.sensor" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sensor" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8194,12 +9198,24 @@ "state" : "translated", "value" : "Сензор" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "传感器" + } } } }, "device.role.name.tak" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "TAK" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8211,12 +9227,24 @@ "state" : "translated", "value" : "TAK" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "TAK" + } } } }, "device.role.name.takTracker" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "TAK Tracker" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8228,12 +9256,24 @@ "state" : "translated", "value" : "ТАК Трекер" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "TAK 追踪器" + } } } }, "device.role.name.tracker" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tracker" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8245,6 +9285,12 @@ "state" : "translated", "value" : "Трекер" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "追踪器" + } } } }, @@ -8301,8 +9347,8 @@ }, "zh-Hans" : { "stringUnit" : { - "state" : "needs_review", - "value" : "中继模式 - Mesh 网络数据包将优先通过此节点路由。此模式可消除不必要的开销,如节点信息、设备遥测和任何其他 Mesh 数据包,从而使设备不显示为 Mesh 网络的一部分。有关此角色的其他特定设置,请参阅转播模式。" + "state" : "translated", + "value" : "仅用于塔顶或山顶的基础设施节点。 不得用于屋顶或移动节点。以最小的开销中继信息。在节点列表中不可见。" } }, "zh-Hant-TW" : { @@ -8366,8 +9412,8 @@ }, "zh-Hans" : { "stringUnit" : { - "state" : "needs_review", - "value" : "纯路由模式 - 自动转发 Mesh 网络中其他节点的消息,中继模式下屏幕会熄灭,Wi-Fi 和蓝牙将会进入睡眠模式,App 将无法连接到电台进行收发操作。" + "state" : "translated", + "value" : "仅用于塔顶或山顶的基础设施节点。 不得用于屋顶或移动节点。 需要特殊的覆盖范围。在节点列表中可见。" } }, "zh-Hant-TW" : { @@ -8431,8 +9477,8 @@ }, "zh-Hans" : { "stringUnit" : { - "state" : "needs_review", - "value" : "路由客户端模式 - 优先转发 Mesh 网络中其他节点的消息,App 也可以连接到电台进行收发操作。" + "state" : "translated", + "value" : "已废弃的角色,使用客户端。" } }, "zh-Hant-TW" : { @@ -8446,6 +9492,12 @@ "device.role.routerlate" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Infrastrukturknoten, der Pakete immer nur einmal weiterleitet, aber erst nach allen anderen Betriebsarten, um eine zusätzliche Abdeckung für lokale Cluster zu gewährleisten. Sichtbar in der Liste der Knoten." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8460,7 +9512,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Broadcasts telemetry packets as priority." + "value" : "Sendet Telemetriepakete mit Priorität." } }, "en" : { @@ -8525,7 +9577,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Optimized for ATAK system communication, reduces routine broadcasts." + "value" : "Optimiert für ATAK-Systemkommunikation, verringert die Anzahl der Routineübertragungen." } }, "en" : { @@ -8590,7 +9642,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Enables automatic TAK PLI broadcasts and reduces routine broadcasts." + "value" : "Aktiviert automatische TAK-PLI-Übertragungen und verringert die Anzahl der Routineübertragungen." } }, "en" : { @@ -8655,7 +9707,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tracker - For use with devices intended as a GPS tracker. Position packets sent from this device will be higher priority, with position broadcasting every two minutes. Smart Position Broadcast will default to off." + "value" : "Sendet GPS-Positionspakete mit Priorität." } }, "en" : { @@ -8737,6 +9789,12 @@ "state" : "translated", "value" : "Директно" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "直频" + } } } }, @@ -8747,6 +9805,12 @@ "state" : "translated", "value" : "Помоћ за директне поруке" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "私信帮助" + } } } }, @@ -8757,6 +9821,12 @@ "state" : "translated", "value" : "Директне поруке користе нову инфраструктуру јавних кључева за енкрипцију. Захтева верзију фирмвера 2.5 или новију." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "私聊使用新的公钥基础设施进行加密。需要 2.5 或更高版本的固件。" + } } } }, @@ -8767,6 +9837,12 @@ "state" : "translated", "value" : "Директне поруке користе дељени кључ за канал." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "私聊使用频道的共享密钥。" + } } } }, @@ -8823,7 +9899,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "直频消息" + "value" : "私聊" } }, "zh-Hant-TW" : { @@ -8847,6 +9923,12 @@ "state" : "translated", "value" : "Онемогућено" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "禁用" + } } } }, @@ -8967,7 +10049,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "隐藏键盘" + "value" : "收起键盘" } }, "zh-Hant-TW" : { @@ -9031,7 +10113,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "屏幕(电台屏幕)" + "value" : "显示" } }, "zh-Hant-TW" : { @@ -9049,6 +10131,12 @@ "state" : "translated", "value" : "Приказ фаренхајта" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "展示华氏度" + } } } }, @@ -9059,6 +10147,12 @@ "state" : "translated", "value" : "Режим приказа" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "显示模式" + } } } }, @@ -9069,6 +10163,12 @@ "state" : "translated", "value" : "Јединице приказа" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "显示单位" + } } } }, @@ -9208,6 +10308,12 @@ "state" : "translated", "value" : "Раздаљина" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "距离" + } } } }, @@ -9218,6 +10324,12 @@ "state" : "translated", "value" : "Документација" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "文档" + } } } }, @@ -9228,6 +10340,12 @@ "state" : "translated", "value" : "Двоструки додир као дугме" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "双击作为按钮" + } } } }, @@ -9238,6 +10356,12 @@ "state" : "translated", "value" : "Дозвољен даунлинк" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用下载" + } } } }, @@ -9248,6 +10372,12 @@ "state" : "translated", "value" : "Ажурирање фирмвера методом превуци-и-испусти" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "拖放升级固件" + } } } }, @@ -9258,6 +10388,12 @@ "state" : "translated", "value" : "Документација ажурирања фирмвера методом превуци-и-испусти" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "拖放升级固件文档" + } } } }, @@ -9268,6 +10404,12 @@ "state" : "translated", "value" : "Превуци-и-испусти је препоручен начин за ажурирање фирмвера на NRF уређајима. Ако ваш iPhone или iPad има USB-C, радиће са вашим уобичајеним USB-C каблом за пуњење. За уређаје са Lightning портом потребан је Apple Lightning to USB адаптер за камеру." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "拖放是更新 NRF 设备固件的推荐方式。如果您的 iPhone 或 iPad 是 USB-C 接口,则可以使用普通的 USB-C 充电线;如果是 Lightning 设备,则需要使用 Apple Lightning to USB 摄像头适配器。" + } } } }, @@ -9437,6 +10579,12 @@ "state" : "translated", "value" : "Емоџи" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Emoji" + } } } }, @@ -9447,6 +10595,12 @@ "state" : "translated", "value" : "Празно" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "空" + } } } }, @@ -9457,6 +10611,12 @@ "state" : "translated", "value" : "Омогући обавештења" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用通知" + } } } }, @@ -9531,6 +10691,12 @@ "state" : "translated", "value" : "Омогућава уређајима са изворним I2S аудио излазом да користе РТТТЛ преко звучника као звучник. Т-Ватцх СКСНУМКС и Т-Децк на пример имају ову могућност." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "使具有本地 I2S 音频输出的设备能够通过扬声器使用 RTTTL,就像使用蜂鸣器一样。例如,T-Watch S3 和 T-Deck 就具有这种功能。" + } } } }, @@ -9541,6 +10707,12 @@ "state" : "translated", "value" : "Омогућава модул сензора детекције. Потребно је да буде омогућен и на чвору са сензором, и на свим чворовима које желите да примате текстуалне поруке сензора детекције или да видите дневник и графикон сензора детекције." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用检测传感器模块,需要在装有传感器的节点和要接收检测传感器文本信息或查看检测传感器日志和图表的任何节点上启用该模块。" + } } } }, @@ -9551,6 +10723,12 @@ "state" : "translated", "value" : "Омогућава модул за чување и пренос. Чување и пренос мора бити омогућено на оба уређаја, клијенту и рутеру." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用存储和转发模块。客户端和路由器设备都必须启用存储和转发功能。" + } } } }, @@ -9561,6 +10739,12 @@ "state" : "translated", "value" : "Омогућавање етернета ће онемогућити блутут везу са апликацијом." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用以太网将禁用应用程序的蓝牙连接。" + } } } }, @@ -9571,6 +10755,12 @@ "state" : "translated", "value" : "Омогућавање ВајФаја ће онемогућити блутут везу са апликацијом." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用 WiFi 将禁用应用程序的蓝牙连接。" + } } } }, @@ -9661,6 +10851,12 @@ "state" : "translated", "value" : "Шифровано" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "加密" + } } } }, @@ -9671,6 +10867,12 @@ "state" : "translated", "value" : "Омогућено шифровање" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用加密" + } } } }, @@ -9687,6 +10889,12 @@ "state" : "translated", "value" : "Уђите у DFU режим" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "进入 DFU 模式" + } } } }, @@ -9697,6 +10905,12 @@ "state" : "translated", "value" : "окружење" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "环境" + } } } }, @@ -9713,6 +10927,12 @@ "state" : "translated", "value" : "Окружење" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "环境" + } } } }, @@ -9723,6 +10943,12 @@ "state" : "translated", "value" : "Дневник метрика окружења" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "环境指标日志" + } } } }, @@ -9739,6 +10965,12 @@ "state" : "translated", "value" : "Избрисати све податке апликације?" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "擦除所有 App 数据?" + } } } }, @@ -9755,6 +10987,12 @@ "state" : "translated", "value" : "Избрисати све податке уређаја и апликације?" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "擦除所有设备和 App 数据?" + } } } }, @@ -9765,6 +11003,12 @@ "state" : "translated", "value" : "Грешка: %@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "错误:%@" + } } } }, @@ -9775,6 +11019,12 @@ "state" : "translated", "value" : "ESP32 OTA ажурирање је у развоју, кликните на дугме испод да бисте послали уређају поруку за поновно покретање у OTA администраторски режим." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "ESP 32 OTA 更新正在进行中,请单击下面的按钮向您的设备发送重新启动进入 OTA 管理信息。" + } } } }, @@ -9785,6 +11035,12 @@ "state" : "translated", "value" : "Ажурирање фирмвера за ESP32 уређај" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "ESP32 设备固件升级" + } } } }, @@ -10165,6 +11421,12 @@ "state" : "translated", "value" : "Омиљени чворови и чворови са недавно примљеним порукама појављују се на врху листе контаката." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "收藏夹和有最近信息的节点会显示在联系人列表的顶部。" + } } } }, @@ -10313,6 +11575,12 @@ "state" : "translated", "value" : "Фирмвер" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "固件" + } } } }, @@ -10323,6 +11591,12 @@ "state" : "translated", "value" : "Документи за ажурирање фирмвера" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "固件升级文档" + } } } }, @@ -10339,6 +11613,12 @@ "state" : "translated", "value" : "Ажурирања фирмвера" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "固件升级" + } } } }, @@ -10460,7 +11740,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "检测到不支持的固件版本,无法连接到电台。" + "value" : "检测到不支持的固件版本,无法连接到设备。" } }, "zh-Hant-TW" : { @@ -10534,6 +11814,12 @@ "state" : "translated", "value" : "За сву MQTT функционалност осим извештаја на мапи, такође морате подесити uplink и downlink за сваки канал који желите да прележете преко MQTT-а.”" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "对于除地图报告外的所有 MQTT 功能,您还必须为希望通过 MQTT 桥接的每个信道设置上行和下行链路。" + } } } }, @@ -10612,6 +11898,12 @@ "state" : "translated", "value" : "Пријатељски назив" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "友好名称" + } } } }, @@ -10622,6 +11914,12 @@ "state" : "translated", "value" : "Пријатељски назив који се користи за форматирање поруке послате на мрежу. На пример: Назив „Motion” довео би до поруке „Motion detected”." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "用于格式化发送到 Mesh 网络的信息的友好名称。例如名称为 “运动”时,发送的信息为 “检测到运动”。" + } } } }, @@ -10836,6 +12134,12 @@ "state" : "translated", "value" : "Добави позицију чвора" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "获取节点位置" + } } } }, @@ -10846,6 +12150,12 @@ "state" : "translated", "value" : "Преузмите NRF DFU из App Store-а" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "从 App Store 获取 NRF DFU" + } } } }, @@ -10856,6 +12166,12 @@ "state" : "translated", "value" : "Добави најновији алфа фирмвер" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "获取最新测试版固件" + } } } }, @@ -10866,6 +12182,12 @@ "state" : "translated", "value" : "Добави најновији стабилни фирмвер" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "获取最新稳定版固件" + } } } }, @@ -10876,6 +12198,12 @@ "state" : "translated", "value" : "GPIO" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPIO" + } } } }, @@ -10936,6 +12264,12 @@ "state" : "translated", "value" : "GPS EN GPIO" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPS EN GPIO" + } } } }, @@ -10946,6 +12280,12 @@ "state" : "translated", "value" : "GPS формат" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPS 格式" + } } } }, @@ -10956,6 +12296,12 @@ "state" : "translated", "value" : "GPS пријем GPIO" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPS Receive GPIO" + } } } }, @@ -10966,6 +12312,12 @@ "state" : "translated", "value" : "GPS предаја GPIO" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "GPS Transmit GPIO" + } } } }, @@ -11403,6 +12755,12 @@ "state" : "translated", "value" : "Онемогућен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "禁用" + } } } }, @@ -11450,6 +12808,12 @@ "state" : "translated", "value" : "Омогућен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用" + } } } }, @@ -11491,6 +12855,12 @@ "state" : "translated", "value" : "Није пристуно" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "不存在" + } } } }, @@ -11507,6 +12877,12 @@ "state" : "translated", "value" : "Групна порука" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "群聊" + } } } }, @@ -11527,6 +12903,12 @@ "state" : "translated", "value" : "Хардвер" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "硬件" + } } } }, @@ -11687,6 +13069,12 @@ "state" : "translated", "value" : "Помози при развоју апликације" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "帮助开发应用程序" + } } } }, @@ -11723,6 +13111,12 @@ "state" : "translated", "value" : "ВИСОК" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "高" + } } } }, @@ -11823,6 +13217,12 @@ "state" : "translated", "value" : "Сат" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "小时" + } } } }, @@ -11843,6 +13243,12 @@ "state" : "translated", "value" : "Колико дуго екран остаје укључен након притиска корисничког дугмета или пријема порука." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "按下用户按钮或收到消息后屏幕保持亮屏的时间。" + } } } }, @@ -11853,6 +13259,12 @@ "state" : "translated", "value" : "Колико често се метрике уређаја шаљу преко мреже. Подразумевано је 30 минута." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备指标通过网格发送的频率。默认为 30 分钟。" + } } } }, @@ -11863,6 +13275,12 @@ "state" : "translated", "value" : "Колико често се метрике снаге шаљу преко мреже. Подразумевано је 30 минута." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "通过网格发送功率指标的频率。默认为 30 分钟。" + } } } }, @@ -11873,6 +13291,12 @@ "state" : "translated", "value" : "Колико често се метрике сензора шаљу преко мреже. Подразумевано је 30 минута." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "通过网格发送传感器指标的频率。默认为 30 分钟。" + } } } }, @@ -11883,6 +13307,12 @@ "state" : "translated", "value" : "Колико често треба да покушамо да добијемо GPS позицију." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "尝试获取 GPS 定位的频率。" + } } } }, @@ -11893,6 +13323,12 @@ "state" : "translated", "value" : "Колико често да пошаљете стање сензора детекције у мрежу, без обзира на детекцију. Подразумевано је да се не шаље никада." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无论是否检测到,向网格发送检测传感器状态的频率。默认为从不。" + } } } }, @@ -11909,6 +13345,12 @@ "state" : "translated", "value" : "Како да ажурираш фирмвер" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "如何升级固件" + } } } }, @@ -12091,6 +13533,12 @@ "state" : "translated", "value" : "IAQ" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "IAQ" + } } } }, @@ -12101,6 +13549,12 @@ "state" : "translated", "value" : "IAQ" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "IAQ " + } } } }, @@ -12111,6 +13565,12 @@ "state" : "translated", "value" : "IAQ %lld" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "IAQ %lld" + } } } }, @@ -12121,6 +13581,12 @@ "state" : "translated", "value" : "Иконица" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "图标" + } } } }, @@ -12131,6 +13597,12 @@ "state" : "translated", "value" : "Ако је DOP постављен, користите HDOP / VDOP вредности уместо PDOP-а" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "如果设置了 DOP,则使用 HDOP / VDOP 值而不是 PDOP" + } } } }, @@ -12141,6 +13613,12 @@ "state" : "translated", "value" : "Ако је омогућено, 'output' пин ће бити активиран на високом нивоу, а ако је онемогућено, биће активиран на ниском нивоу." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "如果启用,“输出 ”引脚将被拉高,禁用则表示拉低。" + } } } }, @@ -12151,6 +13629,12 @@ "state" : "translated", "value" : "Ако је тешко приступити дугмету за ресетовање уређаја, уђите у DFU режим овде." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "如果难以访问设备的重置按钮,请在此进入 DFU 模式。" + } } } }, @@ -12161,6 +13645,12 @@ "state" : "translated", "value" : "Ако је подешено, сви пакети које пошаљете ће бити враћени (ехо) назад на ваш уређај." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "如果设置了,您发送的任何数据包都会回传到设备。" + } } } }, @@ -12171,6 +13661,12 @@ "state" : "translated", "value" : "Ако је подразумевана тема региона превише заузета можете изабрати више локалну тему." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "如果默认区域话题太忙,您可以选择一个更本地化的话题。" + } } } }, @@ -12181,6 +13677,12 @@ "state" : "translated", "value" : "Игнориши MQTT" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "忽略 MQTT" + } } } }, @@ -12191,6 +13693,12 @@ "state" : "translated", "value" : "Игнориши чвор" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "忽略节点" + } } } }, @@ -12201,6 +13709,12 @@ "state" : "translated", "value" : "Игнорисан" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "忽略" + } } } }, @@ -12211,6 +13725,12 @@ "state" : "translated", "value" : "Увозна рута" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "导入路线" + } } } }, @@ -12361,6 +13881,12 @@ "state" : "translated", "value" : "Квалитет ваздуха у затвореном простору" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "室内空气质量" + } } } }, @@ -12371,6 +13897,12 @@ "state" : "translated", "value" : "Квалитет ваздуха у затвореном простору (IAQ)" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "室内空气质量 (IAQ)" + } } } }, @@ -12901,6 +14433,12 @@ "state" : "translated", "value" : "Улази" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "输入" + } } } }, @@ -13367,6 +14905,12 @@ "state" : "translated", "value" : "Четртесет и осам сати" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "四十八小时" + } } } }, @@ -13810,6 +15354,12 @@ "state" : "translated", "value" : "Седамдесет и два сата" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "七十二小时" + } } } }, @@ -14802,6 +16352,12 @@ "state" : "translated", "value" : "Обрнута горња трака за екран у 2 боје" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "倒置顶栏,用于双色显示" + } } } }, @@ -14829,6 +16385,12 @@ "state" : "translated", "value" : "JSON омогућен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用 JSON" + } } } }, @@ -14839,6 +16401,12 @@ "state" : "translated", "value" : "JSON режим је ограничен, нешифрован MQTT излаз за локалну интеграцију са Home Assistant-ом." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "JSON 模式是一种有限的、未加密的 MQTT 输出,用于与家庭助理进行本地集成" + } } } }, @@ -14855,6 +16423,12 @@ "state" : "translated", "value" : "Кључ" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Key" + } } } }, @@ -14979,6 +16553,12 @@ "state" : "translated", "value" : "Последње откривање" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "最后听到" + } } } }, @@ -14995,6 +16575,12 @@ "state" : "translated", "value" : "Ширина" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "纬度" + } } } }, @@ -15005,6 +16591,12 @@ "state" : "translated", "value" : "LED срчани откуцаји" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "LED 心跳" + } } } }, @@ -15015,11 +16607,23 @@ "state" : "translated", "value" : "LED статус" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "LED 状态" + } } } }, "Legacy Administration" : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alte Administrationsart" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -15035,6 +16639,12 @@ "state" : "translated", "value" : "Лиценцирани оператор" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "持证操作员" + } } } }, @@ -15045,6 +16655,12 @@ "state" : "translated", "value" : "Ограничите све периодичне интервале емитовања, посебно телеметрију и позицију. Ако је потребно повећати број скокова, то радите на чворовима на ивицама, а не на оним у средини. MQTT није препоручен када сте ограничени циклусом дужности јер у том случају чвор-рутер ради сав посао." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "限制所有周期性广播间隔,尤其是遥测和位置。如果需要增加跳数,请在边缘节点而不是中间节点上进行。在占空比受限的情况下,不建议使用 MQTT,因为网关节点会承担所有工作。" + } } } }, @@ -15065,6 +16681,12 @@ "state" : "translated", "value" : "Учитавам логове. . ." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "加载日志. . ." + } } } }, @@ -15081,6 +16703,12 @@ "state" : "translated", "value" : "Локација:" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "位置" + } } } }, @@ -15097,6 +16725,12 @@ "state" : "translated", "value" : "Локација:" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "位置:" + } } } }, @@ -15113,6 +16747,12 @@ "state" : "translated", "value" : "Закључан" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "锁定" + } } } }, @@ -15123,6 +16763,12 @@ "state" : "translated", "value" : "Нивои логова" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "日志等级" + } } } }, @@ -15547,6 +17193,12 @@ "state" : "translated", "value" : "Логови" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "日志" + } } } }, @@ -15557,6 +17209,12 @@ "state" : "translated", "value" : "Логови:" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "日志:" + } } } }, @@ -15573,6 +17231,12 @@ "state" : "translated", "value" : "Дуго име" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "长名称" + } } } }, @@ -15589,6 +17253,12 @@ "state" : "translated", "value" : "Дуго име: %@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "长名称: %@" + } } } }, @@ -15599,6 +17269,12 @@ "state" : "translated", "value" : "Дугим притиском на чвор из листе означите као омиљени или искључите звук контакта или обришите разговор." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "长按可收藏联系人或将其静音或删除对话。" + } } } }, @@ -15666,6 +17342,12 @@ "state" : "translated", "value" : "Дужина" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "经度" + } } } }, @@ -15800,6 +17482,12 @@ "lora.signal.strength.bad" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schlechte Signalstärke" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -15817,6 +17505,12 @@ "lora.signal.strength.fair" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordentliche Signalstärke" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -15834,6 +17528,12 @@ "lora.signal.strength.good" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gute Signalstärke" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -15851,6 +17551,12 @@ "lora.signal.strength.none" : { "extractionState" : "manual", "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Verbindung" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -15916,6 +17622,12 @@ "state" : "translated", "value" : "Управљани уређај" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "管理设备" + } } } }, @@ -15996,6 +17708,12 @@ "state" : "translated", "value" : "Опције мапе" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "地图选项" + } } } }, @@ -16006,6 +17724,12 @@ "state" : "translated", "value" : "Интервал објављивања мапе" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "地图发布间隔" + } } } }, @@ -16016,6 +17740,12 @@ "state" : "translated", "value" : "Извештај мапе" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "地图报告" + } } } }, @@ -16026,6 +17756,12 @@ "state" : "translated", "value" : "Подаци плочица мапе" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "瓦片地图数据" + } } } }, @@ -16136,7 +17872,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "删除已缓存的地图区块" + "value" : "删除所有瓦片地图" } }, "zh-Hant-TW" : { @@ -17631,7 +19367,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "MQTT module config received: %@" + "value" : "收到 MQTT 模块配置:%@" } }, "zh-Hant-TW" : { @@ -18138,7 +19874,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Range Test module config received: %@" + "value" : "收到范围测试模块配置: %@" } }, "zh-Hant-TW" : { @@ -19602,6 +21338,12 @@ "state" : "translated", "value" : "Модел" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "模型" + } } } }, @@ -19740,6 +21482,12 @@ "state" : "translated", "value" : "MQTT" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "MQTT" + } } } }, @@ -20162,6 +21910,12 @@ "state" : "translated", "value" : "Име" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "名称" + } } } }, @@ -20178,6 +21932,12 @@ "state" : "translated", "value" : "Име мора бити краће од 30 бајтова" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "名称必须少于 30 字节" + } } } }, @@ -20262,6 +22022,12 @@ "state" : "translated", "value" : "Статус мреже: Наранџаст" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "网络状态 橙色" + } } } }, @@ -20272,6 +22038,12 @@ "state" : "translated", "value" : "Статус мреже: Црвен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "网络状态 红色" + } } } }, @@ -20407,6 +22179,12 @@ "state" : "translated", "value" : "Нова верзија фирмвера је доступна" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "有固件可以更新" + } } } }, @@ -20443,6 +22221,12 @@ "state" : "translated", "value" : "Нема метрика окружења" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "没有环境指标" + } } } }, @@ -20556,6 +22340,12 @@ "state" : "translated", "value" : "Резервна копија података језгра чвора %1$@/%2$@ - %3$@ - %4$@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "节点核心数据备份 %1$@/%2$@ - %3$@ - %4$@" + } } } }, @@ -20884,7 +22674,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "未连接到电台" + "value" : "设备未连接" } }, "zh-Hant-TW" : { @@ -21193,6 +22983,12 @@ "state" : "translated", "value" : "Позитиван за MQTT" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ok to MQTT" + } } } }, @@ -21209,6 +23005,12 @@ "state" : "translated", "value" : "Тип OLED-а" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "OLED 类型" + } } } }, @@ -21284,6 +23086,12 @@ "state" : "translated", "value" : "Увођење за лиценциране оператере захтева фирмвер верзије 2.0.20 или новије. Уверите се да се придржавате локалних прописа и обратите се локалним координаторима за аматерске фреквенције са питањима." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "业余无线电使用需要固件 2.0.20 或更高版本。请务必参考当地法规,并联系当地业余频率协调人员咨询相关问题。" + } } } }, @@ -21348,6 +23156,12 @@ "state" : "translated", "value" : "Отвори подешавања" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "打开设置" + } } } }, @@ -21375,6 +23189,12 @@ "state" : "translated", "value" : "Опциони поља за укључивање при склапању порука о позицији. Што више поља је укључено, порука ће бити већа, што доводи до дужег времена емитовања и већег ризика од губитка пакета" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "包含的字段越多,信息就越大,导致通讯时间更长,丢包风险更高" + } } } }, @@ -21465,6 +23285,12 @@ "state" : "translated", "value" : "Опције" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "选项" + } } } }, @@ -21485,6 +23311,12 @@ "state" : "translated", "value" : "ОТА ажурирања нису подржана на овом NRF уређају." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "OTA 更新不支持 NRF 设备" + } } } }, @@ -21495,6 +23327,12 @@ "state" : "translated", "value" : "ОТА ажурирања нису подржана на вашој платформи." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "OTA 更新不支持你的平台" + } } } }, @@ -21510,6 +23348,12 @@ }, "Output live debug logging over serial, view and export position-redacted device logs over Bluetooth." : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ausgabe von Echtzeit-Fehlersuchprotokollen über die serielle Schnittstelle, Anzeige und Export von positionskorrigierten Geräteprotokollen über Bluetooth." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -22039,7 +23883,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "电台通过手机获取定位的时间间隔,但是向 Mesh 网络中刷新定位的时间间隔由电台控制。" + "value" : "电台通过手机获取定位的时间间隔,但是向 Mesh 网络中发送定位的时间间隔由电台控制。" } }, "zh-Hant-TW" : { @@ -22093,6 +23937,12 @@ "state" : "translated", "value" : "Администрација чвора заснована на PKI захтева фирмвер верзију 2.5 или новију" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基于 PKI 的节点管理,需要 2.5 以上版本的固件" + } } } }, @@ -22103,6 +23953,12 @@ "state" : "translated", "value" : "Молимо вас да се повежете на радио да бисте конфигурисали подешавања." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "请连接电台以修改配置。" + } } } }, @@ -22379,6 +24235,12 @@ "state" : "translated", "value" : "Позиционирање укључено" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用定位" + } } } }, @@ -22389,6 +24251,12 @@ "state" : "translated", "value" : "Позиције ће бити обезбеђене путем GPS-а вашег уређаја. Ако одаберете опцију „онемогућено“ или „није присутно“, можете подесити фиксну позицију." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "位置将由设备 GPS 提供,如果选择禁用或不存在,则可以设置固定位置。" + } } } }, @@ -22432,6 +24300,28 @@ } } }, + "power.metrics.delete" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete all power metrics?" + } + } + } + }, + "power.metrics.log" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Power Metrics Log" + } + } + } + }, "Powered" : { "localizations" : { "de" : { @@ -22518,7 +24408,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "首选电台" + "value" : "默认电台" } }, "zh-Hant-TW" : { @@ -22536,6 +24426,12 @@ "state" : "translated", "value" : "Унапред подешено" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "预设" + } } } }, @@ -22577,11 +24473,23 @@ }, "Primary Admin Key" : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erster Admin-Schlüssel" + } + }, "sr" : { "stringUnit" : { "state" : "translated", "value" : "Основни административни кључ" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "一级管理员密钥" + } } } }, @@ -22597,11 +24505,23 @@ }, "Private Key" : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privater Schlüssel" + } + }, "sr" : { "stringUnit" : { "state" : "translated", "value" : "Приватни кључ" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "私钥" + } } } }, @@ -22612,16 +24532,34 @@ "state" : "translated", "value" : "Информације о пројекту" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "项目信息" + } } } }, "Public Key" : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Öffentlicher Schlüssel" + } + }, "sr" : { "stringUnit" : { "state" : "translated", "value" : "Јавни кључ" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "公钥" + } } } }, @@ -22632,6 +24570,12 @@ "state" : "translated", "value" : "Шифровање јавним кљулем" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "公钥加密" + } } } }, @@ -22642,6 +24586,12 @@ "state" : "translated", "value" : "Неслагање јавних кључева" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "公钥不匹配" + } } } }, @@ -23074,6 +25024,12 @@ "state" : "translated", "value" : "Режим реемитовања" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "转播模式" + } } } }, @@ -23254,6 +25210,12 @@ "state" : "translated", "value" : "Регион" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "区域" + } } } }, @@ -23481,6 +25443,12 @@ "state" : "translated", "value" : "Уклони из игнорисаних" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "从忽略中删除" + } } } }, @@ -23601,6 +25569,12 @@ "state" : "translated", "value" : "Ресетовање подешавања апликације" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "重置 App 设置" + } } } }, @@ -23745,6 +25719,12 @@ "state" : "translated", "value" : "Оцените апликацију" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "审查应用程序" + } } } }, @@ -23942,6 +25922,12 @@ "state" : "translated", "value" : "Ротациони 1" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "旋转一次" + } } } }, @@ -24165,6 +26151,12 @@ "state" : "translated", "value" : "Вожња бицикле" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自行车" + } } } }, @@ -24194,6 +26186,12 @@ "state" : "translated", "value" : "Вожња аута" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "驾驶" + } } } }, @@ -24223,6 +26221,12 @@ "state" : "translated", "value" : "тура бициклом" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自行车旅行" + } } } }, @@ -24252,6 +26256,12 @@ "state" : "translated", "value" : "вожња" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "驾驶" + } } } }, @@ -24281,6 +26291,12 @@ "state" : "translated", "value" : "планинарње" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "徒步" + } } } }, @@ -24304,6 +26320,12 @@ "state" : "translated", "value" : "Вожња преко копна" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "越野" + } } } }, @@ -24333,6 +26355,12 @@ "state" : "translated", "value" : "ски тура" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "滑雪之旅" + } } } }, @@ -24362,6 +26390,12 @@ "state" : "translated", "value" : "шетња" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "步行" + } } } }, @@ -24391,6 +26425,12 @@ "state" : "translated", "value" : "Планинарење" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "徒步" + } } } }, @@ -24414,6 +26454,12 @@ "state" : "translated", "value" : "Оверлендинг" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "越野" + } } } }, @@ -24443,6 +26489,12 @@ "state" : "translated", "value" : "Скијање" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "滑雪" + } } } }, @@ -24472,6 +26524,12 @@ "state" : "translated", "value" : "Шетња" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "步行" + } } } }, @@ -25168,6 +27226,12 @@ "state" : "translated", "value" : "Непознат јавни кључ" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "未知的公钥" + } } } }, @@ -25629,6 +27693,12 @@ "state" : "translated", "value" : "Сачувати корисничу конфигурацију за %@?" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "保存用户配置到 %@?" + } } } }, @@ -25704,6 +27774,12 @@ "state" : "translated", "value" : "Снима CSV са детаљима порука теста домета, тренутно доступно само на ESP32 уређајима са веб сервером." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "保存包含量程测试报文详细信息的 CSV 文件,目前仅适用于配有网络服务器的 ESP32 设备。" + } } } }, @@ -25755,11 +27831,23 @@ }, "Secondary Admin Key" : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zweiter Admin-Schlüssel" + } + }, "sr" : { "stringUnit" : { "state" : "translated", "value" : "Секундарни административни кључ" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "二级管理员密钥" + } } } }, @@ -25776,6 +27864,12 @@ "state" : "translated", "value" : "Сигурност" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "安全" + } } } }, @@ -25792,16 +27886,34 @@ "state" : "translated", "value" : "Сигурносна подешавања" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "安全配置" + } } } }, "Security Config Settings require a firmware version 2.5+" : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sicherheitskonfigurationseinstellungen erfordern eine Firmware mit Version 2.5 oder höher" + } + }, "sr" : { "stringUnit" : { "state" : "translated", "value" : "Сигурносна подешавања захтевају фирмвер верзију 2.5+" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "安全配置需要固件版本 2.5+" + } } } }, @@ -26045,6 +28157,12 @@ "state" : "translated", "value" : "Пошаљи позицију на примарном каналу када се корисничко дугме три пута кликне." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "当用户按钮被点击三次时,在主通道上发送定位。" + } } } }, @@ -26087,6 +28205,12 @@ "state" : "translated", "value" : "Пошаљи ASCII звона са поруком упозорења. Корисно за покретање спољашњег обавештења на звону." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "发送带有警报信息的 ASCII 铃声。用于触发外部铃声通知。" + } } } }, @@ -26103,6 +28227,12 @@ "state" : "translated", "value" : "Пошаљи звоно" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "发送铃声" + } } } }, @@ -26113,6 +28243,12 @@ "state" : "translated", "value" : "Пошаљи сигнал поновног покретања (OTA)" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "发送重启 OTA" + } } } }, @@ -26123,6 +28259,12 @@ "state" : "translated", "value" : "Инерварл пошиљаоца" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "发送间隔" + } } } }, @@ -26133,6 +28275,12 @@ "state" : "translated", "value" : "Метрике сензора" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "传感器指标" + } } } }, @@ -26158,6 +28306,12 @@ }, "Sent out to other nodes on the mesh to allow them to compute a shared secret key." : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wird an andere Knoten im Netz gesendet, damit diese einen gemeinsamen geheimen Schlüssel berechnen können." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -26264,6 +28418,12 @@ }, "Serial Console" : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serielle Konsole" + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -26274,6 +28434,12 @@ }, "Serial Console over the Stream API." : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serielle Konsole über die Stream-API." + } + }, "sr" : { "stringUnit" : { "state" : "translated", @@ -27045,6 +29211,12 @@ "state" : "translated", "value" : "Кратко име" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "短名称" + } } } }, @@ -27061,6 +29233,12 @@ "state" : "translated", "value" : "Кратко име: %@" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "短名称: %@" + } } } }, @@ -27961,6 +30139,12 @@ "state" : "translated", "value" : "Подржани I2C повезани сензори ће бити аутоматски детектовани. Сензори су: BMP280, BME280, BME680, MCP9808, INA219, INA260, LPS22 и SHTC3." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "将自动检测支持 I2C 连接的传感器,包括 BMP280、BME280、BME680、MCP9808、INA219、INA260、LPS22 和 SHTC3。" + } } } }, @@ -28785,6 +30969,12 @@ "state" : "translated", "value" : "Умерено" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "适度" + } } } }, @@ -28855,11 +31045,23 @@ }, "Tertiary Admin Key" : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dritter Admin-Schlüssel" + } + }, "sr" : { "stringUnit" : { "state" : "translated", "value" : "Терцијарни административни кључ" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "三级管理员密钥" + } } } }, @@ -28950,6 +31152,12 @@ "state" : "translated", "value" : "Формат који се користи за приказивање GPS координата на екрану уређаја." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "用于在设备屏幕上显示 GPS 坐标的格式。" + } } } }, @@ -28960,6 +31168,12 @@ "state" : "translated", "value" : "Последња 4 знака MAC адресе уређаја ће бити додата кратком имену како би се подесило BLE име уређаја. Кратко име може бити до 4 бајта дуго." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备 MAC 地址的后 4 位将附加到短名称中,以设置设备的 BLE 名称。 短名称的长度最多为 4 个字节。" + } } } }, @@ -28980,6 +31194,12 @@ "state" : "translated", "value" : "Мештастик апликације за Епл уређаје подржавају верзију фирмвера %@ и новије." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meshtastic Apple 应用程序支持 %@ 及以上版本的固件。" + } } } }, @@ -28990,6 +31210,12 @@ "state" : "translated", "value" : "Минимална промена растојања у метрима која ће се узети у обзир за паметно емитовање позиције." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "智能位置广播考虑的最小距离变化(以米为单位)。" + } } } }, @@ -29000,16 +31226,34 @@ "state" : "translated", "value" : "Најновији јавни кључ за овај чвор се не подудара са претходно снимљеним кључем. Можете избрисати чвор и дозволити му да поново размени кључеве, али ово такође може указивати на озбиљнији безбедносни проблем. Контактирајте корисника преко другог поузданог канала како бисте утврдили да ли је промена кључа резултат фабричког ресетовања или друге намерне акције." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "该节点的最新公钥与之前记录的公钥不匹配。您可以删除该节点,让它重新交换公钥,但这也可能表明存在更严重的安全问题。通过其他可信渠道联系用户,以确定公钥更改是否是由于出厂重置或其他故意行为造成的。" + } } } }, "The primary public key authorized to send admin messages to this node." : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der erste öffentliche Schlüssel, der berechtigt ist, Admin-Nachrichten an diesen Knoten zu senden." + } + }, "sr" : { "stringUnit" : { "state" : "translated", "value" : "Примарни јавни кључ овлашћен за слање административних порука овом чвору." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "授权向该节点发送管理信息的一级管理员公钥。" + } } } }, @@ -29020,6 +31264,12 @@ "state" : "translated", "value" : "Јавни кључ се не подудара са снимљеним кључем. Можете избрисати чвор и дозволити му да поново размени кључеве, али ово може указивати на озбиљнији безбедносни проблем. Контактирајте корисника преко другог поузданог канала како бисте утврдили да ли је промена кључа резултат фабричког ресетовања или друге намерне акције." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "公钥与记录的公钥不匹配。您可以删除节点,让它重新交换公钥,但这可能表明存在更严重的安全问题。通过其他可信渠道联系用户,以确定公钥更改是否是由于出厂重置或其他故意行为造成的。" + } } } }, @@ -29030,6 +31280,12 @@ "state" : "translated", "value" : "Регион у коме ћете користити ваше радио уређаје." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "使用电台的地区。" + } } } }, @@ -29040,16 +31296,34 @@ "state" : "translated", "value" : "Корен тема која ће се користити за MQTT." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "用于 MQTT 的根主题。" + } } } }, "The secondary public key authorized to send admin messages to this node." : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der zweite öffentliche Schlüssel, der berechtigt ist, Admin-Nachrichten an diesen Knoten zu senden." + } + }, "sr" : { "stringUnit" : { "state" : "translated", "value" : "Секундарни јавни кључ овлашћен за слање административних порука овом чвору." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "授权向该节点发送管理信息的二级管理员公钥。" + } } } }, @@ -29076,11 +31350,23 @@ }, "The tertiary public key authorized to send admin messages to this node." : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der dritte öffentliche Schlüssel, der berechtigt ist, Admin-Nachrichten an diesen Knoten zu senden." + } + }, "sr" : { "stringUnit" : { "state" : "translated", "value" : "Терцијарни јавни кључ овлашћен за слање административних порука овом чвору." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "授权向该节点发送管理信息的三级管理员公钥。" + } } } }, @@ -29151,12 +31437,19 @@ } }, "This determines the actual frequency you are transmitting on in the band. If set to 0 this value will be calculated automatically based on the primary channel name." : { + "extractionState" : "stale", "localizations" : { "sr" : { "stringUnit" : { "state" : "translated", "value" : "Ово одређује стварну фреквенцију на којој преносите у опсегу. Ако је постављено на 0, ова вредност ће се аутоматски израчунати на основу назива примарног канала." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "这决定了您在频段内的实际发射频率。如果设置为 0,该值将根据主频道名称自动计算。" + } } } }, @@ -29167,6 +31460,12 @@ "state" : "translated", "value" : "Овај уређај ће слати поруке за тестирање домета у одабраном интервалу." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "该设备将按所选时间间隔发送测距信息。" + } } } }, @@ -29203,6 +31502,12 @@ "state" : "translated", "value" : "Ово ће послати тренутну позицију са вашег телефона и омогућити фиксну позицију." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "这将从手机发送当前位置并启用固定位置。" + } } } }, @@ -29525,7 +31830,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "连接到 LoRa 电台" + "value" : "电台已连接" } }, "zh-Hant-TW" : { @@ -29717,7 +32022,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "大多数 Mesh 上的数据都通过主要频道发送。您可以设置次要频道,以创建额外的消息组,并通过其自己的密钥进行安全保护。 [频道配置提示](https://meshtastic.org/docs/configuration/radio/channels/)" + "value" : "Mesh 网格上的大部分数据都通过主频道发送。您可以设置辅助频道以创建由其自身密钥保护的消息组。[频道配置提示](https://meshtastic.org/docs/configuration/tips/)" } }, "zh-Hant-TW" : { @@ -29973,7 +32278,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "您可以发送和接收直频消息和群聊。在任何信息中,您都可以长按查看可用的操作,如复制、回复、拍一拍、删除以及详情。" + "value" : "您可以发送和接收群聊或私聊消息。在任何消息中,您都可以长按查看可用的操作,如复制、回复、拍一拍、删除以及投递详情。" } }, "zh-Hant-TW" : { @@ -30055,6 +32360,12 @@ "state" : "translated", "value" : "TLS укључен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用 TLS" + } } } }, @@ -30105,6 +32416,29 @@ } } }, + "Trace route received directly by %@ with a SNR of %@ dB" : { + "extractionState" : "stale", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Trace route received directly by %1$@ with a SNR of %2$@ dB" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Захтев за праћење руте комуникације директно примљен од %1$@ са SNR од %2$@ dB." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "由 %1$@ 直接接收的跟踪路由,信噪比为 %2$@ dB" + } + } + } + }, "Trace Route Sent" : { "localizations" : { "sr" : { @@ -30178,6 +32512,12 @@ "state" : "translated", "value" : "Трансмитер укључен" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用传输" + } } } }, @@ -30188,6 +32528,12 @@ "state" : "translated", "value" : "Третирај двоструки додир на подржаним акцелераметрима као притисак корисничког дугмета." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "将支持双击的加速度计视为按下用户按钮。" + } } } }, @@ -30582,6 +32928,12 @@ "state" : "translated", "value" : "Горе Доле 1" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "上下一次" + } } } }, @@ -30756,6 +33108,12 @@ "state" : "translated", "value" : "Укључен узлазни канал" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "启用上传" + } } } }, @@ -30800,6 +33158,12 @@ "state" : "translated", "value" : "Користи PWM излаз (као што је RAK звучник) за мелодије уместо укључивања/искључивања излаза. Ово ће игнорисати подешавања излаза, трајање излаза и активна подешавања и користити подешавање GPIO опције звучника у конфигурацији уређаја." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "使用 PWM 输出(如 RAK 蜂鸣器)代替开/关输出进行调谐。这将忽略输出、输出持续时间和激活设置,而使用设备配置蜂鸣器 GPIO 选项。" + } } } }, @@ -30820,6 +33184,12 @@ "state" : "translated", "value" : "Користи предефинисано подешавање" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "使用预设" + } } } }, @@ -30830,16 +33200,34 @@ "state" : "translated", "value" : "Користи PWM звучник" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "使用 PWM 蜂鸣器" + } } } }, - "Used to create a shared key with a remote device." : { + "Wird verwendet, um einen gemeinsamen Schlüssel mit einem entfernten Gerät zu erstellen." : { "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbunden mit einem Knoten" + } + }, "sr" : { "stringUnit" : { "state" : "translated", "value" : "Користи се за креирање заједничког кључа са удаљеним уређајем." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "用于与远程设备创建共享密钥。" + } } } }, @@ -30914,6 +33302,12 @@ "state" : "translated", "value" : "Корисничка подешавања" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "用户配置" + } } } }, @@ -30924,6 +33318,12 @@ "state" : "translated", "value" : "Кориснички детаљи" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "用户信息" + } } } }, @@ -30934,6 +33334,12 @@ "state" : "translated", "value" : "ИД корисника" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "用户 ID" + } } } }, @@ -30945,6 +33351,12 @@ "state" : "translated", "value" : "Корисник је покренуо прекид везе" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "用户主动断开连接" + } } } }, @@ -31030,6 +33442,12 @@ "state" : "translated", "value" : "Користи мрежну везу на вашем телефону за повезивање са MQTT." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "利用手机上的网络连接到 MQTT。" + } } } }, @@ -31078,6 +33496,12 @@ "state" : "translated", "value" : "Верзија %1$@ укључује значајне оптимизације мреже и обимне измене уређаја и клијентских апликација. Подржане су само верзије чворова %2$@ и новије." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "版本 %1$@ 包括大量网络优化以及对设备和客户端应用程序的广泛更改。仅支持 %2$@ 及以上版本的节点。" + } } } }, @@ -31100,6 +33524,12 @@ "state" : "translated", "value" : "Верзија: %1$@ (%2$@) " } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "版本号: %1$@ (%2$@) " + } } } }, @@ -31149,6 +33579,12 @@ "state" : "translated", "value" : "Преко MQTT-а" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "通过 MQTT" + } } } }, @@ -31345,6 +33781,12 @@ "state" : "translated", "value" : "Временски услови" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "天气状况" + } } } }, @@ -31365,6 +33807,12 @@ "state" : "translated", "value" : "Вебсајт" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "网站" + } } } }, @@ -31381,6 +33829,12 @@ "state" : "translated", "value" : "Шта значи закључавање?" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "锁意味着什么?" + } } } }, @@ -31397,6 +33851,12 @@ "state" : "translated", "value" : "Шта је Мештастик?" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "什么是 Meshtastic?" + } } } }, @@ -31407,6 +33867,12 @@ "state" : "translated", "value" : "Шта ради режим лиценцираног оператера:\n* Поставља име чвора на ваш позивни знак\n* Емитује информације о чвору сваких 10 минута\n* Превазилази фреквенцију, циклус рада и излазну снагу\n* Онемогућава енкрипцију" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "业余无线电模式的作用:\n* 将节点名称设置为您的呼号 \n* 每 10 分钟广播一次节点信息 \n* 覆盖频率、占空比和发射功率 \n* 禁用加密" + } } } }, @@ -31417,6 +33883,12 @@ "state" : "translated", "value" : "Када користите у GPIO режиму, задржите излаз укључен овако дуго." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "在 GPIO 模式下使用时,请将输出保持接通这么长时间。" + } } } }, @@ -31507,6 +33979,12 @@ "state" : "translated", "value" : "X: %1$@, Y: %2$d" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "X: %1$@, Y: %2$d" + } } } }, @@ -31523,6 +34001,12 @@ "state" : "translated", "value" : "X: %1$@, Y: %2$f" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "X: %1$@, Y: %2$f" + } } } }, @@ -31539,6 +34023,12 @@ "state" : "translated", "value" : "X: %1$@, Y: %2$lld" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "X: %1$@, Y: %2$lld" + } } } }, @@ -31549,6 +34039,12 @@ "state" : "translated", "value" : "y" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "y" + } } } }, @@ -31575,6 +34071,12 @@ "state" : "translated", "value" : "Такође можете ажурирати свој Мештастик уређај преко блутута користећи Nordic DFU апликацију." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "您还可以使用 Nordic DFU 应用程序通过蓝牙更新 Meshtastic 设备。" + } } } }, @@ -31585,6 +34087,12 @@ "state" : "translated", "value" : "Ваша тренутна позиција ће бити постављена као фиксна позиција и емитована преко мреже на интервалу позиције." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "您当前的位置将被设置为固定位置,并以定位间隔向 Mesh 网络广播。" + } } } }, @@ -31601,18 +34109,37 @@ "state" : "translated", "value" : "Ваш фирмвер је на најновијој верзији" } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "你的固件已经是最新版本" + } } } + }, + "Your MQTT Server must support TLS." : { + }, "Your MQTT Server must support TLS. Not available via the public mqtt server." : { + "extractionState" : "stale", "localizations" : { "sr" : { "stringUnit" : { "state" : "translated", "value" : "Ваш MQTT сервер мора подржавати TLS. Није доступно преко јавног MQTT сервера." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "您的 MQTT 服务器必须支持 TLS。没有可用的公开 MQTT 服务器。" + } } } + }, + "Your node’s operating frequency is calculated based on the region, modem preset, and this field. When 0, the slot is automatically calculated based on the primary channel name." : { + }, "Your position has been sent with a request for a response with their position. You will receive a notification when a position is returned." : { "localizations" : { @@ -31621,6 +34148,12 @@ "state" : "translated", "value" : "Ваша позиција је послата са захтевом за одговор са њиховом позицијом. Добићете обавештење када се позиција врати." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "您的位置已发送,并请求对方回复其位置。位置返回后,您将收到通知。" + } } } }, @@ -31631,6 +34164,12 @@ "state" : "translated", "value" : "Ваш регион има %lld%% циклус рада. MQTT се не препоручује када сте ограничени циклусом рада, јер ће додатни саобраћај брзо преоптеретити вашу LoRa мрежу." } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "您所在地区的占空比为 %lld%%。在占空比受限的情况下,不建议使用 MQTT,因为额外的流量会很快压垮您的 LoRa 网格。" + } } } }, @@ -31656,4 +34195,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 238ed7d6..f2b3a524 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -37,6 +37,8 @@ 6DA39D8E2A92DC52007E311C /* MeshtasticAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DA39D8D2A92DC52007E311C /* MeshtasticAppDelegate.swift */; }; 6DEDA55A2A957B8E00321D2E /* DetectionSensorLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */; }; 6DEDA55C2A9592F900321D2E /* MessageEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */; }; + 8D3F8A3F2D44BB02009EAAA4 /* PowerMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3F8A3E2D44BB02009EAAA4 /* PowerMetrics.swift */; }; + 8D3F8A412D44C2A6009EAAA4 /* PowerMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3F8A402D44C2A6009EAAA4 /* PowerMetricsLog.swift */; }; B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B399E8A32B6F486400E4488E /* RetryButton.swift */; }; B3E905B12B71F7F300654D07 /* TextMessageField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E905B02B71F7F300654D07 /* TextMessageField.swift */; }; BCB613812C67290800485544 /* SendWaypointIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613802C67290800485544 /* SendWaypointIntent.swift */; }; @@ -290,6 +292,9 @@ 6DA39D8D2A92DC52007E311C /* MeshtasticAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticAppDelegate.swift; sourceTree = ""; }; 6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectionSensorLog.swift; sourceTree = ""; }; 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageEntityExtension.swift; sourceTree = ""; }; + 8D3F8A3D2D44B137009EAAA4 /* MeshtasticDataModelV 49.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 49.xcdatamodel"; sourceTree = ""; }; + 8D3F8A3E2D44BB02009EAAA4 /* PowerMetrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowerMetrics.swift; sourceTree = ""; }; + 8D3F8A402D44C2A6009EAAA4 /* PowerMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowerMetricsLog.swift; sourceTree = ""; }; B399E8A32B6F486400E4488E /* RetryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryButton.swift; sourceTree = ""; }; B3E905B02B71F7F300654D07 /* TextMessageField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextMessageField.swift; sourceTree = ""; }; BCB613802C67290800485544 /* SendWaypointIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendWaypointIntent.swift; sourceTree = ""; }; @@ -706,6 +711,7 @@ 6DEDA5592A957B8E00321D2E /* DetectionSensorLog.swift */, DD15E4F42B8BFC8E00654F61 /* PaxCounterLog.swift */, DDE5B4032B2279A700FCDD05 /* TraceRouteLog.swift */, + 8D3F8A402D44C2A6009EAAA4 /* PowerMetricsLog.swift */, ); path = Nodes; sourceTree = ""; @@ -1022,6 +1028,7 @@ DDF45C332BC1A48E005ED5F2 /* MQTTIcon.swift */, DD5E523D298F5A7D00D21B61 /* Weather */, DD6F65712C6AB8EC0053C113 /* SecureInput.swift */, + 8D3F8A3E2D44BB02009EAAA4 /* PowerMetrics.swift */, ); path = Helpers; sourceTree = ""; @@ -1476,6 +1483,7 @@ DD1925B928CDA93900720036 /* SerialConfigEnums.swift in Sources */, 251926852C3BA97800249DF5 /* FavoriteNodeButton.swift in Sources */, D9C983A02B79D0E800BDBE6A /* AlertButton.swift in Sources */, + 8D3F8A412D44C2A6009EAAA4 /* PowerMetricsLog.swift in Sources */, DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */, DD6F65762C6EA5490053C113 /* AckErrors.swift in Sources */, DDDB445029F8AC9C00EE2349 /* UIImage.swift in Sources */, @@ -1492,6 +1500,7 @@ DDB6ABE428B13FFF00384BA1 /* DisplayEnums.swift in Sources */, DD4975A52B147BA90026544E /* AmbientLightingConfig.swift in Sources */, D93068D92B81509C0066FBC8 /* TapbackResponses.swift in Sources */, + 8D3F8A3F2D44BB02009EAAA4 /* PowerMetrics.swift in Sources */, 2519268A2C3BB1B200249DF5 /* ExchangePositionsButton.swift in Sources */, DD86D40A287F04F100BAEB7A /* InvalidVersion.swift in Sources */, DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */, @@ -1755,7 +1764,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.5.17; + MARKETING_VERSION = 2.5.18; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1789,7 +1798,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.5.17; + MARKETING_VERSION = 2.5.18; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1821,7 +1830,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.5.17; + MARKETING_VERSION = 2.5.18; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1854,7 +1863,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.5.17; + MARKETING_VERSION = 2.5.18; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1966,6 +1975,7 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + 8D3F8A3D2D44B137009EAAA4 /* MeshtasticDataModelV 49.xcdatamodel */, DDA28B1B2D32C89200EF726F /* MeshtasticDataModelV 48.xcdatamodel */, DDDFE7402D0D4A070044463C /* MeshtasticDataModelV 47.xcdatamodel */, DD0BE30C2CB785D8000BA445 /* MeshtasticDataModelV 46.xcdatamodel */, @@ -2015,7 +2025,7 @@ DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = DDA28B1B2D32C89200EF726F /* MeshtasticDataModelV 48.xcdatamodel */; + currentVersion = 8D3F8A3D2D44B137009EAAA4 /* MeshtasticDataModelV 49.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Export/WriteCsvFile.swift b/Meshtastic/Export/WriteCsvFile.swift index 56776554..40913ea7 100644 --- a/Meshtastic/Export/WriteCsvFile.swift +++ b/Meshtastic/Export/WriteCsvFile.swift @@ -46,6 +46,25 @@ func telemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin csvString += ", " csvString += dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized } + } else if metricsType == 2 { + // Create Power Metrics Header + csvString = "Channel 1 Voltage, Channel 1 Current, Channel 2 Voltage, Channel 2 Current, Channel 3 Voltage, Channel 3 Current, \("timestamp".localized)" + for dm in telemetry where dm.metricsType == 2 { + csvString += "\n" + csvString += String(dm.powerCh1Voltage) + csvString += ", " + csvString += String(dm.powerCh1Current) + csvString += ", " + csvString += String(dm.powerCh2Voltage) + csvString += ", " + csvString += String(dm.powerCh2Current) + csvString += ", " + csvString += String(dm.powerCh3Voltage) + csvString += ", " + csvString += String(dm.powerCh3Current) + csvString += ", " + csvString += dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized + } } return csvString } diff --git a/Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift b/Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift index 7d313191..b1bbb8c6 100644 --- a/Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift @@ -22,6 +22,10 @@ extension NodeInfoEntity { return self.telemetries?.filtered(using: NSPredicate(format: "metricsType == 1")).lastObject as? TelemetryEntity } + var latestPowerMetrics: TelemetryEntity? { + return self.telemetries?.filtered(using: NSPredicate(format: "metricsType == 2")).lastObject as? TelemetryEntity + } + var hasPositions: Bool { return self.positions?.count ?? 0 > 0 } @@ -39,6 +43,11 @@ extension NodeInfoEntity { return user?.sensorMessageList.count ?? 0 > 0 } + var hasPowerMetrics: Bool { + let powerMetrics = telemetries?.filter { ($0 as AnyObject).metricsType == 2 } + return powerMetrics?.count ?? 0 > 0 + } + var hasTraceRoutes: Bool { let routes = traceRoutes?.filter { ($0 as AnyObject).response } return routes?.count ?? 0 > 0 diff --git a/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift b/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift index 90b15b5e..a9eea507 100644 --- a/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift @@ -13,7 +13,7 @@ import SwiftUI extension PositionEntity { - static func allPositionsFetchRequest() -> NSFetchRequest { + @MainActor static func allPositionsFetchRequest() -> NSFetchRequest { let request: NSFetchRequest = PositionEntity.fetchRequest() request.fetchLimit = 1000 request.returnsObjectsAsFaults = false @@ -22,9 +22,9 @@ extension PositionEntity { request.sortDescriptors = [NSSortDescriptor(key: "time", ascending: false)] let positionPredicate = NSPredicate(format: "nodePosition != nil && (nodePosition.user.shortName != nil || nodePosition.user.shortName != '') && latest == true") - let pointOfInterest = LocationHelper.currentLocation + let pointOfInterest = LocationsHandler.currentLocation - if pointOfInterest.latitude != LocationHelper.DefaultLocation.latitude && pointOfInterest.longitude != LocationHelper.DefaultLocation.longitude { + if pointOfInterest.latitude != LocationsHandler.DefaultLocation.latitude && pointOfInterest.longitude != LocationsHandler.DefaultLocation.longitude { let d: Double = UserDefaults.meshMapDistance * 1.1 let r: Double = 6371009 let meanLatitidue = pointOfInterest.latitude * .pi / 180 @@ -88,7 +88,7 @@ extension PositionEntity { } extension PositionEntity: MKAnnotation { - public var coordinate: CLLocationCoordinate2D { nodeCoordinate ?? LocationHelper.DefaultLocation } + public var coordinate: CLLocationCoordinate2D { nodeCoordinate ?? LocationsHandler.DefaultLocation } public var title: String? { nodePosition?.user?.shortName ?? "unknown".localized } public var subtitle: String? { time?.formatted() } } diff --git a/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift b/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift index b17ccecf..2f538b62 100644 --- a/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift @@ -60,7 +60,7 @@ extension WaypointEntity { } extension WaypointEntity: MKAnnotation { - public var coordinate: CLLocationCoordinate2D { waypointCoordinate ?? LocationHelper.DefaultLocation } + public var coordinate: CLLocationCoordinate2D { waypointCoordinate ?? LocationsHandler.DefaultLocation } public var title: String? { name ?? "Dropped Pin" } public var subtitle: String? { (longDescription ?? "") + diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 1afb59be..b81c3247 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -27,7 +27,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate @Published var automaticallyReconnect: Bool = true @Published var mqttProxyConnected: Bool = false @Published var mqttError: String = "" - public var minimumVersion = "2.3.2" + public var minimumVersion = "2.3.15" public var connectedVersion: String public var isConnecting: Bool = false public var isConnected: Bool = false @@ -960,6 +960,22 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate } traceRoute?.hops = NSOrderedSet(array: hopNodes) traceRoute?.time = Date() + + if let tr = traceRoute { + let manager = LocalNotificationManager() + manager.notifications = [ + Notification( + id: (UUID().uuidString), + title: "Traceroute Complete", + subtitle: "TR received back from \(destinationHop.name ?? "unknown")", + content: "Hops from: \(tr.hopsTowards), Hops back: \(tr.hopsBack)\n\(tr.routeText ?? "unknown".localized)\n\(tr.routeBackText ?? "unknown".localized)", + target: "nodes", + path: "meshtastic:///nodes?nodenum=\(connectedNode.user?.num ?? 0)" + ) + ] + manager.schedule() + } + do { try context.save() Logger.data.info("💾 Saved Trace Route") diff --git a/Meshtastic/Helpers/LocalNotificationManager.swift b/Meshtastic/Helpers/LocalNotificationManager.swift index 7eb830c3..817c90a4 100644 --- a/Meshtastic/Helpers/LocalNotificationManager.swift +++ b/Meshtastic/Helpers/LocalNotificationManager.swift @@ -71,8 +71,7 @@ class LocalNotificationManager { content.sound = UNNotificationSound.defaultCritical } - let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false) - let request = UNNotificationRequest(identifier: notification.id, content: content, trigger: trigger) + let request = UNNotificationRequest(identifier: notification.id, content: content, trigger: nil) UNUserNotificationCenter.current().add(request) { error in if let error { diff --git a/Meshtastic/Helpers/LocationsHandler.swift b/Meshtastic/Helpers/LocationsHandler.swift index a215667b..99d8b41a 100644 --- a/Meshtastic/Helpers/LocationsHandler.swift +++ b/Meshtastic/Helpers/LocationsHandler.swift @@ -113,6 +113,12 @@ import OSLog } static let DefaultLocation = CLLocationCoordinate2D(latitude: 37.3346, longitude: -122.0090) + static var currentLocation: CLLocationCoordinate2D { + guard let location = shared.manager.location else { + return DefaultLocation + } + return location.coordinate + } static var satsInView: Int { var sats = 0 @@ -139,4 +145,6 @@ import OSLog } return sats } + + } diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index d7a80c17..b792db29 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -682,7 +682,8 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage let logString = String.localizedStringWithFormat("mesh.log.telemetry.received %@".localized, String(packet.from)) MeshLogger.log("📈 \(logString)") - if telemetryMessage.variant != Telemetry.OneOf_Variant.deviceMetrics(telemetryMessage.deviceMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.environmentMetrics(telemetryMessage.environmentMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.localStats(telemetryMessage.localStats) { + + if telemetryMessage.variant != Telemetry.OneOf_Variant.deviceMetrics(telemetryMessage.deviceMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.environmentMetrics(telemetryMessage.environmentMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.localStats(telemetryMessage.localStats) && telemetryMessage.variant != Telemetry.OneOf_Variant.powerMetrics(telemetryMessage.powerMetrics) { /// Other unhandled telemetry packets return } @@ -736,6 +737,38 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage telemetry.numTotalNodes = Int32(truncatingIfNeeded: telemetryMessage.localStats.numTotalNodes) telemetry.metricsType = 4 Logger.statistics.info("📈 [Mesh Statistics] Channel Utilization: \(telemetryMessage.localStats.channelUtilization, privacy: .public) Airtime: \(telemetryMessage.localStats.airUtilTx, privacy: .public) Packets Sent: \(telemetryMessage.localStats.numPacketsTx, privacy: .public) Packets Received: \(telemetryMessage.localStats.numPacketsRx, privacy: .public) Bad Packets Received: \(telemetryMessage.localStats.numPacketsRxBad, privacy: .public) Nodes Online: \(telemetryMessage.localStats.numOnlineNodes, privacy: .public) of \(telemetryMessage.localStats.numTotalNodes, privacy: .public) nodes for Node: \(packet.from.toHex(), privacy: .public)") + } else if telemetryMessage.variant == Telemetry.OneOf_Variant.powerMetrics(telemetryMessage.powerMetrics) { + Logger.data.info("📈 [Power Metrics] Received for Node: \(packet.from.toHex(), privacy: .public)") + + if telemetryMessage.powerMetrics.hasCh1Voltage { + telemetry.powerCh1Voltage = telemetryMessage.powerMetrics.ch1Voltage + telemetry.metricsType = 2 + } + + if telemetryMessage.powerMetrics.hasCh1Current { + telemetry.powerCh1Current = telemetryMessage.powerMetrics.ch1Current + telemetry.metricsType = 2 + } + + if telemetryMessage.powerMetrics.hasCh2Voltage { + telemetry.powerCh2Voltage = telemetryMessage.powerMetrics.ch2Voltage + telemetry.metricsType = 2 + } + + if telemetryMessage.powerMetrics.hasCh1Current { + telemetry.powerCh2Current = telemetryMessage.powerMetrics.ch2Current + telemetry.metricsType = 2 + } + + if telemetryMessage.powerMetrics.hasCh3Voltage { + telemetry.powerCh3Voltage = telemetryMessage.powerMetrics.ch3Voltage + telemetry.metricsType = 2 + } + + if telemetryMessage.powerMetrics.hasCh3Current { + telemetry.powerCh3Current = telemetryMessage.powerMetrics.ch3Current + telemetry.metricsType = 2 + } } telemetry.snr = packet.rxSnr telemetry.rssi = packet.rxRssi diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index 98860495..eba2d41f 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -37,10 +37,7 @@ class MqttClientProxyManager { defaultServerPort = Int(fullHost.components(separatedBy: ":")[1]) ?? (useSsl ? 8883 : 1883) } } - let minimumVersion = "2.3.2" - let currentVersion = UserDefaults.firmwareVersion - let supportedVersion = minimumVersion.compare(currentVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(currentVersion, options: .numeric) == .orderedSame - + if let host = host { let port = defaultServerPort var username = node.mqttConfig?.username @@ -53,7 +50,7 @@ class MqttClientProxyManager { } let root = node.mqttConfig?.root?.count ?? 0 > 0 ? node.mqttConfig?.root : "msh" let prefix = root! - topic = prefix + (supportedVersion ? "/2/e" : "/2/c") + "/#" + topic = prefix + "/2/e" + "/#" let qos = CocoaMQTTQoS(rawValue: UInt8(1))! connect(host: host, port: port, useSsl: useSsl, username: username, password: password, topic: topic, qos: qos, cleanSession: true) } diff --git a/Meshtastic/Info.plist b/Meshtastic/Info.plist index dfbd9ce4..a0552164 100644 --- a/Meshtastic/Info.plist +++ b/Meshtastic/Info.plist @@ -71,13 +71,13 @@ NSCameraUsageDescription We use the camera to share channels using a QR Code NSLocationAlwaysAndWhenInUseUsageDescription - We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device. Route Recording uses location in the background. + We use your location to display it on the mesh map as well as to have GPS coordinates to send to the connected device. Route Recording uses location in the background. NSLocationAlwaysUsageDescription - We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device. + We use your location to display it on the mesh map as well as to have GPS coordinates to send to the connected device. NSLocationUsageDescription - We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device. + We use your location to display it on the mesh map as well as to have GPS coordinates to send to the connected device. NSLocationWhenInUseUsageDescription - We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device. + We use your location to display it on the mesh map as well as to have GPS coordinates to send to the connected device. NSSupportsLiveActivities Privacy – Bluetooth Always Usage Description diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index a702965e..0b4b8e13 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModelV 48.xcdatamodel + MeshtasticDataModelV 49.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 48.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 48.xcdatamodel/contents index 709d5943..1ad6e791 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 48.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 48.xcdatamodel/contents @@ -1,5 +1,5 @@ - + diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 49.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 49.xcdatamodel/contents new file mode 100644 index 00000000..5f12d9d0 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV 49.xcdatamodel/contents @@ -0,0 +1,493 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Views/Helpers/PowerMetrics.swift b/Meshtastic/Views/Helpers/PowerMetrics.swift new file mode 100644 index 00000000..4b4076d5 --- /dev/null +++ b/Meshtastic/Views/Helpers/PowerMetrics.swift @@ -0,0 +1,97 @@ +// +// PowerMetrics.swift +// Meshtastic +// +// Created by Matthew Davies on 1/24/25. +// + +import Foundation +import SwiftUI + +struct PowerMetrics: View { + private let gridItemLayout = Array(repeating: GridItem(.flexible(), spacing: 10), count: 2) + + let metric: TelemetryEntity + + var body: some View { + + LazyVGrid(columns: gridItemLayout) { + + + if(metric.powerCh1Voltage != nil) { + PowerMetricCompactWidget( + type: .voltage, + value: metric.powerCh1Voltage, + title: "Channel 1 Voltage" + ) + } + + if(metric.powerCh1Current != nil) { + PowerMetricCompactWidget( + type: .current, + value: metric.powerCh1Current, + title: "Channel 1 Current" + ) + } + + if(metric.powerCh2Voltage != nil) { + PowerMetricCompactWidget( + type: .voltage, + value: metric.powerCh2Voltage, + title: "Channel 2 Voltage" + ) + } + + if(metric.powerCh2Current != nil) { + PowerMetricCompactWidget( + type: .current, + value: metric.powerCh2Current, + title: "Channel 2 Current" + ) + } + + if(metric.powerCh3Voltage != nil) { + PowerMetricCompactWidget( + type: .voltage, + value: metric.powerCh3Voltage, + title: "Channel 3 Voltage" + ) + } + + if(metric.powerCh3Current != nil) { + PowerMetricCompactWidget( + type: .current, + value: metric.powerCh3Current, + title: "Channel 3 Current" + ) + } + } + } +} + +enum PowerMetricType: String { + case current = "current" + case voltage = "voltage" +} + +struct PowerMetricCompactWidget: View { + let type: PowerMetricType + let value: Float + let title: String + var body: some View { + VStack(alignment: .leading) { + HStack(spacing: 5.0) { + Image(systemName: type == .current ? "bolt.fill" : "powerplug.fill") + .foregroundColor(.accentColor) + .font(.callout) + Text(title) + .font(.caption) + } + Text("\(value, specifier: type == .current ? "%.1f" : "%.2f") \(type == .current ? "mA" : "V")") + .font(type == .current ? .system(size: 35) : .system(size: 30)) + } + .frame(minWidth: 100, idealWidth: 125, maxWidth: 150, minHeight: 120, idealHeight: 130, maxHeight: 140) + .padding() + .background(.tertiary, in: RoundedRectangle(cornerRadius: 20, style: .continuous)) + } +} diff --git a/Meshtastic/Views/Helpers/Weather/NodeWeatherForecast.swift b/Meshtastic/Views/Helpers/Weather/NodeWeatherForecast.swift index 15198c50..5fec377e 100644 --- a/Meshtastic/Views/Helpers/Weather/NodeWeatherForecast.swift +++ b/Meshtastic/Views/Helpers/Weather/NodeWeatherForecast.swift @@ -211,7 +211,7 @@ struct NodeWeatherForecast { struct NodeWeatherForecastView_Previews: PreviewProvider { static var previews: some View { - NodeWeatherForecastView(location: CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude) ) + NodeWeatherForecastView(location: CLLocation(latitude: LocationsHandler.currentLocation.latitude, longitude: LocationsHandler.currentLocation.longitude) ) .aspectRatio(2, contentMode: .fit) .padding() .previewLayout(.sizeThatFits) diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index a8986250..b0602d5f 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -346,9 +346,9 @@ struct UserList: View { } /// Distance if distanceFilter { - let pointOfInterest = LocationHelper.currentLocation + let pointOfInterest = LocationsHandler.currentLocation - if pointOfInterest.latitude != LocationHelper.DefaultLocation.latitude && pointOfInterest.longitude != LocationHelper.DefaultLocation.longitude { + if pointOfInterest.latitude != LocationsHandler.DefaultLocation.latitude && pointOfInterest.longitude != LocationsHandler.DefaultLocation.longitude { let d: Double = maxDistance * 1.1 let r: Double = 6371009 let meanLatitidue = pointOfInterest.latitude * .pi / 180 diff --git a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift index dd49484a..6a0374f2 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift @@ -85,7 +85,7 @@ struct MeshMapContent: MapContent { let nodePositions = Array(positions) as? [PositionEntity] { if showRouteLines { let routeCoords = nodePositions.compactMap({(pos) -> CLLocationCoordinate2D in - return pos.nodeCoordinate ?? LocationHelper.DefaultLocation + return pos.nodeCoordinate ?? LocationsHandler.DefaultLocation }) let gradient = LinearGradient( colors: [Color(nodeColor.lighter().lighter()), Color(nodeColor.lighter()), Color(nodeColor)], @@ -148,9 +148,9 @@ struct MeshMapContent: MapContent { ForEach(routes) { route in if let routeLocations = route.locations, let locations = Array(routeLocations) as? [LocationEntity] { let routeCoords = locations.compactMap {(loc) -> CLLocationCoordinate2D in - return loc.locationCoordinate ?? LocationHelper.DefaultLocation + return loc.locationCoordinate ?? LocationsHandler.DefaultLocation } - Annotation("Start", coordinate: routeCoords.first ?? LocationHelper.DefaultLocation) { + Annotation("Start", coordinate: routeCoords.first ?? LocationsHandler.DefaultLocation) { ZStack { Circle() .fill(Color(.green)) @@ -159,7 +159,7 @@ struct MeshMapContent: MapContent { } } .annotationTitles(.automatic) - Annotation("Finish", coordinate: routeCoords.last ?? LocationHelper.DefaultLocation) { + Annotation("Finish", coordinate: routeCoords.last ?? LocationsHandler.DefaultLocation) { ZStack { Circle() .fill(Color(.black)) diff --git a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift index 71d62590..2c2c267a 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift @@ -38,7 +38,7 @@ struct WaypointForm: View { .font(.largeTitle) Divider() Form { - let distance = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude).distance(from: CLLocation(latitude: waypoint.coordinate.latitude, longitude: waypoint.coordinate.longitude )) + let distance = CLLocation(latitude: LocationsHandler.currentLocation.latitude, longitude: LocationsHandler.currentLocation.longitude).distance(from: CLLocation(latitude: waypoint.coordinate.latitude, longitude: waypoint.coordinate.longitude )) Section(header: Text("Coordinate") ) { HStack { Text("Location:") @@ -335,8 +335,8 @@ struct WaypointForm: View { .padding(.bottom, 5) } /// Distance - if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 { - let metersAway = waypoint.coordinate.distance(from: LocationHelper.currentLocation) + if LocationsHandler.currentLocation.distance(from: LocationsHandler.DefaultLocation) > 0.0 { + let metersAway = waypoint.coordinate.distance(from: LocationsHandler.currentLocation) Label { Text("distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))") .foregroundColor(.primary) diff --git a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift index 706b9d6e..e251c64c 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift @@ -237,6 +237,15 @@ struct NodeDetail: View { } } } + if node.hasPowerMetrics && node.latestPowerMetrics != nil { + Section("Power") { + VStack { + if let metric = node.latestPowerMetrics { + PowerMetrics(metric: metric) + } + } + } + } Section("Logs") { // Metrics NavigationLink { @@ -251,6 +260,18 @@ struct NodeDetail: View { } .disabled(!node.hasDeviceMetrics) + NavigationLink{ + PowerMetricsLog(node: node) + } label: { + Label { + Text("Power Metrics Log") + } icon: { + Image(systemName: "bolt") + .symbolRenderingMode(.multicolor) + } + } + .disabled(!node.hasPowerMetrics) + NavigationLink { NodeMapSwiftUI(node: node, showUserLocation: connectedNode?.num ?? 0 == node.num) } label: { diff --git a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift index 2693f13e..8fe387db 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift @@ -114,12 +114,12 @@ struct NodeListItem: View { .font(UIDevice.current.userInterfaceIdiom == .phone ? .callout : .caption) .foregroundColor(.gray) let trueBearing = getBearingBetweenTwoPoints(point1: myCoord, point2: nodeCoord) - let headingDegrees = Angle.degrees(trueBearing) + let headingDegrees = Measurement(value: trueBearing, unit: UnitAngle.degrees).reciprocal() Image(systemName: "location.north") .font(.callout) .symbolRenderingMode(.multicolor) .clipShape(Circle()) - .rotationEffect(headingDegrees) + .rotationEffect(Angle(degrees: headingDegrees.value)) let heading = Measurement(value: trueBearing, unit: UnitAngle.degrees).reciprocal() Text("\(heading.formatted(.measurement(width: .narrow, numberFormatStyle: .number.precision(.fractionLength(0)))))") .font(UIDevice.current.userInterfaceIdiom == .phone ? .callout : .caption) diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 3bf88f7a..32e01bb2 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -418,9 +418,9 @@ struct NodeList: View { } /// Distance if distanceFilter { - let pointOfInterest = LocationHelper.currentLocation + let pointOfInterest = LocationsHandler.currentLocation - if pointOfInterest.latitude != LocationHelper.DefaultLocation.latitude && pointOfInterest.longitude != LocationHelper.DefaultLocation.longitude { + if pointOfInterest.latitude != LocationsHandler.DefaultLocation.latitude && pointOfInterest.longitude != LocationsHandler.DefaultLocation.longitude { let d: Double = maxDistance * 1.1 let r: Double = 6371009 let meanLatitidue = pointOfInterest.latitude * .pi / 180 diff --git a/Meshtastic/Views/Nodes/PowerMetricsLog.swift b/Meshtastic/Views/Nodes/PowerMetricsLog.swift new file mode 100644 index 00000000..4e0629a6 --- /dev/null +++ b/Meshtastic/Views/Nodes/PowerMetricsLog.swift @@ -0,0 +1,293 @@ +// +// PowerMetricsLog.swift +// Meshtastic +// +// Created by Matthew Davies on 1/24/25. +// + +import Foundation +import SwiftUI +import Charts +import OSLog + +struct PowerMetricsLog: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + @ObservedObject var node: NodeInfoEntity + private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom } + @State private var sortOrder = [KeyPathComparator(\TelemetryEntity.time, order: .reverse)] + @State private var selection: TelemetryEntity.ID? + @State private var chartSelection: Date? + + @State private var isPresentingClearLogConfirm: Bool = false + @State var isExporting = false + @State var exportString = "" + + @State private var channelSelection = 0 + + var powerMetrics: [TelemetryEntity] { + let telemetries = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 2")) + return (telemetries?.reversed() as? [TelemetryEntity]) ?? [] + } + + var minMax: (min: Double, max: Double) { + let allValues = powerMetrics.flatMap { [ + $0.powerCh1Voltage, + $0.powerCh1Current, + $0.powerCh2Voltage, + $0.powerCh2Current, + $0.powerCh3Voltage, + $0.powerCh3Current + ]} + + guard !allValues.isEmpty else { + return (min: -10, max: 10) + } + + return (min: floor(Double(allValues.min()!)), max: ceil(Double(allValues.max()!))) + } + + var body: some View { + VStack { + if node.hasPowerMetrics { + let oneWeekAgo = Calendar.current.date(byAdding: .day, value: -7, to: Date()) + + let chartData = powerMetrics + .filter { $0.time != nil && $0.time! >= oneWeekAgo! } + .sorted { $0.time! < $1.time! } + if chartData.count > 0 { + GroupBox(label: Label("\(powerMetrics.count) Readings Total", systemImage: "chart.xyaxis.line")) { + + // allow switching between different channels + Picker("Select Channel", selection: $channelSelection) { + Text("Channel 1").tag(0) + Text("Channel 2").tag(1) + Text("Channel 3").tag(2) + } + + Chart { + ForEach(chartData, id: \.self) { point in + + let voltage = channelSelection == 0 ? point.powerCh1Voltage : channelSelection == 1 ? point.powerCh2Voltage : point.powerCh3Voltage + let current = channelSelection == 0 ? point.powerCh1Current : channelSelection == 1 ? point.powerCh2Current : point.powerCh3Current + + LineMark( + x: .value("Time", point.time ?? Date()), + y: .value("Voltage", voltage) + ) + .foregroundStyle(by: .value("Series", "Voltage")) + .interpolationMethod(.linear) + .accessibilityLabel("Voltage") + .accessibilityValue("X: \(point.time ?? Date()), Y: \(voltage)") + + LineMark( + x: .value("Time", point.time ?? Date()), + y: .value("Current", current) + ) + .foregroundStyle(by: .value("Series", "Current")) + .interpolationMethod(.linear) + .accessibilityLabel("Current") + .accessibilityValue("X: \(point.time ?? Date()), Y: \(current)") + + } + + if let chartSelection { + RuleMark(x: .value("Second", chartSelection, unit: .second)) + .foregroundStyle(.tertiary.opacity(0.5)) + } + + } + .chartXAxis(content: { + AxisMarks(position: .top) + }) + .chartXAxis(.automatic) + .chartXSelection(value: $chartSelection) + .chartYScale(domain: minMax.min...minMax.max) + .chartForegroundStyleScale([ + "Voltage": .blue, + "Current": .green + ]) + .chartLegend(position: .automatic, alignment: .bottom) + } + } + let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMdjmma", options: 0, locale: Locale.current) + let dateFormatString = (localeDateFormat ?? "M/d/YY j:mma").replacingOccurrences(of: ",", with: "") + + if idiom == .phone { + Table(powerMetrics, selection: $selection, sortOrder: $sortOrder) { + TableColumn("Timestamp") { m in + HStack { + Text(m.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized) + Spacer() + HStack { + VStack { + Text("Channel 1") + HStack { + Image(systemName: "powerplug.fill") + .font(.caption) + .symbolRenderingMode(.multicolor) + Text("\(String(format: "%.2f", m.powerCh1Voltage))V") + } + HStack { + Image(systemName: "bolt.fill") + .font(.caption) + .symbolRenderingMode(.multicolor) + Text("\(String(format: "%.2f", m.powerCh1Current))mA") + } + } + } + Spacer() + HStack { + VStack { + Text("Channel 2") + HStack { + Image(systemName: "powerplug.fill") + .font(.caption) + .symbolRenderingMode(.multicolor) + Text("\(String(format: "%.2f", m.powerCh2Voltage))V") + } + HStack { + Image(systemName: "bolt.fill") + .font(.caption) + .symbolRenderingMode(.multicolor) + Text("\(String(format: "%.2f", m.powerCh2Current))mA") + } + } + } + Spacer() + HStack { + VStack { + Text("Channel 3") + HStack { + Image(systemName: "powerplug.fill") + .font(.caption) + .symbolRenderingMode(.multicolor) + Text("\(String(format: "%.2f", m.powerCh3Voltage))V") + } + HStack { + Image(systemName: "bolt.fill") + .font(.caption) + .symbolRenderingMode(.multicolor) + Text("\(String(format: "%.2f", m.powerCh3Current))mA") + } + } + } + } + } + } + .onChange(of: selection) { _, newSelection in + guard let metrics = powerMetrics.first(where: { $0.id == newSelection }) else { + return + } + chartSelection = metrics.time + } + } else { + Table(powerMetrics, selection: $selection, sortOrder: $sortOrder) { + TableColumn("Ch1 Voltage") { dm in + Text("\(String(format: "%.2f", dm.powerCh1Voltage))V") + } + .width(min: 75) + TableColumn("Ch1 Current") { dm in + Text("\(String(format: "%.2f", dm.powerCh1Current))mA") + } + .width(min: 75) + TableColumn("Ch2 Voltage") { dm in + Text("\(String(format: "%.2f", dm.powerCh2Voltage))V") + } + .width(min: 75) + TableColumn("Ch2 Current") { dm in + Text("\(String(format: "%.2f", dm.powerCh2Current))mA") + } + .width(min: 75) + TableColumn("Ch3 Voltage") { dm in + Text("\(String(format: "%.2f", dm.powerCh3Voltage))V") + } + .width(min: 75) + TableColumn("Ch3 Current") { dm in + Text("\(String(format: "%.2f", dm.powerCh3Current))mA") + } + .width(min: 75) + TableColumn("timestamp") { dm in + Text(dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized) + } + .width(min: 180) + + } + .onChange(of: selection) { _, newSelection in + guard let metrics = powerMetrics.first(where: { $0.id == newSelection }) else { + return + } + chartSelection = metrics.time + } + } + HStack { + Button(role: .destructive) { + isPresentingClearLogConfirm = true + } label: { + Label("clear.log", systemImage: "trash.fill") + } + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(idiom == .phone ? .regular : .large) + .padding(.bottom) + .padding(.leading) + .confirmationDialog( + "are.you.sure", + isPresented: $isPresentingClearLogConfirm, + titleVisibility: .visible + ) { + Button("power.metrics.delete", role: .destructive) { + if clearTelemetry(destNum: node.num, metricsType: 2, context: context) { + Logger.data.notice("Cleared Power Metrics for \(node.num)") + } else { + Logger.data.error("Clear Power Metrics Log Failed") + } + } + } + + Button { + exportString = telemetryToCsvFile(telemetry: powerMetrics, metricsType: 2) + isExporting = true + } label: { + Label("save", systemImage: "square.and.arrow.down") + } + .buttonStyle(.bordered) + .buttonBorderShape(.capsule) + .controlSize(idiom == .phone ? .regular : .large) + .padding(.bottom) + .padding(.trailing) + } + .onChange(of: selection) { _, newSelection in + guard let metrics = powerMetrics.first(where: { $0.id == newSelection }) else { + return + } + chartSelection = metrics.time + } + } else { + ContentUnavailableView("No Power Metrics", systemImage: "slash.circle") + } + } + .navigationTitle("power.metrics.log") + .navigationBarTitleDisplayMode(.inline) + .navigationBarItems(trailing: + ZStack { + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") + }) + .fileExporter( + isPresented: $isExporting, + document: CsvDocument(emptyCsv: exportString), + contentType: .commaSeparatedText, + defaultFilename: String("\(node.user?.longName ?? "Node") \("power.metrics.log".localized)"), + onCompletion: { result in + switch result { + case .success: + self.isExporting = false + Logger.services.info("Power metrics log download succeeded.") + case .failure(let error): + Logger.services.error("Power metrics log download failed: \(error.localizedDescription)") + } + } + ) + } +} diff --git a/Meshtastic/Views/Settings/Routes.swift b/Meshtastic/Views/Settings/Routes.swift index 174221d2..74f9ac72 100644 --- a/Meshtastic/Views/Settings/Routes.swift +++ b/Meshtastic/Views/Settings/Routes.swift @@ -165,7 +165,7 @@ struct Routes: View { if selectedRoute != nil { let locationArray = selectedRoute?.locations?.array as? [LocationEntity] ?? [] let lineCoords = locationArray.compactMap({(location) -> CLLocationCoordinate2D in - return location.locationCoordinate ?? LocationHelper.DefaultLocation + return location.locationCoordinate ?? LocationsHandler.DefaultLocation }) Form { TextField( @@ -248,7 +248,7 @@ struct Routes: View { hasChanges = true } Map { - Annotation("Start", coordinate: lineCoords.first ?? LocationHelper.DefaultLocation) { + Annotation("Start", coordinate: lineCoords.first ?? LocationsHandler.DefaultLocation) { ZStack { Circle() .fill(Color(.green)) @@ -257,7 +257,7 @@ struct Routes: View { } } .annotationTitles(.automatic) - Annotation("Finish", coordinate: lineCoords.last ?? LocationHelper.DefaultLocation) { + Annotation("Finish", coordinate: lineCoords.last ?? LocationsHandler.DefaultLocation) { ZStack { Circle() .fill(Color(.black))