diff --git a/Meshtastic/Enums/BluetoothModes.swift b/Meshtastic/Enums/BluetoothModes.swift index c631defa..69f926e6 100644 --- a/Meshtastic/Enums/BluetoothModes.swift +++ b/Meshtastic/Enums/BluetoothModes.swift @@ -4,6 +4,7 @@ // // Copyright(c) Garth Vander Houwen 8/19/22. // +import Foundation enum BluetoothModes: Int, CaseIterable, Identifiable { @@ -16,11 +17,11 @@ enum BluetoothModes: Int, CaseIterable, Identifiable { get { switch self { case .randomPin: - return "Random PIN" + return NSLocalizedString("bluetooth.mode.randompin", comment: "Random PIN") case .fixedPin: - return "Fixed PIN" + return NSLocalizedString("bluetooth.mode.fixedpin", comment: "Fixed PIN") case .noPin: - return "No PIN (Just Works)" + return NSLocalizedString("bluetooth.mode.nopin", comment: "No PIN (Just Works)") } } } diff --git a/Meshtastic/Enums/CannedMessagesConfigEnums.swift b/Meshtastic/Enums/CannedMessagesConfigEnums.swift index 217bcd60..565e375d 100644 --- a/Meshtastic/Enums/CannedMessagesConfigEnums.swift +++ b/Meshtastic/Enums/CannedMessagesConfigEnums.swift @@ -4,6 +4,7 @@ // // Copyright(c) Garth Vander Houwen 9/10/22. // +import Foundation // Default of 0 is unset enum ConfigPresets : Int, CaseIterable, Identifiable { @@ -18,11 +19,11 @@ enum ConfigPresets : Int, CaseIterable, Identifiable { switch self { case .unset: - return "Manual Configuration" + return NSLocalizedString("canned.messages.preset.manual", comment: "Manual Configuration") case .rakRotaryEncoder: - return "RAK Rotary Encoder Module" + return NSLocalizedString("canned.messages.preset.rakrotary", comment: "RAK Rotary Encoder Module") case .cardKB: - return "M5 Stack Card KB / RAK Keypad" + return NSLocalizedString("canned.messages.preset.cardkb", comment: "M5 Stack Card KB / RAK Keypad") } } } @@ -46,21 +47,21 @@ enum InputEventChars: Int, CaseIterable, Identifiable { switch self { case .none: - return "None" + return NSLocalizedString("inputevent.none", comment: "None") case .up: - return "Up" + return NSLocalizedString("inputevent.up", comment: "Up") case .down: - return "Down" + return NSLocalizedString("inputevent.down", comment: "Down") case .left: - return "Left" + return NSLocalizedString("inputevent.left", comment: "Left") case .right: - return "Right" + return NSLocalizedString("inputevent.right", comment: "Right") case .select: - return "Select" + return NSLocalizedString("inputevent.select", comment: "Select") case .back: - return "Back" + return NSLocalizedString("inputevent.back", comment: "Back") case .cancel: - return "Cancel" + return NSLocalizedString("inputevent.cancel", comment: "Cancel") } } } diff --git a/Meshtastic/Enums/ChannelRoles.swift b/Meshtastic/Enums/ChannelRoles.swift index ac5ad574..83bb30a7 100644 --- a/Meshtastic/Enums/ChannelRoles.swift +++ b/Meshtastic/Enums/ChannelRoles.swift @@ -4,6 +4,7 @@ // // Copyright(c) Garth Vander Houwen 9/21/22. // +import Foundation // Default of 0 is Client enum ChannelRoles: Int, CaseIterable, Identifiable { @@ -18,11 +19,11 @@ enum ChannelRoles: Int, CaseIterable, Identifiable { switch self { case .disabled: - return "Disabled" + return NSLocalizedString("channel.role.disabled", comment: "Disabled") case .primary: - return "Primary" + return NSLocalizedString("channel.role.primary", comment: "Primary") case .secondary: - return "Secondary" + return NSLocalizedString("channel.role.secondary", comment: "Secondary") } } } diff --git a/Meshtastic/Enums/DeviceRoles.swift b/Meshtastic/Enums/DeviceRoles.swift index ba793bd3..c1e8d18c 100644 --- a/Meshtastic/Enums/DeviceRoles.swift +++ b/Meshtastic/Enums/DeviceRoles.swift @@ -21,13 +21,13 @@ enum DeviceRoles: Int, CaseIterable, Identifiable { switch self { case .client: - return "Client (default) - App connected client." + return NSLocalizedString("device.role.client", comment: "Client (default) - App connected client.") case .clientMute: - return "Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh." + return NSLocalizedString("device.role.clientmute", comment: "Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh.") case .router: - return "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep." + return NSLocalizedString("device.role.router", comment: "Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep.") case .routerClient: - return "Router Client - Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client." + return NSLocalizedString("device.role.routerclient", comment: "Router Client - Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client.") } } } diff --git a/Meshtastic/Enums/DisplayEnums.swift b/Meshtastic/Enums/DisplayEnums.swift index a1a6103b..46110532 100644 --- a/Meshtastic/Enums/DisplayEnums.swift +++ b/Meshtastic/Enums/DisplayEnums.swift @@ -49,23 +49,24 @@ enum ScreenOnIntervals: Int, CaseIterable, Identifiable { get { switch self { case .oneMinute: - return "One Minute" + return NSLocalizedString("interval.one.minute", comment: "One Minute") case .fiveMinutes: - return "Five Minutes" + return NSLocalizedString("interval.five.minutes", comment: "Five Minutes") case .tenMinutes: - return "Ten Minutes" + return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes") case .fifteenMinutes: - return "Fifteen Minutes" + return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes") case .thirtyMinutes: - return "Thirty Minutes" + return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes") case .oneHour: - return "One Hour" + return NSLocalizedString("interval.one.hour", comment: "One Hour") case .max: - return "Always On" + return NSLocalizedString("always.on", comment: "Always On") } } } } + // Default of 0 is off enum ScreenCarouselIntervals: Int, CaseIterable, Identifiable { @@ -81,17 +82,17 @@ enum ScreenCarouselIntervals: Int, CaseIterable, Identifiable { get { switch self { case .off: - return "Off" + return NSLocalizedString("off", comment: "Off") case .thirtySeconds: - return "Thirty Seconds" + return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds") case .oneMinute: - return "One Minute" + return NSLocalizedString("interval.one.minute", comment: "One Minute") case .fiveMinutes: - return "Five Minutes" + return NSLocalizedString("interval.five.minutes", comment: "Five Minutes") case .tenMinutes: - return "Ten Minutes" + return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes") case .fifteenMinutes: - return "Fifteen Minutes" + return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes") } } } @@ -109,7 +110,7 @@ enum OledTypes: Int, CaseIterable, Identifiable { get { switch self { case .auto: - return "Automatic Detection" + return NSLocalizedString("automatic.detection", comment: "Automatic Detection") case .ssd1306: return "SSD 1306" case .sh1106: diff --git a/Meshtastic/Enums/MessagingEnums.swift b/Meshtastic/Enums/MessagingEnums.swift index 251fbb6f..9b9f145d 100644 --- a/Meshtastic/Enums/MessagingEnums.swift +++ b/Meshtastic/Enums/MessagingEnums.swift @@ -4,6 +4,7 @@ // // Copyright(c) Garth Vander Houwen 9/30/22. // +import Foundation enum BubblePosition { case left @@ -45,19 +46,19 @@ enum Tapbacks: Int, CaseIterable, Identifiable { get { switch self { case .heart: - return "Heart" + return NSLocalizedString("tapback.heart", comment: "Heart") case .thumbsUp: - return "Thumbs Up" + return NSLocalizedString("tapback.thumbsup", comment: "Thumbs Up") case .thumbsDown: - return "Thumbs Down" + return NSLocalizedString("tapback.thumbsdown", comment: "Thumbs Down") case .haHa: - return "HaHa" + return NSLocalizedString("tapback.haha", comment: "HaHa") case .exclamation: - return "Exclamation Mark" + return NSLocalizedString("tapback.exclamation", comment: "Exclamation Mark") case .question: - return "Question Mark" + return NSLocalizedString("tapback.question", comment: "Question Mark") case .poop: - return "Poop" + return NSLocalizedString("tapback.poop", comment: "Poop") } } } diff --git a/Meshtastic/Enums/PositionConfigEnums.swift b/Meshtastic/Enums/PositionConfigEnums.swift index b70ed62a..081420f0 100644 --- a/Meshtastic/Enums/PositionConfigEnums.swift +++ b/Meshtastic/Enums/PositionConfigEnums.swift @@ -27,27 +27,27 @@ enum PositionBroadcastIntervals: Int, CaseIterable, Identifiable { switch self { case .fifteenSeconds: - return "Fifteen Seconds" + return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds") case .thirtySeconds: - return "Thirty Seconds" + return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds") case .oneMinute: - return "One Minute" + return NSLocalizedString("interval.one.minute", comment: "One Minute") case .fiveMinutes: - return "Five Minutes" + return NSLocalizedString("interval.five.minutes", comment: "Five Minutes") case .tenMinutes: - return "Ten Minutes" + return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes") case .fifteenMinutes: - return "Fifteen Minutes" + return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes") case .thirtyMinutes: - return "Thirty Minutes" + return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes") case .oneHour: - return "One Hour" + return NSLocalizedString("interval.one.hour", comment: "One Hour") case .sixHours: - return "Six Hours" + return NSLocalizedString("interval.six.hours", comment: "Six Hours") case .twelveHours: - return "Twelve Hours" + return NSLocalizedString("interval.twelve.hours", comment: "Twelve Hours") case .twentyFourHours: - return "Twenty Four Hours" + return NSLocalizedString("interval.twentyfour.hours", comment: "Twenty Four Hours") } } } @@ -67,17 +67,17 @@ enum GpsFormats: Int, CaseIterable, Identifiable { get { switch self { case .gpsFormatDec: - return "Decimal Degrees Format" + return NSLocalizedString("gpsformat.dec", comment: "Decimal Degrees Format") case .gpsFormatDms: - return "Degrees Minutes Seconds" + return NSLocalizedString("gpsformat.dms", comment: "Degrees Minutes Seconds") case .gpsFormatUtm: - return "Universal Transverse Mercator" + return NSLocalizedString("gpsformat.utm", comment: "Universal Transverse Mercator") case .gpsFormatMgrs: - return "Military Grid Reference System" + return NSLocalizedString("gpsformat.mgrs", comment: "Military Grid Reference System") case .gpsFormatOlc: - return "Open Location Code (aka Plus Codes)" + return NSLocalizedString("gpsformat.olc", comment: "Open Location Code (aka Plus Codes)") case .gpsFormatOsgr: - return "Ordnance Survey Grid Reference" + return NSLocalizedString("gpsformat.osgr", comment: "Ordnance Survey Grid Reference") } } } @@ -128,39 +128,39 @@ enum GpsUpdateIntervals: Int, CaseIterable, Identifiable { switch self { case .fiveSeconds: - return "Five Seconds" + return NSLocalizedString("interval.five.seconds", comment: "Five Seconds") case .tenSeconds: - return "Ten Seconds" + return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds") case .fifteenSeconds: - return "Fifteen Seconds" + return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds") case .twentySeconds: - return "Twenty Seconds" + return NSLocalizedString("interval.twenty.seconds", comment: "Twenty Seconds") case .twentyFiveSeconds: - return "Twenty Five Seconds" + return NSLocalizedString("interval.twentyfive.seconds", comment: "Twenty Five Seconds") case .thirtySeconds: - return "Thirty Seconds" + return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds") case .oneMinute: - return "One Minute" + return NSLocalizedString("interval.one.minute", comment: "One Minute") case .twoMinutes: - return "Two Minutes" + return NSLocalizedString("interval.two.minutes", comment: "Two Minutes") case .fiveMinutes: - return "Five Minutes" + return NSLocalizedString("interval.five.minutes", comment: "Five Minutes") case .tenMinutes: - return "Ten Minutes" + return NSLocalizedString("interval.ten.minutes", comment: "Ten Minutes") case .fifteenMinutes: - return "Fifteen Minutes" + return NSLocalizedString("interval.fifteen.minutes", comment: "Fifteen Minutes") case .thirtyMinutes: - return "Thirty Minutes" + return NSLocalizedString("interval.thirty.minutes", comment: "Thirty Minutes") case .oneHour: - return "One Hour" + return NSLocalizedString("interval.one.hour", comment: "One Hour") case .sixHours: - return "Six Hours" + return NSLocalizedString("interval.six.hours", comment: "Six Hours") case .twelveHours: - return "Twelve Hours" + return NSLocalizedString("interval.twelve.hours", comment: "Twelve Hours") case .twentyFourHours: - return "Twenty Four Hours" + return NSLocalizedString("interval.twentyfour.hours", comment: "Twenty Four Hours") case .maxInt32: - return "On Boot Only" + return NSLocalizedString("on.boot", comment: "On Boot Only") } } } @@ -168,10 +168,7 @@ enum GpsUpdateIntervals: Int, CaseIterable, Identifiable { enum GpsAttemptTimes: Int, CaseIterable, Identifiable { - case oneSecond = 1 case twoSeconds = 2 - case threeSeconds = 3 - case fourSeconds = 4 case fiveSeconds = 5 case tenSeconds = 10 case fifteenSeconds = 15 @@ -179,6 +176,7 @@ enum GpsAttemptTimes: Int, CaseIterable, Identifiable { case twentyFiveSeconds = 25 case thirtySeconds = 30 case oneMinute = 60 + case twoMinutes = 120 case fiveMinutes = 300 var id: Int { self.rawValue } @@ -186,30 +184,26 @@ enum GpsAttemptTimes: Int, CaseIterable, Identifiable { get { switch self { - case .oneSecond: - return "One Seconds" case .twoSeconds: - return "Two Seconds" - case .threeSeconds: - return "Three Seconds" - case .fourSeconds: - return "Four Seconds" + return NSLocalizedString("interval.two.seconds", comment: "Two Seconds") case .fiveSeconds: - return "Five Seconds" + return NSLocalizedString("interval.five.seconds", comment: "Five Seconds") case .tenSeconds: - return "Ten Seconds" + return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds") case .fifteenSeconds: - return "Fifteen Seconds" + return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds") case .twentySeconds: - return "Twenty Seconds" + return NSLocalizedString("interval.twenty.seconds", comment: "Twenty Seconds") case .twentyFiveSeconds: - return "Twenty Five Seconds" + return NSLocalizedString("interval.twentyfive.seconds", comment: "Twenty Five Seconds") case .thirtySeconds: - return "Thirty Seconds" + return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds") case .oneMinute: - return "One Minute" + return NSLocalizedString("interval.one.minute", comment: "One Minute") + case .twoMinutes: + return NSLocalizedString("interval.two.minutes", comment: "Two Minutes") case .fiveMinutes: - return "Five Minutes" + return NSLocalizedString("interval.five.minutes", comment: "Five Minutes") } } } diff --git a/Meshtastic/Enums/RoutingError.swift b/Meshtastic/Enums/RoutingError.swift index b242f45f..390ad5a6 100644 --- a/Meshtastic/Enums/RoutingError.swift +++ b/Meshtastic/Enums/RoutingError.swift @@ -4,6 +4,7 @@ // // Copyright(c) Garth Vander Houwen 8/4/22. // +import Foundation enum RoutingError: Int, CaseIterable, Identifiable { @@ -16,6 +17,7 @@ enum RoutingError: Int, CaseIterable, Identifiable { case noChannel = 6 case tooLarge = 7 case noResponse = 8 + case dutyCycleLimit = 9 case badRequest = 32 case notAuthorized = 33 @@ -25,56 +27,29 @@ enum RoutingError: Int, CaseIterable, Identifiable { switch self { case .none: - return "No Error." + return NSLocalizedString("routing.acknowledged", comment: "Acknowledged") case .noRoute: - return "No Route" + return NSLocalizedString("routing.noroute", comment: "No Route") case .gotNak: - return "Received a nak" + return NSLocalizedString("routing.gotnak", comment: "Received a negative acknowledgment") case .timeout: - return "Timeout" + return NSLocalizedString("routing.timeout", comment: "Timeout") case .noInterface: - return "No Interface" + return NSLocalizedString("routing.nointerface", comment: "No Interface") case .maxRetransmit: - return "Max Retransmission Reached" + return NSLocalizedString("routing.maxretransmit", comment: "Max Retransmission Reached") case .noChannel: - return "No Channel" + return NSLocalizedString("routing.nochannel", comment: "No Channel") case .tooLarge: - return "The packet is too large" + return NSLocalizedString("routing.toolarge", comment: "The packet is too large") case .noResponse: - return "No Response" + return NSLocalizedString("routing.noresponse", comment: "No Response") + case .dutyCycleLimit: + return NSLocalizedString("routing.dutycyclelimit", comment: "Regional Duty Cycle Limit Reached") case .badRequest: - return "Bad Request" + return NSLocalizedString("routing.badRequest", comment: "Bad Request") case .notAuthorized: - return "Not Authorized" - } - } - } - var description: String { - get { - switch self { - - case .none: - return "This message is not a failure." - case .noRoute: - return "Our node doesn't have a route to the requested destination anymore." - case .gotNak: - return "We received a nak while trying to forward on your behalf." - case .timeout: - return "We timed out while attempting to route this packet." - case .noInterface: - return "No suitable interface could be found for delivering this packet." - case .maxRetransmit: - return "We reached the max retransmission count (Hop Limit) and have received no responses." - case .noChannel: - return "No suitable channel was found for sending this packet (i.e. was requested channel index disabled?)." - case .tooLarge: - return "The packet was too big for sending (exceeds interface MTU after encoding)." - case .noResponse: - return "The request had want_response set, the request reached the destination node, but no service on that node wants to send a response (possibly due to bad channel permissions)." - case .badRequest: - return "The application layer service on the remote node received your request, but considered your request somehow invalid." - case .notAuthorized: - return "The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel)." + return NSLocalizedString("routing.notauthorized", comment: "Not Authorized") } } } @@ -100,10 +75,13 @@ enum RoutingError: Int, CaseIterable, Identifiable { return Routing.Error.tooLarge case .noResponse: return Routing.Error.noResponse + case .dutyCycleLimit: + return Routing.Error.dutyCycleLimit case .badRequest: return Routing.Error.badRequest case .notAuthorized: return Routing.Error.notAuthorized + } } } diff --git a/Meshtastic/Enums/SerialConfigEnums.swift b/Meshtastic/Enums/SerialConfigEnums.swift index 81e4d5b6..f6c4dded 100644 --- a/Meshtastic/Enums/SerialConfigEnums.swift +++ b/Meshtastic/Enums/SerialConfigEnums.swift @@ -4,6 +4,7 @@ // // Copyright(c) Garth Vander Houwen 9/10/22. // +import Foundation enum SerialBaudRates: Int, CaseIterable, Identifiable { @@ -30,7 +31,7 @@ enum SerialBaudRates: Int, CaseIterable, Identifiable { switch self { case .baudDefault: - return "Baud Default" + return NSLocalizedString("default", comment: "Default") case .baud110: return "110 Baud" case .baud300: @@ -118,15 +119,15 @@ enum SerialModeTypes: Int, CaseIterable, Identifiable { get { switch self { case .default: - return "Default" + return NSLocalizedString("serial.mode.default", comment: "Default") case .simple: - return "Simple" + return NSLocalizedString("serial.mode.simple", comment: "Simple") case .proto: - return "Protobufs" + return NSLocalizedString("serial.mode.proto", comment: "Protobufs") case .txtmsg: - return "Text Message" + return NSLocalizedString("serial.mode.txtmsg", comment: "Text Message") case .nmea: - return "NMEA Positions" + return NSLocalizedString("serial.mode.nmea", comment: "NMEA Positions") } } } @@ -166,20 +167,19 @@ enum SerialTimeoutIntervals: Int, CaseIterable, Identifiable { case .unset: return "Unset" case .oneSecond: - return "One Second" + return NSLocalizedString("interval.one.second", comment: "One Second") case .fiveSeconds: - return "Five Seconds" + return NSLocalizedString("interval.five.seconds", comment: "Five Seconds") case .tenSeconds: - return "Ten Seconds" + return NSLocalizedString("interval.ten.seconds", comment: "Ten Seconds") case .fifteenSeconds: - return "Fifteen Seconds" + return NSLocalizedString("interval.fifteen.seconds", comment: "Fifteen Seconds") case .thirtySeconds: - return "Thirty Seconds" + return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds") case .oneMinute: - return "One Minute" + return NSLocalizedString("interval.one.minute", comment: "One Minute") case .fiveMinutes: - return "Five Minutes" - + return NSLocalizedString("interval.five.minutes", comment: "Five Minutes") } } } diff --git a/Meshtastic/Export/WriteCsvFile.swift b/Meshtastic/Export/WriteCsvFile.swift index 9edc9929..b0de58cc 100644 --- a/Meshtastic/Export/WriteCsvFile.swift +++ b/Meshtastic/Export/WriteCsvFile.swift @@ -23,7 +23,7 @@ func TelemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin csvString += ", " csvString += String(dm.airUtilTx) csvString += ", " - csvString += dm.time?.formattedDate(format: "yyyy-MM-dd HH:mm:ss") ?? "Unknown Age" + csvString += dm.time?.formattedDate(format: "yyyy-MM-dd HH:mm:ss") ?? NSLocalizedString("unknown.age", comment: "") } } } else if metricsType == 1 { @@ -44,7 +44,7 @@ func TelemetryToCsvFile(telemetry: [TelemetryEntity], metricsType: Int) -> Strin csvString += ", " csvString += String(dm.current) csvString += ", " - csvString += dm.time?.formattedDate(format: "yyyy-MM-dd HH:mm:ss") ?? "Unknown Age" + csvString += dm.time?.formattedDate(format: "yyyy-MM-dd HH:mm:ss") ?? NSLocalizedString("unknown.age", comment: "") } } } @@ -73,7 +73,7 @@ func PositionToCsvFile(positions: [PositionEntity]) -> String { csvString += ", " csvString += String(pos.snr) csvString += ", " - csvString += pos.time?.formattedDate(format: "yyyy-MM-dd HH:mm:ss") ?? "Unknown Age" + csvString += pos.time?.formattedDate(format: "yyyy-MM-dd HH:mm:ss") ?? NSLocalizedString("unknown.age", comment: "") } return csvString } diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index d021e627..80cce787 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -490,7 +490,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { case .rangeTestApp: MeshLogger.log("â„šī¸ MESH PACKET received for Range Test App UNHANDLED \(try! decodedInfo.packet.jsonString())") case .telemetryApp: - if !invalidVersion { telemetryPacket(packet: decodedInfo.packet, context: context!) } + if !invalidVersion { telemetryPacket(packet: decodedInfo.packet, connectedNode: (self.connectedPeripheral != nil ? connectedPeripheral.num : 0), context: context!) } case .textMessageCompressedApp: MeshLogger.log("â„šī¸ MESH PACKET received for Text Message Compressed App UNHANDLED \(try! decodedInfo.packet.jsonString())") case .zpsApp: diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 5eeea6af..b445db5e 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -1189,7 +1189,7 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana } } -func telemetryPacket(packet: MeshPacket, context: NSManagedObjectContext) { +func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManagedObjectContext) { if let telemetryMessage = try? Telemetry(serializedData: packet.decoded.payload) { @@ -1235,7 +1235,10 @@ func telemetryPacket(packet: MeshPacket, context: NSManagedObjectContext) { } try context.save() - MeshLogger.log("💾 Telemetry Saved for Node: \(packet.from)") + // Only log telemetery from the mesh not the connected device + if connectedNode != Int64(packet.from) { + MeshLogger.log("💾 Telemetry Saved for Node: \(packet.from)") + } } catch { context.rollback() diff --git a/Meshtastic/Views/Helpers/DateTimeText.swift b/Meshtastic/Views/Helpers/DateTimeText.swift index e88a46e6..068664fe 100644 --- a/Meshtastic/Views/Helpers/DateTimeText.swift +++ b/Meshtastic/Views/Helpers/DateTimeText.swift @@ -24,7 +24,7 @@ struct DateTimeText: View { } else { - Text("Unknown Age") + Text("unknown.age") } } } diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 9d3f4600..afe52cb9 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -67,9 +67,9 @@ struct ChannelMessageList: View { .cornerRadius(15) .contextMenu { VStack{ - Text("Channel: \(message.channel)") + Text("channel")+Text(": \(message.channel)") } - Menu("Tapback response") { + Menu("tapback") { ForEach(Tapbacks.allCases) { tb in Button(action: { if bleManager.sendMessage(message: tb.emojiString, toUserNum: 0, channel: channel.index, isEmoji: true, replyID: message.messageId) { @@ -89,16 +89,16 @@ struct ChannelMessageList: View { self.focusedField = .messageText print("I want to reply to \(message.messageId)") }) { - Text("Reply") + Text("reply") Image(systemName: "arrowshape.turn.up.left.2.fill") } Button(action: { UIPasteboard.general.string = message.messagePayload }) { - Text("Copy") + Text("copy") Image(systemName: "doc.on.doc") } - Menu("Message Details") { + Menu("message.details") { VStack { let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp)) Text("Date \(messageDate, style: .date) \(messageDate.formattedDate(format: "h:mm:ss a"))").font(.caption2).foregroundColor(.gray) @@ -110,11 +110,11 @@ struct ChannelMessageList: View { } if currentUser && message.receivedACK { VStack { - Text("Received Ack \(message.receivedACK ? "âœ”ī¸" : "")") + Text("received.ack")+Text(" \(message.receivedACK ? "âœ”ī¸" : "")") } } else if currentUser && message.ackError == 0 { // Empty Error - Text("Waiting. . .") + Text("waiting") } else if currentUser && message.ackError > 0 { let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true) @@ -126,7 +126,7 @@ struct ChannelMessageList: View { if ackDate >= sixMonthsAgo! { Text((ackDate.formattedDate(format: "h:mm:ss a"))).font(.caption2).foregroundColor(.gray) } else { - Text("Unknown Age").font(.caption2).foregroundColor(.gray) + Text("unknown.age").font(.caption2).foregroundColor(.gray) } } } @@ -243,12 +243,12 @@ struct ChannelMessageList: View { } } label: { - Text("share.positon") + Text("share.position") Image(systemName: "mappin.and.ellipse") .symbolRenderingMode(.hierarchical) .imageScale(.large).foregroundColor(.accentColor) } - ProgressView("Bytes: \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) + ProgressView("\(NSLocalizedString("bytes", comment: "")): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) .frame(width: 130) .padding(5) .font(.subheadline) @@ -260,7 +260,7 @@ struct ChannelMessageList: View { ZStack { let kbType = UIKeyboardType(rawValue: UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0) - TextField("Message", text: $typingMessage, axis: .vertical) + TextField("message", text: $typingMessage, axis: .vertical) .onChange(of: typingMessage, perform: { value in totalBytes = value.utf8.count // Only mess with the value if it is too big @@ -300,7 +300,7 @@ struct ChannelMessageList: View { .imageScale(.large).foregroundColor(.accentColor) } - ProgressView("Bytes: \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) + ProgressView("\(NSLocalizedString("bytes", comment: "")): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) .frame(width: 130) .padding(5) .font(.subheadline) diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 3e16389b..2cb9b642 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -68,9 +68,9 @@ struct UserMessageList: View { .cornerRadius(15) .contextMenu { VStack{ - Text("Channel: \(message.channel)") + Text("channel")+Text(": \(message.channel)") } - Menu("Tapback response") { + Menu("tapback") { ForEach(Tapbacks.allCases) { tb in Button(action: { if bleManager.sendMessage(message: tb.emojiString, toUserNum: user.num, channel: 0, isEmoji: true, replyID: message.messageId) { @@ -90,16 +90,16 @@ struct UserMessageList: View { self.focusedField = .messageText print("I want to reply to \(message.messageId)") }) { - Text("Reply") + Text("reply") Image(systemName: "arrowshape.turn.up.left.2.fill") } Button(action: { UIPasteboard.general.string = message.messagePayload }) { - Text("Copy") + Text("copy") Image(systemName: "doc.on.doc") } - Menu("Message Details") { + Menu("message.details") { VStack { let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp)) Text("Date \(messageDate, style: .date) \(messageDate.formattedDate(format: "h:mm:ss a"))").font(.caption2).foregroundColor(.gray) @@ -111,11 +111,11 @@ struct UserMessageList: View { } if currentUser && message.receivedACK { VStack { - Text("Received Ack \(message.receivedACK ? "âœ”ī¸" : "")") + Text("received.ack")+Text(" \(message.receivedACK ? "âœ”ī¸" : "")") } } else if currentUser && message.ackError == 0 { // Empty Error - Text("Waiting. . .") + Text("waiting") } else if currentUser && message.ackError > 0 { let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true) @@ -174,14 +174,14 @@ struct UserMessageList: View { } } HStack { + let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) if currentUser && message.receivedACK { // Ack Received - Text("Acknowledged").font(.caption2).foregroundColor(.gray) + Text("\(ackErrorVal?.display ?? "No Error" )").font(.caption2).foregroundColor(.gray) } else if currentUser && message.ackError == 0 { // Empty Error Text("Waiting to be acknowledged. . .").font(.caption2).foregroundColor(.orange) } else if currentUser && message.ackError > 0 { - let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true) .font(.caption2).foregroundColor(.red) } @@ -243,12 +243,12 @@ struct UserMessageList: View { } } label: { - Text("share.postion") + Text("share.position") Image(systemName: "mappin.and.ellipse") .symbolRenderingMode(.hierarchical) .imageScale(.large).foregroundColor(.accentColor) } - ProgressView("Bytes: \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) + ProgressView("\(NSLocalizedString("bytes", comment: "")): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) .frame(width: 130) .padding(5) .font(.subheadline) @@ -260,7 +260,7 @@ struct UserMessageList: View { HStack(alignment: .top) { ZStack { let kbType = UIKeyboardType(rawValue: UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0) - TextField("Message", text: $typingMessage, axis: .vertical) + TextField("message", text: $typingMessage, axis: .vertical) .onChange(of: typingMessage, perform: { value in totalBytes = value.utf8.count // Only mess with the value if it is too big @@ -297,7 +297,7 @@ struct UserMessageList: View { .symbolRenderingMode(.hierarchical) .imageScale(.large).foregroundColor(.accentColor) } - ProgressView("Bytes: \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) + ProgressView("\(NSLocalizedString("bytes", comment: "")): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) .frame(width: 130) .padding(5) .font(.subheadline) diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index b5efc6df..ee0da7d0 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -138,7 +138,7 @@ struct NodeDetail: View { .font(.title) .foregroundColor(.accentColor) .symbolRenderingMode(.hierarchical) - Text("User Id:").font(.title) + Text("user").font(.title)+Text(":").font(.title) } //Text(node.user?.userId ?? "??????").font(.title).foregroundColor(.gray) Text("!\(String(format:"%02x", node.num))") @@ -176,7 +176,7 @@ struct NodeDetail: View { .font(.title) .foregroundColor(.accentColor) .symbolRenderingMode(.hierarchical) - Text("Last Heard: ").font(.title) + Text("heard.last").font(.title)+Text(":").font(.title) } DateTimeText(dateTime: node.lastHeard) diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 27b1e11f..89f472dc 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -78,8 +78,8 @@ struct MQTTConfig: View { .autocorrectionDisabled() HStack { - Label("Username", systemImage: "person.text.rectangle") - TextField("Server Username", text: $username) + Label("mqtt.username", systemImage: "person.text.rectangle") + TextField("mqtt.username", text: $username) .foregroundColor(.gray) .autocapitalization(.none) .disableAutocorrection(true) @@ -105,8 +105,8 @@ struct MQTTConfig: View { .keyboardType(.default) .scrollDismissesKeyboard(.interactively) HStack { - Label("Password", systemImage: "wallet.pass") - TextField("Server Password", text: $password) + Label("password", systemImage: "wallet.pass") + TextField("password", text: $password) .foregroundColor(.gray) .autocapitalization(.none) .disableAutocorrection(true) diff --git a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift index 6e3f33a9..349373bd 100644 --- a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift @@ -41,20 +41,20 @@ struct SerialConfig: View { Toggle(isOn: $echo) { - Label("Echo", systemImage: "repeat") + Label("echo", systemImage: "repeat") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) Text("If set, any packets you send will be echoed back to your device.") .font(.caption) - Picker("Baud Rate", selection: $baudRate ) { + Picker("Baud", selection: $baudRate ) { ForEach(SerialBaudRates.allCases) { sbr in Text(sbr.description) } } .pickerStyle(DefaultPickerStyle()) - Picker("Timeout", selection: $timeout ) { + Picker("timeout", selection: $timeout ) { ForEach(SerialTimeoutIntervals.allCases) { sti in Text(sti.description) } @@ -63,7 +63,7 @@ struct SerialConfig: View { Text("The amount of time to wait before we consider your packet as done.") .font(.caption) - Picker("Mode", selection: $mode ) { + Picker("mode", selection: $mode ) { ForEach(SerialModeTypes.allCases) { smt in Text(smt.description) } diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index 91d5d78c..0eeb1902 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -86,7 +86,7 @@ struct TelemetryConfig: View { VStack { Form { - Section(header: Text("Update Intervals")) { + Section(header: Text("update.interval")) { Picker("Device Metrics", selection: $deviceUpdateInterval ) { ForEach(UpdateIntervals.allCases) { ui in Text(ui.description) diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index 20d354d0..c71bfaa0 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -37,8 +37,8 @@ struct NetworkConfig: View { } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) HStack { - Label("SSID", systemImage: "network") - TextField("SSID", text: $wifiSsid) + Label("ssid", systemImage: "network") + TextField("ssid", text: $wifiSsid) .foregroundColor(.gray) .autocapitalization(.none) .disableAutocorrection(true) @@ -58,8 +58,8 @@ struct NetworkConfig: View { } .keyboardType(.default) HStack { - Label("Password", systemImage: "wallet.pass") - TextField("Password", text: $wifiPsk) + Label("password", systemImage: "wallet.pass") + TextField("password", text: $wifiPsk) .foregroundColor(.gray) .autocapitalization(.none) .disableAutocorrection(true) diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index cdcc3a07..3c83e242 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -10,16 +10,29 @@ "admin"="Admin"; "admin.log"="Admin Message Log"; "ago"="ago"; +"always.on"="Always On"; "app.settings"="App Settings"; "are.you.sure"="Are you sure?"; "ascii.capable"="ASCII Capable"; "available.radios"="Available Radios"; +"automatic.detection"="Automatic Detection"; "ble.name"="BLE Name"; "bluetooth"="Bluetooth"; "bluetooth.config"="Bluetooth Config"; +"bluetooth.mode.randompin"="Random PIN"; +"bluetooth.mode.fixedpin"="Fixed PIN"; +"bluetooth.mode.nopin"="No PIN (Just Works)"; +"bytes"="Bytes"; "cancel"="Cancel"; "canned.messages"="Canned Messages"; "canned.messages.config"="Canned Messages Config"; +"canned.messages.preset.manual"="Manual Configuration"; +"canned.messages.preset.rakrotary"="RAK Rotary Encoder Module"; +"canned.messages.preset.cardkb"="M5 Stack Card KB / RAK Keypad"; +"channel"="Channel"; +"channel.role.disabled"="Disabled"; +"channel.role.primary"="Primary"; +"channel.role.secondary"="Secondary"; "channels"="Channels (groups)"; "clear.app.data"="Clear App Data"; "connected.radio"="Connected Radio"; @@ -27,31 +40,62 @@ "connected"="Currently Connected"; "connecting"="Connecting . ."; "contacts"="Contacts"; +"copy"="Copy"; "default"="Default"; "delete"="Delete"; "device"="Device"; "device.config"="Device Config"; +"device.role.client"="Client (default) - App connected client."; +"device.role.clientmute"="Client Mute - Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh."; +"device.role.router"="Router - Mesh packets will prefer to be routed over this node. This node will not be used by client apps. The wifi/ble radios and the oled screen will be put to sleep."; +"device.role.routerclient"="Router Client - Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client."; "direct.messages"="Direct Messages"; "dismiss.keyboard"="Dismiss Keyboard"; "display"="Display (Device Screen)"; "display.config"="Display Config"; "distance"="Distance"; "disconnect"="Disconnect"; +"echo"="Echo"; "email.address"="Email Address"; "enabled"="Enabled"; "external.notification"="External Notification"; "external.notification.config"="External Notification Config"; "firmware.version"="Firmware Version"; +"gpsformat.dec"="Decimal Degrees Format"; +"gpsformat.dms"="Degrees Minutes Seconds"; +"gpsformat.utm"="Universal Transverse Mercator"; +"gpsformat.mgrs"="Military Grid Reference System"; +"gpsformat.olc"="Open Location Code (aka Plus Codes)"; +"gpsformat.osgr"="Ordnance Survey Grid Reference"; "heard"="Heard"; +"heard.last"="Last Heard"; "hybrid"="Hybrid"; +"inputevent.none"="None"; +"inputevent.up"="Up"; +"inputevent.down"="Down"; +"inputevent.left"="Left"; +"inputevent.right"="Right"; +"inputevent.select"="Select"; +"inputevent.back"="Back"; +"inputevent.cancel"="Cancel"; +"interval.one.second"="One Second"; +"interval.two.seconds"="Two Seconds"; "interval.five.seconds"="Five Seconds"; "interval.ten.seconds"="Ten Seconds"; "interval.fifteen.seconds"="Fifteen Seconds"; +"interval.twenty.seconds"="Twenty Seconds"; +"interval.twentyfive.seconds"="Twenty Five Seconds"; "interval.thirty.seconds"="Thirty Seconds"; "interval.one.minute"="One Minute"; +"interval.two.minutes"="Two Minutes"; "interval.five.minutes"="Five Minutes"; "interval.ten.minutes"="Ten Minutes"; "interval.fifteen.minutes"="Fifteen Minutes"; +"interval.thirty.minutes"="Thirty Minutes"; +"interval.one.hour"="One Hour"; +"interval.six.hours"="Six Hours"; +"interval.twelve.hours"="Twelve Hours"; +"interval.twentyfour.hours"="Twenty Four Hours"; "keyboard.type"="Keyboard Type"; "logging"="Logging"; "lora"="LoRa"; @@ -59,17 +103,24 @@ "map"="Mesh Map"; "map.type"="Map Type"; "mesh.log"="Mesh Log"; +"message"="Message"; +"message.details"="Message Details"; "messages"="Messages"; +"mode"="Mode"; "module.configuration"="Module Configuration"; "mqtt"="MQTT"; "mqtt.config"="MQTT Config"; +"mqtt.username"="Username"; "network"="Network"; "network.config"="Network Config"; "nodes"="Nodes"; "no.nodes"="No Meshtastic Nodes Found"; "not.connected"="No device connected"; "numbers.punctuation"="Numbers and Punctuation"; +"off"="Off"; +"on.boot"="On Boot Only"; "options"="Options"; +"password"="Password"; "phone.gps"="Phone GPS"; "phone.gps.interval.description"="How frequently your phone will send your location to the device, location updates to the mesh are managed by the device."; "position"="Position"; @@ -79,10 +130,29 @@ "radio.configuration"="Radio Configuration"; "range.test"="Range Test"; "range.test.config"="Range Test Config"; +"reply"="Reply"; +"received.ack"="Received Ack"; +"routing.acknowledged"="Acknowledged"; +"routing.noroute"="No Route"; +"routing.gotnak"="Received a negative acknowledgment"; +"routing.timeout"="Timeout"; +"routing.nointerface"="No Interface"; +"routing.maxretransmit"="Max Retransmission Reached"; +"routing.nochannel"="No Channel"; +"routing.toolarge"="The packet is too large"; +"routing.noresponse"="No Response"; +"routing.dutycyclelimit"="Regional Duty Cycle Limit Reached"; +"routing.badRequest"="Bad Request"; +"routing.notauthorized"="Not Authorized"; "satellite"="Satellite"; "save"="Save"; "serial"="Serial"; "serial.config"="Serial Config"; +"serial.mode.default"="Default"; +"serial.mode.simple"="Simple"; +"serial.mode.proto"="Protobufs"; +"serial.mode.txtmsg"="Text Message"; +"serial.mode.nmea"="NMEA Positions"; "settings"="Settings"; "share.channels"="Share Channels QR Code"; "share.position"="Share Position"; @@ -92,10 +162,21 @@ "select.menu.item"="Select an item from the menu"; "set.region"="Set LoRa Region"; "standard"="Standard"; +"ssid"="SSID"; +"tapback"="Tapback Response"; +"tapback.heart"="Heart"; +"tapback.thumbsup"="Thumbs Up"; +"tapback.thumbsdown"="Thumbs Down"; +"tapback.haha"="HaHa"; +"tapback.exclamation"="Exclamation Mark"; +"tapback.question"="Question Mark"; +"tapback.poop"="Poop"; "telemetry"="Telemetry (Sensors)"; "telemetry.config"="Telemetry Config"; +"timeout"="timeout" "twitter"="Twitter"; "unknown.age"="Unknown Age"; "update.interval"="Update Interval"; "user"="User"; "user.details"="User Details"; +"waiting"="Waiting. . .";