Ver código fonte

Merge branch 'dev' of github.com:nightscout/Trio-dev into beta-watch-fixes

Deniz Cengiz 1 ano atrás
pai
commit
0e10a55220
100 arquivos alterados com 181953 adições e 51610 exclusões
  1. 12 2
      Model/CoreDataStack.swift
  2. 14 0
      Model/Helper/AdjustmentStored+Helper.swift
  3. 9 6
      Model/Helper/CoreDataError.swift
  4. 0 10
      Model/Helper/OverrideStored+helper.swift
  5. 87 3
      Model/Helper/PumpEvent+helper.swift
  6. 11 1
      Trio Watch App Extension/Helper/Helper+ButtonStyles.swift
  7. 20 0
      Trio Watch App Extension/Views/BolusProgressOverlay.swift
  8. 2 2
      Trio Watch App Extension/Views/GlucoseChartView.swift
  9. 9 4
      Trio Watch App Extension/Views/TrioMainWatchView.swift
  10. 16 115
      Trio.xcodeproj/project.pbxproj
  11. 0 23
      Trio/Resources/Base.lproj/InfoPlist.strings
  12. 1059 0
      Trio/Resources/InfoPlist.xcstrings
  13. 0 20
      Trio/Resources/ar.lproj/InfoPlist.strings
  14. 0 20
      Trio/Resources/ca.lproj/InfoPlist.strings
  15. 0 20
      Trio/Resources/da.lproj/InfoPlist.strings
  16. 0 20
      Trio/Resources/de.lproj/InfoPlist.strings
  17. 0 20
      Trio/Resources/es.lproj/InfoPlist.strings
  18. 0 20
      Trio/Resources/fi.lproj/InfoPlist.strings
  19. 0 20
      Trio/Resources/fr.lproj/InfoPlist.strings
  20. 0 20
      Trio/Resources/he.lproj/InfoPlist.strings
  21. 0 20
      Trio/Resources/hu.lproj/InfoPlist.strings
  22. 0 20
      Trio/Resources/it.lproj/InfoPlist.strings
  23. 0 20
      Trio/Resources/nb.lproj/InfoPlist.strings
  24. 0 20
      Trio/Resources/nl.lproj/InfoPlist.strings
  25. 0 20
      Trio/Resources/pl.lproj/InfoPlist.strings
  26. 0 20
      Trio/Resources/pt-BR.lproj/InfoPlist.strings
  27. 0 20
      Trio/Resources/pt-PT.lproj/InfoPlist.strings
  28. 0 20
      Trio/Resources/ru.lproj/InfoPlist.strings
  29. 0 20
      Trio/Resources/sk.lproj/InfoPlist.strings
  30. 0 20
      Trio/Resources/sv.lproj/InfoPlist.strings
  31. 0 20
      Trio/Resources/tr.lproj/InfoPlist.strings
  32. 0 20
      Trio/Resources/uk.lproj/InfoPlist.strings
  33. 0 20
      Trio/Resources/vi.lproj/InfoPlist.strings
  34. 0 20
      Trio/Resources/zh-Hans.lproj/InfoPlist.strings
  35. 9 3
      Trio/Sources/APS/APSManager.swift
  36. 1 1
      Trio/Sources/APS/CGM/AppGroupSource.swift
  37. 8 7
      Trio/Sources/APS/CGM/CGMType.swift
  38. 4 3
      Trio/Sources/APS/CGM/PluginSource.swift
  39. 27 5
      Trio/Sources/APS/FetchGlucoseManager.swift
  40. 25 10
      Trio/Sources/APS/OpenAPS/OpenAPS.swift
  41. 1 1
      Trio/Sources/APS/Storage/ContactImageStorage.swift
  42. 2 2
      Trio/Sources/APS/Storage/GlucoseStorage.swift
  43. 3 3
      Trio/Sources/APS/Storage/OverrideStorage.swift
  44. 3 3
      Trio/Sources/APS/Storage/TempTargetsStorage.swift
  45. 22 7
      Trio/Sources/Application/TrioApp.swift
  46. 4 4
      Trio/Sources/Helpers/BuildDetails.swift
  47. 4 4
      Trio/Sources/Helpers/HKUnit.swift
  48. 180308 0
      Trio/Sources/Localizations/Main/Localizable.xcstrings
  49. 0 2223
      Trio/Sources/Localizations/Main/ar.lproj/Localizable.strings
  50. 0 1917
      Trio/Sources/Localizations/Main/ca.lproj/Localizable.strings
  51. 0 2184
      Trio/Sources/Localizations/Main/da.lproj/Localizable.strings
  52. 0 2229
      Trio/Sources/Localizations/Main/de.lproj/Localizable.strings
  53. 0 2231
      Trio/Sources/Localizations/Main/en.lproj/Localizable.strings
  54. 0 2228
      Trio/Sources/Localizations/Main/es.lproj/Localizable.strings
  55. 0 2229
      Trio/Sources/Localizations/Main/fi.lproj/Localizable.strings
  56. 0 2220
      Trio/Sources/Localizations/Main/fr.lproj/Localizable.strings
  57. 0 2223
      Trio/Sources/Localizations/Main/he.lproj/Localizable.strings
  58. 0 2178
      Trio/Sources/Localizations/Main/hu.lproj/Localizable.strings
  59. 0 2225
      Trio/Sources/Localizations/Main/it.lproj/Localizable.strings
  60. 0 2223
      Trio/Sources/Localizations/Main/nb.lproj/Localizable.strings
  61. 0 2231
      Trio/Sources/Localizations/Main/nl.lproj/Localizable.strings
  62. 0 2225
      Trio/Sources/Localizations/Main/pl.lproj/Localizable.strings
  63. 0 2223
      Trio/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  64. 0 2223
      Trio/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  65. 0 2226
      Trio/Sources/Localizations/Main/ru.lproj/Localizable.strings
  66. 0 2178
      Trio/Sources/Localizations/Main/sk.lproj/Localizable.strings
  67. 0 2223
      Trio/Sources/Localizations/Main/sv.lproj/Localizable.strings
  68. 0 2227
      Trio/Sources/Localizations/Main/tr.lproj/Localizable.strings
  69. 0 2223
      Trio/Sources/Localizations/Main/uk.lproj/Localizable.strings
  70. 0 2180
      Trio/Sources/Localizations/Main/vi.lproj/Localizable.strings
  71. 0 2225
      Trio/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  72. 4 0
      Trio/Sources/Logger/Logger.swift
  73. 16 16
      Trio/Sources/Models/ContactTrickEntry.swift
  74. 2 2
      Trio/Sources/Models/ForecastDisplayType.swift
  75. 2 2
      Trio/Sources/Models/HbA1cDisplayUnit.swift
  76. 2 2
      Trio/Sources/Models/LockScreenView.swift
  77. 2 2
      Trio/Sources/Models/TimeInRangeChartStyle.swift
  78. 2 2
      Trio/Sources/Models/TotalInsulinDisplayType.swift
  79. 2 5
      Trio/Sources/Modules/Adjustments/AdjustmentsDataFlow.swift
  80. 2 2
      Trio/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+Overrides.swift
  81. 5 5
      Trio/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+TempTargets.swift
  82. 65 34
      Trio/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift
  83. 9 9
      Trio/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift
  84. 15 15
      Trio/Sources/Modules/BolusCalculatorConfig/View/BolusCalculatorConfigRootView.swift
  85. 10 6
      Trio/Sources/Modules/CGMSettings/CGMSettingsStateModel.swift
  86. 4 4
      Trio/Sources/Modules/CGMSettings/View/CGMRootView.swift
  87. 11 11
      Trio/Sources/Modules/CalendarEventSettings/View/CalendarEventSettingsRootView.swift
  88. 6 6
      Trio/Sources/Modules/ContactImage/View/ContactImageHelpView.swift
  89. 17 23
      Trio/Sources/Modules/DataTable/DataTableDataFlow.swift
  90. 15 15
      Trio/Sources/Modules/DataTable/View/DataTableRootView.swift
  91. 28 26
      Trio/Sources/Modules/DynamicSettings/View/DynamicSettingsRootView.swift
  92. 12 12
      Trio/Sources/Modules/GeneralSettings/View/UnitsLimitsSettingsRootView.swift
  93. 26 26
      Trio/Sources/Modules/GlucoseNotificationSettings/View/GlucoseNotificationSettingsRootView.swift
  94. 5 5
      Trio/Sources/Modules/HealthKit/View/AppleHealthKitRootView.swift
  95. 1 1
      Trio/Sources/Modules/Home/HomeStateModel+Setup/DeterminationSetup.swift
  96. 9 5
      Trio/Sources/Modules/Home/HomeStateModel.swift
  97. 2 2
      Trio/Sources/Modules/Home/View/Chart/ChartElements/SelectionPopoverView.swift
  98. 17 17
      Trio/Sources/Modules/Home/View/Chart/ChartLegendView.swift
  99. 2 2
      Trio/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift
  100. 0 0
      Trio/Sources/Modules/Home/View/Header/LoopStatusHelpView.swift

+ 12 - 2
Model/CoreDataStack.swift

@@ -105,7 +105,7 @@ class CoreDataStack: ObservableObject {
     private func fetchPersistentHistoryTransactionsAndChanges() async throws {
         let taskContext = newTaskContext()
         taskContext.name = "persistentHistoryContext"
-//        debugPrint("Start fetching persistent history changes from the store ... \(DebuggingIdentifiers.inProgress)")
+        //        debugPrint("Start fetching persistent history changes from the store ... \(DebuggingIdentifiers.inProgress)")
 
         try await taskContext.perform {
             // Execute the persistent history change since the last transaction
@@ -120,7 +120,7 @@ class CoreDataStack: ObservableObject {
     }
 
     private func mergePersistentHistoryChanges(from history: [NSPersistentHistoryTransaction]) {
-//        debugPrint("Received \(history.count) persistent history transactions")
+        //        debugPrint("Received \(history.count) persistent history transactions")
         // Update view context with objectIDs from history change request
         /// - Tag: mergeChanges
         let viewContext = persistentContainer.viewContext
@@ -150,6 +150,16 @@ class CoreDataStack: ObservableObject {
             }
         }
     }
+
+    func initializeStack() throws {
+        // Force initialization of persistent container
+        let container = persistentContainer
+
+        // Verify the store is loaded and available
+        guard container.persistentStoreCoordinator.persistentStores.isNotEmpty else {
+            throw CoreDataError.storeNotInitializedError
+        }
+    }
 }
 
 // MARK: - Delete

+ 14 - 0
Model/Helper/AdjustmentStored+Helper.swift

@@ -0,0 +1,14 @@
+import CoreData
+import Foundation
+
+extension NSPredicate {
+    static var lastActiveAdjustmentNotYetUploadedToNightscout: NSPredicate {
+        let date = Date.oneDayAgo
+        return NSPredicate(
+            format: "date >= %@ AND enabled == %@ AND isUploadedToNS == %@",
+            date as NSDate,
+            true as NSNumber,
+            false as NSNumber
+        )
+    }
+}

+ 9 - 6
Model/Helper/CoreDataError.swift

@@ -7,23 +7,26 @@ enum CoreDataError: Error {
     case persistentHistoryChangeError
     case unexpectedError(error: Error)
     case fetchError
+    case storeNotInitializedError
 }
 
 extension CoreDataError: LocalizedError {
     var errorDescription: String? {
         switch self {
         case .creationError:
-            return NSLocalizedString("Failed to create a new object.", comment: "")
+            return String(localized: "Failed to create a new object.", comment: "")
         case .batchInsertError:
-            return NSLocalizedString("Failed to execute a batch insert request.", comment: "")
+            return String(localized: "Failed to execute a batch insert request.", comment: "")
         case .batchDeleteError:
-            return NSLocalizedString("Failed to execute a batch delete request.", comment: "")
+            return String(localized: "Failed to execute a batch delete request.", comment: "")
         case .persistentHistoryChangeError:
-            return NSLocalizedString("Failed to execute a persistent history change request.", comment: "")
+            return String(localized: "Failed to execute a persistent history change request.", comment: "")
         case let .unexpectedError(error):
-            return NSLocalizedString("Received unexpected error. \(error.localizedDescription)", comment: "")
+            return String(localized: "Received unexpected error. \(error.localizedDescription)", comment: "")
         case .fetchError:
-            return NSLocalizedString("Failed to fetch object \(DebuggingIdentifiers.failed).", comment: "")
+            return String(localized: "Failed to fetch object \(DebuggingIdentifiers.failed).", comment: "")
+        case .storeNotInitializedError:
+            return String(localized: "Failed to initialize Core Data's persistent store.", comment: "")
         }
     }
 }

+ 0 - 10
Model/Helper/OverrideStored+helper.swift

@@ -14,16 +14,6 @@ extension NSPredicate {
             true as NSNumber
         )
     }
-
-    static var lastActiveOverrideNotYetUploadedToNightscout: NSPredicate {
-        let date = Date.oneDayAgo
-        return NSPredicate(
-            format: "date >= %@ AND enabled == %@ AND isUploadedToNS == %@",
-            date as NSDate,
-            true as NSNumber,
-            false as NSNumber
-        )
-    }
 }
 
 extension OverrideStored {

+ 87 - 3
Model/Helper/PumpEvent+helper.swift

@@ -102,7 +102,7 @@ struct BolusDTO: Codable {
     var isExternal: Bool
     var isSMB: Bool
     var duration: Int
-    var _type: String = "Bolus"
+    var _type: String = EventType.bolus.rawValue
 }
 
 struct TempBasalDTO: Codable {
@@ -110,14 +110,14 @@ struct TempBasalDTO: Codable {
     var timestamp: String
     var temp: String
     var rate: Double
-    var _type: String = "TempBasal"
+    var _type: String = EventType.tempBasal.rawValue
 }
 
 struct TempBasalDurationDTO: Codable {
     var id: String
     var timestamp: String
     var duration: Int
-    var _type: String = "TempBasalDuration"
+    var _type: String = EventType.tempBasalDuration.rawValue
 
     private enum CodingKeys: String, CodingKey {
         case id
@@ -127,11 +127,39 @@ struct TempBasalDurationDTO: Codable {
     }
 }
 
+struct SuspendDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.pumpSuspend.rawValue
+}
+
+struct ResumeDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.pumpResume.rawValue
+}
+
+struct RewindDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.rewind.rawValue
+}
+
+struct PrimeDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.prime.rawValue
+}
+
 // Mask distinct DTO subtypes with a common enum that conforms to Encodable
 enum PumpEventDTO: Encodable {
     case bolus(BolusDTO)
     case tempBasal(TempBasalDTO)
     case tempBasalDuration(TempBasalDurationDTO)
+    case suspend(SuspendDTO)
+    case resume(ResumeDTO)
+    case rewind(RewindDTO)
+    case prime(PrimeDTO)
 
     func encode(to encoder: Encoder) throws {
         switch self {
@@ -141,6 +169,14 @@ enum PumpEventDTO: Encodable {
             try tempBasal.encode(to: encoder)
         case let .tempBasalDuration(tempBasalDuration):
             try tempBasalDuration.encode(to: encoder)
+        case let .suspend(suspend):
+            try suspend.encode(to: encoder)
+        case let .resume(resume):
+            try resume.encode(to: encoder)
+        case let .rewind(rewind):
+            try rewind.encode(to: encoder)
+        case let .prime(prime):
+            try prime.encode(to: encoder)
         }
     }
 }
@@ -195,4 +231,52 @@ extension PumpEventStored {
         )
         return .tempBasalDuration(tempBasalDurationDTO)
     }
+
+    func toPumpSuspendDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.pumpSuspend.rawValue else {
+            return nil
+        }
+
+        let suspendDTO = SuspendDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .suspend(suspendDTO)
+    }
+
+    func toPumpResumeDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.pumpResume.rawValue else {
+            return nil
+        }
+
+        let resumeDTO = ResumeDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .resume(resumeDTO)
+    }
+
+    func toRewindDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.rewind.rawValue else {
+            return nil
+        }
+
+        let rewindDTO = RewindDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .rewind(rewindDTO)
+    }
+
+    func toPrimeDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.prime.rawValue else {
+            return nil
+        }
+
+        let primeDTO = PrimeDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .prime(primeDTO)
+    }
 }

+ 11 - 1
Trio Watch App Extension/Helper/Helper+ButtonStyles.swift

