* More strings

* Add duty cycle error
* Don't log local telemetry in the mesh log
This commit is contained in:
Garth Vander Houwen 2022-12-13 17:47:23 -08:00
parent 5da522b911
commit eedbef57fb
21 changed files with 256 additions and 195 deletions

View file

@ -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)")
}
}
}

View file

@ -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")
}
}
}

View file

@ -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")
}
}
}

View file

@ -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.")
}
}
}

View file

@ -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:

View file

@ -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")
}
}
}

View file

@ -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")
}
}
}

View file

@ -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
}
}
}

View file

@ -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")
}
}
}

View file

@ -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
}

View file

@ -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:

View file

@ -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()

View file

@ -24,7 +24,7 @@ struct DateTimeText: View {
} else {
Text("Unknown Age")
Text("unknown.age")
}
}
}

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)
}

View file

@ -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)

View file

@ -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)

View file

@ -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. . .";