@@ -5,6 +5,8 @@ struct WatchOSButtonStyle: ButtonStyle {
     var foregroundColor: Color = .white
     var fontSize: Font = .title2
 
+    @Environment(\.isEnabled) private var isEnabled: Bool
+
     private var fontWeight: Font.Weight {
         switch deviceType {
         case .watch40mm:
@@ -44,11 +46,19 @@ struct WatchOSButtonStyle: ButtonStyle {
     }
 
     func makeBody(configuration: Configuration) -> some View {
+        var buttonBackground: Color {
+            if isEnabled {
+                return Color.tabBar.opacity(configuration.isPressed ? 0.8 : 1.0)
+            } else {
+                return Color.tabBar.opacity(0.4)
+            }
+        }
+
         configuration.label
             .font(fontSize)
             .fontWeight(fontWeight)
             .padding(buttonPadding)
-            .background(Color.tabBar.opacity(configuration.isPressed ? 0.8 : 1.0))
+            .background(buttonBackground)
             .clipShape(Circle())
             .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
     }

+ 20 - 0
Trio Watch App Extension/Views/BolusProgressOverlay.swift

@@ -16,6 +16,25 @@ struct BolusProgressOverlay: View {
         endPoint: .trailing
     )
 
+    private var isWatchStateDated: Bool {
+        // If `lastWatchStateUpdate` is nil, treat as "dated"
+        guard let lastUpdateTimestamp = state.lastWatchStateUpdate else {
+            return true
+        }
+        let now = Date().timeIntervalSince1970
+        let secondsSinceUpdate = now - lastUpdateTimestamp
+        // Return true if last update older than 5 min, so 1 loop cycle
+        return secondsSinceUpdate > 5 * 60
+    }
+
+    private var isSessionUnreachable: Bool {
+        guard let session = state.session else {
+            return true // No session at all => unreachable
+        }
+        // Return true if not .activated OR not reachable
+        return session.activationState != .activated
+    }
+
     var body: some View {
         VStack(spacing: 10) {
             VStack {
@@ -45,6 +64,7 @@ struct BolusProgressOverlay: View {
                 }
                 .buttonStyle(.bordered)
                 .padding()
+                .disabled(isWatchStateDated || isSessionUnreachable)
             }
             .padding()
             .background(Color.black.opacity(0.9))

+ 2 - 2
Trio Watch App Extension/Views/GlucoseChartView.swift

@@ -6,8 +6,8 @@ import SwiftUI
 
 struct GlucoseChartView: View {
     let glucoseValues: [(date: Date, glucose: Double, color: Color)]
-    @Binding var minYAxisValue: Decimal
-    @Binding var maxYAxisValue: Decimal
+    let minYAxisValue: Decimal
+    let maxYAxisValue: Decimal
     @State private var timeWindow: TimeWindow = .threeHours
 
     enum TimeWindow: Int {

+ 9 - 4
Trio Watch App Extension/Views/TrioMainWatchView.swift

@@ -90,8 +90,8 @@ struct TrioMainWatchView: View {
                 // Page 2: Glucose chart
                 GlucoseChartView(
                     glucoseValues: state.glucoseValues,
-                    minYAxisValue: $state.minYAxisValue,
-                    maxYAxisValue: $state.maxYAxisValue
+                    minYAxisValue: state.minYAxisValue,
+                    maxYAxisValue: state.maxYAxisValue
                 )
                 .tag(1)
             }
@@ -139,7 +139,9 @@ struct TrioMainWatchView: View {
                     } label: {
                         Image(systemName: "clock.arrow.2.circlepath")
                             .foregroundStyle(Color.primary, isOverrideActive ? Color.primary : Color.purple)
-                    }.tint(isOverrideActive ? Color.purple : nil)
+                    }
+                    .tint(isOverrideActive ? Color.purple : nil)
+                    .disabled(isWatchStateDated || isSessionUnreachable)
 
                     Button {
                         showingTreatmentMenuSheet = true
@@ -149,13 +151,16 @@ struct TrioMainWatchView: View {
                     }
                     .controlSize(.large)
                     .buttonStyle(WatchOSButtonStyle(deviceType: state.deviceType))
+                    .disabled(isWatchStateDated || isSessionUnreachable)
 
                     Button {
                         showingTempTargetSheet = true
                     } label: {
                         Image(systemName: "target")
                             .foregroundStyle(isTempTargetActive ? Color.primary : Color.loopGreen.opacity(0.75))
-                    }.tint(isTempTargetActive ? Color.loopGreen.opacity(0.75) : nil)
+                    }
+                    .tint(isTempTargetActive ? Color.loopGreen.opacity(0.75) : nil)
+                    .disabled(isWatchStateDated || isSessionUnreachable)
                 }
             }
             .fullScreenCover(isPresented: $showingTreatmentMenuSheet) {

+ 16 - 115
Trio.xcodeproj/project.pbxproj

@@ -28,7 +28,6 @@
 		190EBCC829FF13AA00BA767D /* UserInterfaceSettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC729FF13AA00BA767D /* UserInterfaceSettingsStateModel.swift */; };
 		190EBCCB29FF13CB00BA767D /* UserInterfaceSettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCCA29FF13CB00BA767D /* UserInterfaceSettingsRootView.swift */; };
 		191F62682AD6B05A004D7911 /* NightscoutSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191F62672AD6B05A004D7911 /* NightscoutSettings.swift */; };
-		1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1927C8E82744606D00347C69 /* InfoPlist.strings */; };
 		1935364028496F7D001E0B16 /* Oref2_variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* Oref2_variables.swift */; };
 		193F6CDD2A512C8F001240FD /* Loops.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193F6CDC2A512C8F001240FD /* Loops.swift */; };
 		195D80B42AF6973A00D25097 /* DynamicSettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B32AF6973A00D25097 /* DynamicSettingsRootView.swift */; };
@@ -38,7 +37,6 @@
 		1967DFBE29D052C200759F30 /* Icons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFBD29D052C200759F30 /* Icons.swift */; };
 		1967DFC029D053AC00759F30 /* IconSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFBF29D053AC00759F30 /* IconSelection.swift */; };
 		1967DFC229D053D300759F30 /* IconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFC129D053D300759F30 /* IconImage.swift */; };
-		198377D2266BFFF6004DE65E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
 		19A910302A24BF6300C8951B /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A9102F2A24BF6300C8951B /* StatsView.swift */; };
 		19A910362A24D6D700C8951B /* DateFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A910352A24D6D700C8951B /* DateFilter.swift */; };
 		19A910382A24EF3200C8951B /* ChartsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A910372A24EF3200C8951B /* ChartsView.swift */; };
@@ -212,6 +210,7 @@
 		491D6FBE2D56741C00C49F67 /* TempTargetRunStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491D6FB92D56741C00C49F67 /* TempTargetRunStored+CoreDataClass.swift */; };
 		491D6FBF2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491D6FBA2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift */; };
 		491D6FC02D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491D6FBB2D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift */; };
+		49B9B57F2D5768D2009C6B59 /* AdjustmentStored+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B9B57E2D5768D2009C6B59 /* AdjustmentStored+Helper.swift */; };
 		5075C1608E6249A51495C422 /* TargetsEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDEA2DC60EDE0A3CA54DC73 /* TargetsEditorProvider.swift */; };
 		53F2382465BF74DB1A967C8B /* PumpConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8630D58BDAD6D9C650B9B39 /* PumpConfigProvider.swift */; };
 		581516A42BCED84A00BF67D7 /* DebuggingIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581516A32BCED84A00BF67D7 /* DebuggingIdentifiers.swift */; };
@@ -277,6 +276,8 @@
 		7F7B756BE8543965D9FDF1A2 /* DataTableDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A401509D21F7F35D4E109EDA /* DataTableDataFlow.swift */; };
 		8194B80890CDD6A3C13B0FEE /* SnoozeStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E26904AACA8D9C15D229D675 /* SnoozeStateModel.swift */; };
 		88AB39B23C9552BD6E0C9461 /* ISFEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBB3BAE7494CB771ABAC7B8B /* ISFEditorRootView.swift */; };
+		8A91342A2D63D9A1007F8874 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 8A9134292D63D9A1007F8874 /* Localizable.xcstrings */; };
+		8A91342C2D63D9A2007F8874 /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 8A91342B2D63D9A2007F8874 /* InfoPlist.xcstrings */; };
 		8B759CFCF47B392BB365C251 /* BasalProfileEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F94DD2853CF42BA4E30616 /* BasalProfileEditorDataFlow.swift */; };
 		9702FF92A09C53942F20D7EA /* TargetsEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DD795BA46B193644D48138C /* TargetsEditorRootView.swift */; };
 		9825E5E923F0B8FA80C8C7C7 /* NightscoutConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A48AE3AC813A49A517846A /* NightscoutConfigStateModel.swift */; };
@@ -689,33 +690,8 @@
 		190EBCC529FF138000BA767D /* UserInterfaceSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsProvider.swift; sourceTree = "<group>"; };
 		190EBCC729FF13AA00BA767D /* UserInterfaceSettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsStateModel.swift; sourceTree = "<group>"; };
 		190EBCCA29FF13CB00BA767D /* UserInterfaceSettingsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsRootView.swift; sourceTree = "<group>"; };
-		1918333A26ADA46800F45722 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
 		191F62672AD6B05A004D7911 /* NightscoutSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutSettings.swift; sourceTree = "<group>"; };
-		1927C8E92744611700347C69 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EA2744611800347C69 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EB2744611900347C69 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
-		1927C8EC2744611A00347C69 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8ED2744611B00347C69 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EE2744611C00347C69 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EF2744611D00347C69 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F02744611E00347C69 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F12744611E00347C69 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F22744611F00347C69 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F32744612000347C69 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F42744612100347C69 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F52744612100347C69 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
-		1927C8F62744612200347C69 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
-		1927C8F72744612300347C69 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F82744612400347C69 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F92744612400347C69 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8FA2744612500347C69 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8FB2744612600347C69 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8FE274489BA00347C69 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1935363F28496F7D001E0B16 /* Oref2_variables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Oref2_variables.swift; sourceTree = "<group>"; };
-		193F1E392B44C13B00525770 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		193F1E3A2B44C13B00525770 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
-		193F1E3B2B44C14800525770 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		193F1E3C2B44C14800525770 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
 		193F6CDC2A512C8F001240FD /* Loops.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Loops.swift; sourceTree = "<group>"; };
 		195D80B32AF6973A00D25097 /* DynamicSettingsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicSettingsRootView.swift; sourceTree = "<group>"; };
 		195D80B62AF697B800D25097 /* DynamicSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicSettingsDataFlow.swift; sourceTree = "<group>"; };
@@ -724,32 +700,11 @@
 		1967DFBD29D052C200759F30 /* Icons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Icons.swift; sourceTree = "<group>"; };
 		1967DFBF29D053AC00759F30 /* IconSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconSelection.swift; sourceTree = "<group>"; };
 		1967DFC129D053D300759F30 /* IconImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconImage.swift; sourceTree = "<group>"; };
-		198377D3266BFFF6004DE65E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D5266C0A05004DE65E /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D6266C0A0A004DE65E /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D7266C0A15004DE65E /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
-		198377D8266C0A1C004DE65E /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D9266C0A21004DE65E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DA266C0A2B004DE65E /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DB266C0A32004DE65E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DC266C0A3C004DE65E /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DD266C0A51004DE65E /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DE266C0A69004DE65E /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DF266C0A7F004DE65E /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E0266C0AB5004DE65E /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E1266C0ABF004DE65E /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E2266C0AC8004DE65E /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E3266C0ADC004DE65E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E4266C13D2004DE65E /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
 		199561C0275E61A50077B976 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS8.0.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
-		199732B4271B72DD00129A3F /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
-		199732B5271B9EE900129A3F /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		19A9102F2A24BF6300C8951B /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
 		19A910352A24D6D700C8951B /* DateFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFilter.swift; sourceTree = "<group>"; };
 		19A910372A24EF3200C8951B /* ChartsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartsView.swift; sourceTree = "<group>"; };
 		19B0EF2028F6D66200069496 /* Statistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Statistics.swift; sourceTree = "<group>"; };
-		19C166682756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		19C166692756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
 		19D466A229AA2B80004D5F33 /* MealSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealSettingsDataFlow.swift; sourceTree = "<group>"; };
 		19D466A429AA2BD4004D5F33 /* MealSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealSettingsProvider.swift; sourceTree = "<group>"; };
 		19D466A629AA2C22004D5F33 /* MealSettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealSettingsStateModel.swift; sourceTree = "<group>"; };
@@ -942,6 +897,7 @@
 		491D6FBA2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetRunStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
 		491D6FBB2D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetStored+CoreDataClass.swift"; sourceTree = "<group>"; };
 		491D6FBC2D56741C00C49F67 /* TempTargetStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		49B9B57E2D5768D2009C6B59 /* AdjustmentStored+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AdjustmentStored+Helper.swift"; sourceTree = "<group>"; };
 		4DD795BA46B193644D48138C /* TargetsEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorRootView.swift; sourceTree = "<group>"; };
 		505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ISFEditorStateModel.swift; sourceTree = "<group>"; };
 		581516A32BCED84A00BF67D7 /* DebuggingIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebuggingIdentifiers.swift; sourceTree = "<group>"; };
@@ -1003,6 +959,8 @@
 		7E22146D3DF4853786C78132 /* CarbRatioEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorDataFlow.swift; sourceTree = "<group>"; };
 		8782B44544F38F2B2D82C38E /* NightscoutConfigRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigRootView.swift; sourceTree = "<group>"; };
 		881E04BA5E0A003DE8E0A9C6 /* DataTableRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableRootView.swift; sourceTree = "<group>"; };
+		8A9134292D63D9A1007F8874 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
+		8A91342B2D63D9A2007F8874 /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = "<group>"; };
 		920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorRootView.swift; sourceTree = "<group>"; };
 		9455FA2D92E77A6C4AFED8A3 /* DataTableStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableStateModel.swift; sourceTree = "<group>"; };
 		96653287EDB276A111288305 /* ManualTempBasalDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalDataFlow.swift; sourceTree = "<group>"; };
@@ -1527,7 +1485,7 @@
 		19D440A926B6FEBD008DA6C8 /* Main */ = {
 			isa = PBXGroup;
 			children = (
-				198377D4266BFFF6004DE65E /* Localizable.strings */,
+				8A9134292D63D9A1007F8874 /* Localizable.xcstrings */,
 			);
 			path = Main;
 			sourceTree = "<group>";
@@ -1849,7 +1807,7 @@
 				388E596E25AD96040019842D /* javascript */,
 				3811DEC725C9DA7300A708ED /* Trio.entitlements */,
 				388E596425AD948E0019842D /* Info.plist */,
-				1927C8E82744606D00347C69 /* InfoPlist.strings */,
+				8A91342B2D63D9A2007F8874 /* InfoPlist.xcstrings */,
 				B9CAAEFB2AE70836000F68BC /* branch.txt */,
 				19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */,
 			);
@@ -2354,6 +2312,7 @@
 		5825D1622BD405AE00F36E9B /* Helper */ = {
 			isa = PBXGroup;
 			children = (
+				49B9B57E2D5768D2009C6B59 /* AdjustmentStored+Helper.swift */,
 				581516A82BCEEDF800BF67D7 /* NSPredicates.swift */,
 				583684052BD178DB00070A60 /* GlucoseStored+helper.swift */,
 				58F107732BD1A4D000B1A680 /* Determination+helper.swift */,
@@ -3410,15 +3369,15 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				198377D2266BFFF6004DE65E /* Localizable.strings in Resources */,
+				8A91342C2D63D9A2007F8874 /* InfoPlist.xcstrings in Resources */,
 				CE1F6DE72BAF1A180064EB8D /* BuildDetails.plist in Resources */,
 				38DF178D27733E6800B3528F /* snow.sks in Resources */,
 				388E597225AD9CF10019842D /* json in Resources */,
 				38DF178E27733E6800B3528F /* Assets.xcassets in Resources */,
 				19DA48E829CD339B00EEA1E7 /* Assets.xcassets in Resources */,
+				8A91342A2D63D9A1007F8874 /* Localizable.xcstrings in Resources */,
 				388E596F25AD96040019842D /* javascript in Resources */,
 				B9CAAEFC2AE70836000F68BC /* branch.txt in Resources */,
-				1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -3962,6 +3921,7 @@
 				DD1745222C55524800211FAC /* SMBSettingsProvider.swift in Sources */,
 				BF1667ADE69E4B5B111CECAE /* ManualTempBasalProvider.swift in Sources */,
 				583684062BD178DB00070A60 /* GlucoseStored+helper.swift in Sources */,
+				49B9B57F2D5768D2009C6B59 /* AdjustmentStored+Helper.swift in Sources */,
 				F90692D6274B9A450037068D /* HealthKitStateModel.swift in Sources */,
 				BD1661312B82ADAB00256551 /* CustomProgressView.swift in Sources */,
 				C967DACD3B1E638F8B43BE06 /* ManualTempBasalStateModel.swift in Sources */,
@@ -4146,69 +4106,6 @@
 		};
 /* End PBXTargetDependency section */
 
-/* Begin PBXVariantGroup section */
-		1927C8E82744606D00347C69 /* InfoPlist.strings */ = {
-			isa = PBXVariantGroup;
-			children = (
-				1927C8E92744611700347C69 /* ar */,
-				1927C8EA2744611800347C69 /* ca */,
-				1927C8EB2744611900347C69 /* zh-Hans */,
-				1927C8EC2744611A00347C69 /* da */,
-				1927C8ED2744611B00347C69 /* fi */,
-				1927C8EE2744611C00347C69 /* nl */,
-				1927C8EF2744611D00347C69 /* fr */,
-				1927C8F02744611E00347C69 /* de */,
-				1927C8F12744611E00347C69 /* he */,
-				1927C8F22744611F00347C69 /* it */,
-				1927C8F32744612000347C69 /* nb */,
-				1927C8F42744612100347C69 /* pl */,
-				1927C8F52744612100347C69 /* pt-BR */,
-				1927C8F62744612200347C69 /* pt-PT */,
-				1927C8F72744612300347C69 /* ru */,
-				1927C8F82744612400347C69 /* es */,
-				1927C8F92744612400347C69 /* sv */,
-				1927C8FA2744612500347C69 /* tr */,
-				1927C8FB2744612600347C69 /* uk */,
-				1927C8FE274489BA00347C69 /* Base */,
-				19C166682756EFBD00ED12E3 /* sk */,
-				193F1E392B44C13B00525770 /* hu */,
-				193F1E3B2B44C14800525770 /* vi */,
-			);
-			name = InfoPlist.strings;
-			sourceTree = "<group>";
-		};
-		198377D4266BFFF6004DE65E /* Localizable.strings */ = {
-			isa = PBXVariantGroup;
-			children = (
-				198377D3266BFFF6004DE65E /* en */,
-				198377D5266C0A05004DE65E /* ar */,
-				198377D6266C0A0A004DE65E /* ca */,
-				198377D7266C0A15004DE65E /* zh-Hans */,
-				198377D8266C0A1C004DE65E /* da */,
-				198377D9266C0A21004DE65E /* nl */,
-				198377DA266C0A2B004DE65E /* fr */,
-				198377DB266C0A32004DE65E /* de */,
-				198377DC266C0A3C004DE65E /* he */,
-				198377DD266C0A51004DE65E /* it */,
-				198377DE266C0A69004DE65E /* nb */,
-				198377DF266C0A7F004DE65E /* pl */,
-				198377E0266C0AB5004DE65E /* ru */,
-				198377E1266C0ABF004DE65E /* es */,
-				198377E2266C0AC8004DE65E /* sv */,
-				198377E3266C0ADC004DE65E /* tr */,
-				198377E4266C13D2004DE65E /* uk */,
-				1918333A26ADA46800F45722 /* fi */,
-				199732B4271B72DD00129A3F /* pt-PT */,
-				199732B5271B9EE900129A3F /* pt-BR */,
-				19C166692756EFBD00ED12E3 /* sk */,
-				193F1E3A2B44C13B00525770 /* hu */,
-				193F1E3C2B44C14800525770 /* vi */,
-			);
-			name = Localizable.strings;
-			sourceTree = "<group>";
-		};
-/* End PBXVariantGroup section */
-
 /* Begin XCBuildConfiguration section */
 		388E596525AD948E0019842D /* Debug */ = {
 			isa = XCBuildConfiguration;
@@ -4266,12 +4163,14 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
 				MARKETING_VERSION = "$(APP_VERSION)";
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 			};
 			name = Debug;
@@ -4326,11 +4225,13 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
 				MARKETING_VERSION = "$(APP_VERSION)";
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				SDKROOT = iphoneos;
 				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-O";
 				VALIDATE_PRODUCT = YES;
 			};

+ 0 - 23
Trio/Resources/Base.lproj/InfoPlist.strings

@@ -1,23 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Contacts Usage Description */
-"NSContactsUsageDescription" = "Allows Trio to access your contacts for live updates to your Apple Watch contact complication using the 'Contact Trick' feature.";

Diferenças do arquivo suprimidas por serem muito extensas
+ 1059 - 0
Trio/Resources/InfoPlist.xcstrings


+ 0 - 20
Trio/Resources/ar.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, carbs and insulin";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, carbs and insulin";

+ 0 - 20
Trio/Resources/ca.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/da.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC bruges til at scanne Libre sensorer.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth bliver brugt til at kommunikere med din insulin pumpe og dine glukose monitor enheder";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth bliver brugt til at kommunikere med din insulin pumpe og dine glukose monitor enheder";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For autoriseret adgang til bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalender bruges til at oprette en ny glucose begivenheder.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App bruges til at opbevare blodglukose, insulin og kulhydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App bruges til at opbevare blodglukose, insulin og kulhydrater";

+ 0 - 20
Trio/Resources/de.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC wird zum Scannen von Libre Sensoren benutzt.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth wird verwendet, um mit Insulinpumpen und CGMs zu kommunizieren.";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth wird verwendet, um mit Insulinpumpen und CGMs zu kommunizieren.";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Autorisierung für Bolusabgabe";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "BZ-Werte werden im Kalender als temporärer Termin erstellt.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Die Apple Health App wird zum Speichern von Blutzuckerwerten, Insulin und Kohlenhydraten verwendet";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Die Apple Health App wird zum Speichern von Blutzuckerwerten, Insulin und Kohlenhydraten verwendet";

+ 0 - 20
Trio/Resources/es.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/fi.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/fr.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC est utilisé pour scanner les capteurs Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth est utilisé pour communiquer avec la pompe à insuline et les dispositifs de surveillance continue du glucose";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth est utilisé pour communiquer avec la pompe à insuline et les dispositifs de surveillance continue du glucose";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Pour les accès autorisés au bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Le calendrier est utilisé pour créer un nouvel événement de glycémie.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "L'application Santé est utilisée pour stocker la glycémie, l'insuline et les glucides";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "L'application Santé est utilisée pour stocker la glycémie, l'insuline et les glucides";

+ 0 - 20
Trio/Resources/he.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/hu.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/it.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC è usato per scansionare i sensori di Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Il bluetooth viene utilizzato per comunicare con il microinfusore e i dispositivi CGM";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Il Bluetooth viene utilizzato per comunicare con il microinfusore e i dispositivi CGM";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Per accesso autorizzato al bolo";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Il calendario è usato per creare nuovi eventi di glicemia.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "L'app Salute è usata per memorizzare glicemie, insulina e carboidrati";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "L'app Salute è usata per memorizzare glicemie, insulina e carboidrati";

+ 0 - 20
Trio/Resources/nb.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC brukes til å skanne Libre-sensorer.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth brukes til å kommunisere med insulinpumpe og blodsukkersensor";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth brukes til å kommunisere med insulinpumpe og blodsukkersensor-enheter";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For autorisert tilgang til bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalender brukes til å opprette nye blodsukker-oppføringer.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Helse-appen brukes til å lagre blodsukker, insulin og karbohydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Helse-appen brukes til å lagre blodsukker, insulin og karbohydrater";

+ 0 - 20
Trio/Resources/nl.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC wordt gebruikt voor het scannen van Libre sensoren.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth wordt gebruikt om te communiceren met de insuline pomp en de continue glucose meter";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth wordt gebruikt om te communiceren met de insuline pomp en de continue glucose meter";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Voor geautoriseerde toegang tot bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Agenda wordt gebruikt om nieuwe glucose gebeurtenissen aan te maken.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Apple Gezondheid wordt gebruikt om bloedglucose, insuline en koolhydraten op te slaan";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Apple Gezondheid wordt gebruikt om bloedglucose, insuline en koolhydraten op te slaan";

+ 0 - 20
Trio/Resources/pl.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/pt-BR.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/pt-PT.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/ru.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC используется для сканирования сенсоров Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth используется для связи с инсулиновой помпой и устройствами непрерывного мониторинга глюкозы";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth используется для связи с инсулиновой помпой и устройствами непрерывного мониторинга глюкозы";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Для авторизованного болюса";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Календарь используется для создания новых событий о глюкозе.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Приложение здоровья используется для хранения глюкозы, инсулина и углеводов в крови";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Приложение здоровья используется для хранения глюкозы, инсулина и углеводов в крови";

+ 0 - 20
Trio/Resources/sk.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC sa používa na skenovanie snímačov Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth sa používa pre komunikáciu s inzulínovou pumpou a zariadeniami na kontinuálne monitorovanie krvného cukru";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth sa používa pre komunikáciu s inzulínovou pumpou a zariadeniami na kontinuálne monitorovanie krvného cukru";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Pre autorizovaný prístup k bolusu";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalendár slúži na vytvorenie novej udalosti s glukózou.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Aplikácia Zdravie sa používa na meranie glukózy v krvi, inzulínu a ukladanie sacharidov";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Aplikácia Zdravie sa používa na meranie glukózy v krvi, inzulínu a ukladanie sacharidov";

+ 0 - 20
Trio/Resources/sv.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC används för att skanna Libre-sensorn vid uppstart.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth används för att kommunicera med pump och kontinuerlig glukosmätare";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth används för att kommunicera med insulinpumpen och kontinuerliga glukosmätare";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "För auktoriserad åtkomst till bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalendern används för att skapa kalenderhändelser för glukosvärden.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden, insulin samt kolhydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden, insulin och kolhydrater.";

+ 0 - 20
Trio/Resources/tr.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC Libre sensörlerini taramak için kullanılır.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth, insülin pompası ve sürekli glikoz izleme cihazları ile iletişim kurmak için kullanılır";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth, insülin pompası ve sürekli glikoz izleme cihazları ile iletişim kurmak için kullanılır";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Bolus'a yetkili erişim için";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Takvim, yeni bir glikoz olayı oluşturmak için kullanılır.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/uk.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC використовується для сканування сенсорів Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth використовується для обміну з інсуліновими помпами та безперервним моніторингом глюкози";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth використовується для обміну з інсуліновими помпами та безперервним моніторингом глюкози";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Для авторизованого болюсу";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Для створення нових подій глюкози використовується календар.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Додаток Health використовується для зберігання глюкози в крові, інсуліну та вуглеводів";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Додаток Health використовується для зберігання глюкози в крові, інсуліну та вуглеводів";

+ 0 - 20
Trio/Resources/vi.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC được sử dụng để quét các cảm biến Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth được sử dụng để liên lạc với máy bơm insulin và các thiết bị theo dõi đường huyết liên tục/CGM.";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth được sử dụng để liên lạc với máy bơm insulin và các thiết bị theo dõi đường huyết liên tục/CGM.";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Được cấp quyền truy cập vào bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Lịch \nCalendar được sử dụng để tạo ra một sự kiện glucose mới.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Ứng dụng sức khỏe \nHealth được sử dụng để lưu trữ đường huyết, insulin và carbohydrate";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Ứng dụng sức khỏe \nHealth được sử dụng để lưu trữ đường huyết, insulin và carbohydrate";

+ 0 - 20
Trio/Resources/zh-Hans.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC 用于扫描 Libre 传感器。";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "蓝牙用于与胰岛素泵和连续血糖监测设备进行通信";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "蓝牙用于与胰岛素泵和连续血糖监测设备进行通信";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "用于输注胰岛素授权";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "日历用于创建一个新的葡萄糖事件。";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 9 - 3
Trio/Sources/APS/APSManager.swift

@@ -22,7 +22,7 @@ protocol APSManager {
     func enactTempBasal(rate: Double, duration: TimeInterval) async
     func determineBasal() async -> Bool
     func determineBasalSync() async
-    func simulateDetermineBasal(carbs: Decimal, iob: Decimal) async -> Determination?
+    func simulateDetermineBasal(simulatedCarbsAmount: Decimal, simulatedBolusAmount: Decimal) async -> Determination?
     func roundBolus(amount: Decimal) -> Decimal
     var lastError: CurrentValueSubject<Error?, Never> { get }
     func cancelBolus(_ callback: ((Bool, String) -> Void)?) async
@@ -413,10 +413,16 @@ final class BaseAPSManager: APSManager, Injectable {
         _ = await determineBasal()
     }
 
-    func simulateDetermineBasal(carbs: Decimal, iob: Decimal) async -> Determination? {
+    func simulateDetermineBasal(simulatedCarbsAmount: Decimal, simulatedBolusAmount: Decimal) async -> Determination? {
         do {
             let temp = await fetchCurrentTempBasal(date: Date.now)
-            return try await openAPS.determineBasal(currentTemp: temp, clock: Date(), carbs: carbs, iob: iob, simulation: true)
+            return try await openAPS.determineBasal(
+                currentTemp: temp,
+                clock: Date(),
+                simulatedCarbsAmount: simulatedCarbsAmount,
+                simulatedBolusAmount: simulatedBolusAmount,
+                simulation: true
+            )
         } catch {
             debugPrint(
                 "\(DebuggingIdentifiers.failed) \(#file) \(#function) Error occurred in invokeDummyDetermineBasalSync: \(error)"

+ 1 - 1
Trio/Sources/APS/CGM/AppGroupSource.swift

@@ -115,7 +115,7 @@ struct AppGroupSource: GlucoseSource {
     }
 
     func sourceInfo() -> [String: Any]? {
-        [GlucoseSourceKey.description.rawValue: "Group ID: \(Bundle.main.appGroupSuiteName ?? "Not set"))"]
+        [GlucoseSourceKey.description.rawValue: "Group ID: \(Bundle.main.appGroupSuiteName ?? String(localized: "Not set"))"]
     }
 }
 

+ 8 - 7
Trio/Sources/APS/CGM/CGMType.swift

@@ -18,7 +18,7 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
         case .xdrip:
             return "xDrip4iOS"
         case .simulator:
-            return NSLocalizedString("Glucose Simulator", comment: "Glucose Simulator CGM type")
+            return String(localized: "Glucose Simulator", comment: "Glucose Simulator CGM type")
         case .enlite:
             return "Medtronic Enlite"
         case .plugin:
@@ -52,20 +52,21 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
     var subtitle: String {
         switch self {
         case .none:
-            return NSLocalizedString("None", comment: "No CGM selected")
+            return String(localized: "None", comment: "No CGM selected")
         case .nightscout:
-            return NSLocalizedString("Uses your Nightscout as CGM", comment: "Online or internal server")
+            return String(localized: "Uses your Nightscout as CGM", comment: "Online or internal server")
         case .xdrip:
-            return NSLocalizedString(
+            return String(
+                localized:
                 "Using shared app group with external CGM app xDrip4iOS",
                 comment: "Shared app group xDrip4iOS"
             )
         case .simulator:
-            return NSLocalizedString("Glucose Simulator for Demo Only", comment: "Simple simulator")
+            return String(localized: "Glucose Simulator for Demo Only", comment: "Simple simulator")
         case .enlite:
-            return NSLocalizedString("Minilink transmitter", comment: "Minilink transmitter")
+            return String(localized: "Minilink transmitter", comment: "Minilink transmitter")
         case .plugin:
-            return NSLocalizedString("Plugin CGM", comment: "Plugin CGM")
+            return String(localized: "Plugin CGM", comment: "Plugin CGM")
         }
     }
 }

+ 4 - 3
Trio/Sources/APS/CGM/PluginSource.swift

@@ -7,7 +7,7 @@ import LoopKit
 import LoopKitUI
 
 final class PluginSource: GlucoseSource {
-    private let processQueue = DispatchQueue(label: "DexcomSource.processQueue")
+    private let processQueue = DispatchQueue(label: "CGMPluginSource.processQueue")
     private let glucoseStorage: GlucoseStorage!
     var glucoseManager: FetchGlucoseManager?
 
@@ -159,8 +159,9 @@ extension PluginSource: CGMManagerDelegate {
         UUID().uuidString
     }
 
-    func cgmManager(_: CGMManager, didUpdate status: CGMManagerStatus) {
-        debug(.deviceManager, "DEBUG DID UPDATE STATE")
+    func cgmManager(_ cgmManager: CGMManager, didUpdate status: CGMManagerStatus) {
+        debug(.deviceManager, "CGM Manager did update state to \(status)")
+        
         processQueue.async {
             if self.cgmHasValidSensorSession != status.hasValidSensorSession {
                 self.cgmHasValidSensorSession = status.hasValidSensorSession

+ 27 - 5
Trio/Sources/APS/FetchGlucoseManager.swift

@@ -126,11 +126,15 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
         // if plugin, if not the same pluginID, need to reset the cgmManager
         // if plugin and newManager provides, update cgmManager
         debug(.apsManager, "plugin : \(String(describing: cgmManager?.pluginIdentifier))")
-        if let manager = newManager
-        {
-            cgmManager = manager
-            glucoseSource = nil
-            removeCalibrations()
+
+        if let manager = newManager {
+            // If the pointer to manager is the *same* as our current `cgmManager`, skip re-init
+            if manager !== cgmManager {
+                // or do a more thorough check to see if it is the same class & state
+                removeCalibrations()
+                cgmManager = manager
+                glucoseSource = nil
+            }
         } else if self.cgmGlucoseSourceType == .plugin, cgmManager == nil, let rawCGMManager = rawCGMManager {
             cgmManager = cgmManagerFromRawValue(rawCGMManager)
             updateManagerUnits(cgmManager)
@@ -339,6 +343,24 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
     }
 }
 
+extension FetchGlucoseManager {
+    /// Dispatches given `functionToInvoke` to the CGM manager's queue (if any).
+    func performOnCGMManagerQueue(_ functionToInvoke: @escaping () -> Void) {
+        // If a CGM manager exists and it defines a delegate queue, use it
+        if let cgmManager = self.cgmManager,
+           let managerQueue = cgmManager.delegateQueue
+        {
+            managerQueue.async {
+                functionToInvoke()
+            }
+        } else {
+            // If there's no cgmManager or no queue, just run the block immediately
+            // This possibly executes `functionToInvoke` on main thread
+            functionToInvoke()
+        }
+    }
+}
+
 extension CGMManager {
     typealias RawValue = [String: Any]
 

+ 25 - 10
Trio/Sources/APS/OpenAPS/OpenAPS.swift

@@ -189,7 +189,10 @@ final class OpenAPS {
         }
     }
 
-    private func parsePumpHistory(_ pumpHistoryObjectIDs: [NSManagedObjectID], iob: Decimal? = nil) async -> String {
+    private func parsePumpHistory(
+        _ pumpHistoryObjectIDs: [NSManagedObjectID],
+        simulatedBolusAmount: Decimal? = nil
+    ) async -> String {
         // Return an empty JSON object if the list of object IDs is empty
         guard !pumpHistoryObjectIDs.isEmpty else { return "{}" }
 
@@ -199,9 +202,9 @@ final class OpenAPS {
             var dtos = self.loadAndMapPumpEvents(pumpHistoryObjectIDs)
 
             // Optionally add the IOB as a DTO
-            if let iob = iob {
-                let iobDTO = self.createIOBDTO(iob: iob)
-                dtos.insert(iobDTO, at: 0)
+            if let simulatedBolusAmount = simulatedBolusAmount {
+                let simulatedBolusDTO = self.createSimulatedBolusDTO(simulatedBolusAmount: simulatedBolusAmount)
+                dtos.insert(simulatedBolusDTO, at: 0)
             }
 
             // Convert the DTOs to JSON
@@ -226,12 +229,24 @@ final class OpenAPS {
             if let tempBasalDTO = event.toTempBasalDTOEnum() {
                 eventDTOs.append(tempBasalDTO)
             }
+            if let pumpSuspendDTO = event.toPumpSuspendDTO() {
+                eventDTOs.append(pumpSuspendDTO)
+            }
+            if let pumpResumeDTO = event.toPumpResumeDTO() {
+                eventDTOs.append(pumpResumeDTO)
+            }
+            if let rewindDTO = event.toRewindDTO() {
+                eventDTOs.append(rewindDTO)
+            }
+            if let primeDTO = event.toPrimeDTO() {
+                eventDTOs.append(primeDTO)
+            }
             return eventDTOs
         }
         return dtos
     }
 
-    private func createIOBDTO(iob: Decimal) -> PumpEventDTO {
+    private func createSimulatedBolusDTO(simulatedBolusAmount: Decimal) -> PumpEventDTO {
         let oneSecondAgo = Calendar.current
             .date(
                 byAdding: .second,
@@ -243,7 +258,7 @@ final class OpenAPS {
         let bolusDTO = BolusDTO(
             id: UUID().uuidString,
             timestamp: dateFormatted,
-            amount: Double(iob),
+            amount: Double(simulatedBolusAmount),
             isExternal: false,
             isSMB: true,
             duration: 0,
@@ -255,8 +270,8 @@ final class OpenAPS {
     func determineBasal(
         currentTemp: TempBasal,
         clock: Date = Date(),
-        carbs: Decimal? = nil,
-        iob: Decimal? = nil,
+        simulatedCarbsAmount: Decimal? = nil,
+        simulatedBolusAmount: Decimal? = nil,
         simulation: Bool = false
     ) async throws -> Determination? {
         debug(.openAPS, "Start determineBasal")
@@ -266,7 +281,7 @@ final class OpenAPS {
 
         // Perform asynchronous calls in parallel
         async let pumpHistoryObjectIDs = fetchPumpHistoryObjectIDs() ?? []
-        async let carbs = fetchAndProcessCarbs(additionalCarbs: carbs ?? 0)
+        async let carbs = fetchAndProcessCarbs(additionalCarbs: simulatedCarbsAmount ?? 0)
         async let glucose = fetchAndProcessGlucose()
         async let oref2 = oref2()
         async let profileAsync = loadFileFromStorageAsync(name: Settings.profile)
@@ -287,7 +302,7 @@ final class OpenAPS {
             reservoir,
             preferences
         ) = await (
-            parsePumpHistory(await pumpHistoryObjectIDs, iob: iob),
+            parsePumpHistory(await pumpHistoryObjectIDs, simulatedBolusAmount: simulatedBolusAmount),
             carbs,
             glucose,
             oref2,

+ 1 - 1
Trio/Sources/APS/Storage/ContactImageStorage.swift

@@ -39,7 +39,7 @@ final class BaseContactImageStorage: ContactImageStorage, Injectable {
 
             return fetchedContactImageEntries.compactMap { entry in
                 ContactImageEntry(
-                    name: entry.name ?? "No name provided",
+                    name: entry.name ?? String(localized: "No name provided"),
                     layout: ContactImageLayout(rawValue: entry.layout ?? "Default") ?? .default,
                     ring: ContactImageLargeRing(rawValue: entry.ring ?? "Hidden") ?? .none,
                     primary: ContactImageValue(rawValue: entry.primary ?? "Glucose Reading") ?? .glucose,

+ 2 - 2
Trio/Sources/APS/Storage/GlucoseStorage.swift

@@ -538,9 +538,9 @@ enum GlucoseAlarm {
     var displayName: String {
         switch self {
         case .high:
-            return NSLocalizedString("LOWALERT!", comment: "LOWALERT!")
+            return String(localized: "LOWALERT!", comment: "LOWALERT!")
         case .low:
-            return NSLocalizedString("HIGHALERT!", comment: "HIGHALERT!")
+            return String(localized: "HIGHALERT!", comment: "HIGHALERT!")
         }
     }
 }

+ 3 - 3
Trio/Sources/APS/Storage/OverrideStorage.swift

@@ -210,7 +210,7 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
         let results = await CoreDataStack.shared.fetchEntitiesAsync(
             ofType: OverrideStored.self,
             onContext: backgroundContext,
-            predicate: NSPredicate.lastActiveOverrideNotYetUploadedToNightscout,
+            predicate: NSPredicate.lastActiveAdjustmentNotYetUploadedToNightscout,
             key: "date",
             ascending: false
         )
@@ -225,7 +225,7 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
                     eventType: OverrideStored.EventType.nsExercise,
                     createdAt: override.date ?? Date(),
                     enteredBy: NightscoutExercise.local,
-                    notes: override.name ?? "Custom Override",
+                    notes: override.name ?? String(localized: "Custom Override"),
                     id: UUID(uuidString: override.id ?? UUID().uuidString)
                 )
             }
@@ -256,7 +256,7 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
                     eventType: OverrideStored.EventType.nsExercise,
                     createdAt: (overrideRun.startDate ?? overrideRun.override?.date) ?? Date(),
                     enteredBy: NightscoutExercise.local,
-                    notes: overrideRun.name ?? "Custom Override",
+                    notes: overrideRun.name ?? String(localized: "Custom Override"),
                     id: overrideRun.id
                 )
             }

+ 3 - 3
Trio/Sources/APS/Storage/TempTargetsStorage.swift

@@ -14,7 +14,7 @@ protocol TempTargetsStorage {
     func fetchScheduledTempTargets() async -> [NSManagedObjectID]
     func fetchScheduledTempTarget(for targetDate: Date) async -> [NSManagedObjectID]
     func copyRunningTempTarget(_ tempTarget: TempTargetStored) async -> NSManagedObjectID
-    func deleteOverridePreset(_ objectID: NSManagedObjectID) async
+    func deleteTempTargetPreset(_ objectID: NSManagedObjectID) async
     func loadLatestTempTargetConfigurations(fetchLimit: Int) async -> [NSManagedObjectID]
     func syncDate() -> Date
     func recent() -> [TempTarget]
@@ -216,7 +216,7 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
         return newTempTarget.objectID
     }
 
-    @MainActor func deleteOverridePreset(_ objectID: NSManagedObjectID) async {
+    @MainActor func deleteTempTargetPreset(_ objectID: NSManagedObjectID) async {
         await CoreDataStack.shared.deleteObject(identifiedBy: objectID)
     }
 
@@ -246,7 +246,7 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
         let results = await CoreDataStack.shared.fetchEntitiesAsync(
             ofType: TempTargetStored.self,
             onContext: backgroundContext,
-            predicate: NSPredicate.lastActiveOverrideNotYetUploadedToNightscout, // TODO: create adjustment predicate (OR+TT)
+            predicate: NSPredicate.lastActiveAdjustmentNotYetUploadedToNightscout,
             key: "date",
             ascending: false
         )

+ 22 - 7
Trio/Sources/Application/TrioApp.swift

@@ -13,7 +13,7 @@ import Swinject
     // Read the color scheme preference from UserDefaults; defaults to system default setting
     @AppStorage("colorSchemePreference") private var colorSchemePreference: ColorSchemeOption = .systemDefault
 
-    let coreDataStack = CoreDataStack.shared
+    let coreDataStack: CoreDataStack
 
     @State private var appState = AppState()
 
@@ -68,14 +68,29 @@ import Swinject
             "Trio Started: v\(Bundle.main.releaseVersionNumber ?? "")(\(Bundle.main.buildVersionNumber ?? "")) [buildDate: \(String(describing: BuildDetails.default.buildDate()))] [buildExpires: \(String(describing: BuildDetails.default.calculateExpirationDate()))]"
         )
 
-        // Load services
-        loadServices()
+        // Setup up the Core Data Stack
+        coreDataStack = CoreDataStack.shared
 
-        // Fix bug in iOS 18 related to the translucent tab bar
-        configureTabBarAppearance()
+        do {
+            // Explicitly initialize Core Data Stacak
+            try coreDataStack.initializeStack()
+
+            // Load services
+            loadServices()
+
+            // Fix bug in iOS 18 related to the translucent tab bar
+            configureTabBarAppearance()
 
-        // Clear the persistentHistory and the NSManagedObjects that are older than 90 days every time the app starts
-        cleanupOldData()
+            // Clear the persistentHistory and the NSManagedObjects that are older than 90 days every time the app starts
+            cleanupOldData()
+        } catch {
+            debug(
+                .coreData,
+                "Failed to initialize Core Data Stack: \(error.localizedDescription)"
+            )
+            // Handle initialization failure
+            fatalError("Core Data Stack initialization failed: \(error.localizedDescription)")
+        }
     }
 
     var body: some Scene {

+ 4 - 4
Trio/Sources/Helpers/BuildDetails.swift

@@ -27,8 +27,8 @@ class BuildDetails {
     }
 
     var branchAndSha: String {
-        let branch = dict["com-trio-branch"] as? String ?? "Unknown"
-        let sha = dict["com-trio-commit-sha"] as? String ?? "Unknown"
+        let branch = dict["com-trio-branch"] as? String ?? String(localized: "Unknown")
+        let sha = dict["com-trio-commit-sha"] as? String ?? String(localized: "Unknown")
         return "\(branch) \(sha)"
     }
 
@@ -75,9 +75,9 @@ class BuildDetails {
     // Expiration header based on build type
     var expirationHeaderString: String {
         if isTestFlightBuild() {
-            return "Beta (TestFlight) Expires"
+            return String(localized: "Beta (TestFlight) Expires")
         } else {
-            return "App Expires"
+            return String(localized: "App Expires")
         }
     }
 }

+ 4 - 4
Trio/Sources/Helpers/HKUnit.swift

@@ -44,13 +44,13 @@ extension HKUnit {
 
     var localizedShortUnitString: String {
         if self == HKUnit.millimolesPerLiter {
-            return NSLocalizedString("mmol/L", comment: "The short unit display string for millimoles of glucose per liter")
+            return String(localized: "mmol/L", comment: "The short unit display string for millimoles of glucose per liter")
         } else if self == .milligramsPerDeciliter {
-            return NSLocalizedString("mg/dL", comment: "The short unit display string for milligrams of glucose per decilter")
+            return String(localized: "mg/dL", comment: "The short unit display string for milligrams of glucose per decilter")
         } else if self == .internationalUnit() {
-            return NSLocalizedString("U", comment: "The short unit display string for international units of insulin")
+            return String(localized: "U", comment: "The short unit display string for international units of insulin")
         } else if self == .gram() {
-            return NSLocalizedString("g", comment: "The short unit display string for grams")
+            return String(localized: "g", comment: "The short unit display string for grams")
         } else {
             return String(describing: self)
         }

Diferenças do arquivo suprimidas por serem muito extensas
+ 180308 - 0
Trio/Sources/Localizations/Main/Localizable.xcstrings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2223
Trio/Sources/Localizations/Main/ar.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 1917
Trio/Sources/Localizations/Main/ca.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2184
Trio/Sources/Localizations/Main/da.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2229
Trio/Sources/Localizations/Main/de.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2231
Trio/Sources/Localizations/Main/en.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2228
Trio/Sources/Localizations/Main/es.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2229
Trio/Sources/Localizations/Main/fi.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2220
Trio/Sources/Localizations/Main/fr.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2223
Trio/Sources/Localizations/Main/he.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2178
Trio/Sources/Localizations/Main/hu.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2225
Trio/Sources/Localizations/Main/it.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2223
Trio/Sources/Localizations/Main/nb.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2231
Trio/Sources/Localizations/Main/nl.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2225
Trio/Sources/Localizations/Main/pl.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2223
Trio/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2223
Trio/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2226
Trio/Sources/Localizations/Main/ru.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2178
Trio/Sources/Localizations/Main/sk.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2223
Trio/Sources/Localizations/Main/sv.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2227
Trio/Sources/Localizations/Main/tr.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2223
Trio/Sources/Localizations/Main/uk.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2180
Trio/Sources/Localizations/Main/vi.lproj/Localizable.strings


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 2225
Trio/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings


+ 4 - 0
Trio/Sources/Logger/Logger.swift

@@ -115,6 +115,7 @@ final class Logger {
     static let remoteControl = Logger(category: .remoteControl, reporter: baseReporter)
     static let bolusState = Logger(category: .bolusState, reporter: baseReporter)
     static let watchManager = Logger(category: .watchManager, reporter: baseReporter)
+    static let coreData = Logger(category: .coreData, reporter: baseReporter)
 
     enum Category: String {
         case `default`
@@ -127,6 +128,7 @@ final class Logger {
         case remoteControl
         case bolusState
         case watchManager
+        case coreData
 
         var name: String {
             rawValue.capitalizingFirstLetter()
@@ -144,6 +146,7 @@ final class Logger {
             case .remoteControl: return .remoteControl
             case .bolusState: return .bolusState
             case .watchManager: return .watchManager
+            case .coreData: return .coreData
             }
         }
 
@@ -154,6 +157,7 @@ final class Logger {
             case .apsManager,
                  .bolusState,
                  .businessLogic,
+                 .coreData,
                  .deviceManager,
                  .nightscout,
                  .openAPS,

+ 16 - 16
Trio/Sources/Models/ContactTrickEntry.swift

@@ -130,23 +130,23 @@ enum ContactImageValue: String, JSON, CaseIterable, Identifiable, Codable {
     var displayName: String {
         switch self {
         case .none:
-            return NSLocalizedString("None", comment: "")
+            return String(localized: "None", comment: "")
         case .glucose:
-            return NSLocalizedString("Glucose Reading", comment: "")
+            return String(localized: "Glucose Reading", comment: "")
         case .eventualBG:
-            return NSLocalizedString("Eventual Glucose", comment: "")
+            return String(localized: "Eventual Glucose", comment: "")
         case .delta:
-            return NSLocalizedString("Glucose Delta", comment: "")
+            return String(localized: "Glucose Delta", comment: "")
         case .trend:
-            return NSLocalizedString("Glucose Trend", comment: "")
+            return String(localized: "Glucose Trend", comment: "")
         case .lastLoopDate:
-            return NSLocalizedString("Last Loop Time", comment: "")
+            return String(localized: "Last Loop Time", comment: "")
         case .cob:
-            return NSLocalizedString("COB", comment: "")
+            return String(localized: "COB", comment: "")
         case .iob:
-            return NSLocalizedString("IOB", comment: "")
+            return String(localized: "IOB", comment: "")
         case .ring:
-            return NSLocalizedString("Loop Status", comment: "")
+            return String(localized: "Loop Status", comment: "")
         }
     }
 }
@@ -159,9 +159,9 @@ enum ContactImageLayout: String, JSON, CaseIterable, Identifiable, Codable {
     var displayName: String {
         switch self {
         case .default:
-            return NSLocalizedString("Default", comment: "")
+            return String(localized: "Default", comment: "")
         case .split:
-            return NSLocalizedString("Split", comment: "")
+            return String(localized: "Split", comment: "")
         }
     }
 }
@@ -178,15 +178,15 @@ enum ContactImageLargeRing: String, JSON, CaseIterable, Identifiable, Codable {
     var displayName: String {
         switch self {
         case .none:
-            return NSLocalizedString("Hidden", comment: "")
+            return String(localized: "Hidden", comment: "")
         case .loop:
-            return NSLocalizedString("Loop Status", comment: "")
+            return String(localized: "Loop Status", comment: "")
 //        case .iob:
-//            return NSLocalizedString("Insulin on Board (IOB)", comment: "")
+//            return String(localized: "Insulin on Board (IOB)", comment: "")
 //        case .cob:
-//            return NSLocalizedString("Carbs on Board (COB)", comment: "")
+//            return String(localized: "Carbs on Board (COB)", comment: "")
 //        case .iobcob:
-//            return NSLocalizedString("IOB + COB", comment: "")
+//            return String(localized: "IOB + COB", comment: "")
         }
     }
 }

+ 2 - 2
Trio/Sources/Models/ForecastDisplayType.swift

@@ -7,10 +7,10 @@ enum ForecastDisplayType: String, JSON, CaseIterable, Identifiable, Codable, Has
     var displayName: String {
         switch self {
         case .cone:
-            return NSLocalizedString("Cone", comment: "")
+            return String(localized: "Cone", comment: "")
 
         case .lines:
-            return NSLocalizedString("Lines", comment: "")
+            return String(localized: "Lines", comment: "")
         }
     }
 }

+ 2 - 2
Trio/Sources/Models/HbA1cDisplayUnit.swift

@@ -8,9 +8,9 @@ enum HbA1cDisplayUnit: String, JSON, CaseIterable, Identifiable, Codable, Hashab
     var displayName: String {
         switch self {
         case .percent:
-            return NSLocalizedString("Percent", comment: "")
+            return String(localized: "Percent", comment: "")
         case .mmolMol:
-            return NSLocalizedString("mmol/mol", comment: "")
+            return String(localized: "mmol/mol", comment: "")
         }
     }
 }

+ 2 - 2
Trio/Sources/Models/LockScreenView.swift

@@ -7,9 +7,9 @@ enum LockScreenView: String, JSON, CaseIterable, Identifiable, Codable, Hashable
     var displayName: String {
         switch self {
         case .simple:
-            return NSLocalizedString("Simple", comment: "")
+            return String(localized: "Simple", comment: "")
         case .detailed:
-            return NSLocalizedString("Detailed", comment: "")
+            return String(localized: "Detailed", comment: "")
         }
     }
 }

+ 2 - 2
Trio/Sources/Models/TimeInRangeChartStyle.swift

@@ -8,9 +8,9 @@ enum TimeInRangeChartStyle: String, JSON, CaseIterable, Identifiable, Codable, H
     var displayName: String {
         switch self {
         case .vertical:
-            return NSLocalizedString("Vertical", comment: "")
+            return String(localized: "Vertical", comment: "")
         case .horizontal:
-            return NSLocalizedString("Horizontal", comment: "")
+            return String(localized: "Horizontal", comment: "")
         }
     }
 }

+ 2 - 2
Trio/Sources/Models/TotalInsulinDisplayType.swift

@@ -14,9 +14,9 @@ enum TotalInsulinDisplayType: String, JSON, CaseIterable, Identifiable, Codable,
     var displayName: String {
         switch self {
         case .totalDailyDose:
-            return NSLocalizedString("TDD", comment: "")
+            return String(localized: "TDD", comment: "")
         case .totalInsulinInScope:
-            return NSLocalizedString("TINS", comment: "")
+            return String(localized: "TINS", comment: "")
         }
     }
 }

+ 2 - 5
Trio/Sources/Modules/Adjustments/AdjustmentsDataFlow.swift

@@ -11,15 +11,12 @@ enum Adjustments {
         var id: String { rawValue }
 
         var name: String {
-            var name: String = ""
             switch self {
             case .overrides:
-                name = "Overrides"
+                return String(localized: "Overrides", comment: "Selected Tab")
             case .tempTargets:
-                name = "Temp Targets"
+                return String(localized: "Temp Targets", comment: "Selected Tab")
             }
-
-            return NSLocalizedString(name, comment: "Selected Tab")
         }
     }
 }

+ 2 - 2
Trio/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+Overrides.swift

@@ -224,7 +224,7 @@ extension Adjustments.StateModel {
 
             if let overrideToEdit = try viewContext.existingObject(with: firstID) as? OverrideStored {
                 currentActiveOverride = overrideToEdit
-                activeOverrideName = overrideToEdit.name ?? "Custom Override"
+                activeOverrideName = overrideToEdit.name ?? String(localized: "Custom Override")
             }
         } catch {
             debugPrint(
@@ -248,7 +248,7 @@ extension Adjustments.StateModel {
 
             if let overrideToEdit = try viewContext.existingObject(with: duplicateId) as? OverrideStored {
                 currentActiveOverride = overrideToEdit
-                activeOverrideName = overrideToEdit.name ?? "Custom Override"
+                activeOverrideName = overrideToEdit.name ?? String(localized: "Custom Override")
             }
         } catch {
             debugPrint(

+ 5 - 5
Trio/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+TempTargets.swift

@@ -44,7 +44,7 @@ extension Adjustments.StateModel {
 
             if let tempTargetToEdit = try viewContext.existingObject(with: firstID) as? TempTargetStored {
                 currentActiveTempTarget = tempTargetToEdit
-                activeTempTargetName = tempTargetToEdit.name ?? "Custom Temp Target"
+                activeTempTargetName = tempTargetToEdit.name ?? String(localized: "Custom Temp Target")
                 tempTargetTarget = tempTargetToEdit.target?.decimalValue ?? 0
             }
         } catch {
@@ -254,7 +254,7 @@ extension Adjustments.StateModel {
             )
             tempTargetStorage.saveTempTargetsToStorage([tempTarget])
         } catch {
-            debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to enact Override Preset")
+            debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to enact TempTarget Preset")
         }
     }
 
@@ -313,7 +313,7 @@ extension Adjustments.StateModel {
 
     /// Duplicates the current preset and cancels the previous one.
     @MainActor func duplicateTempTargetPresetAndCancelPreviousTempTarget() async {
-        // We get the current active Preset by using currentActiveTempTarget which can either be a Preset or a custom Override
+        // We get the current active Preset by using currentActiveTempTarget which can either be a Preset or a custom TempTarget
         guard let tempTargetPresetToDuplicate = currentActiveTempTarget,
               tempTargetPresetToDuplicate.isPreset == true else { return }
 
@@ -333,7 +333,7 @@ extension Adjustments.StateModel {
             if let tempTargetToEdit = try viewContext.existingObject(with: duplidateId) as? TempTargetStored
             {
                 currentActiveTempTarget = tempTargetToEdit
-                activeTempTargetName = tempTargetToEdit.name ?? "Custom Temp Target"
+                activeTempTargetName = tempTargetToEdit.name ?? String(localized: "Custom Temp Target")
             }
         } catch {
             debugPrint(
@@ -344,7 +344,7 @@ extension Adjustments.StateModel {
 
     /// Deletes a Temp Target preset.
     func invokeTempTargetPresetDeletion(_ objectID: NSManagedObjectID) async {
-        await tempTargetStorage.deleteOverridePreset(objectID)
+        await tempTargetStorage.deleteTempTargetPreset(objectID)
         setupTempTargetPresetsArray()
     }
 

+ 65 - 34
Trio/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift

@@ -38,13 +38,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier")
+                            hintLabel = String(localized: "Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier")
                         }
                     ),
                     units: state.units,
                     type: .decimal("maxDailySafetyMultiplier"),
-                    label: NSLocalizedString("Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier"),
-                    miniHint: "Limits temporary basal rates to this percentage of your largest basal rate.",
+                    label: String(localized: "Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier"),
+                    miniHint: String(
+                        localized: "Limits temporary basal rates to this percentage of your largest basal rate.",
+                        comment: "Mini Hint for Max Daily Safety Multiplier"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 300%").bold()
@@ -64,7 +67,8 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString(
+                            hintLabel = String(
+                                localized:
                                 "Current Basal Safety Multiplier",
                                 comment: "Current Basal Safety Multiplier"
                             )
@@ -72,8 +76,11 @@ extension AlgorithmAdvancedSettings {
                     ),
                     units: state.units,
                     type: .decimal("currentBasalSafetyMultiplier"),
-                    label: NSLocalizedString("Current Basal Safety Multiplier", comment: "Current Basal Safety Multiplier"),
-                    miniHint: "Limits temporary basal rates to this percentage of the current basal rate.",
+                    label: String(localized: "Current Basal Safety Multiplier", comment: "Current Basal Safety Multiplier"),
+                    miniHint: String(
+                        localized: "Limits temporary basal rates to this percentage of the current basal rate.",
+                        comment: "Mini Hint for Current Basal Safety Multiplier"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 400%").bold()
@@ -95,13 +102,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Duration of Insulin Action"
+                            hintLabel = String(localized: "Duration of Insulin Action", comment: "Duration of Insulin Action")
                         }
                     ),
                     units: state.units,
                     type: .decimal("dia"),
-                    label: "Duration of Insulin Action",
-                    miniHint: "Number of hours insulin is active in your body.",
+                    label: String(localized: "Duration of Insulin Action", comment: "Duration of Insulin Action"),
+                    miniHint: String(
+                        localized: "Number of hours insulin is active in your body.",
+                        comment: "Mini Hint for Duration of Insulin Action"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 10 hours").bold()
@@ -125,14 +135,17 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Use Custom Peak Time", comment: "Use Custom Peak Time")
+                            hintLabel = String(localized: "Use Custom Peak Time", comment: "Use Custom Peak Time")
                         }
                     ),
                     units: state.units,
                     type: .conditionalDecimal("insulinPeakTime"),
-                    label: NSLocalizedString("Use Custom Peak Time", comment: "Use Custom Peak Time"),
-                    conditionalLabel: NSLocalizedString("Insulin Peak Time", comment: "Insulin Peak Time"),
-                    miniHint: "Set a custom time for peak insulin effect.",
+                    label: String(localized: "Use Custom Peak Time", comment: "Use Custom Peak Time"),
+                    conditionalLabel: String(localized: "Insulin Peak Time", comment: "Insulin Peak Time"),
+                    miniHint: String(
+                        localized: "Set a custom time for peak insulin effect.",
+                        comment: "Mini Hint for Insulin Peak Time"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: Set by Insulin Type").bold()
@@ -156,13 +169,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Skip Neutral Temps", comment: "Skip Neutral Temps")
+                            hintLabel = String(localized: "Skip Neutral Temps", comment: "Skip Neutral Temps")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: NSLocalizedString("Skip Neutral Temps", comment: "Skip Neutral Temps"),
-                    miniHint: "Skip neutral temporary basal rates to reduce MDT pump alerts.",
+                    label: String(localized: "Skip Neutral Temps", comment: "Skip Neutral Temps"),
+                    miniHint: String(
+                        localized: "Skip neutral temporary basal rates to reduce MDT pump alerts.",
+                        comment: "Mini Hint for Skip Neutral Temps"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -183,13 +199,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Unsuspend If No Temp", comment: "Unsuspend If No Temp")
+                            hintLabel = String(localized: "Unsuspend If No Temp", comment: "Unsuspend If No Temp")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: NSLocalizedString("Unsuspend If No Temp", comment: "Unsuspend If No Temp"),
-                    miniHint: "Resume pump automatically after suspension.",
+                    label: String(localized: "Unsuspend If No Temp", comment: "Unsuspend If No Temp"),
+                    miniHint: String(
+                        localized: "Resume pump automatically after suspension.",
+                        comment: "Mini Hint for Unsuspend If No Temp"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -208,13 +227,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Suspend Zeros IOB", comment: "Suspend Zeros IOB")
+                            hintLabel = String(localized: "Suspend Zeros IOB", comment: "Suspend Zeros IOB")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: NSLocalizedString("Suspend Zeros IOB", comment: "Suspend Zeros IOB"),
-                    miniHint: "Clear temporary basal rates and reset IOB when suspended.",
+                    label: String(localized: "Suspend Zeros IOB", comment: "Suspend Zeros IOB"),
+                    miniHint: String(
+                        localized: "Clear temporary basal rates and reset IOB when suspended.",
+                        comment: "Mini Hint for Suspend Zeros IOB"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -236,12 +258,12 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Min 5m Carb Impact", comment: "Min 5m Carb Impact")
+                            hintLabel = String(localized: "Min 5m Carb Impact", comment: "Min 5m Carb Impact")
                         }
                     ),
                     units: state.units,
                     type: .decimal("min5mCarbimpact"),
-                    label: NSLocalizedString("Min 5m Carb Impact", comment: "Min 5m Carb Impact"),
+                    label: String(localized: "Min 5m Carb Impact", comment: "Min 5m Carb Impact"),
                     miniHint: "Default impact of carb absorption over a 5 minute interval.",
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
@@ -265,13 +287,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Remaining Carbs Percentage", comment: "Remaining Carbs Percentage")
+                            hintLabel = String(localized: "Remaining Carbs Percentage", comment: "Remaining Carbs Percentage")
                         }
                     ),
                     units: state.units,
                     type: .decimal("remainingCarbsFraction"),
-                    label: NSLocalizedString("Remaining Carbs Percentage", comment: "Remaining Carbs Percentage"),
-                    miniHint: "Percentage of carbs still available if no absorption is detected.",
+                    label: String(localized: "Remaining Carbs Percentage", comment: "Remaining Carbs Percentage"),
+                    miniHint: String(
+                        localized: "Percentage of carbs still available if no absorption is detected.",
+                        comment: "Mini Hint for Remaining Carbs Percentage"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 100%").bold()
@@ -292,13 +317,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Remaining Carbs Cap", comment: "Remaining Carbs Cap")
+                            hintLabel = String(localized: "Remaining Carbs Cap", comment: "Remaining Carbs Cap")
                         }
                     ),
                     units: state.units,
                     type: .decimal("remainingCarbsCap"),
-                    label: NSLocalizedString("Remaining Carbs Cap", comment: "Remaining Carbs Cap"),
-                    miniHint: "Maximum amount of carbs still available if no absorption is detected.",
+                    label: String(localized: "Remaining Carbs Cap", comment: "Remaining Carbs Cap"),
+                    miniHint: String(
+                        localized: "Maximum amount of carbs still available if no absorption is detected.",
+                        comment: "Mini hint for Remaining Carbs Cap"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 90 g").bold()
@@ -319,13 +347,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Noisy CGM Target Multiplier", comment: "Noisy CGM Target Multiplier")
+                            hintLabel = String(localized: "Noisy CGM Target Multiplier", comment: "Noisy CGM Target Multiplier")
                         }
                     ),
                     units: state.units,
                     type: .decimal("noisyCGMTargetMultiplier"),
-                    label: NSLocalizedString("Noisy CGM Target Increase", comment: "Noisy CGM Target Increase"),
-                    miniHint: "Percentage increase of glucose target when CGM is inconsistent.",
+                    label: String(localized: "Noisy CGM Target Increase", comment: "Noisy CGM Target Increase"),
+                    miniHint: String(
+                        localized: "Percentage increase of glucose target when CGM is inconsistent.",
+                        comment: "Mini Hint for Noisy CGM Target Increase"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 130%").bold()
@@ -346,7 +377,7 @@ extension AlgorithmAdvancedSettings {
                     shouldDisplayHint: $shouldDisplayHint,
                     hintLabel: hintLabel ?? "",
                     hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
             .scrollContentBackground(.hidden)

+ 9 - 9
Trio/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift

@@ -105,7 +105,7 @@ extension AutosensSettings {
                         Spacer()
                         Button(
                             action: {
-                                hintLabel = "Autosens"
+                                hintLabel = String(localized: "Autosens")
                                 selectedVerboseHint = AnyView(autosensVerboseHint)
                                 shouldDisplayHint.toggle()
                             },
@@ -134,12 +134,12 @@ extension AutosensSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Autosens Max", comment: "Autosens Max")
+                            hintLabel = String(localized: "Autosens Max", comment: "Autosens Max")
                         }
                     ),
                     units: state.units,
                     type: .decimal("autosensMax"),
-                    label: NSLocalizedString("Autosens Max", comment: "Autosens Max"),
+                    label: String(localized: "Autosens Max", comment: "Autosens Max"),
                     miniHint: "Upper limit of the Autosens Ratio.",
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
@@ -154,7 +154,7 @@ extension AutosensSettings {
                             "Tip: Increasing this value allows automatic adjustments of basal rates to be higher, ISF to be lower, and CR to be lower."
                         )
                     },
-                    headerText: "Glucose Deviations Algorithm"
+                    headerText: String(localized: "Glucose Deviations Algorithm")
                 )
 
                 SettingInputSection(
@@ -165,12 +165,12 @@ extension AutosensSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Autosens Min", comment: "Autosens Min")
+                            hintLabel = String(localized: "Autosens Min", comment: "Autosens Min")
                         }
                     ),
                     units: state.units,
                     type: .decimal("autosensMin"),
-                    label: NSLocalizedString("Autosens Min", comment: "Autosens Min"),
+                    label: String(localized: "Autosens Min", comment: "Autosens Min"),
                     miniHint: "Lower limit of the Autosens Ratio.",
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
@@ -195,12 +195,12 @@ extension AutosensSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Rewind Resets Autosens", comment: "Rewind Resets Autosens")
+                            hintLabel = String(localized: "Rewind Resets Autosens", comment: "Rewind Resets Autosens")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: NSLocalizedString("Rewind Resets Autosens", comment: "Rewind Resets Autosens"),
+                    label: String(localized: "Rewind Resets Autosens", comment: "Rewind Resets Autosens"),
                     miniHint: "Pump rewind initiates a reset in Autosens Ratio.",
                     verboseHint: VStack(alignment: .leading, spacing: 5) {
                         Text("Default: ON").bold()
@@ -226,7 +226,7 @@ extension AutosensSettings {
                     shouldDisplayHint: $shouldDisplayHint,
                     hintLabel: hintLabel ?? "",
                     hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
             .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))

+ 15 - 15
Trio/Sources/Modules/BolusCalculatorConfig/View/BolusCalculatorConfigRootView.swift

@@ -41,12 +41,12 @@ extension BolusCalculatorConfig {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Display Meal Presets"
+                            hintLabel = String(localized: "Display Meal Presets")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: "Display Meal Presets",
+                    label: String(localized: "Display Meal Presets"),
                     miniHint: "Allow the creation of saved, preset meals.",
                     verboseHint: VStack(alignment: .leading, spacing: 10) {
                         Text("Default: ON").bold()
@@ -62,13 +62,13 @@ extension BolusCalculatorConfig {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Recommended Bolus Percentage"
+                            hintLabel = String(localized: "Recommended Bolus Percentage")
                         }
                     ),
                     units: state.units,
                     type: .decimal("overrideFactor"),
-                    label: "Recommended Bolus Percentage",
-                    miniHint: "Percentage of bolus suggested in bolus calculator.",
+                    label: String(localized: "Recommended Bolus Percentage"),
+                    miniHint: String(localized: "Percentage of bolus suggested in bolus calculator."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 80%").bold()
@@ -82,7 +82,7 @@ extension BolusCalculatorConfig {
                             "Tip: If you are a new Trio user, it is not advised to set this to 100% until you have verified that your core settings (basal rates, ISF, and CR) do not need adjusting."
                         )
                     },
-                    headerText: "Calculator Configuration"
+                    headerText: String(localized: "Calculator Configuration")
                 )
 
                 SettingInputSection(
@@ -93,14 +93,14 @@ extension BolusCalculatorConfig {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Fatty Meal"
+                            hintLabel = String(localized: "Fatty Meal")
                         }
                     ),
                     units: state.units,
                     type: .conditionalDecimal("fattyMealFactor"),
-                    label: "Enable Fatty Meal Option",
-                    conditionalLabel: "Fatty Meal Bolus Percentage",
-                    miniHint: "Add and set a bolus option for meals that absorb slowly.",
+                    label: String(localized: "Enable Fatty Meal Option"),
+                    conditionalLabel: String(localized: "Fatty Meal Bolus Percentage"),
+                    miniHint: String(localized: "Add and set a bolus option for meals that absorb slowly."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -127,14 +127,14 @@ extension BolusCalculatorConfig {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Super Bolus"
+                            hintLabel = String(localized: "Super Bolus")
                         }
                     ),
                     units: state.units,
                     type: .conditionalDecimal("sweetMealFactor"),
-                    label: "Enable Super Bolus Option",
-                    conditionalLabel: "Super Bolus Percentage",
-                    miniHint: "Add and set a bolus option for meals that absorb quickly.",
+                    label: String(localized: "Enable Super Bolus Option"),
+                    conditionalLabel: String(localized: "Super Bolus Percentage"),
+                    miniHint: String(localized: "Add and set a bolus option for meals that absorb quickly."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -160,7 +160,7 @@ extension BolusCalculatorConfig {
                     shouldDisplayHint: $shouldDisplayHint,
                     hintLabel: hintLabel ?? "",
                     hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
             .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))

+ 10 - 6
Trio/Sources/Modules/CGMSettings/CGMSettingsStateModel.swift

@@ -134,12 +134,16 @@ extension CGMSettings {
         }
 
         func deleteCGM() {
-            shouldDisplayCGMSetupSheet = false
-
-            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
-                self.fetchGlucoseManager.deleteGlucoseSource()
-                self.completionNotifyingDidComplete(OtherCGMSourceCompletionNotifying())
-            })
+            fetchGlucoseManager.performOnCGMManagerQueue {
+                // Call plugin functionality on the manager queue (or at least attempt to)
+                self.fetchGlucoseManager?.deleteGlucoseSource()
+
+                // UI updates go back to Main
+                DispatchQueue.main.async {
+                    self.shouldDisplayCGMSetupSheet = false
+                    self.completionNotifyingDidComplete(CGMDeletionCompletionNotifying())
+                }
+            }
         }
     }
 }

+ 4 - 4
Trio/Sources/Modules/CGMSettings/View/CGMRootView.swift

@@ -99,13 +99,13 @@ extension CGMSettings {
                             get: { selectedVerboseHint },
                             set: {
                                 selectedVerboseHint = $0.map { AnyView($0) }
-                                hintLabel = "Smooth Glucose Value"
+                                hintLabel = String(localized: "Smooth Glucose Value")
                             }
                         ),
                         units: state.units,
                         type: .boolean,
-                        label: "Smooth Glucose Value",
-                        miniHint: "Smooth CGM readings using Savitzky-Golay filtering.",
+                        label: String(localized: "Smooth Glucose Value"),
+                        miniHint: String(localized: "Smooth CGM readings using Savitzky-Golay filtering."),
                         verboseHint:
                         VStack(alignment: .leading, spacing: 10) {
                             Text("Default: OFF").bold()
@@ -192,7 +192,7 @@ extension CGMSettings {
                                 )
                             }
                         ),
-                        sheetTitle: "Help"
+                        sheetTitle: String(localized: "Help", comment: "Help sheet title")
                     )
                 }
                 .confirmationDialog("CGM Model", isPresented: $showCGMSelection) {

+ 11 - 11
Trio/Sources/Modules/CalendarEventSettings/View/CalendarEventSettingsRootView.swift

@@ -26,13 +26,13 @@ extension CalendarEventSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Create Events in Calendar"
+                            hintLabel = String(localized: "Create Events in Calendar")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: "Create Events in Calendar",
-                    miniHint: "Use calendar events to display current data.",
+                    label: String(localized: "Create Events in Calendar"),
+                    miniHint: String(localized: "Use calendar events to display current data."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -47,7 +47,7 @@ extension CalendarEventSettings {
                         )
                         Text("Note: Once a new calendar event is created, the previous event will be deleted.")
                     },
-                    headerText: "Diabetes Data as Calendar Event"
+                    headerText: String(localized: "Diabetes Data as Calendar Event")
                 )
 
                 if state.calendarIDs.isNotEmpty, state.useCalendar {
@@ -69,13 +69,13 @@ extension CalendarEventSettings {
                             get: { selectedVerboseHint },
                             set: {
                                 selectedVerboseHint = $0.map { AnyView($0) }
-                                hintLabel = "Display Emojis as Labels"
+                                hintLabel = String(localized: "Display Emojis as Labels")
                             }
                         ),
                         units: state.units,
                         type: .boolean,
-                        label: "Display Emojis as Labels",
-                        miniHint: "Use emojis for calendar events. See hint for more details.",
+                        label: String(localized: "Display Emojis as Labels"),
+                        miniHint: String(localized: "Use emojis for calendar events. See hint for more details."),
                         verboseHint: VStack(alignment: .leading, spacing: 10) {
                             Text("Default: OFF").bold()
                             VStack(alignment: .leading, spacing: 5) {
@@ -104,13 +104,13 @@ extension CalendarEventSettings {
                             get: { selectedVerboseHint },
                             set: {
                                 selectedVerboseHint = $0.map { AnyView($0) }
-                                hintLabel = "Display IOB and COB"
+                                hintLabel = String(localized: "Display IOB and COB")
                             }
                         ),
                         units: state.units,
                         type: .boolean,
-                        label: "Display IOB and COB",
-                        miniHint: "Include IOB & COB in the calendar event data.",
+                        label: String(localized: "Display IOB and COB"),
+                        miniHint: String(localized: "Include IOB & COB in the calendar event data."),
                         verboseHint: VStack(alignment: .leading, spacing: 10) {
                             Text("Default: OFF").bold()
                             Text(
@@ -140,7 +140,7 @@ extension CalendarEventSettings {
                     shouldDisplayHint: $shouldDisplayHint,
                     hintLabel: hintLabel ?? "",
                     hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
             .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))

+ 6 - 6
Trio/Sources/Modules/ContactImage/View/ContactImageHelpView.swift

@@ -8,20 +8,20 @@ struct ContactImageHelpView: View {
         NavigationStack {
             List {
                 DefinitionRow(
-                    term: "How Trio Manages Contact Images",
+                    term: String(localized: "How Trio Manages Contact Images"),
                     definition: Text(
                         "Trio will automatically assign a name like 'Trio 1' to any contact image you add, and a create an entry under your iOS Contacts. Use the 'Save' button at the bottom to save your customized contact image."
                     )
                 ).listRowBackground(Color.gray.opacity(0.1))
 
                 DefinitionRow(
-                    term: "Preview Contact Image",
+                    term: String(localized: "Preview Contact Image"),
                     definition: Text(
                         "See a live preview of your contact image design at the top of the screen. Changes made to styles, layouts, or settings are instantly reflected."
                     )
                 ).listRowBackground(Color.gray.opacity(0.1))
 
-                DefinitionRow(term: "Customize Layout and Style", definition: VStack(alignment: .leading) {
+                DefinitionRow(term: String(localized: "Customize Layout and Style"), definition: VStack(alignment: .leading) {
                     Text("Choose from multiple layout options using the Layout Picker in the 'Style' section.")
                     Text("Enable High Contrast Mode for better visibility in certain conditions.")
                     Text("Available Layouts:")
@@ -29,7 +29,7 @@ struct ContactImageHelpView: View {
                     Text("• Split: Divides values into two separate areas of same size.")
                 }).listRowBackground(Color.gray.opacity(0.1))
 
-                DefinitionRow(term: "Set Display Values", definition: VStack(alignment: .leading) {
+                DefinitionRow(term: String(localized: "Set Display Values"), definition: VStack(alignment: .leading) {
                     Text("Select what values to show on the contact image (e.g., glucose, trend, none) for the available slots:")
                     Text("• None: No value displayed.")
                     Text("• Glucose Reading: Current CGM provided glucose value.")
@@ -42,7 +42,7 @@ struct ContactImageHelpView: View {
                     Text("• Last Loop Time: Time of the last algorithm run.")
                 }).listRowBackground(Color.gray.opacity(0.1))
 
-                DefinitionRow(term: "Adjust Ring Settings", definition: VStack(alignment: .leading) {
+                DefinitionRow(term: String(localized: "Adjust Ring Settings"), definition: VStack(alignment: .leading) {
                     Text("Add visual Rings around the contact image to highlight information.")
                     Text("Fine-tune the ring’s Width and Gap to suit your design preferences.")
                     Text("Available Rings:")
@@ -50,7 +50,7 @@ struct ContactImageHelpView: View {
                     Text("• Loop Status: Indicates current loop status (green, yellow, red).")
                 }).listRowBackground(Color.gray.opacity(0.1))
 
-                DefinitionRow(term: "Customize Fonts", definition: VStack(alignment: .leading) {
+                DefinitionRow(term: String(localized: "Customize Fonts"), definition: VStack(alignment: .leading) {
                     Text("Select font size, weight, and width to match your style:")
                     Text("• Font Size: Adjust the main text size.")
                     Text("• Secondary Font Size: Adjust text size for values in split layouts.")

+ 17 - 23
Trio/Sources/Modules/DataTable/DataTableDataFlow.swift

@@ -15,19 +15,16 @@ enum DataTable {
         var id: String { rawValue }
 
         var name: String {
-            var name: String = ""
             switch self {
             case .treatments:
-                name = "Treatments"
+                return String(localized: "Treatments", comment: "History Mode")
             case .meals:
-                name = "Meals"
+                return String(localized: "Meals", comment: "History Mode")
             case .glucose:
-                name = "Glucose"
+                return String(localized: "Glucose", comment: "History Mode")
             case .adjustments:
-                name = "Adjustments"
+                return String(localized: "Adjustments", comment: "History Mode")
             }
-
-            return NSLocalizedString(name, comment: "History Mode")
         }
     }
 
@@ -41,25 +38,22 @@ enum DataTable {
         case resume
 
         var name: String {
-            var name: String = ""
             switch self {
             case .carbs:
-                name = "Carbs"
+                return String(localized: "Carbs", comment: "Treatment type")
             case .fpus:
-                name = "Protein / Fat"
+                return String(localized: "Protein / Fat", comment: "Treatment type")
             case .bolus:
-                name = "Bolus"
+                return String(localized: "Bolus", comment: "Treatment type")
             case .tempBasal:
-                name = "Temp Basal"
+                return String(localized: "Temp Basal", comment: "Treatment type")
             case .tempTarget:
-                name = "Temp Target"
+                return String(localized: "Temp Target", comment: "Treatment type")
             case .suspend:
-                name = "Suspend"
+                return String(localized: "Suspend", comment: "Treatment type")
             case .resume:
-                name = "Resume"
+                return String(localized: "Resume", comment: "Treatment type")
             }
-
-            return NSLocalizedString(name, comment: "Treatment type")
         }
     }
 
@@ -142,24 +136,24 @@ enum DataTable {
             switch type {
             case .carbs:
                 return numberFormatter
-                    .string(from: amount as NSNumber)! + NSLocalizedString(" g", comment: "gram of carbs")
+                    .string(from: amount as NSNumber)! + String(localized: " g", comment: "gram of carbs")
             case .fpus:
                 return numberFormatter
-                    .string(from: amount as NSNumber)! + NSLocalizedString(" g", comment: "gram of carb equilvalents")
+                    .string(from: amount as NSNumber)! + String(localized: " g", comment: "gram of carb equilvalents")
             case .bolus:
                 var bolusText = " "
                 if isSMB ?? false {}
                 else if isExternal ?? false {
-                    bolusText += NSLocalizedString("External", comment: "External Insulin")
+                    bolusText += String(localized: "External", comment: "External Insulin")
                 } else {
-                    bolusText += NSLocalizedString("Manual", comment: "Manual Bolus")
+                    bolusText += String(localized: "Manual", comment: "Manual Bolus")
                 }
 
                 return numberFormatter
-                    .string(from: amount as NSNumber)! + NSLocalizedString(" U", comment: "Insulin unit") + bolusText
+                    .string(from: amount as NSNumber)! + String(localized: " U", comment: "Insulin unit") + bolusText
             case .tempBasal:
                 return numberFormatter
-                    .string(from: amount as NSNumber)! + NSLocalizedString(" U/hr", comment: "Unit insulin per hour")
+                    .string(from: amount as NSNumber)! + String(localized: " U/hr", comment: "Unit insulin per hour")
             case .tempTarget:
                 var converted = amount
                 if units == .mmolL {

+ 15 - 15
Trio/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -227,7 +227,7 @@ extension DataTable {
             let overrides = overrideRunStored.map { override -> AdjustmentItem in
                 AdjustmentItem(
                     id: override.objectID,
-                    name: override.name ?? "Override",
+                    name: override.name ?? String(localized: "Override"),
                     startDate: override.startDate ?? Date(),
                     endDate: override.endDate ?? Date(),
                     target: override.target?.decimalValue,
@@ -238,7 +238,7 @@ extension DataTable {
             let tempTargets = tempTargetRunStored.map { tempTarget -> AdjustmentItem in
                 AdjustmentItem(
                     id: tempTarget.objectID,
-                    name: tempTarget.name ?? "Temp Target",
+                    name: tempTarget.name ?? String(localized: "Temp Target"),
                     startDate: tempTarget.startDate ?? Date(),
                     endDate: tempTarget.endDate ?? Date(),
                     target: tempTarget.target?.decimalValue,
@@ -367,7 +367,7 @@ extension DataTable {
                             ).tint(.red)
                         }
                         .alert(
-                            Text(NSLocalizedString(alertTitle, comment: "")),
+                            Text(alertTitle),
                             isPresented: $isRemoveHistoryItemAlertPresented
                         ) {
                             Button("Cancel", role: .cancel) {}
@@ -380,7 +380,7 @@ extension DataTable {
                                 state.invokeGlucoseDeletionTask(glucoseToDeleteObjectID)
                             }
                         } message: {
-                            Text("\n" + NSLocalizedString(alertMessage, comment: ""))
+                            Text("\n" + alertMessage)
                         }
                     }
                 } else {
@@ -482,18 +482,18 @@ extension DataTable {
                     Text(bolus.isSMB ? "SMB" : item.type ?? "Bolus")
                     Text(
                         (Formatter.decimalFormatterWithTwoFractionDigits.string(from: amount) ?? "0") +
-                            NSLocalizedString(" U", comment: "Insulin unit")
+                            String(localized: " U", comment: "Insulin unit")
                     )
                     .foregroundColor(.secondary)
                     if bolus.isExternal {
-                        Text(NSLocalizedString("External", comment: "External Insulin")).foregroundColor(.secondary)
+                        Text(String(localized: "External", comment: "External Insulin")).foregroundColor(.secondary)
                     }
                 } else if let tempBasal = item.tempBasal, let rate = tempBasal.rate {
                     Image(systemName: "circle.fill").foregroundColor(Color.insulin.opacity(0.4))
                     Text("Temp Basal")
                     Text(
                         (Formatter.decimalFormatterWithTwoFractionDigits.string(from: rate) ?? "0") +
-                            NSLocalizedString(" U/hr", comment: "Unit insulin per hour")
+                            String(localized: " U/hr", comment: "Unit insulin per hour")
                     )
                     .foregroundColor(.secondary)
                     if tempBasal.duration > 0 {
@@ -518,7 +518,7 @@ extension DataTable {
                             alertMessage = Formatter.dateFormatter
                                 .string(from: item.timestamp ?? Date()) + ", " +
                                 (Formatter.decimalFormatterWithTwoFractionDigits.string(from: item.bolus?.amount ?? 0) ?? "0") +
-                                NSLocalizedString(" U", comment: "Insulin unit")
+                                String(localized: " U", comment: "Insulin unit")
 
                             if let bolus = item.bolus {
                                 // Add text snippet, so that alert message is more descriptive for SMBs
@@ -531,7 +531,7 @@ extension DataTable {
                 }
             }
             .alert(
-                Text(NSLocalizedString(alertTitle, comment: "")),
+                Text(alertTitle),
                 isPresented: $isRemoveHistoryItemAlertPresented
             ) {
                 Button("Cancel", role: .cancel) {}
@@ -545,7 +545,7 @@ extension DataTable {
                     state.invokeInsulinDeletionTask(treatmentObjectID)
                 }
             } message: {
-                Text("\n" + NSLocalizedString(alertMessage, comment: ""))
+                Text("\n" + alertMessage)
             }
         }
 
@@ -557,14 +557,14 @@ extension DataTable {
                         Text("Fat / Protein")
                         Text(
                             (Formatter.decimalFormatterWithTwoFractionDigits.string(for: meal.carbs) ?? "0") +
-                                NSLocalizedString(" g", comment: "gram of carbs")
+                                String(localized: " g", comment: "gram of carbs")
                         )
                     } else {
                         Image(systemName: "circle.fill").foregroundColor(Color.loopYellow)
                         Text("Carbs")
                         Text(
                             (Formatter.decimalFormatterWithTwoFractionDigits.string(for: meal.carbs) ?? "0") +
-                                NSLocalizedString(" g", comment: "gram of carb equilvalents")
+                                String(localized: " g", comment: "gram of carb equilvalents")
                         )
                     }
 
@@ -595,7 +595,7 @@ extension DataTable {
                             alertMessage = Formatter.dateFormatter
                                 .string(from: meal.date ?? Date()) + ", " +
                                 (Formatter.decimalFormatterWithTwoFractionDigits.string(for: meal.carbs) ?? "0") +
-                                NSLocalizedString(" g", comment: "gram of carbs")
+                                String(localized: " g", comment: "gram of carbs")
                         }
                         // meal is complex-meal or fpu-only
                         else {
@@ -620,7 +620,7 @@ extension DataTable {
                 .disabled(!state.settingsManager.settings.useFPUconversion && meal.isFPU)
             }
             .alert(
-                Text(NSLocalizedString(alertTitle, comment: "")),
+                Text(alertTitle),
                 isPresented: $isRemoveHistoryItemAlertPresented
             ) {
                 Button("Cancel", role: .cancel) {}
@@ -637,7 +637,7 @@ extension DataTable {
                     )
                 }
             } message: {
-                Text("\n" + NSLocalizedString(alertMessage, comment: ""))
+                Text("\n" + alertMessage)
             }
         }
 

+ 28 - 26
Trio/Sources/Modules/DynamicSettings/View/DynamicSettingsRootView.swift

@@ -49,13 +49,15 @@ extension DynamicSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Activate Dynamic Sensitivity (Dynamic ISF)"
+                            hintLabel = String(localized: "Activate Dynamic Sensitivity (Dynamic ISF)")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: "Activate Dynamic ISF",
-                    miniHint: "Dynamically adjust insulin sensitivity using Dynamic Ratio rather than Autosens Ratio.",
+                    label: String(localized: "Activate Dynamic ISF"),
+                    miniHint: String(
+                        localized: "Dynamically adjust insulin sensitivity using Dynamic Ratio rather than Autosens Ratio."
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -73,7 +75,7 @@ extension DynamicSettings {
                         )
                         .bold()
                     },
-                    headerText: "Dynamic Settings"
+                    headerText: String(localized: "Dynamic Settings")
                 )
 
                 if state.useNewFormula {
@@ -85,13 +87,13 @@ extension DynamicSettings {
                             get: { selectedVerboseHint },
                             set: {
                                 selectedVerboseHint = $0.map { AnyView($0) }
-                                hintLabel = "Activate Dynamic CR (Carb Ratio)"
+                                hintLabel = String(localized: "Activate Dynamic CR (Carb Ratio)")
                             }
                         ),
                         units: state.units,
                         type: .boolean,
-                        label: "Activate Dynamic CR (Carb Ratio)",
-                        miniHint: "Dynamically adjust your Carb Ratio (CR).",
+                        label: String(localized: "Activate Dynamic CR (Carb Ratio)"),
+                        miniHint: String(localized: "Dynamically adjust your Carb Ratio (CR)."),
                         verboseHint:
 
                         VStack(alignment: .leading, spacing: 10) {
@@ -116,13 +118,13 @@ extension DynamicSettings {
                             get: { selectedVerboseHint },
                             set: {
                                 selectedVerboseHint = $0.map { AnyView($0) }
-                                hintLabel = "Use Sigmoid Formula"
+                                hintLabel = String(localized: "Use Sigmoid Formula")
                             }
                         ),
                         units: state.units,
                         type: .boolean,
-                        label: "Use Sigmoid Formula",
-                        miniHint: "Adjust insulin sensitivity using a sigmoid-shaped curve.",
+                        label: String(localized: "Use Sigmoid Formula"),
+                        miniHint: String(localized: "Adjust insulin sensitivity using a sigmoid-shaped curve."),
                         verboseHint:
                         VStack(alignment: .leading, spacing: 10) {
                             Text("Default: OFF").bold()
@@ -152,14 +154,14 @@ extension DynamicSettings {
                                 get: { selectedVerboseHint },
                                 set: {
                                     selectedVerboseHint = $0.map { AnyView($0) }
-                                    hintLabel = "Adjustment Factor (AF)"
+                                    hintLabel = String(localized: "Adjustment Factor (AF)")
                                 }
                             ),
                             // TODO?: include conditional links to Desmos logarithmic graphs based on which .glucose setting is used
                             units: state.units,
                             type: .decimal("adjustmentFactor"),
-                            label: "Adjustment Factor (AF)",
-                            miniHint: "Alter the rate of Dynamic ISF (Sensitivity) adjustments.",
+                            label: String(localized: "Adjustment Factor (AF)"),
+                            miniHint: String(localized: "Alter the rate of Dynamic ISF (Sensitivity) adjustments."),
                             verboseHint:
                             VStack(alignment: .leading, spacing: 10) {
                                 Text("Default: 80%").bold()
@@ -183,13 +185,13 @@ extension DynamicSettings {
                                 get: { selectedVerboseHint },
                                 set: {
                                     selectedVerboseHint = $0.map { AnyView($0) }
-                                    hintLabel = "Sigmoid Adjustment Factor"
+                                    hintLabel = String(localized: "Sigmoid Adjustment Factor")
                                 }
                             ),
                             units: state.units,
                             type: .decimal("adjustmentFactorSigmoid"),
-                            label: "Sigmoid Adjustment Factor",
-                            miniHint: "Alter the rate of dynamic sensitivity adjustments for Sigmoid.",
+                            label: String(localized: "Sigmoid Adjustment Factor"),
+                            miniHint: String(localized: "Alter the rate of dynamic sensitivity adjustments for Sigmoid."),
                             verboseHint:
                             VStack(alignment: .leading, spacing: 10) {
                                 Text("Default: 50%").bold()
@@ -217,13 +219,13 @@ extension DynamicSettings {
                             get: { selectedVerboseHint },
                             set: {
                                 selectedVerboseHint = $0.map { AnyView($0) }
-                                hintLabel = "Weighted Average of TDD"
+                                hintLabel = String(localized: "Weighted Average of TDD")
                             }
                         ),
                         units: state.units,
                         type: .decimal("weightPercentage"),
-                        label: "Weighted Average of TDD",
-                        miniHint: "Weight of 24-hr TDD against 10-day TDD.",
+                        label: String(localized: "Weighted Average of TDD"),
+                        miniHint: String(localized: "Weight of 24-hr TDD against 10-day TDD."),
                         verboseHint:
                         VStack(alignment: .leading, spacing: 10) {
                             Text("Default: 35%").bold()
@@ -246,13 +248,13 @@ extension DynamicSettings {
                             get: { selectedVerboseHint },
                             set: {
                                 selectedVerboseHint = $0.map { AnyView($0) }
-                                hintLabel = "Adjust Basal"
+                                hintLabel = String(localized: "Adjust Basal")
                             }
                         ),
                         units: state.units,
                         type: .boolean,
-                        label: "Adjust Basal",
-                        miniHint: "Use Dynamic Ratio to adjust basal rates.",
+                        label: String(localized: "Adjust Basal"),
+                        miniHint: String(localized: "Use Dynamic Ratio to adjust basal rates."),
                         verboseHint: VStack(alignment: .leading, spacing: 10) {
                             Text("Default: OFF").bold()
                             Text(
@@ -274,13 +276,13 @@ extension DynamicSettings {
                             get: { selectedVerboseHint },
                             set: {
                                 selectedVerboseHint = $0.map { AnyView($0) }
-                                hintLabel = "Minimum Safety Threshold"
+                                hintLabel = String(localized: "Minimum Safety Threshold")
                             }
                         ),
                         units: state.units,
                         type: .decimal("threshold_setting"),
-                        label: "Minimum Safety Threshold",
-                        miniHint: "Increase the safety threshold used to suspend insulin delivery.",
+                        label: String(localized: "Minimum Safety Threshold"),
+                        miniHint: String(localized: "Increase the safety threshold used to suspend insulin delivery."),
                         verboseHint:
                         VStack(alignment: .leading, spacing: 10) {
                             Text("Default: Set by Algorithm").bold()
@@ -321,7 +323,7 @@ extension DynamicSettings {
                     shouldDisplayHint: $shouldDisplayHint,
                     hintLabel: hintLabel ?? "",
                     hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
             .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))

+ 12 - 12
Trio/Sources/Modules/GeneralSettings/View/UnitsLimitsSettingsRootView.swift

@@ -36,12 +36,12 @@ extension UnitsLimitsSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Max IOB", comment: "Max IOB")
+                            hintLabel = String(localized: "Max IOB", comment: "Max IOB")
                         }
                     ),
                     units: state.units,
                     type: .decimal("maxIOB"),
-                    label: NSLocalizedString("Max IOB", comment: "Max IOB"),
+                    label: String(localized: "Max IOB", comment: "Max IOB"),
                     miniHint: "Maximum units of insulin allowed to be active.",
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
@@ -69,13 +69,13 @@ extension UnitsLimitsSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Max Bolus"
+                            hintLabel = String(localized: "Max Bolus")
                         }
                     ),
                     units: state.units,
                     type: .decimal("maxBolus"),
-                    label: "Max Bolus",
-                    miniHint: "Largest bolus of insulin allowed.",
+                    label: String(localized: "Max Bolus"),
+                    miniHint: String(localized: "Largest bolus of insulin allowed."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 10 units").bold()
@@ -95,13 +95,13 @@ extension UnitsLimitsSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Max Basal"
+                            hintLabel = String(localized: "Max Basal")
                         }
                     ),
                     units: state.units,
                     type: .decimal("maxBasal"),
-                    label: "Max Basal",
-                    miniHint: "Largest basal rate allowed.",
+                    label: String(localized: "Max Basal"),
+                    miniHint: String(localized: "Largest basal rate allowed."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 2 units").bold()
@@ -122,13 +122,13 @@ extension UnitsLimitsSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Max COB", comment: "Max COB")
+                            hintLabel = String(localized: "Max COB", comment: "Max COB")
                         }
                     ),
                     units: state.units,
                     type: .decimal("maxCOB"),
-                    label: NSLocalizedString("Max COB", comment: "Max COB"),
-                    miniHint: "Maximum Carbs On Board (COB) allowed.",
+                    label: String(localized: "Max COB", comment: "Max COB"),
+                    miniHint: String(localized: "Maximum Carbs On Board (COB) allowed."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 120 grams of carbs").bold()
@@ -149,7 +149,7 @@ extension UnitsLimitsSettings {
                     shouldDisplayHint: $shouldDisplayHint,
                     hintLabel: hintLabel ?? "",
                     hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
             .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))

+ 26 - 26
Trio/Sources/Modules/GlucoseNotificationSettings/View/GlucoseNotificationSettingsRootView.swift

@@ -48,13 +48,13 @@ extension GlucoseNotificationSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Play Alarm Sound"
+                            hintLabel = String(localized: "Play Alarm Sound")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: "Play Alarm Sound",
-                    miniHint: "Alarm with every Trio notification.",
+                    label: String(localized: "Play Alarm Sound"),
+                    miniHint: String(localized: "Alarm with every Trio notification."),
                     verboseHint: VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
                         Text(
@@ -70,13 +70,13 @@ extension GlucoseNotificationSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Always Notify Pump"
+                            hintLabel = String(localized: "Always Notify Pump")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: "Always Notify Pump",
-                    miniHint: "Always Notify Pump Warnings.",
+                    label: String(localized: "Always Notify Pump"),
+                    miniHint: String(localized: "Always Notify Pump Warnings."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: ON").bold()
@@ -86,7 +86,7 @@ extension GlucoseNotificationSettings {
                         Text("If iOS Trio Notifications is disabled, Trio will display these messages in-app as a banner only.")
                         Text("An example of a Pump Warning is 'Pod Expiration Reminder'")
                     },
-                    headerText: "Trio Information Notifications"
+                    headerText: String(localized: "Trio Information Notifications")
                 )
                 SettingInputSection(
                     decimalValue: $decimalPlaceholder,
@@ -96,13 +96,13 @@ extension GlucoseNotificationSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Always Notify CGM"
+                            hintLabel = String(localized: "Always Notify CGM")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: "Always Notify CGM",
-                    miniHint: "Always Notify CGM Warnings.",
+                    label: String(localized: "Always Notify CGM"),
+                    miniHint: String(localized: "Always Notify CGM Warnings."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: ON").bold()
@@ -121,13 +121,13 @@ extension GlucoseNotificationSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Always Notify Carb"
+                            hintLabel = String(localized: "Always Notify Carb")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: "Always Notify Carb",
-                    miniHint: "Always Notify Carb Warnings.",
+                    label: String(localized: "Always Notify Carb"),
+                    miniHint: String(localized: "Always Notify Carb Warnings."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: ON").bold()
@@ -146,13 +146,13 @@ extension GlucoseNotificationSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Always Notify Algorithm"
+                            hintLabel = String(localized: "Always Notify Algorithm")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: "Always Notify Algorithm",
-                    miniHint: "Always Notify Algorithm Warnings.",
+                    label: String(localized: "Always Notify Algorithm"),
+                    miniHint: String(localized: "Always Notify Algorithm Warnings."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: ON").bold()
@@ -174,20 +174,20 @@ extension GlucoseNotificationSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Show Glucose App Badge"
+                            hintLabel = String(localized: "Show Glucose App Badge")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: "Show Glucose App Badge",
-                    miniHint: "Show your current glucose on Trio app icon.",
+                    label: String(localized: "Show Glucose App Badge"),
+                    miniHint: String(localized: "Show your current glucose on Trio app icon."),
                     verboseHint: VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
                         Text(
                             "This will add your current glucose on the top right of your Trio icon as a red notification badge. Changing setting takes effect on next Glucose reading."
                         )
                     },
-                    headerText: "Various Glucose Notifications"
+                    headerText: String(localized: "Various Glucose Notifications")
                 )
 
                 Section {
@@ -211,7 +211,7 @@ extension GlucoseNotificationSettings {
                             Spacer()
                             Button(
                                 action: {
-                                    hintLabel = "Glucose Notifications"
+                                    hintLabel = String(localized: "Glucose Notifications")
                                     selectedVerboseHint =
                                         AnyView(
                                             VStack(alignment: .leading, spacing: 10) {
@@ -258,13 +258,13 @@ extension GlucoseNotificationSettings {
                             get: { selectedVerboseHint },
                             set: {
                                 selectedVerboseHint = $0.map { AnyView($0) }
-                                hintLabel = "Add Glucose Source to Alarm"
+                                hintLabel = String(localized: "Add Glucose Source to Alarm")
                             }
                         ),
                         units: state.units,
                         type: .boolean,
-                        label: "Add Glucose Source to Alarm",
-                        miniHint: "Source of the glucose reading will be added to the notification.",
+                        label: String(localized: "Add Glucose Source to Alarm"),
+                        miniHint: String(localized: "Source of the glucose reading will be added to the notification."),
                         verboseHint: VStack(alignment: .leading, spacing: 10) {
                             Text("Default: OFF").bold()
                             Text("The source of the glucose reading will be added to the notification.")
@@ -279,7 +279,7 @@ extension GlucoseNotificationSettings {
                     shouldDisplayHint: $shouldDisplayHint,
                     hintLabel: hintLabel ?? "",
                     hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
             .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))
@@ -374,7 +374,7 @@ extension GlucoseNotificationSettings {
                         Spacer()
                         Button(
                             action: {
-                                hintLabel = "Low and High Glucose Alarm Limits"
+                                hintLabel = String(localized: "Low and High Glucose Alarm Limits")
                                 selectedVerboseHint =
                                     AnyView(VStack(alignment: .leading, spacing: 10) {
                                         let low: Decimal = 70

+ 5 - 5
Trio/Sources/Modules/HealthKit/View/AppleHealthKitRootView.swift

@@ -26,20 +26,20 @@ extension AppleHealthKit {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Connect to Apple Health"
+                            hintLabel = String(localized: "Connect to Apple Health")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: "Connect to Apple Health",
-                    miniHint: "Allow Trio to read from and write to Apple Health.",
+                    label: String(localized: "Connect to Apple Health"),
+                    miniHint: String(localized: "Allow Trio to read from and write to Apple Health."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
                         Text("This allows Trio to read from and write to Apple Health.")
                         Text("Warning: You must also give permissions in iOS System Settings for the Health app.").bold()
                     },
-                    headerText: "Apple Health Integration"
+                    headerText: String(localized: "Apple Health Integration")
                 )
 
                 if !state.needShowInformationTextForSetPermissions {
@@ -71,7 +71,7 @@ extension AppleHealthKit {
                     shouldDisplayHint: $shouldDisplayHint,
                     hintLabel: hintLabel ?? "",
                     hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
             .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))

+ 1 - 1
Trio/Sources/Modules/Home/HomeStateModel+Setup/DeterminationSetup.swift

@@ -43,7 +43,7 @@ extension Home.StateModel {
             batchSize: 50,
             propertiesToFetch: ["cob", "iob", "deliverAt", "objectID"]
         )
-        
+
         return await determinationFetchContext.perform {
             guard let fetchedResults = results as? [[String: Any]] else {
                 return []

+ 9 - 5
Trio/Sources/Modules/Home/HomeStateModel.swift

@@ -454,12 +454,16 @@ extension Home {
         }
 
         func deleteCGM() {
-            shouldDisplayCGMSetupSheet = false
-
-            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
+            fetchGlucoseManager.performOnCGMManagerQueue {
+                // Call plugin functionality on the manager queue (or at least attempt to)
                 self.fetchGlucoseManager?.deleteGlucoseSource()
-                self.completionNotifyingDidComplete(CGMDeletionCompletionNotifying())
-            })
+
+                // UI updates go back to Main
+                DispatchQueue.main.async {
+                    self.shouldDisplayCGMSetupSheet = false
+                    self.completionNotifyingDidComplete(CGMDeletionCompletionNotifying())
+                }
+            }
         }
 
         /// Display the eventual status message provided by the manager of the pump

+ 2 - 2
Trio/Sources/Modules/Home/View/Chart/ChartElements/SelectionPopoverView.swift

@@ -80,7 +80,7 @@ struct SelectionPopoverView: ChartContent {
                     Image(systemName: "syringe.fill").frame(width: 15)
                     Text(Formatter.bolusFormatter.string(from: iob) ?? "")
                         .bold()
-                        + Text(NSLocalizedString(" U", comment: "Insulin unit"))
+                        + Text(String(localized: " U", comment: "Insulin unit"))
                 }
                 .foregroundStyle(Color.insulin).font(.body)
             }
@@ -90,7 +90,7 @@ struct SelectionPopoverView: ChartContent {
                     Image(systemName: "fork.knife").frame(width: 15)
                     Text(Formatter.integerFormatter.string(from: selectedCOBValue.cob as NSNumber) ?? "")
                         .bold()
-                        + Text(NSLocalizedString(" g", comment: "gram of carbs"))
+                        + Text(String(localized: " g", comment: "gram of carbs"))
                 }
                 .foregroundStyle(Color.orange).font(.body)
             }

+ 17 - 17
Trio/Sources/Modules/Home/View/Chart/ChartLegendView.swift

@@ -38,7 +38,7 @@ struct ChartLegendView: View {
                         Text("Other Elements & Shapes").bold().padding(.bottom, 5).textCase(.uppercase)
 
                         DefinitionRow(
-                            term: "Scheduled Basal Rate",
+                            term: String(localized: "Scheduled Basal Rate"),
                             definition: VStack(alignment: .leading, spacing: 10) {
                                 Text("This dotted line represents the hourly insulin rate of your scheduled basal insulin.")
                                 Text("To review or change your scheduled basal rates, go to Settings > Therapy > Basal Rates.")
@@ -48,7 +48,7 @@ struct ChartLegendView: View {
                         )
 
                         DefinitionRow(
-                            term: "Temporary Basal Rate (TBR)",
+                            term: String(localized: "Temporary Basal Rate (TBR)"),
                             definition: Text(
                                 "Shows current or past TBRs, which can be set by the oref algorithm or manually."
                             ),
@@ -57,14 +57,14 @@ struct ChartLegendView: View {
                         )
 
                         DefinitionRow(
-                            term: "Pump Suspension",
+                            term: String(localized: "Pump Suspension"),
                             definition: Text("Indicates when insulin delivery was paused, i.e. pump is suspended."),
                             color: Color.loopGray.opacity(colorScheme == .dark ? 0.3 : 0.8),
                             iconString: "square.fill"
                         )
 
                         DefinitionRow(
-                            term: "CGM Glucose Value",
+                            term: String(localized: "CGM Glucose Value"),
                             definition: VStack(alignment: .leading, spacing: 10) {
                                 if state.settingsManager.settings.smoothGlucose {
                                     Text(
@@ -92,14 +92,14 @@ struct ChartLegendView: View {
                         )
 
                         DefinitionRow(
-                            term: "Manual Glucose Measurement",
+                            term: String(localized: "Manual Glucose Measurement"),
                             definition: Text("Manually entered blood glucose, such as a fingerstick test."),
                             color: Color.red,
                             iconString: "drop.fill"
                         )
 
                         DefinitionRow(
-                            term: "Bolus",
+                            term: String(localized: "Bolus"),
                             definition: Text(
                                 "Shows an insulin dose, which can be a small automated dose (super-micro-bolus), a manually entered dose, or one given externally (e.g., a pen shot)."
                             ),
@@ -108,7 +108,7 @@ struct ChartLegendView: View {
                         )
 
                         DefinitionRow(
-                            term: "Carb Entry",
+                            term: String(localized: "Carb Entry"),
                             definition: Text("Tracks the carbohydrates you eat, entered to guide insulin dosing."),
                             color: Color.orange,
                             iconString: "arrowtriangle.down.fill",
@@ -116,7 +116,7 @@ struct ChartLegendView: View {
                         )
 
                         DefinitionRow(
-                            term: "Fat-Protein Carb Equivalent",
+                            term: String(localized: "Fat-Protein Carb Equivalent"),
                             definition: VStack(alignment: .leading, spacing: 10) {
                                 Text(
                                     "Represents carb equivalent for fat and protein, calculated using the Warsaw Method."
@@ -130,7 +130,7 @@ struct ChartLegendView: View {
                         )
 
                         DefinitionRow(
-                            term: "Override",
+                            term: String(localized: "Override"),
                             definition: Text(
                                 "Indicates when an override is or was active, temporarily changing therapy settings (e.g., basal rate, insulin sensitivity, carb ratio, target glucose, or whether Trio can dose SMBs)."
                             ),
@@ -139,7 +139,7 @@ struct ChartLegendView: View {
                         )
 
                         DefinitionRow(
-                            term: "Temporary Target",
+                            term: String(localized: "Temporary Target"),
                             definition: Text(
                                 "Marks when a short-term temporary glucose target is or was active, (potentially) altering when or how much insulin is delivered."
                             ),
@@ -148,7 +148,7 @@ struct ChartLegendView: View {
                         )
 
                         DefinitionRow(
-                            term: "Past Insulin-on-Board (IOB)",
+                            term: String(localized: "Past Insulin-on-Board (IOB)"),
                             definition: Text(
                                 "Shows the IOB value calculated by the algorithm at a specific time in the past. These values are snapshots and won’t change if insulin is added or removed after the fact."
                             ),
@@ -157,7 +157,7 @@ struct ChartLegendView: View {
                         )
 
                         DefinitionRow(
-                            term: "Past Carbs-on-Board (COB)",
+                            term: String(localized: "Past Carbs-on-Board (COB)"),
                             definition: Text(
                                 "Shows the COB value calculated by the algorithm at a specific time in the past. These values are snapshots and won’t change if carbs are added or removed after the fact."
                             ),
@@ -192,7 +192,7 @@ struct ChartLegendView: View {
     var legendLinesView: some View {
         Group {
             DefinitionRow(
-                term: "IOB (Insulin on Board)",
+                term: String(localized: "IOB (Insulin on Board)"),
                 definition: Text(
                     "Forecasts future glucose readings based on the amount of insulin still active in the body."
                 ),
@@ -200,7 +200,7 @@ struct ChartLegendView: View {
             )
 
             DefinitionRow(
-                term: "ZT (Zero-Temp)",
+                term: String(localized: "ZT (Zero-Temp)"),
                 definition: Text(
                     "Forecasts the worst-case future glucose reading scenario if no carbs are absorbed and insulin delivery is stopped until glucose starts rising."
                 ),
@@ -208,7 +208,7 @@ struct ChartLegendView: View {
             )
 
             DefinitionRow(
-                term: "COB (Carbs on Board)",
+                term: String(localized: "COB (Carbs on Board)"),
                 definition: Text(
                     "Forecasts future glucose reading changes by considering the amount of carbohydrates still being absorbed in the body."
                 ),
@@ -216,7 +216,7 @@ struct ChartLegendView: View {
             )
 
             DefinitionRow(
-                term: "UAM (Unannounced Meal)",
+                term: String(localized: "UAM (Unannounced Meal)"),
                 definition: Text(
                     "Forecasts future glucose levels and insulin dosing needs for unexpected meals or other causes of glucose reading increases without prior notice."
                 ),
@@ -227,7 +227,7 @@ struct ChartLegendView: View {
 
     var legendConeOfUncertaintyView: some View {
         DefinitionRow(
-            term: "Cone of Uncertainty",
+            term: String(localized: "Cone of Uncertainty"),
             definition: VStack(alignment: .leading, spacing: 10) {
                 Text(
                     "For simplicity reasons, oref's various forecast curves are displayed as a \"Cone of Uncertainty\" that depicts a possible, forecasted range of future glucose fluctuation based on the current data and the algothim's result."

+ 2 - 2
Trio/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift

@@ -93,9 +93,9 @@ struct CurrentGlucoseView: View {
                         let minutesAgo = -1 * (glucose.last?.date?.timeIntervalSinceNow ?? 0) / 60
                         let text = timaAgoFormatter.string(for: Double(minutesAgo)) ?? ""
                         Text(
-                            minutesAgo <= 1 ? "< 1 " + NSLocalizedString("min", comment: "Short form for minutes") : (
+                            minutesAgo <= 1 ? "< 1 " + String(localized: "min", comment: "Short form for minutes") : (
                                 text + " " +
-                                    NSLocalizedString("min", comment: "Short form for minutes") + " "
+                                    String(localized: "min", comment: "Short form for minutes") + " "
                             )
                         )
                         .font(.caption2).foregroundStyle(colorScheme == .dark ? Color.white.opacity(0.9) : Color.secondary)

+ 0 - 0
Trio/Sources/Modules/Home/View/Header/LoopStatusHelpView.swift


Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff