From 91a5ba006c7fd0ec59740b63d9ef4b4e1b0810ae Mon Sep 17 00:00:00 2001 From: Garry Casey Date: Sun, 1 Nov 2026 13:47:06 +0000 Subject: [PATCH 1/5] Add a Trends screen for glucose, insulin and Loop status over time Trends reviews one period at a time (a day, 7, 14, 30 or 90 days) on one screen: summary tiles (average with SD and CV, GMI, insulin, carbs and boluses per day, Loop active), time in range, the daily pattern (AGP), basal and bolus, insulin per day and Loop status with its gaps. A one-day period leads with that day's timeline. Tapping a day opens a day page with the insulin timeline and active insulin and carbs, stepping a day at a time. The period exports as a PDF report or a CSV with one row per day. Inside Loop's 7 days the figures come from Loop's own stores, so manual and automatic boluses, temp basals, historical targets and presets are known. Older days come from Apple Health. Closed and open loop are recorded from launch, because Loop's automation history only covers 36 hours. Trends opens from Settings, beside Statistics, as a sheet with its own navigation and a Done button. Statistics and Current Setup are unchanged. This is part 1 of 6 splitting #2621. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01QHnVP7ynB62LNuuYMy3yvH --- Loop.xcodeproj/project.pbxproj | 44 + .../accent-ink.colorset/Contents.json | 38 + .../agp-inner.colorset/Contents.json | 38 + .../agp-outer.colorset/Contents.json | 38 + .../basal-edge.colorset/Contents.json | 38 + .../basal.colorset/Contents.json | 38 + .../bolus.colorset/Contents.json | 38 + .../carbs-ink.colorset/Contents.json | 38 + .../control-thumb.colorset/Contents.json | 38 + .../glucose-ink.colorset/Contents.json | 38 + .../ink-muted.colorset/Contents.json | 38 + .../insulin-ink.colorset/Contents.json | 38 + .../range-high.colorset/Contents.json | 38 + .../range-in.colorset/Contents.json | 38 + .../range-low.colorset/Contents.json | 38 + .../range-very-high.colorset/Contents.json | 38 + .../range-very-low.colorset/Contents.json | 38 + .../target-band.colorset/Contents.json | 38 + Loop/Localizable.xcstrings | 19686 +++++++++++++++- Loop/Managers/LoopAppManager.swift | 10 + Loop/Redesign/DayCharts.swift | 548 + Loop/Redesign/DayDetailView.swift | 198 + Loop/Redesign/LoopDesign.swift | 341 + Loop/Redesign/LoopHistoryMath.swift | 352 + Loop/Redesign/LoopHistoryModels.swift | 342 + Loop/Redesign/LoopHistoryProvider.swift | 450 + Loop/Redesign/TrendsComponents.swift | 766 + Loop/Redesign/TrendsScreen.swift | 555 + Loop/View Models/SettingsViewModel.swift | 2 + Loop/Views/SettingsView.swift | 18 + LoopTests/Managers/LoopDataManagerTests.swift | 56 + LoopTests/Models/LoopHistoryMathTests.swift | 340 + 32 files changed, 24350 insertions(+), 4 deletions(-) create mode 100644 Loop/DerivedAssetsBase.xcassets/accent-ink.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/agp-inner.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/agp-outer.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/basal-edge.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/basal.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/bolus.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/carbs-ink.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/control-thumb.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/glucose-ink.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/ink-muted.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/insulin-ink.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/range-high.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/range-in.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/range-low.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/range-very-high.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/range-very-low.colorset/Contents.json create mode 100644 Loop/DerivedAssetsBase.xcassets/target-band.colorset/Contents.json create mode 100644 Loop/Redesign/DayCharts.swift create mode 100644 Loop/Redesign/DayDetailView.swift create mode 100644 Loop/Redesign/LoopDesign.swift create mode 100644 Loop/Redesign/LoopHistoryMath.swift create mode 100644 Loop/Redesign/LoopHistoryModels.swift create mode 100644 Loop/Redesign/LoopHistoryProvider.swift create mode 100644 Loop/Redesign/TrendsComponents.swift create mode 100644 Loop/Redesign/TrendsScreen.swift create mode 100644 LoopTests/Models/LoopHistoryMathTests.swift diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index b147fc93e2..f87be065c4 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -726,6 +726,15 @@ 8DC1CE32F39301122B550325 /* LoopKitTypeAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */; }; ABD9218EB98C9216FBB04382 /* LoopKitTypeAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */; }; AF79DFAB0F55143D66022668 /* LoopKitTypeAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */; }; + 89C0C34F420190E8CC7D03B1 /* DayCharts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F40DE2F3FC02CBA3BE7AF2F /* DayCharts.swift */; }; + 332CC9FC011E78CE5BEA583C /* LoopDesign.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BD3DDF41B598A8C4B5CEB92 /* LoopDesign.swift */; }; + 0C70F4D862F6D3FDB3D91D87 /* LoopHistoryMath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21622B2355AED8F6A12FE17C /* LoopHistoryMath.swift */; }; + 2ADBB92A8ABB74360D18EEFA /* LoopHistoryModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 471FFD525A4EB897745608BE /* LoopHistoryModels.swift */; }; + A743DC26D066EE5AC88C2F45 /* LoopHistoryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */; }; + 6A6C1E1C7A26EC88AFB4FAB1 /* DayDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */; }; + 591F13AF435B37B4818E2F2B /* TrendsComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */; }; + 5CD0888FF33D6F844A81E638 /* TrendsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF0FD64B2B3E9C9AA8764684 /* TrendsScreen.swift */; }; + 97CF13197936D2794766C84A /* LoopHistoryMathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B08644C3031D9E815F9892 /* LoopHistoryMathTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1596,6 +1605,15 @@ 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LoopKitTypeAliases.swift; sourceTree = ""; }; 36421A4B9676EF68CDDC4ECD /* AlgorithmBridge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AlgorithmBridge.swift; sourceTree = ""; }; 6C372374D3D3D9C5F8E3AD93 /* TemporaryScheduleOverrideAlgorithmTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemporaryScheduleOverrideAlgorithmTests.swift; sourceTree = ""; }; + 6F40DE2F3FC02CBA3BE7AF2F /* DayCharts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayCharts.swift; sourceTree = ""; }; + 0BD3DDF41B598A8C4B5CEB92 /* LoopDesign.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopDesign.swift; sourceTree = ""; }; + 21622B2355AED8F6A12FE17C /* LoopHistoryMath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopHistoryMath.swift; sourceTree = ""; }; + 471FFD525A4EB897745608BE /* LoopHistoryModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopHistoryModels.swift; sourceTree = ""; }; + 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopHistoryProvider.swift; sourceTree = ""; }; + E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayDetailView.swift; sourceTree = ""; }; + 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendsComponents.swift; sourceTree = ""; }; + AF0FD64B2B3E9C9AA8764684 /* TrendsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendsScreen.swift; sourceTree = ""; }; + 42B08644C3031D9E815F9892 /* LoopHistoryMathTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopHistoryMathTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1918,6 +1936,7 @@ 43776F8E1B8022E90074EA36 /* Loop */ = { isa = PBXGroup; children = ( + 8574687EF267DC59043C0242 /* Redesign */, C16DA84022E8E104008624C2 /* Plugins */, B66D1F322E6A5D6600471149 /* Localizable.xcstrings */, B66D1F382E6A5D6600471149 /* InfoPlist.xcstrings */, @@ -2816,6 +2835,7 @@ 6C1F8E3BA2D45079B3E2F512 /* OutcomesPackageTests.swift */, BE36167100E59D7413C50F63 /* SetupComparisonTests.swift */, FEEDF0000000000000000005 /* GlucoseStatisticsTests.swift */, + 42B08644C3031D9E815F9892 /* LoopHistoryMathTests.swift */, A9DFAFB224F0415E00950D1E /* CarbBackfillRequestUserInfoTests.swift */, A963B279252CEBAE0062AA12 /* SetBolusUserInfoTests.swift */, C19008FF252271BB00721625 /* SimpleBolusCalculatorTests.swift */, @@ -2992,6 +3012,21 @@ path = Fixtures; sourceTree = ""; }; + 8574687EF267DC59043C0242 /* Redesign */ = { + isa = PBXGroup; + children = ( + AF0FD64B2B3E9C9AA8764684 /* TrendsScreen.swift */, + 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */, + E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */, + 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */, + 471FFD525A4EB897745608BE /* LoopHistoryModels.swift */, + 21622B2355AED8F6A12FE17C /* LoopHistoryMath.swift */, + 0BD3DDF41B598A8C4B5CEB92 /* LoopDesign.swift */, + 6F40DE2F3FC02CBA3BE7AF2F /* DayCharts.swift */, + ); + path = Redesign; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -3650,6 +3685,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5CD0888FF33D6F844A81E638 /* TrendsScreen.swift in Sources */, + 591F13AF435B37B4818E2F2B /* TrendsComponents.swift in Sources */, + 6A6C1E1C7A26EC88AFB4FAB1 /* DayDetailView.swift in Sources */, + A743DC26D066EE5AC88C2F45 /* LoopHistoryProvider.swift in Sources */, + 2ADBB92A8ABB74360D18EEFA /* LoopHistoryModels.swift in Sources */, + 0C70F4D862F6D3FDB3D91D87 /* LoopHistoryMath.swift in Sources */, + 332CC9FC011E78CE5BEA583C /* LoopDesign.swift in Sources */, + 89C0C34F420190E8CC7D03B1 /* DayCharts.swift in Sources */, C17824A51E1AD4D100D9D25C /* ManualBolusRecommendation.swift in Sources */, 897A5A9624C2175B00C4E71D /* BolusEntryView.swift in Sources */, 4F70C2131DE90339006380B7 /* StatusExtensionContext.swift in Sources */, @@ -4101,6 +4144,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 97CF13197936D2794766C84A /* LoopHistoryMathTests.swift in Sources */, A9DF02CB24F72B9E00B7C988 /* CriticalEventLogTests.swift in Sources */, 1D80313D24746274002810DF /* AlertStoreTests.swift in Sources */, C1777A6625A125F100595963 /* ManualEntryDoseViewModelTests.swift in Sources */, diff --git a/Loop/DerivedAssetsBase.xcassets/accent-ink.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/accent-ink.colorset/Contents.json new file mode 100644 index 0000000000..af109ce19a --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/accent-ink.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.000", + "green": "0.384", + "blue": "0.800", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.039", + "green": "0.518", + "blue": "1.000", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/agp-inner.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/agp-inner.colorset/Contents.json new file mode 100644 index 0000000000..2ddc0f5f90 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/agp-inner.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.000", + "green": "0.690", + "blue": "1.000", + "alpha": "0.340" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.388", + "green": "0.729", + "blue": "1.000", + "alpha": "0.380" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/agp-outer.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/agp-outer.colorset/Contents.json new file mode 100644 index 0000000000..270771d475 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/agp-outer.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.000", + "green": "0.690", + "blue": "1.000", + "alpha": "0.160" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.388", + "green": "0.729", + "blue": "1.000", + "alpha": "0.180" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/basal-edge.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/basal-edge.colorset/Contents.json new file mode 100644 index 0000000000..fa72f9b84c --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/basal-edge.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.890", + "green": "0.725", + "blue": "0.514", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.431", + "green": "0.322", + "blue": "0.196", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/basal.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/basal.colorset/Contents.json new file mode 100644 index 0000000000..76f27a3287 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/basal.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.988", + "green": "0.890", + "blue": "0.761", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.263", + "green": "0.188", + "blue": "0.106", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/bolus.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/bolus.colorset/Contents.json new file mode 100644 index 0000000000..30b11e52a8 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/bolus.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.663", + "green": "0.282", + "blue": "0.039", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "1.000", + "green": "0.639", + "blue": "0.278", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/carbs-ink.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/carbs-ink.colorset/Contents.json new file mode 100644 index 0000000000..df21da3860 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/carbs-ink.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.184", + "green": "0.490", + "blue": "0.086", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.349", + "green": "0.894", + "blue": "0.200", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/control-thumb.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/control-thumb.colorset/Contents.json new file mode 100644 index 0000000000..34b15edebe --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/control-thumb.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "1.000", + "green": "1.000", + "blue": "1.000", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.388", + "green": "0.388", + "blue": "0.400", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/glucose-ink.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/glucose-ink.colorset/Contents.json new file mode 100644 index 0000000000..b704d8af81 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/glucose-ink.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.000", + "green": "0.439", + "blue": "0.722", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.388", + "green": "0.729", + "blue": "1.000", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/ink-muted.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/ink-muted.colorset/Contents.json new file mode 100644 index 0000000000..6eda3190f3 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/ink-muted.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.424", + "green": "0.424", + "blue": "0.439", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.682", + "green": "0.682", + "blue": "0.698", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/insulin-ink.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/insulin-ink.colorset/Contents.json new file mode 100644 index 0000000000..1acea580ea --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/insulin-ink.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.541", + "green": "0.227", + "blue": "0.024", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "1.000", + "green": "0.639", + "blue": "0.278", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/range-high.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/range-high.colorset/Contents.json new file mode 100644 index 0000000000..28f578bf71 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/range-high.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.949", + "green": "0.718", + "blue": "0.020", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "1.000", + "green": "0.839", + "blue": "0.039", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/range-in.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/range-in.colorset/Contents.json new file mode 100644 index 0000000000..3d0f452a75 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/range-in.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.184", + "green": "0.659", + "blue": "0.310", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.196", + "green": "0.843", + "blue": "0.294", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/range-low.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/range-low.colorset/Contents.json new file mode 100644 index 0000000000..1349d6a59e --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/range-low.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.898", + "green": "0.282", + "blue": "0.302", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "1.000", + "green": "0.624", + "blue": "0.604", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/range-very-high.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/range-very-high.colorset/Contents.json new file mode 100644 index 0000000000..b2851438d1 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/range-very-high.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.541", + "green": "0.380", + "blue": "0.000", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.788", + "green": "0.541", + "blue": "0.000", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/range-very-low.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/range-very-low.colorset/Contents.json new file mode 100644 index 0000000000..46e9e70555 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/range-very-low.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.608", + "green": "0.110", + "blue": "0.122", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "1.000", + "green": "0.271", + "blue": "0.227", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/DerivedAssetsBase.xcassets/target-band.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/target-band.colorset/Contents.json new file mode 100644 index 0000000000..6ff45863c5 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/target-band.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.000", + "green": "0.690", + "blue": "1.000", + "alpha": "0.140" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "0.388", + "green": "0.729", + "blue": "1.000", + "alpha": "0.180" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/Localizable.xcstrings b/Loop/Localizable.xcstrings index e8be2e8501..7eaca9b0de 100644 --- a/Loop/Localizable.xcstrings +++ b/Loop/Localizable.xcstrings @@ -12,6 +12,18908 @@ }, " -" : { + }, + "%1$@ d" : { + "comment" : "Trends period picker, short label for a period of several days, e.g. 7 d. (1: number of days)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ يوم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ d" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ d" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ T" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ d" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ pv" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ j" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ ימ׳" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ nap" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@日" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ d" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ d" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ d" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ d" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ z" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ д" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ d" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ d" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ д" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@天" + } + } + } + }, + "%1$@ entries" : { + "comment" : "Trends Carbs per day tile detail: average number of carb entries per day, a fractional value such as 2.5, so not pluralised. (1: number with one decimal)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ إدخالات" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "záznamů: %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ registreringer" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ Einträge" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ entradas" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ kirjausta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ entrées" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ רשומות" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ bejegyzés" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ inserimenti" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@件" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ registreringer" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ registraties" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "wpisów: %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ registros" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ intrări" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "записей: %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "záznamov: %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ registreringar" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ giriş" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "записів: %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ lần nhập" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@条" + } + } + } + }, + "%1$@ g" : { + "comment" : "Format string for an amount of carbohydrate in grams. (1: grams)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ г" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ gr" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ г" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ g" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ 克" + } + } + } + }, + "%1$@ h" : { + "comment" : "Time in range row: average hours a day spent in the band, abbreviated. (1: hours with one decimal)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ س" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ h" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ t" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ Std" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ h" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ h" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ h" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ שע׳" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ óra" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ h" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@時間" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ t" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ u" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ godz." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ h" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ h" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ ч" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ h" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ h" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ sa" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ год" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ giờ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ 小时" + } + } + } + }, + "%1$@ manual, %2$@ automatic" : { + "comment" : "VoiceOver: how the bolus divides. (1: manual units, 2: automatic units)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ يدوي، %2$@ تلقائي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ ručně, %2$@ automaticky" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ manuel, %2$@ automatisk" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ manuell, %2$@ automatisch" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ manual, %2$@ automático" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ manuaalinen, %2$@ automaattinen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ manuel, %2$@ automatique" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ ידני, %2$@ אוטומטי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ kézi, %2$@ automatikus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ manuale, %2$@ automatico" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "手動 %1$@、自動 %2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ manuell, %2$@ automatisk" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ handmatig, %2$@ automatisch" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ ręcznie, %2$@ automatycznie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ manual, %2$@ automático" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ manual, %2$@ automat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ вручную, %2$@ автоматически" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ ručne, %2$@ automaticky" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ manuell, %2$@ automatisk" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ manuel, %2$@ otomatik" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ вручну, %2$@ автоматично" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ thủ công, %2$@ tự động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "手动 %1$@,自动 %2$@" + } + } + } + }, + "%1$@ mmol/mol" : { + "comment" : "Trends GMI tile detail: the GMI in IFCC units. (1: value)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mmol/mol" + } + } + } + }, + "%1$@ of %2$@" : { + "comment" : "Trends Loop active tile detail: time in closed loop out of the time Loop status is known. (1: duration in closed loop, e.g. 6d 4h, 2: duration covered, e.g. 7d) Trends: how many days of the period are averaged. (1: days averaged, a number; 2: days in the period, e.g. 14 days)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ من %2$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ z %2$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ af %2$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ von %2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ de %2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ / %2$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ sur %2$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ מתוך %2$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ / %2$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ su %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ / %2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ av %2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ van %2$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ z %2$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ de %2$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ din %2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ из %2$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ z %2$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ av %2$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ / %2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ з %2$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ / %2$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ / %2$@" + } + } + } + }, + "%1$@ U/h" : { + "comment" : "Format string for an insulin rate in units per hour. (1: rate)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ وحدة لكل ساعة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U/hod" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ E/t" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ IE/Std" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U/h" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U/h" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U/h" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/hr %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U/óra" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U/ora" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ 単位/時" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ E/t" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ E/uur" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ J/h" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U/h" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U/oră" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ Ед/ч" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ J/hod" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ IE/h" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ Ü/Sa" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U/год" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U/giờ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U/小时" + } + } + } + }, + "%1$@%%" : { + "comment" : "A percentage. (1: number)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %%" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %%" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %%" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %%" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %%" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %%" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%%%1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%%" + } + } + } + }, + "%1$@%% basal" : { + "comment" : "Trends Insulin per day tile detail: share of insulin delivered as basal. (1: percentage number)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% القاعدية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazál %1$@%%" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% basal" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% Basal" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% basal" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% basaalia" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% basal" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% Basal" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% basal" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% basale" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサル %1$@%%" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% basal" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% basaal" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "baza %1$@%%" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% basal" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% bazală" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "базал %1$@%%" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazál %1$@%%" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% basal" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%%%1$@ bazal" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "базал %1$@%%" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% Basal" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础 %1$@%%" + } + } + } + }, + "%1$@%% basal · %2$@" : { + "comment" : "Trends Insulin per day tile detail when only some days are averaged. (1: basal share as a percentage number, 2: days averaged, e.g. 5 of 14 days)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% القاعدية · %2$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazál %1$@%% · %2$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% basal · %2$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% Basal · %2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% basal · %2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% basaalia · %2$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% basal · %2$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% Basal · %2$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% basal · %2$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% basale · %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサル %1$@%% · %2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% basal · %2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% basaal · %2$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "baza %1$@%% · %2$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% basal · %2$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% bazală · %2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "базал %1$@%% · %2$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazál %1$@%% · %2$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% basal · %2$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%%%1$@ bazal · %2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "базал %1$@%% · %2$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% Basal · %2$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础 %1$@%% · %2$@" + } + } + } + }, + "%1$@%% CGM active" : { + "comment" : "Trends Time in range card: share of the period with CGM readings. (1: percentage number)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% CGM نشط" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM aktivní %1$@%%" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% CGM aktiv" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% CGM aktiv" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM activo %1$@%%" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% CGM aktiivinen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM actif %1$@ %%" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% CGM פעיל" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% CGM aktív" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM attivo %1$@%%" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM稼働 %1$@%%" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% CGM aktiv" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% CGM actief" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM aktywny %1$@%%" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM ativo %1$@%%" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%% CGM activ" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM активен %1$@%%" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM aktívny %1$@%%" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %% CGM aktiv" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%%%1$@ CGM aktif" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM активний %1$@%%" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM hoạt động %1$@%%" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM 在线 %1$@%%" + } + } + } + }, + "%1$@, %2$@ %3$@: %4$@ percent, %5$@ hours a day" : { + "comment" : "VoiceOver label for a Time in range row. (1: band name, e.g. In range, 2: glucose thresholds, e.g. 3.9–10.0, 3: glucose unit, 4: percentage number, 5: average hours a day with one decimal)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@، %2$@ %3$@: %4$@ بالمئة، %5$@ ساعة يوميًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: procenta — %4$@, hodin denně — %5$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ procent, %5$@ timer om dagen" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ Prozent, %5$@ Stunden pro Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ por ciento, %5$@ horas al día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ prosenttia, %5$@ tuntia päivässä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@ : %4$@ pour cent, %5$@ heures par jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ אחוז, %5$@ שעות ביום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ százalék, napi %5$@ óra" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ percento, %5$@ ore al giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@、%2$@ %3$@:%4$@パーセント、1日%5$@時間" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ prosent, %5$@ timer i døgnet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ procent, %5$@ uur per dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: procent — %4$@, godzin dziennie — %5$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ por cento, %5$@ horas por dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ procente, %5$@ ore pe zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: процент — %4$@, часов в день — %5$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: percentá — %4$@, hodín denne — %5$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ procent, %5$@ timmar per dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: yüzde %4$@, günde %5$@ saat" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: відсоток — %4$@, годин на день — %5$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ %3$@: %4$@ phần trăm, %5$@ giờ mỗi ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@,%2$@ %3$@:百分之%4$@,每天%5$@小时" + } + } + } + }, + "%lld days" : { + "comment" : "A number of days, e.g. a Trends period or how many days are averaged. (1: number of days) Trends period picker, VoiceOver name for a period of several days. (1: number of days)", + "localizations" : { + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld day" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld days" + } + } + } + } + }, + "ar" : { + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld يوم" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld يوم" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld يوم" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld أيام" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld يومًا" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld يوم" + } + } + } + } + }, + "cs" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld den" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dny" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dne" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dní" + } + } + } + } + }, + "da" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dag" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dage" + } + } + } + } + }, + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Tag" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Tage" + } + } + } + } + }, + "es" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld día" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld días" + } + } + } + } + }, + "fi" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld päivä" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld päivää" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld jour" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld jours" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld jours" + } + } + } + } + }, + "he" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld יום" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld ימים" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld ימים" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld ימים" + } + } + } + } + }, + "hu" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nap" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nap" + } + } + } + } + }, + "it" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld giorno" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld giorni" + } + } + } + } + }, + "ja" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld日" + } + } + } + } + }, + "nb" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dag" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dager" + } + } + } + } + }, + "nl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dag" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dagen" + } + } + } + } + }, + "pl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dzień" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dni" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dni" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dnia" + } + } + } + } + }, + "pt-BR" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dia" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dias" + } + } + } + } + }, + "ro" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld zi" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld zile" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld de zile" + } + } + } + } + }, + "ru" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld день" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld дня" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld дней" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld дня" + } + } + } + } + }, + "sk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld deň" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dni" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dňa" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dní" + } + } + } + } + }, + "sv" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dag" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld dagar" + } + } + } + } + }, + "tr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld gün" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld gün" + } + } + } + } + }, + "uk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld день" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld дні" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld днів" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld дня" + } + } + } + } + }, + "vi" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld ngày" + } + } + } + } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld天" + } + } + } + } + } + } + }, + "%lld entries" : { + "comment" : "Trends Carbs tile detail for a one-day period: how many carb entries were made that day. (1: number)", + "localizations" : { + "ar" : { + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld إدخالات" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "إدخال واحد" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "إدخالان" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld إدخالات" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld إدخالًا" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld إدخال" + } + } + } + } + }, + "cs" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld záznam" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld záznamy" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld záznamu" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld záznamů" + } + } + } + } + }, + "da" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registrering" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registreringer" + } + } + } + } + }, + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Eintrag" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Einträge" + } + } + } + } + }, + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld entry" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld entries" + } + } + } + } + }, + "es" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld entrada" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld entradas" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld entradas" + } + } + } + } + }, + "fi" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kirjaus" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kirjausta" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld entrée" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld entrées" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld entrées" + } + } + } + } + }, + "he" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "רשומה אחת" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld רשומות" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld רשומות" + } + } + } + } + }, + "hu" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld bejegyzés" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld bejegyzés" + } + } + } + } + }, + "it" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld inserimento" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld inserimenti" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld inserimenti" + } + } + } + } + }, + "ja" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld件" + } + } + } + } + }, + "nb" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registrering" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registreringer" + } + } + } + } + }, + "nl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registratie" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registraties" + } + } + } + } + }, + "pl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld wpis" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld wpisy" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld wpisów" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld wpisu" + } + } + } + } + }, + "pt-BR" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registro" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registros" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registros" + } + } + } + } + }, + "ro" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld intrare" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld intrări" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld de intrări" + } + } + } + } + }, + "ru" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld запись" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld записи" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld записей" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld записи" + } + } + } + } + }, + "sk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld záznam" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld záznamy" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld záznamu" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld záznamov" + } + } + } + } + }, + "sv" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registrering" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registreringar" + } + } + } + } + }, + "tr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld giriş" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld giriş" + } + } + } + } + }, + "uk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld запис" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld записи" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld записів" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld запису" + } + } + } + } + }, + "vi" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld lần nhập" + } + } + } + } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld条" + } + } + } + } + } + } + }, + "%lld shorter gaps" : { + "comment" : "Chart note: how many other, shorter gaps in glucose readings there were; follows the longest gap. (1: number of gaps)", + "localizations" : { + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld shorter gap" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld shorter gaps" + } + } + } + } + }, + "ar" : { + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوة أقصر" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوة أقصر" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوة أقصر" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوات أقصر" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوة أقصر" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوة أقصر" + } + } + } + } + }, + "cs" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratší výpadek" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratší výpadky" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratšího výpadku" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratších výpadků" + } + } + } + } + }, + "da" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere hul" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere huller" + } + } + } + } + }, + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kürzere Lücke" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kürzere Lücken" + } + } + } + } + }, + "es" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interrupción más corta" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interrupciones más cortas" + } + } + } + } + }, + "fi" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld lyhyempi katko" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld lyhyempää katkoa" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interruption plus courte" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interruptions plus courtes" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interruptions plus courtes" + } + } + } + } + }, + "he" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld פער קצר יותר" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld פערים קצרים יותר" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld פערים קצרים יותר" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld פערים קצרים יותר" + } + } + } + } + }, + "hu" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld rövidebb kimaradás" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld rövidebb kimaradás" + } + } + } + } + }, + "it" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interruzione più breve" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interruzioni più brevi" + } + } + } + } + }, + "ja" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "ほか短い欠損%lld件" + } + } + } + } + }, + "nb" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere hull" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere hull" + } + } + } + } + }, + "nl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere onderbreking" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere onderbrekingen" + } + } + } + } + }, + "pl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld krótsza przerwa" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld krótsze przerwy" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld krótszych przerw" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld krótszej przerwy" + } + } + } + } + }, + "pt-BR" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interrupção mais curta" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interrupções mais curtas" + } + } + } + } + }, + "ro" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld întrerupere mai scurtă" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld întreruperi mai scurte" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld de întreruperi mai scurte" + } + } + } + } + }, + "ru" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld короткий пропуск" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротких пропуска" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротких пропусков" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротких пропуска" + } + } + } + } + }, + "sk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratší výpadok" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratšie výpadky" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratšieho výpadku" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratších výpadkov" + } + } + } + } + }, + "sv" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortare lucka" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortare luckor" + } + } + } + } + }, + "tr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld daha kısa boşluk" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld daha kısa boşluk" + } + } + } + } + }, + "uk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротший пропуск" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротші пропуски" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротших пропусків" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротшого пропуску" + } + } + } + } + }, + "vi" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld khoảng trống ngắn hơn" + } + } + } + } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "另有%lld段较短中断" + } + } + } + } + } + } + }, + "+ %1$@ automatic" : { + "comment" : "Trends Boluses per day tile detail: average number of automatic boluses per day on top of the manual ones, a fractional value such as 4.2. (1: number with one decimal)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ تلقائي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automat." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatiske" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatisch" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automáticos" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automaattista" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatiques" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ אוטומטיים" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatikus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatici" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ 自動 %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatiske" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatisch" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automat." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automáticos" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automate" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ авто" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automat." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatiska" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ otomatik" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ авто" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ tự động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ 自动 %1$@" + } + } + } + }, + "25–75%" : { + "comment" : "Trends Daily pattern legend: band between the 25th and 75th percentiles of glucose", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75 %" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75 %" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75 %" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75 %" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%25–75" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + } + } + }, + "5–95%" : { + "comment" : "Trends Daily pattern legend: band between the 5th and 95th percentiles of glucose", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95 %" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95 %" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95 %" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95 %" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%5–95" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + } + } + }, + "Active insulin %1$@, active carbs %2$@" : { + "comment" : "VoiceOver summary of the active insulin and carbs chart. (1: insulin on board, e.g. 1.25 U, or unknown; 2: carbs on board, e.g. 20 g, or unknown)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الأنسولين النشط %1$@، الكارب النشط %2$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivní inzulín %1$@, aktivní sacharidy %2$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin %1$@, aktive kulhydrater %2$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Insulin %1$@, aktive KH %2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina activa %1$@, carbohidratos activos %2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akt. insuliini %1$@, akt. hiilari %2$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuline active %1$@, glucides actifs %2$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אינסולין פעיל %1$@, פחמימות פעילות %2$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktív inzulin %1$@, aktív szénhidrát %2$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina attiva %1$@, carboidrati attivi %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "残存インスリン %1$@、残存糖質 %2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin %1$@, aktive karbohydrater %2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve insuline %1$@, actieve koolhydraten %2$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktywna insulina %1$@, aktywne węglowodany %2$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina ativa %1$@, carboidratos ativos %2$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulină activă %1$@, carbohidrați activi %2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активный инсулин %1$@, активные углеводы %2$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktívny inzulín %1$@, aktívne sacharidy %2$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin %1$@, aktiva kolhydrater %2$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktif insülin %1$@, aktif karb %2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активний інсулін %1$@, активні вуглеводи %2$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin còn hoạt động %1$@, Carbs còn hoạt động %2$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "活性胰岛素 %1$@,活性碳水 %2$@" + } + } + } + }, + "Active insulin and carbs" : { + "comment" : "Title of the day page card charting insulin and carbs on board", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الأنسولين والكارب النشطان" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivní inzulín a sacharidy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin og kulhydrater" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Insulin und KH" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina y carbohidratos activos" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiivinen insuliini ja hiilihydraatit" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuline et glucides actifs" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אינסולין ופחמימות פעילים" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktív inzulin és szénhidrát" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina e carboidrati attivi" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "残存インスリンと残存糖質" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin og karbohydrater" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve insuline en koolhydraten" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktywna insulina i węglowodany" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina e carboidratos ativos" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulină activă și carbohidrați activi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активный инсулин и углеводы" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktívny inzulín a sacharidy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin och kolhydrater" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktif insülin ve karb" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активний інсулін і вуглеводи" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin và Carbs còn hoạt động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "活性胰岛素和碳水" + } + } + } + }, + "Aim: over 70%% in range, under 4%% low." : { + "comment" : "Trends Time in range card footnote: the consensus targets for time in range and time low", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الهدف: أكثر من 70%% ضمن النطاق، وأقل من 4%% منخفض." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cíl: přes 70%% v rozsahu, pod 4%% nízko." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: over 70 %% i området, under 4 %% lavt." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ziel: über 70 %% im Zielbereich, unter 4 %% niedrig." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objetivo: más del 70%% en rango, menos del 4%% bajo." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tavoite: yli 70 %% tavoitealueella, alle 4 %% matalalla." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objectif : plus de 70 %% dans la cible, moins de 4 %% en hypo." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "יעד: מעל 70%% בטווח, מתחת ל-4%% נמוך." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cél: több mint 70%% tartományban, kevesebb mint 4%% alacsony." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obiettivo: oltre il 70%% nell'intervallo, meno del 4%% bassa." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "目標:範囲内70%%超、低値4%%未満。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: over 70 %% i målområdet, under 4 %% lavt." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doel: meer dan 70%% binnen bereik, minder dan 4%% laag." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cel: ponad 70%% w zakresie, poniżej 4%% niskich." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meta: mais de 70%% no alvo, menos de 4%% baixo." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Țintă: peste 70%% în interval, sub 4%% scăzut." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цель: в диапазоне более 70%%, низкая — менее 4%%." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cieľ: nad 70%% v rozsahu, pod 4%% nízko." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: över 70 %% inom målområdet, under 4 %% lågt." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hedef: aralıkta %%70'in üzeri, düşükte %%4'ün altı." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Мета: у діапазоні понад 70%%, низька — менше 4%%." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mục tiêu: trên 70%% trong khoảng, dưới 4%% thấp." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "目标:范围内超过70%%,低血糖低于4%%。" + } + } + } + }, + "Automatic" : { + "comment" : "Chart legend: hatched bars are automatic boluses given by Loop", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تلقائي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatické" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaattinen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatique" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אוטומטי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatikus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatico" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "自動" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatyczne" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматические" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatické" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otomatik" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматичні" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tự động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自动" + } + } + } + }, + "Automatic %1$@" : { + "comment" : "Basal and bolus: bolus Loop gave automatically. (1: units, e.g. 5.4 U)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تلقائي %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaticky %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaattinen %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatique %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אוטומטי %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatikus %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatico %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "自動 %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatycznie %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automat %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматически %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaticky %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otomatik %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматично %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tự động %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自动 %1$@" + } + } + } + }, + "Automatic bolus" : { + "comment" : "Chart callout label for a bolus Loop delivered automatically near the touched time Logbook row title: a bolus Loop delivered automatically", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus تلقائي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatický bolus" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk bolus" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatischer Bolus" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolo automático" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaattinen bolus" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus automatique" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus אוטומטי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatikus bólus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolo automatico" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "自動ボーラス" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk bolus" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatische bolus" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus automatyczny" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus automático" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus automat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматический болюс" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatický bolus" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk bolus" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otomatik bolus" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматичний болюс" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus tự động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自动大剂量" + } + } + } + }, + "Average %1$@ U" : { + "comment" : "Trends Insulin per day legend: the dashed line showing average total insulin per day. (1: units)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المتوسط %1$@ وحدة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Průměr %1$@ U" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gennemsnit %1$@ E" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durchschnitt %1$@ IE" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Promedio %1$@ U" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keskiarvo %1$@ U" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Moyenne %1$@ U" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ממוצע U %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Átlag %1$@ U" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Media %1$@ U" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均 %1$@ 単位" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gjennomsnitt %1$@ E" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gemiddeld %1$@ E" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Średnio %1$@ J" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Média %1$@ U" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medie %1$@ U" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Среднее %1$@ Ед." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Priemer %1$@ j" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medel %1$@ E" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ortalama %1$@ Ü" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Середнє %1$@ U" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trung bình %1$@ U" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均 %1$@ U" + } + } + } + }, + "average per day" : { + "comment" : "Trends Basal and bolus card subtitle when the figures are averaged per day over the period", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المتوسط اليومي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "průměr za den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "gennemsnit pr. dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durchschnitt pro Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "promedio por día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "keskiarvo päivässä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "moyenne par jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ממוצע ליום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "napi átlag" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "media al giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "1日あたりの平均" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "gjennomsnitt per dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "gemiddeld per dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "średnio dziennie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "média por dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "medie pe zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "в среднем за день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "priemer za deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "medel per dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "günlük ortalama" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "у середньому за день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "trung bình mỗi ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日平均" + } + } + } + }, + "Basal" : { + "comment" : "Chart callout label for the basal rate at the touched time Label of a tile showing the current basal rate (Now screen) or the day's basal insulin total (Today so far totals) Logbook filter chip: show only basal changes", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القاعدية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaalinen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basale" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサル" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaal" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podstawowy" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazală" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazálna" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazal" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础" + } + } + } + }, + "Basal %1$@, %2$@ percent" : { + "comment" : "VoiceOver: basal units and share. (1: units, 2: percentage number)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القاعدية %1$@، %2$@ بالمئة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál %1$@, %2$@ procent" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ procent" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ Prozent" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ por ciento" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaali %1$@, %2$@ prosenttia" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ pour cent" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ אחוז" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ százalék" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basale %1$@, %2$@ percento" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサル %1$@、%2$@ パーセント" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ prosent" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaal %1$@, %2$@ procent" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podstawowy %1$@, %2$@ procent" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ por cento" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazală %1$@, %2$@ la sută" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал %1$@, %2$@ процентов" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál %1$@, %2$@ percent" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ procent" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazal %1$@, yüzde %2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал %1$@, %2$@ відсотків" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ phần trăm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础 %1$@,百分之 %2$@" + } + } + } + }, + "Basal and bolus" : { + "comment" : "Trends and day page card title: insulin split into basal and bolus", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القاعدية والبلعة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál a bolus" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal og bolus" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal und Bolus" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal y bolo" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaali ja bolus" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal et bolus" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal ו-Bolus" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal és bólus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basale e bolo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサルとボーラス" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal og bolus" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaal en bolus" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina podstawowa i bolus" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal e bolus" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazală și bolus" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал и болюс" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál a bolus" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal och bolus" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazal ve bolus" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал і болюс" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal và bolus" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础与大剂量" + } + } + } + }, + "basal and bolus" : { + "comment" : "Trends Insulin per day card subtitle when each bar is one day's basal and bolus", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القاعدية و Bolus" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazál a bolus" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal og bolus" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal und Bolus" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal y bolo" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "basaali ja bolus" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal et bolus" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal ו-Bolus" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal és bólus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "basale e bolo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサルとボーラス" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal og bolus" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "basaal en bolus" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "baza i bolus" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal e bolus" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazală și bolus" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "базал и болюс" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazál a bolus" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal och bolus" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazal ve bolus" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "базал і болюс" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal và Bolus" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础和大剂量" + } + } + } + }, + "Basal given" : { + "comment" : "Chart legend: filled area is basal insulin actually delivered", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القاعدية المُعطاة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podaný bazál" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Givet basal" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abgegebene Basalrate" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal administrada" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annettu basaali" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal administré" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal שניתן" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leadott basal" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basale erogata" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "注入済みベーサル" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gitt basal" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toegediend basaal" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podana baza" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal entregue" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazală administrată" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поданный базал" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podaný bazál" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Given basal" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verilen bazal" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поданий базал" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal đã tiêm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已输注基础" + } + } + } + }, + "Bolus %1$@, %2$@ percent" : { + "comment" : "VoiceOver: bolus units and share. (1: units, 2: percentage number)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "البلعة %1$@، %2$@ بالمئة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ procent" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ procent" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ Prozent" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolo %1$@, %2$@ por ciento" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ prosenttia" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ pour cent" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ אחוז" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bólus %1$@, %2$@ százalék" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolo %1$@, %2$@ percento" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ボーラス %1$@、%2$@ パーセント" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ prosent" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ procent" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ procent" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ por cento" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ la sută" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Болюс %1$@, %2$@ процентов" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ percent" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ procent" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, yüzde %2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Болюс %1$@, %2$@ відсотків" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus %1$@, %2$@ phần trăm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "大剂量 %1$@,百分之 %2$@" + } + } + } + }, + "Boluses" : { + "comment" : "Trends summary tile for a one-day period: how many manual boluses were given that day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الجرعات" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boluser" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boli" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolos" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boluksia" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בולוסים" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bóluszok" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boli" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ボーラス" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boluser" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolussen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusy" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusuri" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Болюсы" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusdoser" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Болюси" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Liều bolus" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "大剂量次数" + } + } + } + }, + "Boluses per day" : { + "comment" : "Trends summary tile: average number of manual boluses per day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "جرعات Bolus يوميًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusů denně" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boluser pr. dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boli pro Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolos por día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boluksia päivässä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus par jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus ליום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bólusok naponta" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boli al giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "1日のボーラス回数" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boluser per dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolussen per dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusy dziennie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus por dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusuri pe zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Болюсов в день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusy za deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusar per dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Günlük bolus sayısı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Болюсів на день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Số lần Bolus mỗi ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日大剂量次数" + } + } + } + }, + "Carbs" : { + "comment" : "Label of a tile showing the day's total carbohydrate Logbook filter chip: show only carb entries Logbook row title: a carb entry", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carbs" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sacharidy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kulhydrater" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "KH" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carbohidratos" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hiilihydraatit" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glucides" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carbs" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Szénhidrát" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carb." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "カーボ" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Karbohydrater" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koolhydraten" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Węglowodany" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carbs" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carbohidrați" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Углеводы" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sacharidy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kolhydrater" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Karb" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вуглеводи" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carbs" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "碳水化合物" + } + } + } + }, + "Carbs per day" : { + "comment" : "Trends summary tile: average grams of carbohydrate entered per day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الكارب يوميًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sacharidy denně" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kulhydrater pr. dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "KH pro Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carbohidratos por día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hiilihydraatit päivässä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glucides par jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "פחמימות ליום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Szénhidrát naponta" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carboidrati al giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "1日のカーボ" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Karbohydrater per dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koolhydraten per dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Węglowodany dziennie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carbs por dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carbohidrați pe zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Углеводов в день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sacharidy za deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kolhydrater per dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Günlük karb" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вуглеводів на день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carbs mỗi ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日碳水" + } + } + } + }, + "Closed loop" : { + "comment" : "Trends Loop status: time Loop was running in closed loop", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حلقة مغلقة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavřená smyčka" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Circuito cerrado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suljettu säätö" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boucle fermée" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zárt hurok" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop chiuso" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クローズドループ" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve loop" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pętla zamknięta" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ciclo fechado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buclă închisă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутый цикл" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavretá slučka" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sluten loop" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kapalı döngü" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутий цикл" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vòng lặp kín" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "闭环" + } + } + } + }, + "Closed loop off" : { + "comment" : "Reason shown for a stretch of time when Loop was in open loop because closed loop was turned off", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الحلقة المغلقة متوقفة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavřená smyčka vypnuta" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop slået fra" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop aus" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Circuito cerrado desactivado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suljettu säätö pois" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boucle fermée désactivée" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop כבוי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zárt hurok kikapcsolva" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop chiuso disattivato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クローズドループ オフ" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop av" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve loop uit" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pętla zamknięta wyłączona" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ciclo fechado desligado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buclă închisă oprită" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутый цикл выключен" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavretá slučka vypnutá" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sluten loop av" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kapalı döngü kapalı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутий цикл вимкнено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vòng lặp kín tắt" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "闭环已关闭" + } + } + } + }, + "COB" : { + "comment" : "Abbreviation for carbs on board (active carbohydrate) Abbreviation for carbs on board; label of a tile on the Now screen", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "GA" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB" + } + } + } + }, + "COB (g, right)" : { + "comment" : "Chart legend: carbs on board in grams, on the right axis", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g، يمين)" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, vpravo)" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, højre)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, rechts)" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, derecha)" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, oikea)" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "GA (g, droite)" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, ימין)" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, jobb)" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, destra)" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB(g、右)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, høyre)" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, rechts)" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, prawa oś)" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, direita)" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, dreapta)" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (г, справа)" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, vpravo)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, höger)" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (gr, sağ)" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (г, праворуч)" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB (g, bên phải)" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "COB(克,右轴)" + } + } + } + }, + "Configuration error" : { + "comment" : "Reason Loop could not run: its settings are incomplete", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "خطأ في الإعدادات" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chyba konfigurace" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurationsfejl" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurationsfehler" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error de configuración" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Määritysvirhe" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur de configuration" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שגיאת תצורה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurációs hiba" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore di configurazione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "設定エラー" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurasjonsfeil" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuratiefout" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Błąd konfiguracji" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erro de configuração" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eroare de configurare" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ошибка настроек" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chyba konfigurácie" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfigurationsfel" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yapılandırma hatası" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Помилка налаштувань" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lỗi cấu hình" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "配置错误" + } + } + } + }, + "Connection error" : { + "comment" : "Reason Loop could not run: no connected devices, or a device connection failed", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "خطأ في الاتصال" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chyba připojení" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forbindelsesfejl" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindungsfehler" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error de conexión" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteysvirhe" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur de connexion" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שגיאת חיבור" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kapcsolódási hiba" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore di connessione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "接続エラー" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilkoblingsfeil" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindingsfout" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Błąd połączenia" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erro de conexão" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eroare de conexiune" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ошибка подключения" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chyba pripojenia" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anslutningsfel" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bağlantı hatası" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Помилка підключення" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lỗi kết nối" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接错误" + } + } + } + }, + "Couldn't export" : { + "comment" : "Alert title when exporting the Trends PDF or CSV fails", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعذّر التصدير" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nelze exportovat" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunne ikke eksportere" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Export fehlgeschlagen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo exportar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vienti epäonnistui" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportation impossible" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא ניתן היה לייצא" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nem sikerült exportálni" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile esportare" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "書き出せませんでした" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunne ikke eksportere" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exporteren mislukt" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie udało się wyeksportować" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível exportar" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nu s-a putut exporta" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удалось экспортировать" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nedá sa exportovať" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunde inte exportera" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dışa aktarılamadı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не вдалося експортувати" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không thể xuất" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法导出" + } + } + } + }, + "Couldn't load this day: %1$@" : { + "comment" : "Day page error when the day's history can't be read. (1: system error description)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعذّر تحميل هذا اليوم: %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nelze načíst tento den: %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunne ikke indlæse denne dag: %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dieser Tag konnte nicht geladen werden: %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo cargar este día: %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivää ei voitu ladata: %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de charger cette journée : %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא ניתן היה לטעון יום זה: %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nem sikerült betölteni ezt a napot: %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile caricare questo giorno: %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "この日を読み込めませんでした:%1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunne ikke laste inn denne dagen: %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deze dag kan niet worden geladen: %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie udało się wczytać tego dnia: %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível carregar este dia: %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nu s-a putut încărca această zi: %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удалось загрузить этот день: %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tento deň sa nedá načítať: %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunde inte läsa in dagen: %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bu gün yüklenemedi: %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не вдалося завантажити цей день: %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không thể tải ngày này: %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法载入这一天:%1$@" + } + } + } + }, + "Couldn't load Trends: %1$@" : { + "comment" : "Trends error message when the history can't be read. (1: error description)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعذّر تحميل الاتجاهات: %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nelze načíst trendy: %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunne ikke indlæse Tendenser: %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trends konnten nicht geladen werden: %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudieron cargar las tendencias: %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendejä ei voitu ladata: %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de charger les tendances : %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא ניתן היה לטעון מגמות: %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nem sikerült betölteni a trendeket: %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile caricare le tendenze: %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "トレンドを読み込めませんでした:%1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunne ikke laste inn Trender: %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trends kunnen niet worden geladen: %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie udało się wczytać trendów: %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível carregar as tendências: %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nu s-au putut încărca tendințele: %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удалось загрузить тренды: %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy sa nedajú načítať: %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunde inte läsa in Trender: %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eğilimler yüklenemedi: %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не вдалося завантажити тренди: %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không thể tải Xu hướng: %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法载入趋势:%1$@" + } + } + } + }, + "Covers %1$@: Loop keeps 7 days of runs, and closed or open loop is recorded from when this version of Loop first ran." : { + "comment" : "Trends Loop status footnote when the status covers only part of the period. (1: date range covered)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يغطي %1$@: يحتفظ Loop بسجل تشغيلات 7 أيام، وتُسجَّل الحلقة المغلقة أو المفتوحة منذ أول تشغيل لهذا الإصدار من Loop." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pokrývá %1$@: Loop uchovává běhy za 7 dní a uzavřená či otevřená smyčka se zaznamenává od prvního spuštění této verze Loop." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dækker %1$@: Loop gemmer kørsler i 7 dage, og lukket eller åbent Loop registreres fra første gang denne version af Loop kørte." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Umfasst %1$@: Loop speichert Durchläufe 7 Tage lang, und Closed oder Open Loop wird erst ab dem ersten Start dieser Loop-Version aufgezeichnet." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cubre %1$@: Loop guarda 7 días de ejecuciones, y el circuito cerrado o abierto se registra desde la primera vez que se ejecutó esta versión de Loop." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kattaa %1$@: Loop säilyttää 7 päivän ajot, ja suljettu tai avoin säätö tallennetaan siitä lähtien, kun tämä Loopin versio käynnistettiin ensimmäisen kerran." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couvre %1$@ : Loop conserve 7 jours d'exécutions, et la boucle fermée ou ouverte est enregistrée depuis le premier lancement de cette version de Loop." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מכסה %1$@: Loop שומר 7 ימים של הרצות, ו-Closed Loop או Open Loop נרשמים מאז ההפעלה הראשונה של גרסה זו של Loop." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lefedett időszak: %1$@. A Loop 7 napnyi futást őriz meg, a zárt vagy nyitott hurok pedig a Loop ezen verziójának első futtatásától kerül rögzítésre." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copre %1$@: Loop conserva 7 giorni di esecuzioni e il loop chiuso o aperto è registrato dal primo avvio di questa versione di Loop." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "対象期間 %1$@:Loopは実行記録を7日間保持し、クローズド/オープンループはこのバージョンのLoopを初めて起動した時点から記録されます。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dekker %1$@: Loop lagrer kjøringer i 7 dager, og lukket eller åpen Loop registreres fra første gang denne versjonen av Loop kjørte." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Omvat %1$@: Loop bewaart 7 dagen aan loop-runs, en actieve of open loop wordt vastgelegd vanaf de eerste keer dat deze versie van Loop draaide." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obejmuje %1$@: Loop przechowuje cykle z 7 dni, a pętla zamknięta lub otwarta jest zapisywana od pierwszego uruchomienia tej wersji Loop." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cobre %1$@: o Loop guarda 7 dias de execuções, e o ciclo fechado ou aberto é registrado desde a primeira vez que esta versão do Loop foi executada." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acoperă %1$@: Loop păstrează 7 zile de rulări, iar bucla închisă sau deschisă este înregistrată de la prima rulare a acestei versiuni de Loop." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Охвачено: %1$@. Loop хранит запуски за 7 дней, а замкнутый или открытый цикл записывается с первого запуска этой версии Loop." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pokrýva %1$@: Loop uchováva behy za 7 dní a uzavretá či otvorená slučka sa zaznamenáva od prvého spustenia tejto verzie Loop." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Omfattar %1$@: Loop sparar 7 dagars körningar, och sluten eller öppen loop registreras från när den här versionen av Loop först kördes." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kapsanan dönem %1$@: Loop 7 günlük çalışma kaydı tutar; kapalı veya açık döngü, Loop'un bu sürümünün ilk çalıştığı andan itibaren kaydedilir." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Охоплено: %1$@. Loop зберігає запуски за 7 днів, а замкнутий чи відкритий цикл записується з першого запуску цієї версії Loop." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bao gồm %1$@: Loop lưu các lần chạy trong 7 ngày, và trạng thái vòng lặp kín hoặc mở được ghi lại từ khi phiên bản Loop này chạy lần đầu." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "覆盖 %1$@:Loop 保留 7 天的运行记录,闭环或开环状态从此版本 Loop 首次运行时开始记录。" + } + } + } + }, + "CSV, one row per day" : { + "comment" : "Trends export menu item: share a CSV spreadsheet file with one row for each day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV، صف واحد لكل يوم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, jeden řádek na den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, én række pr. dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, eine Zeile pro Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, una fila por día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, yksi rivi päivää kohden" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, une ligne par jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, שורה אחת לכל יום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, naponta egy sor" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, una riga al giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV(1日1行)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, én rad per dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, één rij per dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, jeden wiersz na dzień" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, uma linha por dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, un rând pe zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, одна строка на день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, jeden riadok na deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, en rad per dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, gün başına bir satır" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, один рядок на день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV, mỗi ngày một dòng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "CSV,每天一行" + } + } + } + }, + "Daily pattern" : { + "comment" : "Trends card title: the ambulatory glucose profile, glucose by time of day across the period", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "النمط اليومي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denní profil" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Døgnmønster" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tagesprofil" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Patrón diario" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vuorokausiprofiili" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Profil journalier" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "דפוס יומי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Napi mintázat" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Andamento giornaliero" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "日内パターン" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Døgnmønster" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dagpatroon" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Profil dobowy" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Padrão diário" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipar zilnic" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Суточный профиль" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denný profil" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dygnsmönster" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Günlük örüntü" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добовий профіль" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mẫu hằng ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日模式" + } + } + } + }, + "Daily pattern over %1$@. Median highest %2$@ at %3$@, lowest %4$@ at %5$@" : { + "comment" : "VoiceOver label for the Trends Daily pattern chart. (1: days with readings, e.g. 14 days; 2: highest median glucose; 3: time of day; 4: lowest median glucose; 5: time of day)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "النمط اليومي خلال %1$@. أعلى وسيط %2$@ عند %3$@، وأدنى وسيط %4$@ عند %5$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denní profil za %1$@. Nejvyšší medián %2$@ v %3$@, nejnižší %4$@ v %5$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Døgnmønster over %1$@. Median højest %2$@ kl. %3$@, lavest %4$@ kl. %5$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tagesprofil über %1$@. Median am höchsten %2$@ um %3$@, am niedrigsten %4$@ um %5$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Patrón diario de %1$@. Mediana más alta %2$@ a las %3$@, más baja %4$@ a las %5$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vuorokausiprofiili %1$@ ajalta. Korkein mediaani %2$@ klo %3$@, matalin %4$@ klo %5$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Profil journalier sur %1$@. Médiane la plus haute %2$@ à %3$@, la plus basse %4$@ à %5$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "דפוס יומי במשך %1$@. חציון גבוה ביותר %2$@ ב-%3$@, נמוך ביותר %4$@ ב-%5$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Napi mintázat, %1$@. Legmagasabb medián %2$@ ekkor: %3$@, legalacsonyabb %4$@ ekkor: %5$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Andamento giornaliero su %1$@. Mediana più alta %2$@ alle %3$@, più bassa %4$@ alle %5$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@の日内パターン。中央値の最高は%3$@の%2$@、最低は%5$@の%4$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Døgnmønster over %1$@. Median høyest %2$@ kl. %3$@, lavest %4$@ kl. %5$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dagpatroon over %1$@. Mediaan hoogst %2$@ om %3$@, laagst %4$@ om %5$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Profil dobowy z %1$@. Najwyższa mediana %2$@ o %3$@, najniższa %4$@ o %5$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Padrão diário em %1$@. Mediana mais alta %2$@ às %3$@, mais baixa %4$@ às %5$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipar zilnic pe %1$@. Mediana cea mai mare %2$@ la %3$@, cea mai mică %4$@ la %5$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Суточный профиль за %1$@. Наибольшая медиана %2$@ в %3$@, наименьшая %4$@ в %5$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denný profil za %1$@. Najvyšší medián %2$@ o %3$@, najnižší %4$@ o %5$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dygnsmönster över %1$@. Högsta median %2$@ kl. %3$@, lägsta %4$@ kl. %5$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ boyunca günlük örüntü. En yüksek medyan %3$@ saatinde %2$@, en düşük %5$@ saatinde %4$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добовий профіль за %1$@. Найвища медіана %2$@ о %3$@, найнижча %4$@ о %5$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mẫu hằng ngày trong %1$@. Trung vị cao nhất %2$@ lúc %3$@, thấp nhất %4$@ lúc %5$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@的每日模式。中位数最高 %2$@(%3$@),最低 %4$@(%5$@)" + } + } + } + }, + "Daily pattern: not enough days" : { + "comment" : "VoiceOver label for the Trends Daily pattern chart when fewer than 5 days have readings", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "النمط اليومي: لا توجد أيام كافية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denní profil: málo dní" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Døgnmønster: ikke nok dage" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tagesprofil: nicht genug Tage" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Patrón diario: no hay suficientes días" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vuorokausiprofiili: liian vähän päiviä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Profil journalier : pas assez de jours" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "דפוס יומי: אין מספיק ימים" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Napi mintázat: nincs elég nap" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Andamento giornaliero: giorni insufficienti" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "日内パターン:日数が不足しています" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Døgnmønster: ikke nok dager" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dagpatroon: niet genoeg dagen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Profil dobowy: za mało dni" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Padrão diário: dias insuficientes" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tipar zilnic: prea puține zile" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Суточный профиль: недостаточно дней" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denný profil: málo dní" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dygnsmönster: för få dagar" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Günlük örüntü: yeterli gün yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добовий профіль: замало днів" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mẫu hằng ngày: không đủ số ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日模式:天数不足" + } + } + } + }, + "Day" : { + "comment" : "Appearance option and app icon name: the light (daytime) version", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نهار" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "יום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nap" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "日" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dzień" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "День" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gündüz" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "День" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "日" + } + } + } + }, + "Day (Trends period)" : { + "comment" : "Trends period picker: a period of one calendar day (not daytime)", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Day" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يوم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "יום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nap" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "日" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dzień" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "День" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gün" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "День" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "日" + } + } + } + }, + "Export" : { + "comment" : "Trends menu title: export the period as a PDF report or CSV file", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تصدير" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportovat" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eksporter" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportieren" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vie" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exporter" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ייצוא" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportálás" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esporta" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "書き出し" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eksporter" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exporteer" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eksportuj" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportar" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportați" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Экспорт" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportovať" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportera" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dışa Aktar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Експорт" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xuất" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "导出" + } + } + } + }, + "From Apple Health: Loop keeps 7 days, so this day has no temp basal or schedule detail." : { + "comment" : "Day page note when the day is older than Loop's 7-day history and was read from Apple Health", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "من Apple Health: يحتفظ Loop بـ 7 أيام فقط، لذا لا توجد تفاصيل Temp Basal أو الجدول لهذا اليوم." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Z aplikace Zdraví: Loop uchovává 7 dní, takže pro tento den chybí podrobnosti o dočasném bazálu a plánu." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fra Apple Sundhed: Loop gemmer 7 dage, så denne dag har ingen detaljer om midlertidig basal eller tidsplan." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aus Apple Health: Loop speichert 7 Tage, daher gibt es für diesen Tag keine Details zu temporären Basalraten oder zum Zeitplan." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "De Apple Salud: Loop guarda 7 días, así que este día no tiene detalle de basal temporal ni de programación." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Terveydestä: Loop säilyttää 7 päivää, joten tältä päivältä ei ole tilapäisen basaalin tai ohjelman tietoja." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Depuis Apple Santé : Loop conserve 7 jours, donc ce jour n'a pas de détail de basal temp. ni de programmation." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מ-Apple Health: Loop שומר 7 ימים, ולכן אין ליום זה פרטי Temp Basal או תוכנית." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Az Apple Egészség appból: a Loop 7 napot őriz meg, így ehhez a naphoz nincsenek ideiglenes basal- és ütemezési részletek." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Da Apple Salute: Loop conserva 7 giorni, quindi questo giorno non ha dettagli su basale temporanea o programmazione." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appleヘルスケアから取得:Loopは7日間のみ保持するため、この日には一時ベーサルやスケジュールの詳細がありません。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fra Apple Helse: Loop lagrer 7 dager, så denne dagen har ingen detaljer om midlertidig basal eller tidsplan." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uit Apple Health: Loop bewaart 7 dagen, dus deze dag heeft geen details over tijdelijk basaal of het schema." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Z aplikacji Zdrowie: Loop przechowuje 7 dni, więc dla tego dnia brak szczegółów bazy tymczasowej i harmonogramu." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Do app Saúde: o Loop guarda 7 dias, então este dia não tem detalhes de basal temporária nem da programação." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Din Apple Sănătate: Loop păstrează 7 zile, așa că această zi nu are detalii despre bazala temporară sau program." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Из приложения «Здоровье»: Loop хранит 7 дней, поэтому для этого дня нет данных о временном базале и расписании." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Z aplikácie Zdravie: Loop uchováva 7 dní, preto pre tento deň chýbajú podrobnosti o dočasnom bazáli a pláne." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Från Apple Hälsa: Loop sparar 7 dagar, så den här dagen saknar detaljer om temporär basal och schema." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Health'ten: Loop 7 gün saklar, bu nedenle bu gün için geçici bazal veya program ayrıntısı yok." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "З програми «Здоровʼя»: Loop зберігає 7 днів, тому для цього дня немає даних про тимчасовий базал і розклад." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Từ Apple Health: Loop chỉ lưu 7 ngày, nên ngày này không có chi tiết Temp Basal hoặc lịch biểu." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "来自 Apple 健康:Loop 只保留 7 天,因此这一天没有临时基础率或预设详情。" + } + } + } + }, + "Gaps" : { + "comment" : "Trends Loop status button and screen title: list of times Loop wasn't in closed loop", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الفجوات" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Výpadky" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Huller" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lücken" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interrupciones" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Katkot" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interruptions" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "פערים" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kimaradások" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interruzioni" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "中断" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hull" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onderbrekingen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Przerwy" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interrupções" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Întreruperi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пропуски" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Výpadky" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luckor" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boşluklar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пропуски" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gián đoạn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "中断" + } + } + } + }, + "Glucose too old" : { + "comment" : "Reason Loop could not run: the latest glucose reading was too old", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "قراءة السكر قديمة جدًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glukóza je příliš stará" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glukosemåling for gammel" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blutzuckerwert zu alt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glucosa demasiado antigua" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glukoosi liian vanha" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glycémie trop ancienne" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קריאת הסוכר ישנה מדי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Túl régi glükózérték" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glicemia troppo vecchia" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "血糖値が古すぎます" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blodsukkermåling for gammel" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glucosewaarde te oud" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glukoza zbyt stara" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glicose muito antiga" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glicemie prea veche" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Данные глюкозы устарели" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glykémia je príliš stará" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glukosvärdet är för gammalt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan şekeri verisi çok eski" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дані глюкози застаріли" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dữ liệu đường huyết quá cũ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "血糖数据过旧" + } + } + } + }, + "In range" : { + "comment" : "Time in range band: glucose 70–180 mg/dL (3.9–10.0 mmol/L) Time-in-range band name: glucose 70–180 mg/dL (3.9–10.0 mmol/L)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ضمن النطاق" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "V rozsahu" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "I området" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Im Zielbereich" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "En rango" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tavoitealueella" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dans la cible" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בטווח" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tartományban" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nell'intervallo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "範囲内" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "I målområdet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Binnen bereik" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "W zakresie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "No alvo" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "În interval" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "В диапазоне" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "V rozsahu" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inom målområdet" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aralıkta" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "У діапазоні" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trong khoảng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "范围内" + } + } + } + }, + "Insulin per day" : { + "comment" : "Trends tile and card title: average total insulin per day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الأنسولين يوميًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulín za den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin pr. dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin pro Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina por día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuliini päivässä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuline par jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אינסולין ליום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulin naponta" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina al giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "1日のインスリン量" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin per dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuline per dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina dziennie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina por dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulină pe zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Инсулин в день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulín za deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin per dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Günlük insülin" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Інсулін на день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin mỗi ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日胰岛素" + } + } + } + }, + "Insulin per day, average %1$@" : { + "comment" : "VoiceOver label for the Trends Insulin per day chart. (1: average insulin per day, e.g. 32.1 U)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الأنسولين يوميًا، المتوسط %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulín za den, průměr %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin pr. dag, gennemsnit %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin pro Tag, Durchschnitt %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina por día, promedio %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuliini päivässä, keskiarvo %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuline par jour, moyenne %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אינסולין ליום, ממוצע %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulin naponta, átlag %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina al giorno, media %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "1日のインスリン量、平均 %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin per dag, gjennomsnitt %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuline per dag, gemiddeld %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina dziennie, średnio %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina por dia, média %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulină pe zi, medie %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Инсулин в день, в среднем %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulín za deň, priemer %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin per dag, medel %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Günlük insülin, ortalama %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Інсулін на день, у середньому %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin mỗi ngày, trung bình %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日胰岛素,平均 %1$@" + } + } + } + }, + "Insulin per day, weekly average, average %1$@" : { + "comment" : "VoiceOver label for the Trends Insulin per day chart when bars are weekly averages. (1: average insulin per day, e.g. 32.1 U)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الأنسولين يوميًا، متوسط أسبوعي، المتوسط %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulín za den, týdenní průměr, průměr %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin pr. dag, ugegennemsnit, gennemsnit %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin pro Tag, Wochendurchschnitt, Durchschnitt %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina por día, promedio semanal, promedio %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuliini päivässä, viikkokeskiarvo, keskiarvo %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuline par jour, moyenne hebdomadaire, moyenne %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אינסולין ליום, ממוצע שבועי, ממוצע %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulin naponta, heti átlag, átlag %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina al giorno, media settimanale, media %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "1日のインスリン量、週平均、平均 %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin per dag, ukesgjennomsnitt, gjennomsnitt %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuline per dag, weekgemiddelde, gemiddeld %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina dziennie, średnia tygodniowa, średnio %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina por dia, média semanal, média %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulină pe zi, medie săptămânală, medie %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Инсулин в день, среднее за неделю, в среднем %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulín za deň, týždenný priemer, priemer %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin per dag, veckomedel, medel %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Günlük insülin, haftalık ortalama, ortalama %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Інсулін на день, середнє за тиждень, у середньому %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin mỗi ngày, trung bình theo tuần, trung bình %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日胰岛素,按周平均,平均 %1$@" + } + } + } + }, + "Insulin timeline, %1$@, bolus %2$@, basal %3$@, carbs %4$@" : { + "comment" : "VoiceOver summary of the day's insulin timeline chart with no glucose readings. (1: weekday and date; 2: bolus total, e.g. 12.5 U; 3: basal total; 4: carbs total, e.g. 140 g)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مخطط الأنسولين، %1$@، Bolus %2$@، القاعدية %3$@، الكارب %4$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Časová osa inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kulhydrater %4$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin-Zeitleiste, %1$@, Bolus %2$@, Basal %3$@, KH %4$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cronología de insulina, %1$@, bolo %2$@, basal %3$@, carbohidratos %4$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuliinin aikajana, %1$@, bolus %2$@, basaali %3$@, hiilihydraatit %4$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chronologie de l'insuline, %1$@, bolus %2$@, basal %3$@, glucides %4$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ציר זמן אינסולין, %1$@, Bolus %2$@, Basal %3$@, פחמימות %4$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulin idővonal, %1$@, bólus %2$@, basal %3$@, szénhidrát %4$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cronologia insulina, %1$@, bolo %2$@, basale %3$@, carboidrati %4$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "インスリンのタイムライン、%1$@、ボーラス %2$@、ベーサル %3$@、カーボ %4$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, karbohydrater %4$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulinetijdlijn, %1$@, bolus %2$@, basaal %3$@, koolhydraten %4$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oś czasu insuliny, %1$@, bolus %2$@, baza %3$@, węglowodany %4$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Linha do tempo de insulina, %1$@, bolus %2$@, basal %3$@, carbs %4$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cronologie insulină, %1$@, bolus %2$@, bazală %3$@, carbohidrați %4$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Хронология инсулина, %1$@, болюс %2$@, базал %3$@, углеводы %4$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Časová os inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kolhydrater %4$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "İnsülin zaman çizelgesi, %1$@, bolus %2$@, bazal %3$@, karb %4$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Хронологія інсуліну, %1$@, болюс %2$@, базал %3$@, вуглеводи %4$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dòng thời gian insulin, %1$@, Bolus %2$@, Basal %3$@, Carbs %4$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "胰岛素时间线,%1$@,大剂量 %2$@,基础 %3$@,碳水 %4$@" + } + } + } + }, + "Insulin timeline, %1$@, bolus %2$@, basal %3$@, carbs %4$@, latest glucose %5$@ %6$@" : { + "comment" : "VoiceOver summary of the day's insulin timeline chart. (1: weekday and date; 2: bolus total, e.g. 12.5 U; 3: basal total; 4: carbs total, e.g. 140 g; 5: latest glucose value; 6: glucose unit, mmol/L or mg/dL)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مخطط الأنسولين، %1$@، Bolus %2$@، القاعدية %3$@، الكارب %4$@، آخر قراءة سكر %5$@ %6$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Časová osa inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@, poslední glukóza %5$@ %6$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kulhydrater %4$@, seneste glukose %5$@ %6$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin-Zeitleiste, %1$@, Bolus %2$@, Basal %3$@, KH %4$@, letzter Blutzucker %5$@ %6$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cronología de insulina, %1$@, bolo %2$@, basal %3$@, carbohidratos %4$@, última glucosa %5$@ %6$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuliinin aikajana, %1$@, bolus %2$@, basaali %3$@, hiilihydraatit %4$@, viimeisin glukoosi %5$@ %6$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chronologie de l'insuline, %1$@, bolus %2$@, basal %3$@, glucides %4$@, dernière glycémie %5$@ %6$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ציר זמן אינסולין, %1$@, Bolus %2$@, Basal %3$@, פחמימות %4$@, סוכר אחרון %5$@ %6$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulin idővonal, %1$@, bólus %2$@, basal %3$@, szénhidrát %4$@, legutóbbi glükóz %5$@ %6$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cronologia insulina, %1$@, bolo %2$@, basale %3$@, carboidrati %4$@, ultima glicemia %5$@ %6$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "インスリンのタイムライン、%1$@、ボーラス %2$@、ベーサル %3$@、カーボ %4$@、最新の血糖値 %5$@ %6$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, karbohydrater %4$@, siste blodsukker %5$@ %6$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulinetijdlijn, %1$@, bolus %2$@, basaal %3$@, koolhydraten %4$@, laatste glucose %5$@ %6$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oś czasu insuliny, %1$@, bolus %2$@, baza %3$@, węglowodany %4$@, ostatnia glukoza %5$@ %6$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Linha do tempo de insulina, %1$@, bolus %2$@, basal %3$@, carbs %4$@, última glicose %5$@ %6$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cronologie insulină, %1$@, bolus %2$@, bazală %3$@, carbohidrați %4$@, ultima glicemie %5$@ %6$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Хронология инсулина, %1$@, болюс %2$@, базал %3$@, углеводы %4$@, последняя глюкоза %5$@ %6$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Časová os inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@, posledná glykémia %5$@ %6$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kolhydrater %4$@, senaste glukos %5$@ %6$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "İnsülin zaman çizelgesi, %1$@, bolus %2$@, bazal %3$@, karb %4$@, son KŞ %5$@ %6$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Хронологія інсуліну, %1$@, болюс %2$@, базал %3$@, вуглеводи %4$@, остання глюкоза %5$@ %6$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dòng thời gian insulin, %1$@, Bolus %2$@, Basal %3$@, Carbs %4$@, đường huyết mới nhất %5$@ %6$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "胰岛素时间线,%1$@,大剂量 %2$@,基础 %3$@,碳水 %4$@,最新血糖 %5$@ %6$@" + } + } + } + }, + "Invalid future glucose" : { + "comment" : "Reason Loop could not run: a glucose reading had a timestamp in the future", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "قراءة سكر مستقبلية غير صالحة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glukóza s budoucím časem" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ugyldig glukosemåling i fremtiden" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ungültiger Blutzuckerwert in der Zukunft" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glucosa futura no válida" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Virheellinen tulevaisuuden glukoosiarvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glycémie future non valide" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קריאת סוכר עתידית לא תקינה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Érvénytelen, jövőbeli glükózérték" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glicemia futura non valida" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "未来時刻の無効な血糖値" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ugyldig blodsukkermåling i fremtiden" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ongeldige glucosewaarde in de toekomst" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glukoza z przyszłą datą" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glicose futura inválida" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glicemie invalidă, din viitor" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Глюкоза с временем в будущем" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glykémia s budúcim časom" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ogiltigt glukosvärde i framtiden" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geçersiz, ileri tarihli KŞ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Глюкоза з часом у майбутньому" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đường huyết có thời gian trong tương lai không hợp lệ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无效的未来血糖" + } + } + } + }, + "IOB" : { + "comment" : "Abbreviation for insulin on board (active insulin) Abbreviation for insulin on board; label of a tile on the Now screen", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "IA" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB" + } + } + } + }, + "IOB (U, left)" : { + "comment" : "Chart legend: insulin on board in units, on the left axis", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (وحدة، يسار)" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (U, vlevo)" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (E, venstre)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (IE, links)" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (U, izquierda)" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (U, vasen)" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "IA (U, gauche)" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (U, שמאל)" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (U, bal)" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (U, sinistra)" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB(U、左)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (E, venstre)" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (E, links)" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (J, lewa oś)" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (U, esquerda)" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (U, stânga)" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (Ед, слева)" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (j, vľavo)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (E, vänster)" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (Ü, sol)" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (U, ліворуч)" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB (U, bên trái)" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "IOB(U,左轴)" + } + } + } + }, + "Limited" : { + "comment" : "Trends Loop status: time closed loop was on but Loop wasn't completing runs", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "محدود" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Omezeno" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Begrænset" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eingeschränkt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limitado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rajoitettu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limitée" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מוגבל" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Korlátozott" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limitato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "制限あり" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Begrenset" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beperkt" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ograniczona" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limitado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Limitat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ограничен" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obmedzená" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Begränsad" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sınırlı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Обмежено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hạn chế" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "受限" + } + } + } + }, + "Loop active" : { + "comment" : "Trends summary tile: share of the period Loop spent in closed loop", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop نشط" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aktivní" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aktiv" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aktiv" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop activo" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aktiivinen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop actif" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop פעיל" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aktív" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop attivo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop稼働" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aktiv" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop actief" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aktywny" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop ativo" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop activ" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop активен" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aktívny" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aktiv" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aktif" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop активний" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop hoạt động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop 运行" + } + } + } + }, + "Loop didn't run" : { + "comment" : "Reason shown for a stretch of time when Loop did not complete a run and recorded no error", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لم يعمل Loop" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop neproběhl" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop kørte ikke" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop ist nicht gelaufen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop no se ejecutó" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop ei ajanut" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop ne s'est pas exécuté" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop לא רץ" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "A Loop nem futott" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop non eseguito" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopが実行されませんでした" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop kjørte ikke" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop heeft niet gedraaid" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop nie działał" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "O Loop não executou" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop nu a rulat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop не запускался" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop neprebehol" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop kördes inte" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop çalışmadı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop не запускався" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop không chạy" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop 未运行" + } + } + } + }, + "Loop only works out active insulin and carbs for the days it keeps" : { + "comment" : "Day page placeholder for active insulin and carbs on a day older than Loop's 7-day history", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يحسب Loop الأنسولين والكارب النشطين فقط للأيام التي يحتفظ بها" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop počítá aktivní inzulín a sacharidy jen pro dny, které uchovává" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop beregner kun aktivt insulin og kulhydrater for de dage, den gemmer" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop berechnet aktives Insulin und aktive KH nur für die gespeicherten Tage" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop solo calcula la insulina y los carbohidratos activos de los días que guarda" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop laskee aktiivisen insuliinin ja hiilihydraatit vain säilyttämilleen päiville" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop calcule l'insuline et les glucides actifs uniquement pour les jours qu'il conserve" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop מחשב אינסולין ופחמימות פעילים רק עבור הימים שהוא שומר" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "A Loop csak a megőrzött napokra számolja ki az aktív inzulint és szénhidrátot" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop calcola insulina e carboidrati attivi solo per i giorni che conserva" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopが残存インスリンと残存糖質を計算するのは、保持している日のみです" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop beregner bare aktivt insulin og karbohydrater for dagene den lagrer" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop berekent actieve insuline en koolhydraten alleen voor de dagen die het bewaart" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop oblicza aktywną insulinę i węglowodany tylko dla przechowywanych dni" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "O Loop só calcula a insulina e os carboidratos ativos dos dias que guarda" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop calculează insulina și carbohidrații activi doar pentru zilele pe care le păstrează" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop рассчитывает активный инсулин и углеводы только за хранимые дни" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop počíta aktívny inzulín a sacharidy len pre dni, ktoré uchováva" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop beräknar bara aktivt insulin och kolhydrater för de dagar som sparas" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aktif insülini ve karbı yalnızca sakladığı günler için hesaplar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop розраховує активний інсулін і вуглеводи лише за дні, які зберігає" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop chỉ tính insulin và Carbs còn hoạt động cho những ngày nó lưu" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop 只为其保留的日期计算活性胰岛素和碳水" + } + } + } + }, + "Loop status" : { + "comment" : "Trends card title: how long Loop spent in closed loop, limited or open loop", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حالة Loop" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stav Loop" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-status" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-Status" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estado de Loop" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopin tila" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "État de Loop" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מצב Loop" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop állapota" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stato di Loop" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopの状態" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-status" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopstatus" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stan Loop" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Status do Loop" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Starea Loop" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Статус Loop" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stav Loop" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopstatus" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop durumu" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Стан Loop" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trạng thái Loop" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop 状态" + } + } + } + }, + "Loop status is recorded from when this version of Loop first ran." : { + "comment" : "Trends Loop status footnote when there is no Loop status history yet", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تُسجَّل حالة Loop منذ أول تشغيل لهذا الإصدار من Loop." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stav Loop se zaznamenává od prvního spuštění této verze Loop." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-status registreres fra første gang denne version af Loop kørte." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Loop-Status wird ab dem ersten Start dieser Loop-Version aufgezeichnet." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El estado de Loop se registra desde la primera vez que se ejecutó esta versión de Loop." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopin tila tallennetaan siitä lähtien, kun tämä Loopin versio käynnistettiin ensimmäisen kerran." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'état de Loop est enregistré depuis le premier lancement de cette version de Loop." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מצב Loop נרשם מאז ההפעלה הראשונה של גרסה זו של Loop." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "A Loop állapota a Loop ezen verziójának első futtatásától kerül rögzítésre." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lo stato di Loop è registrato dal primo avvio di questa versione di Loop." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopの状態は、このバージョンのLoopを初めて起動した時点から記録されます。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-status registreres fra første gang denne versjonen av Loop kjørte." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De loopstatus wordt vastgelegd vanaf de eerste keer dat deze versie van Loop draaide." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stan Loop jest zapisywany od pierwszego uruchomienia tej wersji Loop." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "O status do Loop é registrado desde a primeira vez que esta versão do Loop foi executada." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Starea Loop este înregistrată de la prima rulare a acestei versiuni de Loop." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Статус Loop записывается с первого запуска этой версии Loop." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stav Loop sa zaznamenáva od prvého spustenia tejto verzie Loop." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopstatus registreras från när den här versionen av Loop först kördes." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop durumu, Loop'un bu sürümünün ilk çalıştığı andan itibaren kaydedilir." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Стан Loop записується з першого запуску цієї версії Loop." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trạng thái Loop được ghi lại từ khi phiên bản Loop này chạy lần đầu." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop 状态从此版本 Loop 首次运行时开始记录。" + } + } + } + }, + "Loop trends" : { + "comment" : "Title of the exported Trends PDF report", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اتجاهات Loop" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-tendenser" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-Trends" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendencias de Loop" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trendit" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendances Loop" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מגמות Loop" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trendek" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendenze Loop" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopトレンド" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trender" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trends" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendências do Loop" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendințe Loop" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тренды Loop" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trender" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop eğilimleri" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тренди Loop" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xu hướng Loop" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop 趋势" + } + } + } + }, + "Loop trends %1$@" : { + "comment" : "File name of an exported Trends report or CSV, without the extension. (1: date range of the period)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اتجاهات Loop %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-tendenser %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-Trends %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendencias de Loop %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trendit %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendances Loop %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מגמות Loop %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trendek %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendenze Loop %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopトレンド %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trender %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trends %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendências do Loop %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendințe Loop %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тренды Loop %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trender %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop eğilimleri %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тренди Loop %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xu hướng Loop %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop 趋势 %1$@" + } + } + } + }, + "Manual %1$@" : { + "comment" : "Basal and bolus: bolus the person gave. (1: units, e.g. 16.2 U)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يدوي %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ručně %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuaalinen %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ידני %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kézi %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuale %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "手動 %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Handmatig %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ręcznie %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вручную %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ručne %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вручну %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thủ công %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "手动 %1$@" + } + } + } + }, + "Median" : { + "comment" : "Trends Daily pattern legend: the median glucose line", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الوسيط" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medián" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Median" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Median" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediana" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediaani" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Médiane" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חציון" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medián" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediana" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "中央値" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Median" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediaan" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediana" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediana" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediană" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Медиана" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medián" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Median" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medyan" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Медіана" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trung vị" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "中位数" + } + } + } + }, + "Missing data error" : { + "comment" : "Reason Loop could not run: data it needs was missing", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "خطأ بيانات مفقودة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chyba chybějících dat" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fejl: manglende data" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fehler: fehlende Daten" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error de datos faltantes" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tietoja puuttuu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur de données manquantes" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שגיאת נתונים חסרים" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hiányzó adat" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore dati mancanti" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "データ欠落エラー" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Feil: manglende data" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fout: ontbrekende gegevens" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Błąd brakujących danych" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erro de dados ausentes" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eroare: date lipsă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ошибка: нет данных" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chyba chýbajúcich údajov" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data saknas" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eksik veri hatası" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Помилка: немає даних" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lỗi thiếu dữ liệu" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "数据缺失错误" + } + } + } + }, + "Needs 10 days of CGM" : { + "comment" : "Trends GMI tile detail when fewer than 10 days of CGM data are available", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يحتاج إلى 10 أيام من CGM" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vyžaduje 10 dní CGM" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kræver 10 dages CGM" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benötigt 10 Tage CGM" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Requiere 10 días de CGM" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaatii 10 päivää CGM-dataa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nécessite 10 jours de CGM" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "נדרשים 10 ימי CGM" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "10 nap CGM-adat szükséges" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servono 10 giorni di CGM" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGMデータが10日分必要" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Krever 10 dager med CGM" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vereist 10 dagen CGM" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wymaga 10 dni CGM" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Requer 10 dias de CGM" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Necesită 10 zile de CGM" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нужно 10 дней CGM" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vyžaduje 10 dní CGM" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kräver 10 dagars CGM" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "10 günlük CGM gerekir" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Потрібно 10 днів CGM" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cần 10 ngày dữ liệu CGM" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "需要 10 天 CGM 数据" + } + } + } + }, + "Next day" : { + "comment" : "Accessibility label for the day page button that steps forward a day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اليوم التالي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Další den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Næste dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nächster Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Día siguiente" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seuraava päivä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jour suivant" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "היום הבא" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Következő nap" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giorno successivo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "翌日" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neste dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Volgende dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Następny dzień" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Próximo dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ziua următoare" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Следующий день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ďalší deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nästa dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sonraki gün" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Наступний день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ngày sau" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "后一天" + } + } + } + }, + "Next period" : { + "comment" : "VoiceOver label for the Trends button that moves forward one period", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الفترة التالية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Další období" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Næste periode" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nächster Zeitraum" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Período siguiente" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seuraava jakso" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Période suivante" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "התקופה הבאה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Következő időszak" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Periodo successivo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "次の期間" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neste periode" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Volgende periode" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Następny okres" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Próximo período" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perioada următoare" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Следующий период" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ďalšie obdobie" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nästa period" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sonraki dönem" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Наступний період" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Khoảng thời gian sau" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "下一时段" + } + } + } + }, + "No active insulin or carbs on this day" : { + "comment" : "Day page placeholder when there was no insulin or carbs on board all day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا يوجد أنسولين أو كارب نشط في هذا اليوم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "V tento den nebyl žádný aktivní inzulín ani sacharidy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intet aktivt insulin eller kulhydrater denne dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kein aktives Insulin und keine aktiven KH an diesem Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin insulina ni carbohidratos activos este día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei aktiivista insuliinia tai hiilihydraatteja tänä päivänä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune insuline ni glucides actifs ce jour-là" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין אינסולין או פחמימות פעילים ביום זה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ezen a napon nem volt aktív inzulin vagy szénhidrát" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna insulina o carboidrati attivi in questo giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "この日は残存インスリンも残存糖質もありません" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen aktivt insulin eller karbohydrater denne dagen" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen actieve insuline of koolhydraten op deze dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak aktywnej insuliny i węglowodanów tego dnia" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem insulina ou carboidratos ativos neste dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fără insulină sau carbohidrați activi în această zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "В этот день не было активного инсулина или углеводов" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "V tento deň nebol žiadny aktívny inzulín ani sacharidy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inget aktivt insulin eller kolhydrater denna dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bu gün aktif insülin veya karb yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цього дня не було активного інсуліну чи вуглеводів" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có insulin hoặc Carbs còn hoạt động trong ngày này" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "这一天没有活性胰岛素或碳水" + } + } + } + }, + "No CGM data" : { + "comment" : "Trends Average tile detail when the period has no glucose readings", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد بيانات CGM" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žádná data CGM" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine CGM-Daten" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin datos del CGM" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei CGM-dataa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune donnée CGM" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין נתוני CGM" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs CGM-adat" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun dato CGM" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGMデータなし" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen CGM-gegevens" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak danych CGM" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem dados do CGM" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fără date CGM" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет данных CGM" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žiadne údaje CGM" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga CGM-data" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM verisi yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає даних CGM" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có dữ liệu CGM" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无 CGM 数据" + } + } + } + }, + "No CGM data %1$@ – %2$@" : { + "comment" : "Chart note: a gap in glucose readings. (1: start time; 2: end time)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد بيانات CGM %1$@ – %2$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žádná data CGM %1$@ – %2$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data %1$@ – %2$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine CGM-Daten %1$@ – %2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin datos del CGM %1$@ – %2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei CGM-dataa %1$@ – %2$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune donnée CGM %1$@ – %2$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין נתוני CGM %1$@ – %2$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs CGM-adat %1$@ – %2$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun dato CGM %1$@ – %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGMデータなし %1$@ – %2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data %1$@ – %2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen CGM-gegevens %1$@ – %2$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak danych CGM %1$@ – %2$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem dados do CGM %1$@ – %2$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fără date CGM %1$@ – %2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет данных CGM %1$@ – %2$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žiadne údaje CGM %1$@ – %2$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga CGM-data %1$@ – %2$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM verisi yok %1$@ – %2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає даних CGM %1$@ – %2$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có dữ liệu CGM %1$@ – %2$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无 CGM 数据 %1$@ – %2$@" + } + } + } + }, + "No CGM data %1$@ – %2$@ and %3$@" : { + "comment" : "Chart note: the longest gap in glucose readings, plus the shorter ones. (1: start time; 2: end time; 3: number of shorter gaps, e.g. 2 shorter gaps)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد بيانات CGM %1$@ – %2$@ و %3$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žádná data CGM %1$@ – %2$@ a %3$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data %1$@ – %2$@ og %3$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine CGM-Daten %1$@ – %2$@ und %3$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin datos del CGM %1$@ – %2$@ y %3$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei CGM-dataa %1$@ – %2$@ ja %3$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune donnée CGM %1$@ – %2$@ et %3$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין נתוני CGM %1$@ – %2$@ ו-%3$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs CGM-adat %1$@ – %2$@, és %3$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun dato CGM %1$@ – %2$@ e %3$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGMデータなし %1$@ – %2$@(%3$@)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data %1$@ – %2$@ og %3$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen CGM-gegevens %1$@ – %2$@ en %3$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak danych CGM %1$@ – %2$@ oraz %3$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem dados do CGM %1$@ – %2$@ e %3$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fără date CGM %1$@ – %2$@ și %3$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет данных CGM %1$@ – %2$@ и ещё %3$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žiadne údaje CGM %1$@ – %2$@ a %3$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga CGM-data %1$@ – %2$@ och %3$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM verisi yok %1$@ – %2$@ ve %3$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає даних CGM %1$@ – %2$@ і ще %3$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có dữ liệu CGM %1$@ – %2$@ và %3$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无 CGM 数据 %1$@ – %2$@,%3$@" + } + } + } + }, + "No CGM data in this period" : { + "comment" : "Trends chart placeholder when the period has no glucose readings", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد بيانات CGM في هذه الفترة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žádná data CGM v tomto období" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data i denne periode" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine CGM-Daten in diesem Zeitraum" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin datos del CGM en este período" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei CGM-dataa tällä jaksolla" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune donnée CGM sur cette période" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין נתוני CGM בתקופה זו" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs CGM-adat ebben az időszakban" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun dato CGM in questo periodo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "この期間のCGMデータなし" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data i denne perioden" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen CGM-gegevens in deze periode" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak danych CGM w tym okresie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem dados do CGM neste período" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fără date CGM în această perioadă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет данных CGM за этот период" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žiadne údaje CGM v tomto období" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga CGM-data under denna period" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bu dönemde CGM verisi yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає даних CGM за цей період" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có dữ liệu CGM trong khoảng thời gian này" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "此时段无 CGM 数据" + } + } + } + }, + "No CGM data on this day" : { + "comment" : "Chart note: there are no glucose readings for the whole day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد بيانات CGM في هذا اليوم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žádná data CGM v tento den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data denne dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine CGM-Daten an diesem Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin datos del CGM este día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei CGM-dataa tänä päivänä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune donnée CGM ce jour-là" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין נתוני CGM ביום זה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs CGM-adat ezen a napon" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun dato CGM in questo giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "この日のCGMデータなし" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data denne dagen" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen CGM-gegevens op deze dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak danych CGM tego dnia" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem dados do CGM neste dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fără date CGM în această zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет данных CGM за этот день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žiadne údaje CGM v tento deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga CGM-data denna dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bu günde CGM verisi yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає даних CGM за цей день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có dữ liệu CGM trong ngày này" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "这一天无 CGM 数据" + } + } + } + }, + "No CGM data yet today" : { + "comment" : "Chart note: there are no glucose readings so far today", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد بيانات CGM اليوم حتى الآن" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dnes zatím žádná data CGM" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data endnu i dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Heute noch keine CGM-Daten" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aún no hay datos del CGM hoy" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei vielä CGM-dataa tänään" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune donnée CGM aujourd'hui pour l'instant" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין עדיין נתוני CGM היום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ma még nincs CGM-adat" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ancora nessun dato CGM oggi" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "今日はまだCGMデータなし" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data ennå i dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vandaag nog geen CGM-gegevens" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dziś jeszcze brak danych CGM" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ainda sem dados do CGM hoje" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Încă nu există date CGM azi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сегодня пока нет данных CGM" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dnes zatiaľ žiadne údaje CGM" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga CGM-data än i dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bugün henüz CGM verisi yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сьогодні ще немає даних CGM" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hôm nay chưa có dữ liệu CGM" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "今天尚无 CGM 数据" + } + } + } + }, + "No gaps: Loop ran closed loop all through this period." : { + "comment" : "Trends Gaps list when Loop was in closed loop for the whole period", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا فجوات: عمل Loop بالحلقة المغلقة طوال هذه الفترة." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žádné výpadky: Loop běžel v uzavřené smyčce celé období." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen huller: Loop kørte lukket Loop i hele perioden." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Lücken: Loop lief im gesamten Zeitraum im Closed Loop." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin interrupciones: Loop funcionó en circuito cerrado durante todo el período." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei katkoja: Loop toimi suljetulla säädöllä koko jakson ajan." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune interruption : Loop est resté en boucle fermée toute la période." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין פערים: Loop פעל ב-Closed Loop לאורך כל התקופה." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs kimaradás: a Loop az egész időszakban zárt hurokban futott." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna interruzione: Loop è rimasto in loop chiuso per tutto il periodo." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "中断なし:この期間中、Loopは常にクローズドループで動作しました。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen hull: Loop kjørte lukket Loop gjennom hele perioden." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen onderbrekingen: Loop draaide de hele periode in actieve loop." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak przerw: Loop działał w pętli zamkniętej przez cały okres." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem interrupções: o Loop ficou em ciclo fechado durante todo o período." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fără întreruperi: Loop a rulat în buclă închisă toată perioada." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пропусков нет: Loop работал в замкнутом цикле весь период." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žiadne výpadky: Loop bežal v uzavretej slučke celé obdobie." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga luckor: Loop körde sluten loop under hela perioden." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boşluk yok: Loop bu dönem boyunca kapalı döngüde çalıştı." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пропусків немає: Loop працював у замкнутому циклі весь період." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không gián đoạn: Loop chạy vòng lặp kín suốt khoảng thời gian này." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无中断:此时段内 Loop 一直以闭环运行。" + } + } + } + }, + "No insulin recorded in this period" : { + "comment" : "Trends Insulin per day chart placeholder when no insulin was delivered in the period", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لم يُسجَّل أنسولين في هذه الفترة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "V tomto období nebyl zaznamenán žádný inzulín" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intet insulin registreret i denne periode" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kein Insulin in diesem Zeitraum erfasst" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se registró insulina en este período" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei kirjattua insuliinia tällä jaksolla" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune insuline enregistrée sur cette période" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא נרשם אינסולין בתקופה זו" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs rögzített inzulin ebben az időszakban" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna insulina registrata in questo periodo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "この期間のインスリン記録なし" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen insulin registrert i denne perioden" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen insuline vastgelegd in deze periode" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak zapisanej insuliny w tym okresie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nenhuma insulina registrada neste período" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nicio insulină înregistrată în această perioadă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "За этот период инсулин не записан" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "V tomto období nebol zaznamenaný žiadny inzulín" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inget insulin registrerat under denna period" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bu dönemde kaydedilmiş insülin yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "За цей період інсулін не записано" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có insulin được ghi lại trong khoảng thời gian này" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "此时段内无胰岛素记录" + } + } + } + }, + "No loop runs kept" : { + "comment" : "Trends Loop active tile detail when no Loop run history is available for the period", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد تشغيلات Loop محفوظة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žádné uložené běhy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen Loop-kørsler gemt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Loop-Durchläufe gespeichert" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se guardaron ejecuciones de Loop" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei tallennettuja Loop-ajoja" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune exécution de Loop conservée" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא נשמרו הרצות Loop" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs megőrzött Loop-futás" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna esecuzione di Loop conservata" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop実行記録なし" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen Loop-kjøringer lagret" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen loop-runs bewaard" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak zapisanych cykli" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nenhuma execução do Loop guardada" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nu sunt păstrate rulări Loop" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет сохранённых запусков" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žiadne uložené behy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga loopkörningar sparade" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saklanan Loop çalışması yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає збережених запусків" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không lưu lần chạy Loop nào" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无 Loop 运行记录" + } + } + } + }, + "No loop runs recorded in this period" : { + "comment" : "Trends Loop status placeholder when there is no Loop run history for the period", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لم تُسجَّل تشغيلات Loop في هذه الفترة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "V tomto období nebyly zaznamenány žádné běhy Loop" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen Loop-kørsler registreret i denne periode" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Loop-Durchläufe in diesem Zeitraum erfasst" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se registraron ejecuciones de Loop en este período" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei Loop-ajoja tällä jaksolla" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune exécution de Loop enregistrée sur cette période" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא נרשמו הרצות Loop בתקופה זו" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs rögzített Loop-futás ebben az időszakban" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna esecuzione di Loop registrata in questo periodo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "この期間のLoop実行記録なし" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen Loop-kjøringer registrert i denne perioden" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen loop-runs vastgelegd in deze periode" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak zapisanych cykli Loop w tym okresie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nenhuma execução do Loop registrada neste período" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nicio rulare Loop înregistrată în această perioadă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "За этот период запуски Loop не записаны" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "V tomto období neboli zaznamenané žiadne behy Loop" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga loopkörningar registrerade under denna period" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bu dönemde kaydedilmiş Loop çalışması yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "За цей період запуски Loop не записано" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có lần chạy Loop nào được ghi lại trong khoảng thời gian này" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "此时段内无 Loop 运行记录" + } + } + } + }, + "Not enough days for a pattern: %1$@ of 5" : { + "comment" : "Trends Daily pattern footnote when fewer than 5 days have readings; the chart shows raw readings instead. (1: days with readings)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أيام غير كافية لنمط: %1$@ من 5" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Málo dní pro profil: %1$@ z 5" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ikke nok dage til et mønster: %1$@ af 5" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nicht genug Tage für ein Profil: %1$@ von 5" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No hay suficientes días para un patrón: %1$@ de 5" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Liian vähän päiviä profiiliin: %1$@ / 5" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pas assez de jours pour un profil : %1$@ sur 5" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין מספיק ימים לדפוס: %1$@ מתוך 5" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs elég nap a mintázathoz: %1$@ / 5" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giorni insufficienti per un andamento: %1$@ su 5" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "パターン作成には日数不足:%1$@ / 5日" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ikke nok dager for et mønster: %1$@ av 5" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niet genoeg dagen voor een patroon: %1$@ van 5" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Za mało dni na profil: %1$@ z 5" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dias insuficientes para um padrão: %1$@ de 5" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prea puține zile pentru un tipar: %1$@ din 5" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Мало дней для профиля: %1$@ из 5" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Málo dní pre profil: %1$@ z 5" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "För få dagar för ett mönster: %1$@ av 5" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Örüntü için yeterli gün yok: %1$@ / 5" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замало днів для профілю: %1$@ з 5" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không đủ ngày để tạo mẫu: %1$@ / 5" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "天数不足以生成模式:%1$@ / 5" + } + } + } + }, + "Now" : { + "comment" : "Title at the top of the Now screen, showing current glucose and Loop status Title of the Now tab in the redesigned interface, showing current glucose and Loop status", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الآن" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nyní" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jetzt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ahora" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nyt" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Maintenant" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "עכשיו" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Most" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ora" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "現在" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nå" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nu" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Teraz" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agora" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acum" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сейчас" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Teraz" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nu" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Şimdi" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Зараз" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hiện tại" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "当前" + } + } + } + }, + "Open loop / manual" : { + "comment" : "Trends Loop status: time closed loop was switched off", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حلقة مفتوحة / يدوي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otevřená smyčka / ručně" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Åbent Loop / manuel" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open Loop / manuell" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Circuito abierto / manual" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avoin säätö / manuaalinen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boucle ouverte / manuel" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open Loop / ידני" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nyitott hurok / kézi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop aperto / manuale" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "オープンループ/手動" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Åpen Loop / manuell" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open loop / handmatig" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pętla otwarta / ręcznie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ciclo aberto / manual" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buclă deschisă / manual" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Открытый цикл / вручную" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otvorená slučka / ručne" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Öppen loop / manuell" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Açık döngü / manuel" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Відкритий цикл / вручну" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vòng lặp mở / thủ công" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "开环 / 手动" + } + } + } + }, + "Opens the full day" : { + "comment" : "VoiceOver hint for the Today so far card on the Now screen", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يفتح اليوم كاملًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otevře celý den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Åbner hele dagen" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Öffnet den ganzen Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abre el día completo" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avaa koko päivän" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvre la journée complète" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "פותח את היום המלא" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Megnyitja a teljes napot" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apre il giorno completo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "1日全体を開きます" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Åpner hele dagen" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opent de hele dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otwiera cały dzień" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abre o dia completo" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deschide ziua completă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Открывает весь день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otvorí celý deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Öppnar hela dagen" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tüm günü açar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Відкриває весь день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mở toàn bộ ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "打开全天视图" + } + } + } + }, + "PDF report" : { + "comment" : "Trends export menu item: share a PDF report of the period", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تقرير PDF" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zpráva PDF" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "PDF-rapport" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "PDF-Bericht" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informe PDF" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "PDF-raportti" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rapport PDF" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "דוח PDF" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "PDF-jelentés" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Report PDF" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "PDFレポート" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "PDF-rapport" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "PDF-rapport" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Raport PDF" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Relatório PDF" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Raport PDF" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отчёт PDF" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Správa PDF" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "PDF-rapport" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PDF raporu" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Звіт PDF" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Báo cáo PDF" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "PDF 报告" + } + } + } + }, + "per day, weekly average" : { + "comment" : "Trends Insulin per day card subtitle when each bar is a week's average daily total", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يوميًا، متوسط أسبوعي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "za den, týdenní průměr" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "pr. dag, ugegennemsnit" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "pro Tag, Wochendurchschnitt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "por día, promedio semanal" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "päivässä, viikkokeskiarvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "par jour, moyenne hebdomadaire" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ליום, ממוצע שבועי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "naponta, heti átlag" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "al giorno, media settimanale" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "1日あたり、週平均" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "per dag, ukesgjennomsnitt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "per dag, weekgemiddelde" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "dziennie, średnia tygodniowa" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "por dia, média semanal" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "pe zi, medie săptămânală" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "в день, среднее за неделю" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "za deň, týždenný priemer" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "per dag, veckomedel" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "günlük, haftalık ortalama" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "на день, середнє за тиждень" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "mỗi ngày, trung bình theo tuần" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日,按周平均" + } + } + } + }, + "Previous day" : { + "comment" : "Accessibility label for the day page button that steps back a day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اليوم السابق" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Předchozí den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forrige dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorheriger Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Día anterior" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edellinen päivä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jour précédent" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "היום הקודם" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Előző nap" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giorno precedente" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "前日" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forrige dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorige dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poprzedni dzień" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dia anterior" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ziua anterioară" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Предыдущий день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predchádzajúci deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Föregående dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Önceki gün" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Попередній день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ngày trước" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "前一天" + } + } + } + }, + "Previous period" : { + "comment" : "VoiceOver label for the Trends button that moves back one period", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الفترة السابقة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Předchozí období" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forrige periode" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorheriger Zeitraum" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Período anterior" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edellinen jakso" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Période précédente" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "התקופה הקודמת" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Előző időszak" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Periodo precedente" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "前の期間" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forrige periode" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorige periode" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poprzedni okres" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Período anterior" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perioada anterioară" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Предыдущий период" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predchádzajúce obdobie" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Föregående period" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Önceki dönem" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Попередній період" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Khoảng thời gian trước" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "上一时段" + } + } + } + }, + "Projected" : { + "comment" : "Chart legend: dashed lines show Loop's projection of insulin and carbs on board", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المتوقع" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Předpověď" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forventet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prognose" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Proyectado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ennuste" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Projeté" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "צפי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Előrevetített" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Previsto" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "予測" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beregnet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verwacht" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prognozowane" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Projetado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Proiectat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Прогнозируемые" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predpoveď" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prognos" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Öngörülen" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Прогнозовані" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dự kiến" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "预计" + } + } + } + }, + "Pump data too old" : { + "comment" : "Reason Loop could not run: the latest pump data was too old", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "بيانات المضخة قديمة جدًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data pumpy jsou příliš stará" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpedata for gamle" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpendaten zu alt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datos de la microinfusadora demasiado antiguos" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpun tiedot liian vanhoja" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Données de la pompe trop anciennes" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "נתוני המשאבה ישנים מדי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Túl régi pumpaadatok" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dati pompa troppo vecchi" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ポンプデータが古すぎます" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpedata for gamle" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompgegevens te oud" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dane pompy zbyt stare" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dados da bomba muito antigos" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Date pompă prea vechi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Данные помпы устарели" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Údaje pumpy sú príliš staré" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpdata för gamla" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompa verisi çok eski" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дані помпи застаріли" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dữ liệu bơm quá cũ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "胰岛素泵数据过旧" + } + } + } + }, + "Pump manager error" : { + "comment" : "Reason Loop could not run: the pump reported an error", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "خطأ في مدير المضخة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chyba správce pumpy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpefejl" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpenfehler" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error del gestor de la microinfusadora" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpunhallinnan virhe" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur du gestionnaire de pompe" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שגיאת מנהל המשאבה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpakezelő-hiba" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore del gestore pompa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ポンプマネージャのエラー" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpefeil" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompfout" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Błąd menedżera pompy" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erro do gerenciador da bomba" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eroare manager pompă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ошибка менеджера помпы" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chyba správcu pumpy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fel i pumphanteraren" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompa yöneticisi hatası" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Помилка менеджера помпи" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lỗi trình quản lý bơm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "胰岛素泵管理器错误" + } + } + } + }, + "Pump suspended" : { + "comment" : "Reason Loop could not run: insulin delivery on the pump was suspended", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المضخة متوقفة مؤقتًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpa pozastavena" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpe suspenderet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpe unterbrochen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Microinfusadora suspendida" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumppu pysäytetty" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompe suspendue" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "המשאבה מושהית" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpa felfüggesztve" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompa sospesa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ポンプ一時停止中" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpe stanset" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pomp onderbroken" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompa wstrzymana" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bomba suspensa" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompă suspendată" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Помпа приостановлена" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpa pozastavená" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pump pausad" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompa askıya alındı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Помпу призупинено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bơm đã tạm ngưng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "胰岛素泵已暂停" + } + } + } + }, + "Recommendation expired" : { + "comment" : "Reason Loop could not run: its dosing recommendation expired before it was used", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "انتهت صلاحية التوصية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doporučení vypršelo" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anbefaling udløbet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Empfehlung abgelaufen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recomendación caducada" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suositus vanhentunut" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recommandation expirée" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "תוקף ההמלצה פג" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lejárt javaslat" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suggerimento scaduto" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "推奨の有効期限切れ" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anbefaling utløpt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Advies verlopen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zalecenie wygasło" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recomendação expirada" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recomandare expirată" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Рекомендация устарела" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Odporúčanie vypršalo" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rekommendationen har gått ut" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Önerinin süresi doldu" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Рекомендація застаріла" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Khuyến nghị đã hết hạn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "建议已过期" + } + } + } + }, + "SD %1$@ · CV %2$@%%" : { + "comment" : "Trends Average tile detail: standard deviation and coefficient of variation of glucose. (1: standard deviation in the glucose unit, 2: coefficient of variation as a percentage number)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@ %%" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · VK %2$@ %%" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "DE %1$@ · CV %2$@%%" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@ %%" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ET %1$@ · CV %2$@ %%" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "DS %1$@ · CV %2$@%%" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@ %%" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "DP %1$@ · CV %2$@%%" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@ %%" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %%%2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + } + } + }, + "Summary" : { + "comment" : "Trends card title: the summary statistics tiles", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الملخص" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Souhrn" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oversigt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Übersicht" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resumen" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteenveto" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Résumé" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "סיכום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Összegzés" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riepilogo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "概要" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oversikt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Overzicht" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podsumowanie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resumo" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rezumat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сводка" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Súhrn" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sammanfattning" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Özet" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підсумок" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tóm tắt" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "摘要" + } + } + } + }, + "Suspended" : { + "comment" : "Chart callout value: insulin delivery was suspended at the touched time Shown in the Basal tile on the Now screen when insulin delivery is suspended", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "متوقف مؤقتًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pozastaveno" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suspenderet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unterbrochen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suspendida" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pysäytetty" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suspendu" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מושהה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Felfüggesztve" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sospesa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "一時停止中" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stanset" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onderbroken" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wstrzymano" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suspensa" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suspendat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Приостановлено" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pozastavené" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pausad" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Askıya alındı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Призупинено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã tạm ngưng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已暂停" + } + } + } + }, + "this day" : { + "comment" : "Trends Basal and bolus card subtitle when the period is one complete day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "هذا اليوم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "tento den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "denne dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "dieser Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "este día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "tämä päivä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ce jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "יום זה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "ez a nap" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "questo giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "この日" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "denne dagen" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "deze dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "ten dzień" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "este dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "această zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "этот день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "tento deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "denna dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "bu gün" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "цей день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "ngày này" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "当天" + } + } + } + }, + "Time" : { + "comment" : "Carb entry row label: the exact time the food was eaten", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الوقت" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Čas" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tidspunkt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uhrzeit" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hora" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aika" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Heure" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שעה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Időpont" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ora" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "時刻" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tidspunkt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tijd" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Godzina" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hora" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ora" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Время" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Čas" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tid" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saat" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Час" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thời gian" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "时间" + } + } + } + }, + "Time in range" : { + "comment" : "Trends card title: share of glucose readings in each range", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الوقت ضمن النطاق" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Čas v rozsahu" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tid i området" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeit im Zielbereich" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo en rango" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aika tavoitealueella" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temps dans la cible" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "זמן בטווח" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tartományban töltött idő" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tempo nell'intervallo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "範囲内時間" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tid i målområdet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tijd binnen bereik" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Czas w zakresie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tempo no alvo" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Timp în interval" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Время в диапазоне" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Čas v rozsahu" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tid inom målområdet" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aralıkta geçen süre" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Час у діапазоні" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thời gian trong khoảng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "目标范围内时间" + } + } + } + }, + "Time in range, daily pattern, insulin per day and Loop status" : { + "comment" : "Trends settings descriptive text", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الوقت ضمن النطاق والنمط اليومي والإنسولين اليومي وحالة Loop" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Čas v rozmezí, denní průběh, inzulin za den a stav Loop" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tid i område, døgnmønster, insulin pr. dag og Loop-status" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeit im Zielbereich, Tagesverlauf, Insulin pro Tag und Loop-Status" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo en rango, patrón diario, insulina por día y estado de Loop" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aika tavoitealueella, päivittäinen kuvio, insuliini päivässä ja Loopin tila" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temps dans la cible, profil quotidien, insuline par jour et état de Loop" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "זמן בטווח, דפוס יומי, אינסולין ליום ומצב Loop" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Céltartományban töltött idő, napi mintázat, napi inzulin és Loop állapot" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tempo nell'intervallo, andamento giornaliero, insulina al giorno e stato di Loop" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "範囲内時間、日内パターン、1日のインスリン、Loopの状態" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tid i målområdet, døgnmønster, insulin per dag og Loop-status" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tijd in bereik, dagpatroon, insuline per dag en loopstatus" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Czas w zakresie, profil dobowy, insulina na dzień i stan Loop" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tempo no alvo, padrão diário, insulina por dia e status do Loop" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Timp în interval, tipar zilnic, insulină pe zi și starea Loop" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Время в диапазоне, суточный профиль, инсулин в день и статус Loop" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Čas v rozmedzí, denný priebeh, inzulín za deň a stav Loop" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tid inom målområdet, dygnsmönster, insulin per dag och Loopstatus" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hedef aralıkta geçen süre, günlük örüntü, günlük insülin ve Loop durumu" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Час у діапазоні, добовий профіль, інсулін на день і стан Loop" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thời gian trong ngưỡng, mẫu hình hằng ngày, insulin mỗi ngày và trạng thái Loop" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "目标范围内时间、每日模式、每日胰岛素和 Loop 状态" + } + } + } + }, + "Today" : { + "comment" : "Day page title when showing today", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اليوم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dnes" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "I dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Heute" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoy" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tänään" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aujourd'hui" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "היום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ma" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oggi" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "今日" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "I dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vandaag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dziś" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoje" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сегодня" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dnes" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "I dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bugün" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сьогодні" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hôm nay" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "今天" + } + } + } + }, + "today so far" : { + "comment" : "Trends Basal and bolus card subtitle when the figures are today's total so far", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اليوم حتى الآن" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "dnes zatím" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "i dag indtil nu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "heute bisher" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "hoy hasta ahora" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "tänään tähän mennessä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "aujourd'hui jusqu'ici" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "היום עד כה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "ma eddig" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "oggi finora" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "今日のこれまで" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "i dag så langt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "vandaag tot nu toe" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "dziś do tej pory" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "hoje até agora" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "azi până acum" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "сегодня на данный момент" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "dnes zatiaľ" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "hittills i dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "bugün şu ana kadar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "сьогодні наразі" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "hôm nay đến giờ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "今日至今" + } + } + } + }, + "Today so far" : { + "comment" : "Title of the card on the Now screen showing today's insulin and carbs", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اليوم حتى الآن" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dnes zatím" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "I dag indtil nu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Heute bisher" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoy hasta ahora" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tänään tähän mennessä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aujourd'hui jusqu'ici" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "היום עד כה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ma eddig" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oggi finora" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "今日のこれまで" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "I dag så langt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vandaag tot nu toe" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dziś do tej pory" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoje até agora" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azi până acum" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сегодня на данный момент" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dnes zatiaľ" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hittills i dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bugün şu ana kadar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сьогодні наразі" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hôm nay đến giờ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "今日至今" + } + } + } + }, + "Total" : { + "comment" : "Basal and bolus total row: basal plus bolus", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الإجمالي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Celkem" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "I alt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesamt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteensä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "סה״כ" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Összesen" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totale" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "合計" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totalt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totaal" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Razem" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всего" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spolu" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totalt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toplam" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Усього" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tổng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "总计" + } + } + } + }, + "Total %1$@" : { + "comment" : "VoiceOver: total insulin. (1: units)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الإجمالي %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Celkem %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "I alt %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesamt %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteensä %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "סה״כ %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Összesen %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totale %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "合計 %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totalt %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totaal %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Razem %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всего %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spolu %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totalt %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toplam %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Усього %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tổng %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "总计 %1$@" + } + } + } + }, + "Total so far" : { + "comment" : "Basal and bolus total row for today, which isn't over yet", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الإجمالي حتى الآن" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Celkem zatím" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "I alt indtil nu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesamt bisher" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total hasta ahora" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteensä tähän mennessä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total jusqu'ici" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "סה״כ עד כה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Összesen eddig" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totale finora" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "これまでの合計" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totalt så langt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totaal tot nu toe" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Razem do tej pory" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total até agora" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total până acum" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всего на данный момент" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spolu zatiaľ" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totalt hittills" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Şu ana kadar toplam" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Усього наразі" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tổng đến giờ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "至今总计" + } + } + } + }, + "Total so far %1$@" : { + "comment" : "VoiceOver: today's total insulin so far. (1: units)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الإجمالي حتى الآن %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Celkem zatím %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "I alt indtil nu %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesamt bisher %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total hasta ahora %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yhteensä tähän mennessä %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total jusqu'ici %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "סה״כ עד כה %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Összesen eddig %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totale finora %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "これまでの合計 %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totalt så langt %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totaal tot nu toe %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Razem do tej pory %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total até agora %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total până acum %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всего на данный момент %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spolu zatiaľ %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totalt hittills %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Şu ana kadar toplam %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Усього наразі %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tổng đến giờ %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "至今总计 %1$@" + } + } + } + }, + "Touch and hold a chart to see exact values." : { + "comment" : "Hint under the day page charts explaining how to scrub them", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المس مخططًا مع الاستمرار لرؤية القيم الدقيقة." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podržte prst na grafu a uvidíte přesné hodnoty." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tryk og hold på et diagram for at se præcise værdier." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Halte ein Diagramm gedrückt, um genaue Werte zu sehen." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mantén pulsado un gráfico para ver los valores exactos." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pidä kaaviota painettuna nähdäksesi tarkat arvot." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Touchez un graphique de manière prolongée pour voir les valeurs exactes." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "גע והחזק תרשים כדי לראות ערכים מדויקים." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Érints meg és tarts lenyomva egy diagramot a pontos értékekért." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tieni premuto su un grafico per vedere i valori esatti." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "グラフを長押しすると正確な値が表示されます。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trykk og hold på et diagram for å se nøyaktige verdier." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Houd een grafiek ingedrukt om exacte waarden te zien." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dotknij i przytrzymaj wykres, aby zobaczyć dokładne wartości." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toque e segure um gráfico para ver os valores exatos." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Țineți apăsat pe un grafic pentru a vedea valorile exacte." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нажмите и удерживайте график, чтобы увидеть точные значения." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podržte prst na grafe a uvidíte presné hodnoty." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tryck och håll på ett diagram för att se exakta värden." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tam değerleri görmek için bir grafiğe dokunup basılı tutun." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Торкніться й утримуйте графік, щоб побачити точні значення." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chạm và giữ biểu đồ để xem giá trị chính xác." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "按住图表可查看精确数值。" + } + } + } + }, + "Trends" : { + "comment" : "Title of the Trends tab Title of the Trends tab in the redesigned interface", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الاتجاهات" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendenser" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trends" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendencias" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendit" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendances" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מגמות" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendek" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendenze" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "トレンド" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trender" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trends" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendências" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendințe" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тренды" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trender" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eğilimler" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тренди" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xu hướng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "趋势" + } + } + } + }, + "unknown" : { + "comment" : "VoiceOver value when active insulin or carbs at the current time isn't known", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "غير معروف" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "neznámé" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "ukendt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "unbekannt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "desconocido" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "tuntematon" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "inconnu" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא ידוע" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "ismeretlen" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "sconosciuto" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "不明" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "ukjent" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "onbekend" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "nieznane" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "desconhecido" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "necunoscut" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "неизвестно" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "neznáme" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "okänt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "bilinmiyor" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "невідомо" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "không rõ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "未知" + } + } + } + }, + "Unknown error" : { + "comment" : "Reason Loop could not run: an unexpected error", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "خطأ غير معروف" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neznámá chyba" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ukendt fejl" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unbekannter Fehler" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error desconocido" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tuntematon virhe" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur inconnue" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שגיאה לא ידועה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ismeretlen hiba" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore sconosciuto" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "不明なエラー" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ukjent feil" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Onbekende fout" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nieznany błąd" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erro desconhecido" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eroare necunoscută" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Неизвестная ошибка" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neznáma chyba" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Okänt fel" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bilinmeyen hata" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Невідома помилка" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lỗi không xác định" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "未知错误" + } + } + } + }, + "Very high" : { + "comment" : "Time in range band: glucose above 250 mg/dL (13.9 mmol/L) Time-in-range band name: glucose above 250 mg/dL (13.9 mmol/L)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مرتفع جدًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velmi vysoká" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meget høj" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sehr hoch" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muy alta" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erittäin korkea" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Très élevée" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "גבוה מאוד" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nagyon magas" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Molto alta" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "非常に高い" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svært høy" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeer hoog" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bardzo wysoka" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muito alta" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foarte ridicată" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очень высокая" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Veľmi vysoká" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mycket högt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Çok yüksek" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дуже висока" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rất cao" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "非常高" + } + } + } + }, + "Very low" : { + "comment" : "Time in range band: glucose below 54 mg/dL (3.0 mmol/L) Time-in-range band name: glucose below 54 mg/dL (3.0 mmol/L)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "منخفض جدًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velmi nízká" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meget lav" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sehr niedrig" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muy baja" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erittäin matala" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Très basse" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "נמוך מאוד" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nagyon alacsony" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Molto bassa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "非常に低い" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svært lav" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeer laag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bardzo niska" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muito baixa" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foarte scăzută" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очень низкая" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Veľmi nízka" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mycket lågt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Çok düşük" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дуже низька" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rất thấp" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "非常低" + } + } + } }, " - " : { @@ -2423,6 +21325,48 @@ "state" : "translated", "value" : "%1$@ U" } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ وحدة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ 単位" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ U" + } } } }, @@ -9054,7 +27998,147 @@ "comment" : "title for when automation was unsuccessful" }, "Average" : { - "comment" : "Average glucose metric title" + "comment" : "Average glucose metric title", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المتوسط" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Průměr" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gennemsnit" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durchschnitt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Promedio" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keskiarvo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Moyenne" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ממוצע" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Átlag" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Media" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gjennomsnitt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gemiddeld" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Średnia" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Média" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medie" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Среднее" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Priemer" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medelvärde" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ortalama" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Середнє" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trung bình" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均值" + } + } + } }, "Average Glucose" : { @@ -9922,6 +29006,24 @@ "state" : "translated", "value" : "大剂量" } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bólus" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Болюс" + } } } }, @@ -21095,6 +40197,24 @@ "state" : "translated", "value" : "克" } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "g" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "g" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "г" + } } } }, @@ -21307,6 +40427,18 @@ "state" : "translated", "value" : "血糖" } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glükóz" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Глюкоза" + } } } }, @@ -22061,7 +41193,147 @@ "comment" : "Apple Health settings descriptive text" }, "GMI" : { - "comment" : "Glucose Management Indicator metric title" + "comment" : "Glucose Management Indicator metric title", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "GMI" + } + } + } }, "Go to Settings > Focus." : { "comment" : "Focus modes step 1" @@ -22159,7 +41431,147 @@ }, "High" : { - "comment" : "Time-in-range band label (181–250 mg/dL)" + "comment" : "Time-in-range band label (181–250 mg/dL)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مرتفع" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vysoká" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Høj" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoch" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alta" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Korkea" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Élevée" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "גבוה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Magas" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alta" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "高値" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Høy" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoog" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wysoka" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alta" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ridicată" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Высокая" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vysoká" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Högt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yüksek" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Висока" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cao" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "高" + } + } + } }, "High Glucose" : { "localizations" : { @@ -23121,6 +42533,30 @@ "state" : "translated", "value" : "胰岛素" } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulín" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulin" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulín" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Інсулін" + } } } }, @@ -27024,7 +46460,147 @@ } }, "Low" : { - "comment" : "Time-in-range band label (54–69 mg/dL)" + "comment" : "Time-in-range band label (54–69 mg/dL)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "منخفض" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nízká" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lav" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niedrig" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Baja" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Matala" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basse" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "נמוך" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alacsony" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bassa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "低値" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lav" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niska" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Baixa" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Scăzută" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Низкая" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nízka" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lågt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Düşük" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Низька" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thấp" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "低" + } + } + } }, "Low Glucose" : { "comment" : "Title for bolus screen warning when glucose is below glucose warning limit.\nTitle for bolus screen warning when glucose is below suspend threshold, but a bolus is recommended", @@ -31045,6 +50621,18 @@ "state" : "translated", "value" : "OK" } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" + } } } }, @@ -35531,6 +55119,18 @@ "state" : "translated", "value" : "重试" } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Újra" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Спробувати ще раз" + } } } }, @@ -35876,6 +55476,66 @@ "state" : "translated", "value" : "预设" } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المجدول" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plánovaný" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ohjelmoitu" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ütemezett" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "スケジュール" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Programada" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plánovaný" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schemalagd" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "За розкладом" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Theo lịch" + } } } }, @@ -40595,6 +60255,24 @@ "state" : "translated", "value" : "U" } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "U" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "U" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "U" + } } } }, diff --git a/Loop/Managers/LoopAppManager.swift b/Loop/Managers/LoopAppManager.swift index 10d4fcfc12..aee8e93430 100644 --- a/Loop/Managers/LoopAppManager.swift +++ b/Loop/Managers/LoopAppManager.swift @@ -517,6 +517,14 @@ class LoopAppManager: NSObject { analyticsServicesManager.application(didFinishLaunchingWithOptions: launchOptions) + // Loop's automation history only goes back 36 hours, so the Trends screen keeps its own longer record. + // The debounce keeps a quick off-and-on from being recorded as open loop. + ObservablePublishers.tracking(settingsManager, keyPath: \.dosingEnabled) + .debounce(for: .seconds(15), scheduler: RunLoop.main) + .removeDuplicates() + .sink { LoopModeHistory.shared.record(closedLoop: $0) } + .store(in: &cancellables) + state = state.next await loopDataManager.updateDisplayState() @@ -618,6 +626,8 @@ class LoopAppManager: NSObject { return SetupHistory(settings: settings, experiments: experiments) } viewModel.outcomesPackageBuilder = criticalEventLogExportManager.map { OutcomesPackageBuilder(logs: $0.logs) } + viewModel.historyProvider = LoopHistoryProvider(deviceManager: deviceDataManager, loopDataManager: loopDataManager, + settingsManager: settingsManager, temporaryPresetsManager: temporaryPresetsManager) return viewModel }() diff --git a/Loop/Redesign/DayCharts.swift b/Loop/Redesign/DayCharts.swift new file mode 100644 index 0000000000..83e2032d74 --- /dev/null +++ b/Loop/Redesign/DayCharts.swift @@ -0,0 +1,548 @@ +// +// DayCharts.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import SwiftUI +import Charts +import UIKit + +private typealias P = LoopDesign.Palette + +// MARK: - Shared axis helpers + +enum DayAxis { + /// Midnight, 6 AM, noon and 6 PM inside `window`, by the clock, so a day when the clocks change still marks 6 AM. + static func quarterDayMarks(in window: DateInterval, calendar: Calendar = .current) -> [Date] { + var marks: [Date] = [] + var day = calendar.startOfDay(for: window.start) + while day <= window.end { + for hour in [0, 6, 12, 18] { + if let mark = calendar.date(bySettingHour: hour, minute: 0, second: 0, of: day), window.contains(mark) { + marks.append(mark) + } + } + guard let next = calendar.date(byAdding: .day, value: 1, to: day) else { break } + day = next + } + return marks + } + + static func hourLabel(_ date: Date) -> String { + date.formatted(.dateTime.hour()) + } + + /// Gaps of more than 30 minutes between readings, so a chart can say where data is missing. + static func gaps(in points: [GlucosePoint], window: DateInterval, until end: Date) -> [DateInterval] { + let limit: TimeInterval = 30 * 60 + var gaps: [DateInterval] = [] + var previous = window.start + for point in points.sorted(by: { $0.date < $1.date }) where window.contains(point.date) { + if point.date.timeIntervalSince(previous) > limit { + gaps.append(DateInterval(start: previous, end: point.date)) + } + previous = point.date + } + let last = min(end, window.end) + if last.timeIntervalSince(previous) > limit { + gaps.append(DateInterval(start: previous, end: last)) + } + return gaps + } + + static func gapMessage(_ gaps: [DateInterval], window: DateInterval, until end: Date) -> String? { + guard let longest = gaps.max(by: { $0.duration < $1.duration }) else { return nil } + // A gap over the whole period would read "0:00 – 0:00". + if gaps.count == 1, longest.start == window.start, longest.end == min(end, window.end) { + return window.contains(end) + ? NSLocalizedString("No CGM data yet today", comment: "Chart note: there are no glucose readings so far today") + : NSLocalizedString("No CGM data on this day", comment: "Chart note: there are no glucose readings for the whole day") + } + let start = LoopFormat.time(longest.start) + let end = LoopFormat.time(longest.end) + if gaps.count > 1 { + let others = String.localizedStringWithFormat(NSLocalizedString("%lld shorter gaps", comment: "Chart note: how many other, shorter gaps in glucose readings there were; follows the longest gap. (1: number of gaps)"), gaps.count - 1) + return String(format: NSLocalizedString("No CGM data %1$@ – %2$@ and %3$@", comment: "Chart note: the longest gap in glucose readings, plus the shorter ones. (1: start time; 2: end time; 3: number of shorter gaps, e.g. 2 shorter gaps)"), start, end, others) + } + return String(format: NSLocalizedString("No CGM data %1$@ – %2$@", comment: "Chart note: a gap in glucose readings. (1: start time; 2: end time)"), start, end) + } +} + +// MARK: - InsulinTimeline + +/// Carbs, glucose and insulin for one day on one shared time axis. +/// +/// The lanes are drawn in a single chart with stacked y ranges so they can't drift apart: insulin 0–30, +/// glucose 36–92 (the fixed 2–20 mmol/L scale), carb markers at 97. +struct InsulinTimelineView: View { + let history: DayHistory + let unit: GlucoseUnitStyle + let targetBands: [TargetBand] + var now: Date = Date() + var height: CGFloat = 260 + + @State private var selection: Date? + @Environment(\.colorScheme) private var colorScheme + + private static let insulinTop = 30.0 + private static let basalTop = 12.0 + private static let glucoseRange = 36.0...92.0 + private static let carbLane = 97.0 + + private var window: DateInterval { history.window } + + private func y(glucose mgdl: Double) -> Double { + let domain = unit.dayDomain + let value = min(max(unit.value(mgdl), domain.lowerBound), domain.upperBound) + let fraction = (value - domain.lowerBound) / (domain.upperBound - domain.lowerBound) + return Self.glucoseRange.lowerBound + fraction * (Self.glucoseRange.upperBound - Self.glucoseRange.lowerBound) + } + + private var maxBasal: Double { + max(0.5, (history.basal.map(\.rate) + history.scheduledBasal.map(\.rate)).max() ?? 0) + } + + private var maxBolus: Double { + max(1, history.boluses.map(\.units).max() ?? 0) + } + + private func y(basal rate: Double) -> Double { rate / maxBasal * Self.basalTop } + private func y(bolus units: Double) -> Double { max(1.5, units / maxBolus * (Self.insulinTop - 4)) } + + private var gaps: [DateInterval] { DayAxis.gaps(in: history.glucose, window: window, until: now) } + + /// Labels closer than this to the previous label are dropped so they don't overprint; the marks still draw. + private var labelSpacing: TimeInterval { window.duration / 7 } + + private func spacedIDs(_ items: [T], date: (T) -> Date, id: (T) -> String) -> Set { + var kept = Set() + var last: Date? + for item in items.sorted(by: { date($0) < date($1) }) { + if let last, date(item).timeIntervalSince(last) < labelSpacing { continue } + kept.insert(id(item)) + last = date(item) + } + return kept + } + + /// One label per run of carb entries closer together than `labelSpacing`, giving the run's total. + /// It sits after the run's last marker, so no later marker in the run draws over it. + private var carbLabels: [String: Double] { + var labels: [String: Double] = [:] + var runStart: Date? + var runLast: (id: String, total: Double)? + for carb in history.carbs.sorted(by: { $0.date < $1.date }) { + if let start = runStart, let last = runLast, carb.date.timeIntervalSince(start) < labelSpacing { + runLast = (carb.id, last.total + carb.grams) + } else { + if let last = runLast { labels[last.id] = last.total } + runStart = carb.date + runLast = (carb.id, carb.grams) + } + } + if let last = runLast { labels[last.id] = last.total } + return labels + } + private var labelledBoluses: Set { spacedIDs(history.boluses.filter { !$0.automatic }, date: \.date, id: \.id) } + + var body: some View { + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + chart + .frame(height: height) + if let message = DayAxis.gapMessage(gaps, window: window, until: now), history.source == .loop || !history.glucose.isEmpty { + Text(message) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(LoopDesign.Palette.inkMuted) + } + } + } + + private var chart: some View { + Chart { + // Quarter-day rules + ForEach(DayAxis.quarterDayMarks(in: window), id: \.self) { mark in + RuleMark(x: .value("Time", mark)) + .foregroundStyle(P.grid) + .lineStyle(StrokeStyle(lineWidth: 1)) + } + + // Glucose lane + ForEach(Array(targetBands.enumerated()), id: \.offset) { _, band in + RectangleMark(xStart: .value("Start", band.start), xEnd: .value("End", band.end), + yStart: .value("Low", y(glucose: band.range.lowerBound)), yEnd: .value("High", y(glucose: band.range.upperBound))) + .foregroundStyle(P.targetBand) + } + ForEach(Array(history.glucose.enumerated()), id: \.offset) { _, point in + PointMark(x: .value("Time", point.date), y: .value("Glucose", y(glucose: point.mgdl))) + .symbolSize(10) + .foregroundStyle(P.glucose) + } + + // Carb markers + let carbLabels = carbLabels + ForEach(history.carbs) { carb in + PointMark(x: .value("Time", carb.date), y: .value("Carbs", Self.carbLane)) + .symbolSize(60) + .foregroundStyle(P.carbs) + .annotation(position: .trailing, spacing: 2) { + if let grams = carbLabels[carb.id] { + Text(LoopFormat.grams(grams)) + .font(LoopDesign.Typeface.caption) + .foregroundStyle(P.carbsInk) + } + } + } + + // Delivered basal, edged, then the schedule dashed over it + // Draw each recorded segment independently; a gap must not become an interpolated delivery area. + ForEach(Array(history.basal.enumerated()), id: \.offset) { _, segment in + RectangleMark(xStart: .value("Start", segment.start), xEnd: .value("End", segment.end), + yStart: .value("Base", 0), yEnd: .value("Basal", y(basal: segment.rate))) + .foregroundStyle(P.basal) + RuleMark(xStart: .value("Start", segment.start), xEnd: .value("End", segment.end), y: .value("Basal", y(basal: segment.rate))) + .foregroundStyle(P.basalEdge) + .lineStyle(StrokeStyle(lineWidth: 1.5)) + } + ForEach(Array(history.scheduledBasal.enumerated()), id: \.offset) { _, value in + RuleMark(xStart: .value("Start", value.start), xEnd: .value("End", value.end), y: .value("Scheduled", y(basal: value.rate))) + .foregroundStyle(P.basalScheduled) + .lineStyle(StrokeStyle(lineWidth: 1, dash: [3, 3])) + } + + // Boluses over basal: solid for manual with a label, hatched for automatic. + // Each sits in a card-coloured slot a point wider, so it never touches the basal behind it. + ForEach(history.boluses) { bolus in + RectangleMark(x: .value("Time", bolus.date), yStart: .value("Base", 0), yEnd: .value("Bolus", y(bolus: bolus.units)), width: .fixed(7)) + .foregroundStyle(P.surfaceRaised) + } + let bolusLabels = labelledBoluses + ForEach(history.boluses) { bolus in + if bolus.automatic { + RectangleMark(x: .value("Time", bolus.date), yStart: .value("Base", 0), yEnd: .value("Bolus", y(bolus: bolus.units)), width: .fixed(5)) + .foregroundStyle(Hatch.paint(P.bolus, scheme: colorScheme)) + } else { + RectangleMark(x: .value("Time", bolus.date), yStart: .value("Base", 0), yEnd: .value("Bolus", y(bolus: bolus.units)), width: .fixed(5)) + .foregroundStyle(P.bolus) + .annotation(position: .top, spacing: 2) { + if bolusLabels.contains(bolus.id) { + Text(LoopFormat.number(bolus.units, decimals: 1)) + .font(LoopDesign.Typeface.caption) + .foregroundStyle(P.insulinInk) + } + } + } + } + + // Now + if window.contains(now) { + RuleMark(x: .value("Now", now)) + .foregroundStyle(P.inkMuted) + .lineStyle(StrokeStyle(lineWidth: 1, dash: [2, 3])) + } + + if let selection { + RuleMark(x: .value("Selected", selection)) + .foregroundStyle(P.ink.opacity(0.6)) + .lineStyle(StrokeStyle(lineWidth: 1)) + } + } + .chartXScale(domain: window.start...window.end) + .chartYScale(domain: 0...104) + .chartXAxis { + AxisMarks(values: DayAxis.quarterDayMarks(in: window)) { value in + AxisValueLabel { + if let date = value.as(Date.self) { + Text(DayAxis.hourLabel(date)) + } + } + .font(LoopDesign.Typeface.caption) + .foregroundStyle(P.inkMuted) + } + } + .chartYAxis { + AxisMarks(position: .leading, values: unit.dayTicks.map { y(glucose: unit.mgdl($0)) }) { value in + AxisGridLine().foregroundStyle(P.grid) + AxisValueLabel { + if let mapped = value.as(Double.self), let tick = unit.dayTicks.first(where: { abs(y(glucose: unit.mgdl($0)) - mapped) < 0.01 }) { + Text(tick.formatted()) + } + } + .font(LoopDesign.Typeface.caption) + .foregroundStyle(P.inkMuted) + } + } + .chartOverlay { proxy in + ScrubOverlay(proxy: proxy, selection: $selection, clampTo: window) + } + .overlay(alignment: .top) { + if let selection { + ScrubCallout(rows: scrubRows(at: selection)) + .padding(.top, 2) + .allowsHitTesting(false) + } + } + .accessibilityElement(children: .ignore) + .accessibilityLabel(accessibilitySummary) + } + + private func scrubRows(at date: Date) -> [ScrubCallout.Row] { + var rows: [ScrubCallout.Row] = [.init(label: nil, value: LoopFormat.time(date), color: P.ink)] + if let glucose = nearest(history.glucose.map { ($0.date, $0.mgdl) }, to: date, within: 10 * 60) { + rows.append(.init(label: NSLocalizedString("Glucose", comment: "Chart callout label for the glucose reading at the touched time"), value: "\(unit.format(glucose)) \(unit.unitName)", color: P.glucoseInk)) + } + if let iob = nearest(history.iob.map { ($0.date, $0.value) }, to: date, within: 10 * 60) { + rows.append(.init(label: NSLocalizedString("IOB", comment: "Abbreviation for insulin on board (active insulin)"), value: LoopFormat.doseUnits(iob), color: P.insulinInk)) + } + if let cob = nearest(history.cob.map { ($0.date, $0.value) }, to: date, within: 10 * 60) { + rows.append(.init(label: NSLocalizedString("COB", comment: "Abbreviation for carbs on board (active carbohydrate)"), value: LoopFormat.grams(cob), color: P.carbsInk)) + } + if let segment = LoopHistoryMath.basalRate(at: date, in: history.basal) { + let value = segment.kind == .suspended + ? NSLocalizedString("Suspended", comment: "Chart callout value: insulin delivery was suspended at the touched time") + : LoopFormat.rate(segment.rate) + rows.append(.init(label: NSLocalizedString("Basal", comment: "Chart callout label for the basal rate at the touched time"), value: value, color: P.ink)) + } + if let bolus = history.boluses.min(by: { abs($0.date.timeIntervalSince(date)) < abs($1.date.timeIntervalSince(date)) }), + abs(bolus.date.timeIntervalSince(date)) < 5 * 60 { + rows.append(.init(label: bolus.automatic + ? NSLocalizedString("Automatic bolus", comment: "Chart callout label for a bolus Loop delivered automatically near the touched time") + : NSLocalizedString("Bolus", comment: "Chart callout label for a bolus near the touched time"), + value: LoopFormat.doseUnits(bolus.units), color: P.insulinInk)) + } + return rows + } + + private var accessibilitySummary: String { + let day = LoopFormat.weekdayDay(window.start) + let bolus = LoopFormat.totalUnits(history.bolusUnits) + let basal = LoopFormat.totalUnits(history.basalUnits) + let carbs = LoopFormat.grams(history.carbGrams) + if let last = history.glucose.last { + return String(format: NSLocalizedString("Insulin timeline, %1$@, bolus %2$@, basal %3$@, carbs %4$@, latest glucose %5$@ %6$@", comment: "VoiceOver summary of the day's insulin timeline chart. (1: weekday and date; 2: bolus total, e.g. 12.5 U; 3: basal total; 4: carbs total, e.g. 140 g; 5: latest glucose value; 6: glucose unit, mmol/L or mg/dL)"), day, bolus, basal, carbs, unit.format(last.mgdl), unit.unitName) + } + return String(format: NSLocalizedString("Insulin timeline, %1$@, bolus %2$@, basal %3$@, carbs %4$@", comment: "VoiceOver summary of the day's insulin timeline chart with no glucose readings. (1: weekday and date; 2: bolus total, e.g. 12.5 U; 3: basal total; 4: carbs total, e.g. 140 g)"), day, bolus, basal, carbs) + } +} + +func nearest(_ values: [(Date, Double)], to date: Date, within limit: TimeInterval) -> Double? { + guard let best = values.min(by: { abs($0.0.timeIntervalSince(date)) < abs($1.0.timeIntervalSince(date)) }), + abs(best.0.timeIntervalSince(date)) <= limit else { + return nil + } + return best.1 +} + +// MARK: - Scrubbing + +/// Touch and hold then drag to scrub; a plain swipe still scrolls the page. +/// +/// A UIKit long-press recogniser is used because a SwiftUI long-press-then-drag gesture inside a table cell +/// takes every drag that starts on the chart, so the page couldn't scroll past it. +struct ScrubOverlay: View { + let proxy: ChartProxy + @Binding var selection: Date? + let clampTo: DateInterval + + var body: some View { + GeometryReader { geometry in + if #available(iOS 18.0, *) { + Color.clear + .contentShape(Rectangle()) + .gesture(ScrubPressRecognizer { location in + update(location, in: geometry) + } onEnd: { + selection = nil + }) + } else { + Color.clear + } + } + } + + private func update(_ location: CGPoint, in geometry: GeometryProxy) { + guard let frame = proxy.plotFrame else { return } + let origin = geometry[frame].origin + if let date: Date = proxy.value(atX: location.x - origin.x) { + selection = min(max(date, clampTo.start), clampTo.end) + } + } +} + +@available(iOS 18.0, *) +private struct ScrubPressRecognizer: UIGestureRecognizerRepresentable { + let onChange: (CGPoint) -> Void + let onEnd: () -> Void + + func makeUIGestureRecognizer(context: Context) -> UILongPressGestureRecognizer { + let recognizer = UILongPressGestureRecognizer() + recognizer.minimumPressDuration = 0.3 + recognizer.allowableMovement = 12 + return recognizer + } + + func handleUIGestureRecognizerAction(_ recognizer: UILongPressGestureRecognizer, context: Context) { + switch recognizer.state { + case .began, .changed: + onChange(context.converter.localLocation) + case .ended, .cancelled, .failed: + onEnd() + default: + break + } + } +} + +struct ScrubCallout: View { + struct Row: Hashable { + let label: String? + let value: String + let color: Color + } + + let rows: [Row] + + var body: some View { + VStack(alignment: .leading, spacing: 2) { + ForEach(rows, id: \.self) { row in + HStack(spacing: LoopDesign.Space.s1) { + if let label = row.label { + Text(label).foregroundStyle(P.inkMuted) + } + Text(row.value).foregroundStyle(row.color).fontWeight(.semibold) + } + } + } + .font(LoopDesign.Typeface.footnote.monospacedDigit()) + .padding(.horizontal, LoopDesign.Space.s2) + .padding(.vertical, LoopDesign.Space.s1) + .background(P.surfaceRaised, in: RoundedRectangle(cornerRadius: LoopDesign.Radius.sm)) + .overlay(RoundedRectangle(cornerRadius: LoopDesign.Radius.sm).strokeBorder(P.separator)) + } +} + +/// Insulin and carbs on board over the same window, each against its own coloured axis. +struct IOBCOBChartView: View { + let iob: [TimedValue] + let cob: [TimedValue] + let window: DateInterval + var now: Date = Date() + var height: CGFloat = 140 + + @State private var selection: Date? + + private var iobMax: Double { max(1, (iob.map(\.value).max() ?? 0) * 1.1) } + private var iobMin: Double { min(0, (iob.map(\.value).min() ?? 0) * 1.1) } + private var cobMax: Double { max(10, (cob.map(\.value).max() ?? 0) * 1.1) } + + /// COB drawn on the IOB scale. + private func scaled(cob value: Double) -> Double { value / cobMax * iobMax } + + private var iobTicks: [Double] { + let step = Self.niceStep(iobMax / 2) + return stride(from: (iobMin / step).rounded(.down) * step, through: iobMax, by: step).map { $0 } + } + + private var cobTicks: [Double] { + let step = Self.niceStep(cobMax / 2) + return stride(from: 0, through: cobMax, by: step).map { $0 } + } + + static func niceStep(_ raw: Double) -> Double { + guard raw > 0 else { return 1 } + let magnitude = pow(10, floor(log10(raw))) + let normalized = raw / magnitude + let nice: Double = normalized <= 1 ? 1 : normalized <= 2 ? 2 : normalized <= 5 ? 5 : 10 + return nice * magnitude + } + + var body: some View { + Chart { + RuleMark(y: .value("Zero", 0)) + .foregroundStyle(P.inkMuted) + .lineStyle(StrokeStyle(lineWidth: 1)) + + ForEach(Array(cob.enumerated()), id: \.offset) { _, value in + AreaMark(x: .value("Time", value.date), yStart: .value("Base", 0), yEnd: .value("COB", scaled(cob: value.value)), series: .value("Series", "cob-fill")) + .foregroundStyle(P.carbs.opacity(0.14)) + LineMark(x: .value("Time", value.date), y: .value("COB", scaled(cob: value.value)), series: .value("Series", value.date > now ? "cob-projected" : "cob")) + .foregroundStyle(P.carbs) + .lineStyle(StrokeStyle(lineWidth: 1.5, dash: value.date > now ? [4, 3] : [])) + } + ForEach(Array(iob.enumerated()), id: \.offset) { _, value in + AreaMark(x: .value("Time", value.date), yStart: .value("Base", 0), yEnd: .value("IOB", value.value), series: .value("Series", "iob-fill")) + .foregroundStyle(P.insulin.opacity(0.14)) + LineMark(x: .value("Time", value.date), y: .value("IOB", value.value), series: .value("Series", value.date > now ? "iob-projected" : "iob")) + .foregroundStyle(P.insulin) + .lineStyle(StrokeStyle(lineWidth: 1.5, dash: value.date > now ? [4, 3] : [])) + } + if window.contains(now) { + RuleMark(x: .value("Now", now)) + .foregroundStyle(P.inkMuted) + .lineStyle(StrokeStyle(lineWidth: 1, dash: [1, 3])) + } + if let selection { + RuleMark(x: .value("Selected", selection)).foregroundStyle(P.ink.opacity(0.6)) + } + } + .frame(height: height) + .chartXScale(domain: window.start...window.end) + .chartYScale(domain: iobMin...iobMax) + .chartXAxis { + AxisMarks(values: DayAxis.quarterDayMarks(in: window)) { value in + AxisGridLine().foregroundStyle(P.grid) + AxisValueLabel { + if let date = value.as(Date.self) { Text(DayAxis.hourLabel(date)) } + } + .font(LoopDesign.Typeface.caption) + .foregroundStyle(P.inkMuted) + } + } + .chartYAxis { + AxisMarks(position: .leading, values: iobTicks) { value in + AxisGridLine().foregroundStyle(P.grid) + AxisValueLabel { + if let v = value.as(Double.self) { Text(v.formatted(.number.precision(.fractionLength(0...1)))) } + } + .font(LoopDesign.Typeface.caption) + .foregroundStyle(P.insulinInk) + } + AxisMarks(position: .trailing, values: cobTicks.map { scaled(cob: $0) }) { value in + AxisValueLabel { + if let v = value.as(Double.self) { Text((v / iobMax * cobMax).formatted(.number.precision(.fractionLength(0)))) } + } + .font(LoopDesign.Typeface.caption) + .foregroundStyle(P.carbsInk) + } + } + .chartOverlay { proxy in + ScrubOverlay(proxy: proxy, selection: $selection, clampTo: window) + } + .overlay(alignment: .top) { + if let selection { + ScrubCallout(rows: scrubRows(at: selection)).allowsHitTesting(false) + } + } + .accessibilityElement(children: .ignore) + .accessibilityLabel(accessibilitySummary) + } + + private func scrubRows(at date: Date) -> [ScrubCallout.Row] { + var rows: [ScrubCallout.Row] = [.init(label: nil, value: LoopFormat.time(date), color: P.ink)] + if let value = nearest(iob.map { ($0.date, $0.value) }, to: date, within: 10 * 60) { + rows.append(.init(label: NSLocalizedString("IOB", comment: "Abbreviation for insulin on board (active insulin)"), value: LoopFormat.doseUnits(value), color: P.insulinInk)) + } + if let value = nearest(cob.map { ($0.date, $0.value) }, to: date, within: 10 * 60) { + rows.append(.init(label: NSLocalizedString("COB", comment: "Abbreviation for carbs on board (active carbohydrate)"), value: LoopFormat.grams(value), color: P.carbsInk)) + } + return rows + } + + private var accessibilitySummary: String { + let iobNow = nearest(iob.map { ($0.date, $0.value) }, to: now, within: 10 * 60) + let cobNow = nearest(cob.map { ($0.date, $0.value) }, to: now, within: 10 * 60) + let unknown = NSLocalizedString("unknown", comment: "VoiceOver value when active insulin or carbs at the current time isn't known") + return String(format: NSLocalizedString("Active insulin %1$@, active carbs %2$@", comment: "VoiceOver summary of the active insulin and carbs chart. (1: insulin on board, e.g. 1.25 U, or unknown; 2: carbs on board, e.g. 20 g, or unknown)"), iobNow.map(LoopFormat.doseUnits) ?? unknown, cobNow.map(LoopFormat.grams) ?? unknown) + } +} diff --git a/Loop/Redesign/DayDetailView.swift b/Loop/Redesign/DayDetailView.swift new file mode 100644 index 0000000000..0f8186b71b --- /dev/null +++ b/Loop/Redesign/DayDetailView.swift @@ -0,0 +1,198 @@ +// +// DayDetailView.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import SwiftUI + +private typealias P = LoopDesign.Palette + +/// One full day on InsulinTimeline with IOB and COB under it, stepping a day at a time. +struct DayDetailView: View { + let provider: LoopHistoryProvider + + @State private var day: Date + @State private var history: DayHistory? + @State private var targetBands: [TargetBand] = [] + @State private var now = Date() + @State private var loadError: String? + + init(provider: LoopHistoryProvider, day: Date) { + self.provider = provider + _day = State(initialValue: Calendar.current.startOfDay(for: day)) + } + + private var window: DateInterval { + DateInterval(start: day, end: Calendar.current.date(byAdding: .day, value: 1, to: day)!) + } + + private var isToday: Bool { Calendar.current.isDate(day, inSameDayAs: now) } + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: LoopDesign.Space.s6) { + dayStepper + if let history { + LoopCard { + CardHeader(title: NSLocalizedString("Insulin", comment: "Title of the day page card showing insulin, carbs and glucose on one timeline")) { + HStack(spacing: 4) { + Text(LoopFormat.totalUnits(history.totalUnits)).foregroundStyle(P.insulinInk) + Text("·") + Text(LoopFormat.grams(history.carbGrams)).foregroundStyle(P.carbsInk) + } + } + InsulinTimelineView(history: history, unit: provider.unitStyle, targetBands: targetBands, + now: now, height: 300) + ChartLegend(items: DayTotalsTiles.legend) + if history.source == .health { + Text(NSLocalizedString("From Apple Health: Loop keeps 7 days, so this day has no temp basal or schedule detail.", comment: "Day page note when the day is older than Loop's 7-day history and was read from Apple Health")) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + if #available(iOS 18.0, *) { + Text(NSLocalizedString("Touch and hold a chart to see exact values.", comment: "Hint under the day page charts explaining how to scrub them")) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + } + LoopCard { + CardHeader(title: NSLocalizedString("Basal and bolus", comment: "Trends card title: insulin split into basal and bolus")) + InsulinSplitView(split: history.insulinSplit, partial: isToday) + ValueTile(label: NSLocalizedString("Carbs", comment: "Label of a tile showing the day's total carbohydrate")) { + Text(LoopFormat.grams(history.carbGrams)) + .font(LoopDesign.Typeface.statValueSmall) + .foregroundStyle(P.carbsInk) + } + .fixedSize(horizontal: false, vertical: true) + } + LoopCard { + CardHeader(title: NSLocalizedString("Active insulin and carbs", comment: "Title of the day page card charting insulin and carbs on board")) + if history.iob.isEmpty && history.cob.isEmpty { + ChartPlaceholder(message: history.source == .health + ? NSLocalizedString("Loop only works out active insulin and carbs for the days it keeps", comment: "Day page placeholder for active insulin and carbs on a day older than Loop's 7-day history") + : NSLocalizedString("No active insulin or carbs on this day", comment: "Day page placeholder when there was no insulin or carbs on board all day")) + } else { + IOBCOBChartView(iob: history.iob, cob: history.cob, window: window, now: now) + } + ChartLegend(items: [ + LegendItem(swatch: .solid(P.insulin), label: NSLocalizedString("IOB (U, left)", comment: "Chart legend: insulin on board in units, on the left axis")), + LegendItem(swatch: .solid(P.carbs), label: NSLocalizedString("COB (g, right)", comment: "Chart legend: carbs on board in grams, on the right axis")), + LegendItem(swatch: .dash(P.inkMuted), label: NSLocalizedString("Projected", comment: "Chart legend: dashed lines show Loop's projection of insulin and carbs on board")), + ]) + } + } else if let loadError { + ChartPlaceholder(message: String(format: NSLocalizedString("Couldn't load this day: %1$@", comment: "Day page error when the day's history can't be read. (1: system error description)"), loadError)) + } else { + ProgressView() + .frame(maxWidth: .infinity, minHeight: 300) + } + } + .padding(LoopDesign.Space.s4) + } + .background(P.surface) + .navigationTitle(isToday ? NSLocalizedString("Today", comment: "Day page title when showing today") : LoopFormat.weekdayDay(day)) + .navigationBarTitleDisplayMode(.inline) + .task(id: day) { + now = Date() + let requestedWindow = window + history = nil + loadError = nil + do { + async let historyFetch = provider.dayHistory(requestedWindow) + async let targetsFetch = provider.glucoseTargetBands(in: requestedWindow) + let (result, targets) = try await (historyFetch, targetsFetch) + guard !Task.isCancelled, window == requestedWindow else { return } + history = result + targetBands = targets + } catch { + guard !Task.isCancelled, window == requestedWindow else { return } + loadError = error.localizedDescription + } + } + } + + private var dayStepper: some View { + HStack { + StepButton(systemName: "chevron.left", label: NSLocalizedString("Previous day", comment: "Accessibility label for the day page button that steps back a day")) { + day = Calendar.current.date(byAdding: .day, value: -1, to: day)! + } + Spacer() + Text(LoopFormat.day(day)) + .font(LoopDesign.Typeface.headline) + .foregroundStyle(P.ink) + Spacer() + StepButton(systemName: "chevron.right", label: NSLocalizedString("Next day", comment: "Accessibility label for the day page button that steps forward a day"), enabled: !isToday) { + day = Calendar.current.date(byAdding: .day, value: 1, to: day)! + } + } + } +} + +/// A day on one timeline with its totals, for a one-day period on Trends. +struct DayTimelineCard: View { + let title: String + let history: DayHistory + let unit: GlucoseUnitStyle + let targetBands: [TargetBand] + var now: Date = Date() + var height: CGFloat = 230 + /// The bolus, basal and carb tiles; off where a Basal and bolus card follows. + var showsTotals = true + + static var todayTitle: String { + NSLocalizedString("Today so far", comment: "Title of the card on the Now screen showing today's insulin and carbs") + } + + var body: some View { + LoopCard { + CardHeader(title: title) { + HStack(spacing: 4) { + Text(LoopFormat.totalUnits(history.totalUnits)).foregroundStyle(P.insulinInk) + Text(verbatim: "·") + Text(LoopFormat.grams(history.carbGrams)).foregroundStyle(P.carbsInk) + } + } + InsulinTimelineView(history: history, unit: unit, targetBands: targetBands, now: now, height: height) + if showsTotals { + DayTotalsTiles(history: history) + } + ChartLegend(items: DayTotalsTiles.legend) + } + } +} + +/// Bolus, basal and carb totals under a timeline. +struct DayTotalsTiles: View { + let history: DayHistory + + static let legend: [LegendItem] = [ + LegendItem(swatch: .solid(P.bolus), label: NSLocalizedString("Bolus", comment: "Chart legend: solid bars are manual boluses")), + LegendItem(swatch: .hatch(P.bolus), label: NSLocalizedString("Automatic", comment: "Chart legend: hatched bars are automatic boluses given by Loop")), + LegendItem(swatch: .basal, label: NSLocalizedString("Basal given", comment: "Chart legend: filled area is basal insulin actually delivered")), + LegendItem(swatch: .dash(P.basalScheduled), label: NSLocalizedString("Scheduled", comment: "Chart legend: dashed line is the scheduled basal rate")), + ] + + var body: some View { + AdaptiveRow { + ValueTile(label: NSLocalizedString("Bolus", comment: "Label of a tile showing the day's total bolus insulin")) { + Text(LoopFormat.totalUnits(history.bolusUnits)) + .font(LoopDesign.Typeface.statValueSmall) + .foregroundStyle(P.insulinInk) + } + ValueTile(label: NSLocalizedString("Basal", comment: "Label of a tile showing the current basal rate (Now screen) or the day's basal insulin total (Today so far totals)")) { + // Plain ink: basal is the pale fill on the chart, and orange here read as bolus. + Text(LoopFormat.totalUnits(history.basalUnits)) + .font(LoopDesign.Typeface.statValueSmall) + .foregroundStyle(P.ink) + } + ValueTile(label: NSLocalizedString("Carbs", comment: "Label of a tile showing the day's total carbohydrate")) { + Text(LoopFormat.grams(history.carbGrams)) + .font(LoopDesign.Typeface.statValueSmall) + .foregroundStyle(P.carbsInk) + } + } + .fixedSize(horizontal: false, vertical: true) + } +} diff --git a/Loop/Redesign/LoopDesign.swift b/Loop/Redesign/LoopDesign.swift new file mode 100644 index 0000000000..0fb74edffd --- /dev/null +++ b/Loop/Redesign/LoopDesign.swift @@ -0,0 +1,341 @@ +// +// LoopDesign.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import SwiftUI +import UIKit + +/// The tokens from the Loop design system (`design/project/tokens.json`). +/// +/// Quantity colours reuse Loop's existing assets (`glucose`, `insulin`, `carbs`, `fresh`, `warning`, `accent`) +/// so a DerivedAssetsOverride still recolours them. Grounds and text map onto the iOS system colours they +/// were taken from; the rest are colour sets in DerivedAssetsBase.xcassets named after their token. +/// +/// Insulin and carb amounts are set in `insulinInk` and `carbsInk` wherever they stand alone. Glucose, basal, +/// counts and figures beside a legend swatch stay `ink`. Red is only for something that needs action now. +enum LoopDesign { + + enum Palette { + // Grounds + static let surface = Color(uiColor: .systemGroupedBackground) + static let surfaceRaised = Color(uiColor: .secondarySystemGroupedBackground) + static let surfaceSunken = Color(uiColor: .systemGray5) + static let controlThumb = Color("control-thumb") + static let separator = Color(uiColor: .opaqueSeparator) + static let grid = Color(uiColor: .systemGray5) + + // Text + static let ink = Color(uiColor: .label) + static let inkMuted = Color("ink-muted") + static let accentInk = Color("accent-ink") + + // Quantities + static let glucose = Color("glucose") + static let glucoseInk = Color("glucose-ink") + static let targetBand = Color("target-band") + static let agpOuter = Color("agp-outer") + static let agpInner = Color("agp-inner") + /// Insulin as a quantity and an action: the Bolus button and the active insulin curve. + static let insulin = Color("insulin") + /// Every insulin amount set as text, whether a bolus, a day's total or active insulin. + static let insulinInk = Color("insulin-ink") + static let basal = Color("basal") + static let basalEdge = Color("basal-edge") + /// Basal is a pale, quiet orange with a tan edge; bolus is insulin's orange made deep. Both stay orange, + /// and their lightness is far enough apart (3:1 even where they touch) to tell them apart at a glance. + static let bolus = Color("bolus") + static let basalScheduled = inkMuted + static let carbs = Color("carbs") + static let carbsInk = Color("carbs-ink") + + // Time in range ladder, top to bottom + static let rangeVeryHigh = Color("range-very-high") + static let rangeHigh = Color("range-high") + static let rangeIn = Color("range-in") + static let rangeLow = Color("range-low") + static let rangeVeryLow = Color("range-very-low") + + // Loop state + static let statusAutomated = Color("fresh") + static let statusWarning = Color("warning") + } + + enum Space { + static let s1: CGFloat = 4 + static let s2: CGFloat = 8 + static let s3: CGFloat = 12 + static let s4: CGFloat = 16 + static let s6: CGFloat = 24 + static let s8: CGFloat = 32 + } + + enum Radius { + static let sm: CGFloat = 6 + static let md: CGFloat = 10 + static let lg: CGFloat = 14 + } + + /// Text styles follow Dynamic Type; the large numbers are fixed sizes in SF Pro Rounded with tabular figures. + enum Typeface { + static let statValue = Font.system(.title, design: .rounded).weight(.semibold).monospacedDigit() + static let statValueSmall = Font.system(.title3, design: .rounded).weight(.semibold).monospacedDigit() + static let title = Font.title.bold() + static let headline = Font.headline + static let body = Font.body + static let subhead = Font.subheadline + static let footnote = Font.footnote + static let caption = Font.caption2.weight(.medium).monospacedDigit() + } +} + +// MARK: - Card + +struct LoopCard: View { + var padding: CGFloat = LoopDesign.Space.s4 + @ViewBuilder var content: Content + + var body: some View { + VStack(alignment: .leading, spacing: LoopDesign.Space.s3) { + content + } + .padding(padding) + .frame(maxWidth: .infinity, alignment: .leading) + .background(LoopDesign.Palette.surfaceRaised, in: RoundedRectangle(cornerRadius: LoopDesign.Radius.lg, style: .continuous)) + } +} + +/// A card title with an optional trailing detail or action, as on every card in the mock-ups. +struct CardHeader: View { + let title: String + @ViewBuilder var trailing: Trailing + + var body: some View { + HStack(alignment: .firstTextBaseline) { + Text(title) + .font(LoopDesign.Typeface.headline) + .foregroundStyle(LoopDesign.Palette.ink) + .accessibilityAddTraits(.isHeader) + Spacer(minLength: LoopDesign.Space.s2) + trailing + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(LoopDesign.Palette.inkMuted) + } + } +} + +extension CardHeader where Trailing == EmptyView { + init(title: String) { + self.init(title: title) { EmptyView() } + } +} + +// MARK: - Legend + +struct LegendItem: Identifiable { + enum Swatch { + case solid(Color) + case outline(Color) + case dash(Color) + case dot(Color) + case hatch(Color) + case basal + } + + let id = UUID() + let swatch: Swatch + let label: String +} + +struct ChartLegend: View { + let items: [LegendItem] + + var body: some View { + FlowLayout(spacing: LoopDesign.Space.s3, lineSpacing: LoopDesign.Space.s1) { + ForEach(items) { item in + HStack(spacing: LoopDesign.Space.s1) { + LegendSwatch(swatch: item.swatch) + Text(item.label) + } + .accessibilityElement(children: .combine) + } + } + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(LoopDesign.Palette.inkMuted) + } +} + +/// The 10-point key for one kind of mark, shared by legends and the basal and bolus rows. +struct LegendSwatch: View { + let swatch: LegendItem.Swatch + @Environment(\.colorScheme) private var scheme + + var body: some View { + switch swatch { + case .solid(let color): + RoundedRectangle(cornerRadius: 2).fill(color).frame(width: 10, height: 10) + case .outline(let color): + RoundedRectangle(cornerRadius: 2).strokeBorder(color, lineWidth: 1.5).frame(width: 10, height: 10) + case .dash(let color): + Path { path in + path.move(to: CGPoint(x: 0, y: 1)) + path.addLine(to: CGPoint(x: 14, y: 1)) + } + .stroke(color, style: StrokeStyle(lineWidth: 2, dash: [4, 2])) + .frame(width: 14, height: 2) + case .dot(let color): + Circle().fill(color).frame(width: 8, height: 8) + case .hatch(let color): + RoundedRectangle(cornerRadius: 2).fill(Hatch.paint(color, scheme: scheme)).frame(width: 10, height: 10) + case .basal: + VStack(spacing: 0) { + Rectangle().fill(LoopDesign.Palette.basalEdge).frame(height: 2) + Rectangle().fill(LoopDesign.Palette.basal) + } + .frame(width: 10, height: 10) + .clipShape(RoundedRectangle(cornerRadius: 2)) + } + } +} + +/// Diagonal stripes over a pale tint of a colour: automatic bolus, wherever a mark is wide enough to show it. +enum Hatch { + static func paint(_ color: Color, scheme: ColorScheme) -> ImagePaint { + let resolved = UIColor(color).resolvedColor(with: UITraitCollection(userInterfaceStyle: scheme == .dark ? .dark : .light)) + let size: CGFloat = 5 + let image = UIGraphicsImageRenderer(size: CGSize(width: size, height: size)).image { context in + resolved.withAlphaComponent(scheme == .dark ? 0.3 : 0.22).setFill() + context.fill(CGRect(x: 0, y: 0, width: size, height: size)) + resolved.setStroke() + // One stripe corner to corner, plus the ends of its neighbours, so tiles join into unbroken lines. + let path = UIBezierPath() + path.lineWidth = 1.4 + for offset in [-size, 0, size] { + path.move(to: CGPoint(x: offset, y: size)) + path.addLine(to: CGPoint(x: offset + size, y: 0)) + } + path.stroke() + } + return ImagePaint(image: Image(uiImage: image)) + } +} + +/// Wraps its children onto new lines, for legends and chips. +struct FlowLayout: Layout { + var spacing: CGFloat + var lineSpacing: CGFloat + + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { + let width = proposal.width ?? .infinity + var x: CGFloat = 0, y: CGFloat = 0, lineHeight: CGFloat = 0, maxX: CGFloat = 0 + for subview in subviews { + let size = subview.sizeThatFits(.unspecified) + if x > 0 && x + size.width > width { + x = 0 + y += lineHeight + lineSpacing + lineHeight = 0 + } + x += size.width + spacing + maxX = max(maxX, x - spacing) + lineHeight = max(lineHeight, size.height) + } + return CGSize(width: maxX, height: y + lineHeight) + } + + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { + var x = bounds.minX, y = bounds.minY, lineHeight: CGFloat = 0 + for subview in subviews { + let size = subview.sizeThatFits(.unspecified) + if x > bounds.minX && x + size.width > bounds.maxX { + x = bounds.minX + y += lineHeight + lineSpacing + lineHeight = 0 + } + subview.place(at: CGPoint(x: x, y: y), proposal: ProposedViewSize(size)) + x += size.width + spacing + lineHeight = max(lineHeight, size.height) + } + } +} + +/// A row that stacks vertically at accessibility text sizes, so tiles and buttons keep their numbers on one line. +struct AdaptiveRow: View { + @Environment(\.dynamicTypeSize) private var typeSize + var spacing: CGFloat = LoopDesign.Space.s2 + @ViewBuilder var content: Content + + var body: some View { + let layout = typeSize.isAccessibilitySize + ? AnyLayout(VStackLayout(alignment: .leading, spacing: spacing)) + : AnyLayout(HStackLayout(spacing: spacing)) + layout { content } + } +} + +// MARK: - Tiles + +/// A small sunken tile with a label over a value, as used under the timeline. +struct ValueTile: View { + let label: String + @ViewBuilder var value: Value + + var body: some View { + VStack(alignment: .leading, spacing: 2) { + Text(label) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(LoopDesign.Palette.inkMuted) + .lineLimit(1) + .minimumScaleFactor(0.7) + .allowsTightening(true) + // A value with a long unit ("0.04 単位") shrinks to fit rather than breaking mid-word. + value + .lineLimit(1) + .minimumScaleFactor(0.6) + } + .padding(LoopDesign.Space.s3) + .frame(maxWidth: .infinity, minHeight: 64, maxHeight: .infinity, alignment: .topLeading) + .background(LoopDesign.Palette.surfaceSunken, in: RoundedRectangle(cornerRadius: LoopDesign.Radius.md, style: .continuous)) + .accessibilityElement(children: .combine) + } +} + +/// The round previous and next buttons beside a date or period. +struct StepButton: View { + let systemName: String + let label: String + var enabled = true + let action: () -> Void + + var body: some View { + Button(action: action) { + Image(systemName: systemName) + .font(.footnote.weight(.semibold)) + .frame(width: 30, height: 30) + .background(LoopDesign.Palette.surfaceRaised, in: Circle()) + .overlay(Circle().strokeBorder(LoopDesign.Palette.separator)) + .frame(width: 44, height: 44) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .foregroundStyle(enabled ? LoopDesign.Palette.ink : LoopDesign.Palette.inkMuted.opacity(0.5)) + .disabled(!enabled) + .accessibilityLabel(label) + } +} + +/// A message in place of a chart that has nothing to draw, saying why. +struct ChartPlaceholder: View { + let message: String + var height: CGFloat = 120 + + var body: some View { + Text(message) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(LoopDesign.Palette.inkMuted) + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity, minHeight: height) + .background(LoopDesign.Palette.surfaceSunken.opacity(0.5), in: RoundedRectangle(cornerRadius: LoopDesign.Radius.md)) + } +} diff --git a/Loop/Redesign/LoopHistoryMath.swift b/Loop/Redesign/LoopHistoryMath.swift new file mode 100644 index 0000000000..0bc1080785 --- /dev/null +++ b/Loop/Redesign/LoopHistoryMath.swift @@ -0,0 +1,352 @@ +// +// LoopHistoryMath.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import Foundation + +/// Pure calculations behind the Trends screen. Nothing here touches a store. +enum LoopHistoryMath { + + // MARK: Glucose statistics + + struct GlucoseStats: Equatable { + let count: Int + let meanMgdl: Double + let sdMgdl: Double + /// Coefficient of variation, 0–1. + let cv: Double + /// Glucose management indicator, as a percentage (Bergenstal 2018). + let gmiPercent: Double + var gmiMmolMol: Double { (gmiPercent - 2.152) * 10.929 } + } + + static func glucoseStats(_ points: [GlucosePoint]) -> GlucoseStats? { + guard points.count >= 2 else { return nil } + let values = points.map(\.mgdl) + let mean = values.reduce(0, +) / Double(values.count) + let variance = values.reduce(0) { $0 + pow($1 - mean, 2) } / Double(values.count - 1) + let sd = variance.squareRoot() + return GlucoseStats(count: values.count, meanMgdl: mean, sdMgdl: sd, cv: sd / mean, gmiPercent: 3.31 + 0.02392 * mean) + } + + /// The share of the period with CGM readings, assuming one reading every 5 minutes. + static func cgmActiveFraction(_ points: [GlucosePoint], over interval: DateInterval) -> Double { + let expected = interval.duration / 300 + guard expected > 0 else { return 0 } + return min(1, Double(points.count) / expected) + } + + // MARK: Time in range + + enum RangeBand: Int, CaseIterable, Identifiable { + case veryHigh, high, inRange, low, veryLow + + var id: Int { rawValue } + + static func band(for mgdl: Double) -> RangeBand { + switch mgdl { + case ..<54: return .veryLow + case ..<70: return .low + case ...180: return .inRange + case ...250: return .high + default: return .veryHigh + } + } + } + + /// Fractions (0–1) per band, in ladder order. Empty input gives all zeros. + static func timeInRange(_ points: [GlucosePoint]) -> [RangeBand: Double] { + var counts: [RangeBand: Int] = [:] + for point in points { + counts[RangeBand.band(for: point.mgdl), default: 0] += 1 + } + let total = Double(points.count) + return Dictionary(uniqueKeysWithValues: RangeBand.allCases.map { band in + (band, total > 0 ? Double(counts[band] ?? 0) / total : 0) + }) + } + + /// Whole-number percentages that add up to 100 (largest remainder), so the labels never sum to 99 or 101. + static func roundedPercentages(_ fractions: [RangeBand: Double]) -> [RangeBand: Int] { + let raw = RangeBand.allCases.map { (band: $0, value: (fractions[$0] ?? 0) * 100) } + let total = raw.reduce(0) { $0 + $1.value } + guard total > 0 else { return Dictionary(uniqueKeysWithValues: RangeBand.allCases.map { ($0, 0) }) } + var floors = raw.map { (band: $0.band, floor: Int($0.value.rounded(.down)), remainder: $0.value - $0.value.rounded(.down)) } + var missing = Int(total.rounded()) - floors.reduce(0) { $0 + $1.floor } + for index in floors.indices.sorted(by: { floors[$0].remainder > floors[$1].remainder }) where missing > 0 { + floors[index].floor += 1 + missing -= 1 + } + return Dictionary(uniqueKeysWithValues: floors.map { ($0.band, $0.floor) }) + } + + // MARK: AGP + + struct AGPSlot: Equatable { + /// Minutes after midnight at the start of the slot. + let minute: Int + let p5: Double + let p25: Double + let p50: Double + let p75: Double + let p95: Double + } + + /// Readings folded onto one day in `slotMinutes` slots, as mg/dL percentiles. Slots with fewer than 3 readings are skipped. + static func agp(_ points: [GlucosePoint], slotMinutes: Int = 15, calendar: Calendar = .current) -> [AGPSlot] { + var slots: [Int: [Double]] = [:] + for point in points { + let parts = calendar.dateComponents([.hour, .minute], from: point.date) + let minute = (parts.hour ?? 0) * 60 + (parts.minute ?? 0) + slots[minute / slotMinutes, default: []].append(point.mgdl) + } + return slots.keys.sorted().compactMap { key in + let values = slots[key]!.sorted() + guard values.count >= 3 else { return nil } + return AGPSlot(minute: key * slotMinutes, + p5: percentile(values, 0.05), p25: percentile(values, 0.25), p50: percentile(values, 0.5), + p75: percentile(values, 0.75), p95: percentile(values, 0.95)) + } + } + + /// Linear-interpolated percentile of sorted values. + static func percentile(_ sorted: [Double], _ p: Double) -> Double { + guard !sorted.isEmpty else { return .nan } + let position = p * Double(sorted.count - 1) + let lower = Int(position.rounded(.down)), upper = Int(position.rounded(.up)) + let weight = position - Double(lower) + return sorted[lower] * (1 - weight) + sorted[upper] * weight + } + + /// The number of distinct calendar days with readings. + static func daysWithReadings(_ points: [GlucosePoint], calendar: Calendar = .current) -> Int { + Set(points.map { calendar.startOfDay(for: $0.date) }).count + } + + // MARK: Days + + /// Midnight-to-midnight intervals covering `interval`, oldest first, the last one cut off at `interval.end`. + static func days(in interval: DateInterval, calendar: Calendar = .current) -> [DateInterval] { + var result: [DateInterval] = [] + var start = calendar.startOfDay(for: interval.start) + while start < interval.end { + guard let next = calendar.date(byAdding: .day, value: 1, to: start) else { break } + result.append(DateInterval(start: max(start, interval.start), end: min(next, interval.end))) + start = next + } + return result + } + + struct DayTotal: Equatable, Identifiable { + let day: Date + let split: InsulinSplit + /// False for a day still in progress, so its partial total doesn't pull per-day averages down. + var isComplete = true + var id: Date { day } + var basal: Double { split.basal } + var bolus: Double { split.bolus } + var total: Double { split.total } + + init(day: Date, split: InsulinSplit, isComplete: Bool = true) { + self.day = day + self.split = split + self.isComplete = isComplete + } + + /// A total with no manual or automatic detail: all of its bolus counts as manual. + init(day: Date, basal: Double, bolus: Double, isComplete: Bool = true) { + self.init(day: day, split: InsulinSplit(basal: basal, manualBolus: bolus), isComplete: isComplete) + } + } + + /// The days per-day insulin figures average over: days with insulin, leaving out a day still in progress + /// unless no complete day has any. + static func averagingDays(_ totals: [DayTotal]) -> [DayTotal] { + let recorded = totals.filter { $0.total > 0 } + let complete = recorded.filter(\.isComplete) + return complete.isEmpty ? recorded : complete + } + + /// One source for the summary, legend and chart rule, before any weekly aggregation. + static func averageDailyInsulin(_ totals: [DayTotal]) -> Double { + let days = averagingDays(totals) + guard !days.isEmpty else { return 0 } + return days.reduce(0) { $0 + $1.total } / Double(days.count) + } + + /// Averages daily totals by calendar week (Monday start), for periods past 30 days. + static func weekly(_ totals: [DayTotal], calendar: Calendar = .current) -> [DayTotal] { + var cal = calendar + cal.firstWeekday = 2 + let groups = Dictionary(grouping: totals) { cal.dateInterval(of: .weekOfYear, for: $0.day)?.start ?? $0.day } + return groups.keys.sorted().map { week in + DayTotal(day: week, split: InsulinSplit.average(averagingDays(groups[week]!).map(\.split))) + } + } + + // MARK: Basal + + struct ScheduleValue { + let start: Date + let end: Date + let rate: Double + } + + /// A basal record from the pump: scheduled delivery, a temp basal or a suspend. + struct BasalDelivery { + let start: Date + let end: Date + /// U/h; 0 for a suspend. + let rate: Double + let kind: BasalSegment.Kind + let automatic: Bool + } + + /// Delivered basal over `window` from the pump's records (the latest-starting one wins where they overlap). + /// + /// From `recordsStart` on, time without a basal record counts at `programmedRates`, since that is what the pump + /// delivers on its own; this matches Loop's own insulin totals (`InsulinDeliveryHistory`). Before it, or without + /// it, gaps stay unknown. + static func basalSegments(window: DateInterval, schedule: [ScheduleValue], deliveries temps: [BasalDelivery], + programmedRates: [ScheduleValue] = [], recordsStart: Date? = nil) -> [BasalSegment] { + var cuts = Set([window.start, window.end]) + if let recordsStart, window.contains(recordsStart) { cuts.insert(recordsStart) } + for value in schedule + programmedRates { + if window.contains(value.start) { cuts.insert(value.start) } + if window.contains(value.end) { cuts.insert(value.end) } + } + for temp in temps { + if window.contains(temp.start) { cuts.insert(temp.start) } + if window.contains(temp.end) { cuts.insert(temp.end) } + } + let sorted = cuts.sorted() + let sortedTemps = temps.sorted { $0.start < $1.start } + // The furthest end among records up to each index bounds the backward search, so a week of five-minute + // records stays linear rather than scanning every record for every cut. + var furthestEnd: [Date] = [] + furthestEnd.reserveCapacity(sortedTemps.count) + for temp in sortedTemps { furthestEnd.append(max(furthestEnd.last ?? temp.end, temp.end)) } + func latestRecord(covering mid: Date) -> BasalDelivery? { + var low = 0, high = sortedTemps.count + while low < high { + let middle = (low + high) / 2 + if sortedTemps[middle].start <= mid { low = middle + 1 } else { high = middle } + } + var index = low - 1 + while index >= 0, furthestEnd[index] > mid { + if mid < sortedTemps[index].end { return sortedTemps[index] } + index -= 1 + } + return nil + } + var segments: [BasalSegment] = [] + for (a, b) in zip(sorted, sorted.dropFirst()) where b > a { + let mid = a.addingTimeInterval(b.timeIntervalSince(a) / 2) + let scheduled = schedule.first { $0.start <= mid && mid < $0.end }?.rate + let segment: BasalSegment + if let temp = latestRecord(covering: mid) { + segment = BasalSegment(start: a, end: b, rate: temp.kind == .suspended ? 0 : temp.rate, scheduledRate: scheduled, + kind: temp.kind, automatic: temp.automatic) + } else if let recordsStart, a >= recordsStart, + let programmed = programmedRates.first(where: { $0.start <= mid && mid < $0.end })?.rate { + segment = BasalSegment(start: a, end: b, rate: programmed, scheduledRate: scheduled, kind: .scheduled, automatic: false) + } else { + continue + } + if let last = segments.last, last.end == segment.start, last.rate == segment.rate, last.kind == segment.kind, + last.scheduledRate == segment.scheduledRate, last.automatic == segment.automatic { + segments[segments.count - 1] = BasalSegment(start: last.start, end: segment.end, rate: last.rate, + scheduledRate: last.scheduledRate, kind: last.kind, automatic: last.automatic) + } else { + segments.append(segment) + } + } + return segments + } + + /// Basal rate at a moment, from delivered segments. + static func basalRate(at date: Date, in segments: [BasalSegment]) -> BasalSegment? { + segments.last { $0.start <= date && date < $0.end } + } + + // MARK: Loop modes + + struct LoopRun { + let date: Date + let succeeded: Bool + let error: String? + } + + struct ModeChange: Codable, Equatable { + let date: Date + let closedLoop: Bool + } + + /// Splits `interval` into closed, limited and open stretches. + /// + /// Each 5-minute slot is open when closed loop was switched off (from `modeChanges`), closed when Loop completed a + /// run in the 15 minutes before the slot ends, and limited otherwise. Before the first mode change the setting is + /// unknown, so the result starts at the later of `coverageStart` and the first mode change. + static func loopModeSpans(interval: DateInterval, runs: [LoopRun], modeChanges: [ModeChange], coverageStart: Date?, + recency: TimeInterval = 15 * 60, slot: TimeInterval = 5 * 60) -> [LoopModeSpan] { + guard let coverageStart, let firstChange = modeChanges.map(\.date).min() else { return [] } + let start = max(interval.start, coverageStart, firstChange) + guard interval.end > start else { return [] } + + let sortedRuns = runs.sorted { $0.date < $1.date } + let changes = modeChanges.sorted { $0.date < $1.date } + var spans: [LoopModeSpan] = [] + var runIndex = 0 + var lastSuccess: Date? = nil + var lastError: String? = nil + var changeIndex = 0 + var closedSetting: Bool? = nil + + var t = start + while t < interval.end { + let slotEnd = min(t.addingTimeInterval(slot), interval.end) + while runIndex < sortedRuns.count && sortedRuns[runIndex].date <= slotEnd { + let run = sortedRuns[runIndex] + if run.succeeded { + lastSuccess = run.date + lastError = nil + } else { + lastError = run.error ?? lastError + } + runIndex += 1 + } + while changeIndex < changes.count && changes[changeIndex].date <= slotEnd { + closedSetting = changes[changeIndex].closedLoop + changeIndex += 1 + } + + let mode: LoopModeSpan.Mode + let cause: String? + if closedSetting == false { + mode = .open + cause = NSLocalizedString("Closed loop off", comment: "Reason shown for a stretch of time when Loop was in open loop because closed loop was turned off") + } else if let lastSuccess, slotEnd.timeIntervalSince(lastSuccess) <= recency { + mode = .closed + cause = nil + } else { + mode = .limited + cause = lastError ?? NSLocalizedString("Loop didn't run", comment: "Reason shown for a stretch of time when Loop did not complete a run and recorded no error") + } + + if let last = spans.last, last.mode == mode, last.cause == cause, last.end == t { + spans[spans.count - 1] = LoopModeSpan(start: last.start, end: slotEnd, mode: mode, cause: cause) + } else { + spans.append(LoopModeSpan(start: t, end: slotEnd, mode: mode, cause: cause)) + } + t = slotEnd + } + return spans + } + + static func duration(of mode: LoopModeSpan.Mode, in spans: [LoopModeSpan]) -> TimeInterval { + spans.filter { $0.mode == mode }.reduce(0) { $0 + $1.duration } + } +} diff --git a/Loop/Redesign/LoopHistoryModels.swift b/Loop/Redesign/LoopHistoryModels.swift new file mode 100644 index 0000000000..e1acc34c89 --- /dev/null +++ b/Loop/Redesign/LoopHistoryModels.swift @@ -0,0 +1,342 @@ +// +// LoopHistoryModels.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import Foundation +import HealthKit +import LoopKit +import LoopAlgorithm + +/// Plain value types the redesigned screens draw from. Glucose is kept in mg/dL and converted only for display, +/// so the statistics don't depend on the display unit. + +struct GlucosePoint: Equatable { + let date: Date + let mgdl: Double +} + +struct BolusEvent: Equatable, Identifiable { + let id: String + let date: Date + let units: Double + /// Issued by Loop (automatic bolus) rather than by the person. + let automatic: Bool + /// Logged in Loop by hand rather than delivered by the pump; these can be deleted. + let manuallyEntered: Bool + /// Kept so a manually entered dose can be deleted from the dose store. + var dose: DoseEntry? = nil + /// False when the record doesn't say whether Loop or the person gave it, such as another app's doses in Apple Health. + var automaticKnown = true + + static func == (lhs: BolusEvent, rhs: BolusEvent) -> Bool { + lhs.id == rhs.id && lhs.date == rhs.date && lhs.units == rhs.units && lhs.automatic == rhs.automatic + } +} + +struct BasalSegment: Equatable { + enum Kind: Equatable { + /// The scheduled rate, with no temp basal running. + case scheduled + /// A temp basal, set by Loop or by hand. + case temp + case suspended + } + + let start: Date + let end: Date + /// The rate actually delivered, in U/h. + let rate: Double + /// The profile rate at the time, in U/h, if known. + let scheduledRate: Double? + let kind: Kind + let automatic: Bool + + var hours: Double { end.timeIntervalSince(start) / 3600 } + var units: Double { rate * hours } + /// Units delivered above (positive) or below (negative) the schedule. + var netUnits: Double { + guard let scheduledRate else { return 0 } + return (rate - scheduledRate) * hours + } + + func clamped(to interval: DateInterval) -> BasalSegment? { + let s = max(start, interval.start), e = min(end, interval.end) + guard e > s else { return nil } + return BasalSegment(start: s, end: e, rate: rate, scheduledRate: scheduledRate, kind: kind, automatic: automatic) + } +} + +struct ScheduledBasalPoint: Equatable { + let start: Date + let end: Date + let rate: Double +} + +/// The correction range over part of a chart, in mg/dL. +struct TargetBand: Equatable { + let start: Date + let end: Date + let range: ClosedRange +} + +struct CarbEvent: Equatable, Identifiable { + let id: String + let date: Date + let grams: Double + let absorptionHours: Double? + let foodType: String? + let createdByLoop: Bool + /// Kept so the entry can be edited or deleted. + var entry: StoredCarbEntry? = nil + + static func == (lhs: CarbEvent, rhs: CarbEvent) -> Bool { + lhs.id == rhs.id && lhs.date == rhs.date && lhs.grams == rhs.grams + } +} + +struct TimedValue: Equatable { + let date: Date + let value: Double +} + +/// Everything known about one day or reporting period, for InsulinTimeline. +struct DayHistory { + var window: DateInterval + var glucose: [GlucosePoint] = [] + var boluses: [BolusEvent] = [] + var basal: [BasalSegment] = [] + var scheduledBasal: [ScheduledBasalPoint] = [] + var carbs: [CarbEvent] = [] + var iob: [TimedValue] = [] + var cob: [TimedValue] = [] + /// Where the data came from, so older days can say they are missing Loop's own detail. + var source: HistorySource = .loop + + var bolusUnits: Double { boluses.reduce(0) { $0 + $1.units } } + var basalUnits: Double { basal.reduce(0) { $0 + $1.units } } + var carbGrams: Double { carbs.reduce(0) { $0 + $1.grams } } + var totalUnits: Double { bolusUnits + basalUnits } + var insulinSplit: InsulinSplit { InsulinSplit(basal: basalUnits, boluses: boluses) } + + /// Only what falls inside `interval`, with basal cut at its edges. + func clipped(to interval: DateInterval) -> DayHistory { + var history = self + let inside: (Date) -> Bool = { $0 >= interval.start && $0 < interval.end } + history.window = interval + history.glucose = glucose.filter { inside($0.date) } + history.boluses = boluses.filter { inside($0.date) } + history.basal = basal.compactMap { $0.clamped(to: interval) } + history.carbs = carbs.filter { inside($0.date) } + history.iob = iob.filter { inside($0.date) } + history.cob = cob.filter { inside($0.date) } + return history + } +} + +/// Basal and bolus for one day, or averaged per day, with bolus split into manual and automatic. +struct InsulinSplit: Equatable { + var basal = 0.0 + var manualBolus = 0.0 + var automaticBolus = 0.0 + /// Bolus that doesn't say whether it was manual or automatic; while there is any, the split isn't shown. + var unclassifiedBolus = 0.0 + + var bolus: Double { manualBolus + automaticBolus + unclassifiedBolus } + var total: Double { basal + bolus } + var isClassified: Bool { unclassifiedBolus < 0.005 } + + /// Shares in whole percent that add up to 100. + var bolusPercent: Int { total > 0 ? Int((bolus / total * 100).rounded()) : 0 } + var basalPercent: Int { total > 0 ? 100 - bolusPercent : 0 } +} + +extension InsulinSplit { + init(basal: Double, boluses: [BolusEvent]) { + self.basal = basal + for bolus in boluses { + if !bolus.automaticKnown { + unclassifiedBolus += bolus.units + } else if bolus.automatic { + automaticBolus += bolus.units + } else { + manualBolus += bolus.units + } + } + } + + /// The per-day average of several days' splits. + static func average(_ splits: [InsulinSplit]) -> InsulinSplit { + guard !splits.isEmpty else { return InsulinSplit() } + let n = Double(splits.count) + return InsulinSplit(basal: splits.reduce(0) { $0 + $1.basal } / n, + manualBolus: splits.reduce(0) { $0 + $1.manualBolus } / n, + automaticBolus: splits.reduce(0) { $0 + $1.automaticBolus } / n, + unclassifiedBolus: splits.reduce(0) { $0 + $1.unclassifiedBolus } / n) + } +} + +enum HistorySource { + /// Loop's own stores: manual versus automatic, temp basals and the schedule are all known. + case loop + /// Apple Health, for days older than Loop keeps. Doses are split into basal and bolus only. + case health +} + +/// One stretch of the period in a single loop mode. +struct LoopModeSpan: Equatable { + enum Mode: Equatable { + case closed + case limited + case open + } + + let start: Date + let end: Date + let mode: Mode + let cause: String? + + var duration: TimeInterval { end.timeIntervalSince(start) } +} + +// MARK: - Glucose unit + +struct GlucoseUnitStyle: Equatable { + let isMmol: Bool + + init(unit: LoopUnit) { + isMmol = unit == .millimolesPerLiter + } + + init(isMmol: Bool) { + self.isMmol = isMmol + } + + static let mgdlPerMmol = 18.0182 + + var unitName: String { isMmol ? "mmol/L" : "mg/dL" } + + func value(_ mgdl: Double) -> Double { isMmol ? mgdl / Self.mgdlPerMmol : mgdl } + func mgdl(_ displayValue: Double) -> Double { isMmol ? displayValue * Self.mgdlPerMmol : displayValue } + + func format(_ mgdl: Double) -> String { + isMmol ? LoopFormat.number(mgdl / Self.mgdlPerMmol, decimals: 1) : LoopFormat.number(mgdl, decimals: 0) + } + + /// Fixed chart scales from the design: 2–20 mmol/L on day views, 0–22 on AGP. + var dayDomain: ClosedRange { isMmol ? 2...20 : 36...360 } + var agpDomain: ClosedRange { isMmol ? 0...22 : 0...400 } + var dayTicks: [Double] { isMmol ? [4, 10, 16] : [70, 180, 300] } + var agpTicks: [Double] { isMmol ? [0, 4, 10, 16, 22] : [0, 70, 180, 250, 400] } +} + +// MARK: - Formatting + +enum LoopFormat { + /// A number in the user's locale, e.g. "7.3" or "7,3", with no grouping so values read like Loop's own screens. + static func number(_ value: Double, decimals: Int) -> String { + value.formatted(.number.precision(.fractionLength(decimals)).grouping(.never)) + } + + static func doseUnits(_ units: Double) -> String { + String(format: NSLocalizedString("%1$@ U", comment: "Format string for an amount of insulin in units. (1: amount)"), number(units, decimals: 2)) + } + + static func totalUnits(_ units: Double) -> String { + String(format: NSLocalizedString("%1$@ U", comment: "Format string for an amount of insulin in units. (1: amount)"), number(units, decimals: 1)) + } + + static func rate(_ rate: Double) -> String { + String(format: NSLocalizedString("%1$@ U/h", comment: "Format string for an insulin rate in units per hour. (1: rate)"), number(rate, decimals: 2)) + } + + static func grams(_ grams: Double) -> String { + String(format: NSLocalizedString("%1$@ g", comment: "Format string for an amount of carbohydrate in grams. (1: grams)"), number(grams, decimals: 0)) + } + + static func signed(_ value: Double, decimals: Int = 2) -> String { + let s = number(abs(value), decimals: decimals) + if abs(value) < pow(10, -Double(decimals)) / 2 { return "+" + s } + return (value < 0 ? "−" : "+") + s + } + + static func time(_ date: Date) -> String { + date.formatted(date: .omitted, time: .shortened) + } + + /// "25 Oct 2026" + static func day(_ date: Date) -> String { + date.formatted(.dateTime.day().month(.abbreviated).year()) + } + + /// "Sun 25 Oct" + static func weekdayDay(_ date: Date) -> String { + date.formatted(.dateTime.weekday(.abbreviated).day().month(.abbreviated)) + } + + /// "12 – 25 Oct 2026", "28 Sep – 25 Oct 2026", "20 Dec 2025 – 2 Jan 2026", in the user's locale. + static func range(_ start: Date, _ end: Date, calendar: Calendar = .current) -> String { + if calendar.isDate(start, inSameDayAs: end) { + return day(end) + } + let formatter = DateIntervalFormatter() + formatter.calendar = calendar + formatter.dateTemplate = "dMMMy" + return formatter.string(from: start, to: end) + } + + /// "13d 11h", "9h 45m", "40m" in English; abbreviated in the user's language. + static func duration(_ interval: TimeInterval) -> String { + let minutes = Int((interval / 60).rounded()) + let formatter = DateComponentsFormatter() + formatter.unitsStyle = .abbreviated + formatter.allowedUnits = minutes >= 1440 ? [.day, .hour] : minutes >= 60 ? [.hour, .minute] : [.minute] + formatter.zeroFormattingBehavior = .dropAll + return formatter.string(from: TimeInterval(minutes * 60)) ?? "" + } +} + +// MARK: - Store conversion boundaries + +extension BolusEvent { + init?(dose: DoseEntry, in interval: DateInterval) { + guard dose.type == .bolus, dose.startDate >= interval.start, dose.startDate < interval.end else { return nil } + self.init(id: dose.syncIdentifier ?? "\(dose.startDate.timeIntervalSince1970)", date: dose.startDate, + units: dose.unitsInDeliverableIncrements, automatic: dose.automatic ?? false, + manuallyEntered: dose.manuallyEntered, dose: dose) + } +} + +extension LoopHistoryMath.BasalDelivery { + init?(dose: DoseEntry) { + let kind: BasalSegment.Kind + switch dose.type { + case .basal: kind = .scheduled + case .tempBasal: kind = .temp + case .suspend: kind = .suspended + default: return nil + } + let hours = dose.endDate.timeIntervalSince(dose.startDate) / 3600 + guard hours > 0 else { return nil } + self.init(start: dose.startDate, end: dose.endDate, + rate: kind == .suspended ? 0 : dose.unitsInDeliverableIncrements / hours, + kind: kind, automatic: dose.automatic ?? (kind == .temp)) + } +} + +extension BasalSegment { + init?(healthSample sample: HKQuantitySample, in interval: DateInterval) { + let hours = sample.endDate.timeIntervalSince(sample.startDate) / 3600 + guard hours > 0 else { return nil } + let scheduled = (sample.metadata?["com.loopkit.InsulinKit.MetadataKeyScheduledBasalRate"] as? HKQuantity)? + .doubleValue(for: HKUnit.internationalUnit().unitDivided(by: .hour())) + let segment = BasalSegment(start: sample.startDate, end: sample.endDate, + rate: sample.quantity.doubleValue(for: .internationalUnit()) / hours, + scheduledRate: scheduled, kind: .temp, + automatic: (sample.metadata?["com.loopkit.InsulinKit.MetadataKeyAutomaticallyIssued"] as? NSNumber)?.boolValue ?? false) + guard let clamped = segment.clamped(to: interval) else { return nil } + self = clamped + } +} diff --git a/Loop/Redesign/LoopHistoryProvider.swift b/Loop/Redesign/LoopHistoryProvider.swift new file mode 100644 index 0000000000..5bdc47d9f6 --- /dev/null +++ b/Loop/Redesign/LoopHistoryProvider.swift @@ -0,0 +1,450 @@ +// +// LoopHistoryProvider.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import Foundation +import HealthKit +import LoopKit +import LoopCore +import LoopAlgorithm +import os + +/// Reads glucose, insulin, carbs and loop runs for the redesigned screens. +/// +/// Loop keeps `LOOP_LOCAL_CACHE_DURATION_DAYS` (7) days in its own stores. Inside that window everything comes from +/// those stores, so manual and automatic doses, temp basals and the schedule are known. Older days come from Apple +/// Health, which has glucose, carbs and insulin split only into basal and bolus. +final class LoopHistoryProvider { + + private let deviceManager: DeviceDataManager + private let loopDataManager: LoopDataManager + private let settingsManager: SettingsManager + private let temporaryPresetsManager: TemporaryPresetsManager + private let dosingDecisionStore: DosingDecisionStoreProtocol + private let doseStore: DoseStore + private let carbStore: CarbStore + private let glucoseStore: GlucoseStore + private let healthStore = HKHealthStore() + private let log = OSLog(category: "LoopHistoryProvider") + private let recordsStartCache = OSAllocatedUnfairLock<(fetched: Date, value: Date)?>(initialState: nil) + + @MainActor + init(deviceManager: DeviceDataManager, loopDataManager: LoopDataManager, settingsManager: SettingsManager, temporaryPresetsManager: TemporaryPresetsManager) { + self.deviceManager = deviceManager + self.loopDataManager = loopDataManager + self.settingsManager = settingsManager + self.temporaryPresetsManager = temporaryPresetsManager + self.dosingDecisionStore = loopDataManager.dosingDecisionStore + self.doseStore = deviceManager.doseStore + self.carbStore = deviceManager.carbStore + self.glucoseStore = deviceManager.glucoseStore + } + + @MainActor + var unitStyle: GlucoseUnitStyle { + GlucoseUnitStyle(unit: deviceManager.displayGlucosePreference.unit) + } + + /// The start of what Loop keeps locally. + var localHistoryStart: Date { + Date().addingTimeInterval(-Bundle.main.localCacheDuration) + } + + @MainActor + var glucoseTargetRange: ClosedRange? { + guard let range = temporaryPresetsManager.effectiveCorrectionRange() else { return nil } + return range.lowerBound.doubleValue(for: .milligramsPerDeciliter)...range.upperBound.doubleValue(for: .milligramsPerDeciliter) + } + + /// The targets that actually applied, including dated presets and changes to the stored profile. + func glucoseTargetBands(in interval: DateInterval) async throws -> [TargetBand] { + if let store = settingsManager.settingsStore { + let previous = try await store.getStoredSettings(end: interval.start, limit: 1).first + if previous?.glucoseTargetRangeSchedule == nil { + let changes = try await store.getStoredSettings(start: interval.start, end: interval.end) + // LoopKit's historical query requires at least one valid profile, even if settings records exist. + guard changes.contains(where: { $0.glucoseTargetRangeSchedule != nil }) else { return [] } + } + } + return try await loopDataManager.glucoseTargetBands(in: interval) + } + + // MARK: - Days + + /// Everything in `window`; IOB and COB only when asked, as they are the slowest part. + func dayHistory(_ window: DateInterval, includeOnBoard: Bool = true) async throws -> DayHistory { + var history = DayHistory(window: window) + let boundary = localHistoryStart + + if window.end > boundary { + let local = DateInterval(start: max(window.start, boundary), end: window.end) + async let glucose = localGlucose(local) + async let insulin = localInsulin(local) + async let carbs = localCarbs(local) + history.glucose = try await glucose + (history.boluses, history.basal, history.scheduledBasal) = try await insulin + history.carbs = try await carbs + if includeOnBoard { + async let iob = localIOB(local) + async let cob = localCOB(local) + history.iob = try await iob + history.cob = try await cob + } + } + + if window.start < boundary { + let older = DateInterval(start: window.start, end: min(window.end, boundary)) + async let glucose = healthGlucose(older) + async let insulin = healthInsulin(older) + async let carbs = healthCarbs(older) + let (boluses, basal) = try await insulin + history.glucose = try await glucose + history.glucose + history.boluses = boluses + history.boluses + history.basal = basal + history.basal + history.carbs = try await carbs + history.carbs + if window.end <= boundary { + history.source = .health + } + } + try Task.checkCancellation() + return history + } + + // MARK: - Loop runs + + /// Loop runs in the local window, oldest first, with when the record of them begins. + func loopRuns() async throws -> (runs: [LoopHistoryMath.LoopRun], coverageStart: Date?) { + let start = localHistoryStart + var runs: [LoopHistoryMath.LoopRun] + if let store = dosingDecisionStore as? DosingDecisionStore { + // Decode only the fields a run needs, and only Loop's local window. + runs = try await store.getDosingDecisions(start: start, end: Date(), as: LoopRunRecord.self) + .filter { $0.reason == "loop" } + .map { record in + LoopHistoryMath.LoopRun(date: record.date, succeeded: (record.errors ?? []).isEmpty, + error: record.errors?.first.map { Self.describe(issue: $0.id) }) + } + } else { + runs = try await anchoredLoopRuns() + } + try Task.checkCancellation() + runs = runs.filter { $0.date >= start }.sorted { $0.date < $1.date } + return (runs, runs.first?.date) + } + + private struct LoopRunRecord: Decodable { + struct Issue: Decodable { let id: String } + let date: Date + let reason: String + let errors: [Issue]? + } + + /// Fetch each snapshot independently: overlapping reloads must not append the same anchored page twice. + private func anchoredLoopRuns() async throws -> [LoopHistoryMath.LoopRun] { + var anchor: DosingDecisionStore.QueryAnchor? + var runs: [LoopHistoryMath.LoopRun] = [] + while true { + let page: (DosingDecisionStore.QueryAnchor, [StoredDosingDecision]) = try await withCheckedThrowingContinuation { continuation in + dosingDecisionStore.executeDosingDecisionQuery(fromQueryAnchor: anchor, limit: 1000) { result in + switch result { + case .success(let anchor, let decisions): continuation.resume(returning: (anchor, decisions)) + case .failure(let error): continuation.resume(throwing: error) + } + } + } + try Task.checkCancellation() + anchor = page.0 + runs += page.1.filter { $0.reason == "loop" }.map { decision in + LoopHistoryMath.LoopRun(date: decision.date, succeeded: decision.errors.isEmpty, + error: decision.errors.first.map { Self.describe(issue: $0.id) }) + } + if page.1.count < 1000 { break } + } + return runs + } + + /// "glucoseTooOld" → "Glucose too old". Loop's own issue ids are localised; any other id falls back to the + /// id split into English words. + static func describe(issue id: String) -> String { + switch id { + case "configurationError": return NSLocalizedString("Configuration error", comment: "Reason Loop could not run: its settings are incomplete") + case "connectionError": return NSLocalizedString("Connection error", comment: "Reason Loop could not run: no connected devices, or a device connection failed") + case "missingDataError": return NSLocalizedString("Missing data error", comment: "Reason Loop could not run: data it needs was missing") + case "glucoseTooOld": return NSLocalizedString("Glucose too old", comment: "Reason Loop could not run: the latest glucose reading was too old") + case "invalidFutureGlucose": return NSLocalizedString("Invalid future glucose", comment: "Reason Loop could not run: a glucose reading had a timestamp in the future") + case "pumpDataTooOld": return NSLocalizedString("Pump data too old", comment: "Reason Loop could not run: the latest pump data was too old") + case "recommendationExpired": return NSLocalizedString("Recommendation expired", comment: "Reason Loop could not run: its dosing recommendation expired before it was used") + case "pumpSuspended": return NSLocalizedString("Pump suspended", comment: "Reason Loop could not run: insulin delivery on the pump was suspended") + case "pumpManagerError": return NSLocalizedString("Pump manager error", comment: "Reason Loop could not run: the pump reported an error") + case "unknownError": return NSLocalizedString("Unknown error", comment: "Reason Loop could not run: an unexpected error") + default: break + } + var words: [String] = [] + var current = "" + for character in id { + if character.isUppercase, !current.isEmpty { + words.append(current) + current = "" + } + current.append(character) + } + words.append(current) + let sentence = words.joined(separator: " ").lowercased() + return sentence.prefix(1).uppercased() + sentence.dropFirst() + } + + // MARK: - Loop's stores + + private func localGlucose(_ interval: DateInterval) async throws -> [GlucosePoint] { + do { + let samples = try await glucoseStore.getGlucoseSamples(start: interval.start, end: interval.end) + return samples + .filter { !$0.isDisplayOnly } + .map { GlucosePoint(date: $0.startDate, mgdl: $0.quantity.doubleValue(for: .milligramsPerDeciliter)) } + } catch { + os_log("Glucose fetch failed: %{public}@", log: log, type: .error, String(describing: error)) + throw error + } + } + + /// Doses for `interval`, with the start of Loop's insulin records (its earliest local dose). A found start is kept + /// for a few minutes so day pages and periods don't each read the whole local history; when it isn't known, + /// one read from the start of the local history serves both. No dose before `interval.end` says nothing about + /// later days, so that isn't kept. + private func doses(for interval: DateInterval) async throws -> (doses: [DoseEntry], recordsStart: Date?) { + let localStart = localHistoryStart + if interval.start <= localStart { + let doses = try await doseStore.getNormalizedDoseEntries(start: interval.start, end: interval.end) + return (doses, doses.map(\.startDate).min()) + } + if let cached = recordsStartCache.withLock({ $0 }), Date().timeIntervalSince(cached.fetched) < 300 { + return (try await doseStore.getNormalizedDoseEntries(start: interval.start, end: interval.end), cached.value) + } + let local = try await doseStore.getNormalizedDoseEntries(start: localStart, end: interval.end) + let recordsStart = local.map(\.startDate).min() + if let recordsStart { + recordsStartCache.withLock { $0 = (fetched: Date(), value: recordsStart) } + } + return (local.filter { + $0.startDate < interval.end && ($0.type == .bolus ? $0.startDate >= interval.start : $0.endDate > interval.start) + }, recordsStart) + } + + private func localInsulin(_ interval: DateInterval) async throws -> ([BolusEvent], [BasalSegment], [ScheduledBasalPoint]) { + let doses: [DoseEntry] + let recordsStart: Date? + do { + (doses, recordsStart) = try await self.doses(for: interval) + } catch { + os_log("Dose fetch failed: %{public}@", log: log, type: .error, String(describing: error)) + throw error + } + + let boluses = doses.compactMap { BolusEvent(dose: $0, in: interval) } + + let basalHistory = try await loopDataManager.settingsProvider.getBasalHistory(startDate: interval.start, endDate: interval.end) + let temporaryPresetsManager = self.temporaryPresetsManager + let overrides = await MainActor.run { + temporaryPresetsManager.presetHistory.getOverrideHistory(startDate: interval.start, endDate: interval.end) + } + // A resolved daily schedule repeats recent presets on every day. Use the stored profile and dated overrides. + let scheduleValues = overrides.applyBasal(over: basalHistory) + let schedule = scheduleValues.map { LoopHistoryMath.ScheduleValue(start: $0.startDate, end: $0.endDate, rate: $0.value) } + let deliveries = doses.compactMap { LoopHistoryMath.BasalDelivery(dose: $0) } + let now = Date() + let window = DateInterval(start: interval.start, end: max(interval.start, min(interval.end, now))) + // As in Loop's insulin totals: after the first record, unrecorded time runs at the pump's programmed rate. + let programmed = basalHistory.map { LoopHistoryMath.ScheduleValue(start: $0.startDate, end: $0.endDate, rate: $0.value) } + let basal = window.duration > 0 + ? LoopHistoryMath.basalSegments(window: window, schedule: schedule, deliveries: deliveries, + programmedRates: programmed, recordsStart: recordsStart) + : [] + let scheduled = schedule.map { ScheduledBasalPoint(start: max($0.start, interval.start), end: min($0.end, interval.end), rate: $0.rate) } + return (boluses, basal, scheduled) + } + + private func localCarbs(_ interval: DateInterval) async throws -> [CarbEvent] { + do { + let entries = try await carbStore.getCarbEntries(start: interval.start, end: interval.end) + return entries.map { entry in + CarbEvent(id: entry.syncIdentifier ?? "\(entry.startDate.timeIntervalSince1970)", + date: entry.startDate, + grams: entry.quantity.doubleValue(for: .gram), + absorptionHours: entry.absorptionTime.map { $0 / 3600 }, + foodType: entry.foodType, + createdByLoop: entry.createdByCurrentApp, + entry: entry) + } + } catch { + os_log("Carb fetch failed: %{public}@", log: log, type: .error, String(describing: error)) + throw error + } + } + + private func localIOB(_ interval: DateInterval) async throws -> [TimedValue] { + try await loopDataManager.insulinOnBoardTimeline(in: interval).map { TimedValue(date: $0.startDate, value: $0.value) } + } + + private func localCOB(_ interval: DateInterval) async throws -> [TimedValue] { + try await loopDataManager.carbsOnBoardTimeline(in: interval).map { TimedValue(date: $0.startDate, value: $0.quantity.doubleValue(for: .gram)) } + } + + // MARK: - Apple Health + + private func healthSamples(_ type: HKQuantityType, _ interval: DateInterval, options: HKQueryOptions = .strictStartDate) async throws -> [HKQuantitySample] { + guard interval.duration > 0 else { return [] } + let log = self.log + return try await withCheckedThrowingContinuation { continuation in + let predicate = HKQuery.predicateForSamples(withStart: interval.start, end: interval.end, options: options) + let sort = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true) + let query = HKSampleQuery(sampleType: type, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [sort]) { _, samples, error in + if let error { + os_log("Health query for %{public}@ failed: %{public}@", log: log, type: .error, type.identifier, String(describing: error)) + continuation.resume(throwing: error) + } else { + continuation.resume(returning: (samples as? [HKQuantitySample]) ?? []) + } + } + healthStore.execute(query) + } + } + + /// Several apps can write the same CGM reading to Health; keep one reading per 2½ minutes. + private func healthGlucose(_ interval: DateInterval) async throws -> [GlucosePoint] { + let samples = try await healthSamples(HKQuantityType(.bloodGlucose), interval) + var points: [GlucosePoint] = [] + let own = HKSource.default().bundleIdentifier + for sample in samples where !sample.isDisplayOnly + && (FeatureFlags.observeHealthKitGlucoseSamplesFromOtherApps || sample.sourceRevision.source.bundleIdentifier == own) { + if let last = points.last, sample.startDate.timeIntervalSince(last.date) < 150 { continue } + points.append(GlucosePoint(date: sample.startDate, mgdl: sample.quantity.doubleValue(for: .milligramsPerDeciliter))) + } + return points + } + + private func healthInsulin(_ interval: DateInterval) async throws -> ([BolusEvent], [BasalSegment]) { + let samples = try await healthSamples(HKQuantityType(.insulinDelivery), interval, options: []) + var boluses: [BolusEvent] = [] + var basal: [BasalSegment] = [] + let own = HKSource.default().bundleIdentifier + for sample in samples where FeatureFlags.observeHealthKitDoseSamplesFromOtherApps || sample.sourceRevision.source.bundleIdentifier == own { + let units = sample.quantity.doubleValue(for: .internationalUnit()) + let reason = (sample.metadata?[HKMetadataKeyInsulinDeliveryReason] as? NSNumber)?.intValue + let automaticFlag = (sample.metadata?["com.loopkit.InsulinKit.MetadataKeyAutomaticallyIssued"] as? NSNumber)?.boolValue + let automatic = automaticFlag ?? false + if reason == HKInsulinDeliveryReason.basal.rawValue { + if let segment = BasalSegment(healthSample: sample, in: interval) { + basal.append(segment) + } + } else if sample.startDate >= interval.start && sample.startDate < interval.end { + boluses.append(BolusEvent(id: sample.uuid.uuidString, date: sample.startDate, units: units, automatic: automatic, manuallyEntered: false, + automaticKnown: automaticFlag != nil)) + } + } + return (boluses, basal) + } + + private func healthCarbs(_ interval: DateInterval) async throws -> [CarbEvent] { + let samples = try await healthSamples(HKQuantityType(.dietaryCarbohydrates), interval) + let own = HKSource.default().bundleIdentifier + return samples + .filter { FeatureFlags.observeHealthKitCarbSamplesFromOtherApps || $0.sourceRevision.source.bundleIdentifier == own } + .map { sample in + CarbEvent(id: sample.uuid.uuidString, date: sample.startDate, grams: sample.quantity.doubleValue(for: .gram()), + absorptionHours: nil, foodType: sample.metadata?[HKMetadataKeyFoodType] as? String, + createdByLoop: sample.sourceRevision.source.bundleIdentifier == own) + } + } +} + +// MARK: - Loop mode history + +/// Records when closed loop is switched on and off, because Loop's stored runs don't say which mode they ran in. +/// The Trends screen can only call time "open loop" from the first record on. +final class LoopModeHistory { + static let shared = LoopModeHistory() + + private let defaults: UserDefaults + private let key = "com.loopkit.Loop.redesign.loopModeChanges" + private let maximumAge: TimeInterval = .days(100) + + init(defaults: UserDefaults = .standard) { + self.defaults = defaults + } + + var changes: [LoopHistoryMath.ModeChange] { + guard let data = defaults.data(forKey: key), + let changes = try? JSONDecoder().decode([LoopHistoryMath.ModeChange].self, from: data) else { + return [] + } + return changes + } + + func record(closedLoop: Bool, at date: Date = Date()) { + var changes = self.changes + guard changes.last?.closedLoop != closedLoop else { return } + changes.append(.init(date: date, closedLoop: closedLoop)) + // Keep the last change before the retention window: it establishes the mode at its start. + let cutoff = date.addingTimeInterval(-maximumAge) + if let anchor = changes.lastIndex(where: { $0.date < cutoff }), anchor > 0 { + changes.removeFirst(anchor) + } + if let data = try? JSONEncoder().encode(changes) { + defaults.set(data, forKey: key) + } + } +} + +// MARK: - Loop's on-board history + +extension LoopDataManager { + /// A historical chart must use the profile and presets from that time, rather than repeat today's targets. + func glucoseTargetBands(in interval: DateInterval) async throws -> [TargetBand] { + guard settingsProvider.settings.glucoseTargetRangeSchedule != nil else { return [] } + let schedule = try await settingsProvider.getTargetRangeHistory(startDate: interval.start, endDate: interval.end) + var bands = schedule.map { + TargetBand(start: max($0.startDate, interval.start), end: min($0.endDate, interval.end), + range: $0.value.lowerBound.doubleValue(for: .milligramsPerDeciliter)...$0.value.upperBound.doubleValue(for: .milligramsPerDeciliter)) + } + let overrides = temporaryPresetsManager.presetHistory.getOverrideHistory(startDate: interval.start, endDate: interval.end) + for override in overrides.sorted(by: { $0.startDate < $1.startDate }) { + guard let target = override.settings.targetRange, + let active = override.activeInterval.intersection(with: interval), active.duration > 0 else { continue } + bands = bands.flatMap { band -> [TargetBand] in + var pieces: [TargetBand] = [] + if band.start < active.start { + pieces.append(TargetBand(start: band.start, end: min(band.end, active.start), range: band.range)) + } + if band.end > active.end { + pieces.append(TargetBand(start: max(band.start, active.end), end: band.end, range: band.range)) + } + return pieces + } + [TargetBand(start: active.start, end: active.end, + range: target.lowerBound.doubleValue(for: .milligramsPerDeciliter)...target.upperBound.doubleValue(for: .milligramsPerDeciliter))] + } + return bands.filter { $0.end > $0.start }.sorted { $0.start < $1.start } + } + + /// Insulin on board across `interval`, from the doses and basal history Loop keeps, as `iobValues` is for the display window. + func insulinOnBoardTimeline(in interval: DateInterval) async throws -> [InsulinValue] { + let dosesStart = interval.start.addingTimeInterval(-InsulinMath.defaultInsulinActivityDuration) + let doses = try await doseStore.getNormalizedDoseEntries(start: dosesStart, end: interval.end) + .map { $0.simpleDose(with: insulinModel(for: $0.insulinType)) } + let basalStart = min(dosesStart, doses.map(\.startDate).min() ?? dosesStart) + let basal = try await settingsProvider.getBasalHistory(startDate: basalStart, endDate: interval.end) + let overrides = temporaryPresetsManager.presetHistory.getOverrideHistory(startDate: basalStart, endDate: interval.end) + return doses.annotated(with: overrides.applyBasal(over: basal).algorithmSchedule()) + .insulinOnBoardTimeline(from: interval.start, to: interval.end) + .map(\.loopKit) + } + + /// Carbs on board across `interval`, using the same absorption review as Loop's carb screen. + func carbsOnBoardTimeline(in interval: DateInterval) async throws -> [CarbValue] { + let review = try await fetchCarbAbsorptionReview(start: interval.start.addingTimeInterval(-CarbMath.maximumAbsorptionTimeInterval), end: interval.end) + return review.carbStatuses.dynamicCarbsOnBoard(from: interval.start, to: interval.end, absorptionModel: carbAbsorptionModel.model) + .map(\.loopKit) + } +} diff --git a/Loop/Redesign/TrendsComponents.swift b/Loop/Redesign/TrendsComponents.swift new file mode 100644 index 0000000000..5f93573c36 --- /dev/null +++ b/Loop/Redesign/TrendsComponents.swift @@ -0,0 +1,766 @@ +// +// TrendsComponents.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import SwiftUI +import Charts + +private typealias P = LoopDesign.Palette + +// MARK: - RangeSelector + +enum TrendsPeriod: Int, CaseIterable, Identifiable { + case day = 1 + case week = 7 + case twoWeeks = 14 + case month = 30 + case quarter = 90 + + var id: Int { rawValue } + + var label: String { + self == .day + ? NSLocalizedString("Day (Trends period)", value: "Day", comment: "Trends period picker: a period of one calendar day (not daytime)") + : String(format: NSLocalizedString("%1$@ d", comment: "Trends period picker, short label for a period of several days, e.g. 7 d. (1: number of days)"), + LoopFormat.number(Double(rawValue), decimals: 0)) + } + + var accessibilityName: String { + self == .day + ? NSLocalizedString("Day (Trends period)", value: "Day", comment: "Trends period picker: a period of one calendar day (not daytime)") + : String.localizedStringWithFormat(NSLocalizedString("%lld days", comment: "Trends period picker, VoiceOver name for a period of several days. (1: number of days)"), rawValue) + } + + /// The period ending on `endDay` (inclusive), cut off at `now` when it ends today. + func interval(endingOn endDay: Date, now: Date = Date(), calendar: Calendar = .current) -> DateInterval { + let endOfDay = calendar.date(byAdding: .day, value: 1, to: calendar.startOfDay(for: endDay))! + let start = calendar.date(byAdding: .day, value: -rawValue, to: endOfDay)! + return DateInterval(start: start, end: min(endOfDay, now)) + } +} + +struct RangeSelectorView: View { + @Binding var period: TrendsPeriod + @Binding var endDay: Date + var now: Date = Date() + + private var calendar: Calendar { .current } + + private var canStepForward: Bool { + !calendar.isDate(endDay, inSameDayAs: now) && endDay < now + } + + var body: some View { + VStack(spacing: LoopDesign.Space.s3) { + HStack(spacing: 2) { + ForEach(TrendsPeriod.allCases) { option in + Button { + withAnimation(.snappy(duration: 0.2)) { period = option } + } label: { + Text(option.label) + .font(.footnote.weight(.semibold)) + .foregroundStyle(P.ink) + .frame(maxWidth: .infinity, minHeight: 30) + .background { + if option == period { + Capsule().fill(P.controlThumb).shadow(color: .black.opacity(0.12), radius: 3, y: 1) + } + } + .contentShape(Capsule()) + } + .buttonStyle(.plain) + .accessibilityLabel(option.accessibilityName) + .accessibilityAddTraits(option == period ? .isSelected : []) + } + } + .padding(2) + .background(P.surfaceSunken, in: Capsule()) + + HStack { + StepButton(systemName: "chevron.left", label: NSLocalizedString("Previous period", comment: "VoiceOver label for the Trends button that moves back one period")) { step(-1) } + Spacer() + Text(LoopFormat.range(period.interval(endingOn: endDay, now: now).start, endDay)) + .font(LoopDesign.Typeface.headline.monospacedDigit()) + .foregroundStyle(P.ink) + Spacer() + StepButton(systemName: "chevron.right", label: NSLocalizedString("Next period", comment: "VoiceOver label for the Trends button that moves forward one period"), enabled: canStepForward) { step(1) } + } + } + } + + private func step(_ direction: Int) { + guard let next = calendar.date(byAdding: .day, value: direction * period.rawValue, to: endDay) else { return } + endDay = min(next, now) + } +} + +// MARK: - StatGrid + +struct StatTile: Identifiable { + let label: String + let value: String + let unit: String? + let detail: String? + var valueColor: Color = LoopDesign.Palette.ink + var id: String { label } +} + +struct StatGridView: View { + @Environment(\.dynamicTypeSize) private var typeSize + let tiles: [StatTile] + + /// Pairs of tiles, so both tiles in a row take the taller one's height; one per row at accessibility sizes. + private var rows: [[StatTile]] { + let shown = Array(tiles.prefix(6)) + let perRow = typeSize.isAccessibilitySize ? 1 : 2 + return stride(from: 0, to: shown.count, by: perRow).map { Array(shown[$0.. some View { + VStack(alignment: .leading, spacing: 2) { + Text(tile.label) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + HStack(alignment: .firstTextBaseline, spacing: 2) { + Text(tile.value) + .font(LoopDesign.Typeface.statValue) + .foregroundStyle(tile.valueColor) + .minimumScaleFactor(0.7) + .lineLimit(1) + if let unit = tile.unit { + Text(unit) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + } + if let detail = tile.detail { + Text(detail) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + .lineLimit(2) + } + } + .padding(LoopDesign.Space.s3) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .background(P.surfaceSunken, in: RoundedRectangle(cornerRadius: LoopDesign.Radius.md, style: .continuous)) + .accessibilityElement(children: .combine) + } +} + +// MARK: - TimeInRange + +extension LoopHistoryMath.RangeBand { + /// The band's name in the user's language. + var localizedName: String { + switch self { + case .veryHigh: return NSLocalizedString("Very high", comment: "Time in range band: glucose above 250 mg/dL (13.9 mmol/L)") + case .high: return NSLocalizedString("High", comment: "Time in range band: glucose 181–250 mg/dL (10.1–13.9 mmol/L)") + case .inRange: return NSLocalizedString("In range", comment: "Time in range band: glucose 70–180 mg/dL (3.9–10.0 mmol/L)") + case .low: return NSLocalizedString("Low", comment: "Time in range band: glucose 54–69 mg/dL (3.0–3.8 mmol/L)") + case .veryLow: return NSLocalizedString("Very low", comment: "Time in range band: glucose below 54 mg/dL (3.0 mmol/L)") + } + } + + /// Consensus thresholds with the user's decimal separator, e.g. "3,9–10,0". + func localizedThresholds(_ unit: GlucoseUnitStyle) -> String { + let decimals = unit.isMmol ? 1 : 0 + let n: (Double) -> String = { LoopFormat.number($0, decimals: decimals) } + switch (self, unit.isMmol) { + case (.veryHigh, true): return "> " + n(13.9) + case (.high, true): return n(10.1) + "–" + n(13.9) + case (.inRange, true): return n(3.9) + "–" + n(10.0) + case (.low, true): return n(3.0) + "–" + n(3.8) + case (.veryLow, true): return "< " + n(3.0) + case (.veryHigh, false): return "> " + n(250) + case (.high, false): return n(181) + "–" + n(250) + case (.inRange, false): return n(70) + "–" + n(180) + case (.low, false): return n(54) + "–" + n(69) + case (.veryLow, false): return "< " + n(54) + } + } +} + +/// "12%" in the user's language. +private func percentText(_ value: Double, decimals: Int = 0) -> String { + String(format: NSLocalizedString("%1$@%%", comment: "A percentage. (1: number)"), LoopFormat.number(value, decimals: decimals)) +} + +struct TimeInRangeView: View { + @Environment(\.dynamicTypeSize) private var typeSize + + /// Fractions per band, 0–1. + let fractions: [LoopHistoryMath.RangeBand: Double] + let unit: GlucoseUnitStyle + + private var percentages: [LoopHistoryMath.RangeBand: Int] { LoopHistoryMath.roundedPercentages(fractions) } + + static func color(_ band: LoopHistoryMath.RangeBand) -> Color { + switch band { + case .veryHigh: return P.rangeVeryHigh + case .high: return P.rangeHigh + case .inRange: return P.rangeIn + case .low: return P.rangeLow + case .veryLow: return P.rangeVeryLow + } + } + + var body: some View { + HStack(alignment: .top, spacing: LoopDesign.Space.s4) { + bar + .frame(width: 14) + VStack(spacing: LoopDesign.Space.s2) { + ForEach(LoopHistoryMath.RangeBand.allCases) { band in + row(band) + } + } + } + .fixedSize(horizontal: false, vertical: true) + } + + private var bar: some View { + GeometryReader { geometry in + let minimum: CGFloat = 4 + let bands = LoopHistoryMath.RangeBand.allCases + let raw = bands.map { CGFloat(fractions[$0] ?? 0) } + let floors = raw.map { $0 > 0 ? max(minimum, $0 * geometry.size.height) : 0 } + let spare = geometry.size.height - floors.reduce(0, +) + // Trim the space the minimums borrowed from the largest band. + let largest = raw.indices.max { raw[$0] < raw[$1] } ?? 2 + VStack(spacing: 1) { + ForEach(Array(bands.enumerated()), id: \.offset) { index, band in + let height = index == largest ? max(0, floors[index] + spare) : floors[index] + if height > 0 { + Rectangle().fill(Self.color(band)).frame(height: max(0, height - 1)) + } + } + } + .clipShape(Capsule()) + } + .accessibilityHidden(true) + } + + private func row(_ band: LoopHistoryMath.RangeBand) -> some View { + let fraction = fractions[band] ?? 0 + let hours = fraction * 24 + let thresholds = Text(band.localizedThresholds(unit)) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + return VStack(alignment: .leading, spacing: 0) { + HStack(alignment: .firstTextBaseline, spacing: LoopDesign.Space.s2) { + Text(percentText(Double(percentages[band] ?? 0))) + .font(LoopDesign.Typeface.statValueSmall) + .foregroundStyle(P.ink) + .frame(minWidth: typeSize.isAccessibilitySize ? nil : 52, alignment: .trailing) + Text(band.localizedName) + .font(LoopDesign.Typeface.subhead) + .foregroundStyle(P.ink) + if !typeSize.isAccessibilitySize { + thresholds + } + Spacer(minLength: LoopDesign.Space.s1) + Text(String(format: NSLocalizedString("%1$@ h", comment: "Time in range row: average hours a day spent in the band, abbreviated. (1: hours with one decimal)"), + LoopFormat.number(hours, decimals: 1))) + .font(LoopDesign.Typeface.footnote.monospacedDigit()) + .foregroundStyle(P.inkMuted) + } + if typeSize.isAccessibilitySize { + thresholds + } + } + .accessibilityElement(children: .ignore) + .accessibilityLabel(String(format: NSLocalizedString("%1$@, %2$@ %3$@: %4$@ percent, %5$@ hours a day", comment: "VoiceOver label for a Time in range row. (1: band name, e.g. In range, 2: glucose thresholds, e.g. 3.9–10.0, 3: glucose unit, 4: percentage number, 5: average hours a day with one decimal)"), + band.localizedName, band.localizedThresholds(unit), unit.unitName, + LoopFormat.number(Double(percentages[band] ?? 0), decimals: 0), LoopFormat.number(hours, decimals: 1))) + } +} + +// MARK: - AGP + +struct TrendsAGPChart: View { + let slots: [LoopHistoryMath.AGPSlot] + let readings: [GlucosePoint] + let days: Int + let unit: GlucoseUnitStyle + let targetRange: ClosedRange? + var height: CGFloat = 180 + + private func v(_ mgdl: Double) -> Double { + min(max(unit.value(mgdl), unit.agpDomain.lowerBound), unit.agpDomain.upperBound) + } + + private static let hourTicks = [0, 360, 720, 1080, 1440] + + private static func label(minute: Int) -> String { + let minute = minute % 1440 + let date = Calendar.current.date(bySettingHour: minute / 60, minute: minute % 60, second: 0, of: Date())! + return DayAxis.hourLabel(date) + } + + var enoughDays: Bool { days >= 5 } + + var body: some View { + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + if readings.isEmpty { + ChartPlaceholder(message: NSLocalizedString("No CGM data in this period", comment: "Trends chart placeholder when the period has no glucose readings"), height: height) + } else { + chart.frame(height: height) + if !enoughDays { + Text(String(format: NSLocalizedString("Not enough days for a pattern: %1$@ of 5", comment: "Trends Daily pattern footnote when fewer than 5 days have readings; the chart shows raw readings instead. (1: days with readings)"), + LoopFormat.number(Double(days), decimals: 0))) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + } + } + } + + private func minuteOfDay(_ date: Date) -> Int { + let parts = Calendar.current.dateComponents([.hour, .minute], from: date) + return (parts.hour ?? 0) * 60 + (parts.minute ?? 0) + } + + private var chart: some View { + Chart { + if let targetRange { + RectangleMark(xStart: .value("Start", 0), xEnd: .value("End", 1440), + yStart: .value("Low", v(targetRange.lowerBound)), yEnd: .value("High", v(targetRange.upperBound))) + .foregroundStyle(P.targetBand) + } + if enoughDays { + ForEach(slots, id: \.minute) { slot in + AreaMark(x: .value("Time", slot.minute), yStart: .value("5%", v(slot.p5)), yEnd: .value("95%", v(slot.p95)), series: .value("Band", "outer")) + .foregroundStyle(P.agpOuter) + .interpolationMethod(.monotone) + } + ForEach(slots, id: \.minute) { slot in + AreaMark(x: .value("Time", slot.minute), yStart: .value("25%", v(slot.p25)), yEnd: .value("75%", v(slot.p75)), series: .value("Band", "inner")) + .foregroundStyle(P.agpInner) + .interpolationMethod(.monotone) + } + ForEach(slots, id: \.minute) { slot in + LineMark(x: .value("Time", slot.minute), y: .value("Median", v(slot.p50)), series: .value("Band", "median")) + .foregroundStyle(P.glucose) + .lineStyle(StrokeStyle(lineWidth: 2.5)) + .interpolationMethod(.monotone) + } + } else { + ForEach(Array(readings.enumerated()), id: \.offset) { _, point in + PointMark(x: .value("Time", minuteOfDay(point.date)), y: .value("Glucose", v(point.mgdl))) + .symbolSize(8) + .foregroundStyle(P.glucose) + } + } + } + .chartXScale(domain: 0...1440) + .chartYScale(domain: unit.agpDomain) + .chartXAxis { + AxisMarks(values: Self.hourTicks) { value in + AxisGridLine().foregroundStyle(P.grid) + AxisValueLabel { + if let minute = value.as(Int.self) { Text(Self.label(minute: minute)) } + } + .font(LoopDesign.Typeface.caption) + .foregroundStyle(P.inkMuted) + } + } + .chartYAxis { + AxisMarks(position: .leading, values: unit.agpTicks) { _ in + AxisGridLine().foregroundStyle(P.grid) + AxisValueLabel().font(LoopDesign.Typeface.caption).foregroundStyle(P.inkMuted) + } + } + .accessibilityElement(children: .ignore) + .accessibilityLabel(accessibilitySummary) + } + + private var accessibilitySummary: String { + guard enoughDays, let high = slots.max(by: { $0.p50 < $1.p50 }), let low = slots.min(by: { $0.p50 < $1.p50 }) else { + return NSLocalizedString("Daily pattern: not enough days", comment: "VoiceOver label for the Trends Daily pattern chart when fewer than 5 days have readings") + } + let span = String.localizedStringWithFormat(NSLocalizedString("%lld days", comment: "A number of days, e.g. a Trends period or how many days are averaged. (1: number of days)"), days) + return String(format: NSLocalizedString("Daily pattern over %1$@. Median highest %2$@ at %3$@, lowest %4$@ at %5$@", comment: "VoiceOver label for the Trends Daily pattern chart. (1: days with readings, e.g. 14 days; 2: highest median glucose; 3: time of day; 4: lowest median glucose; 5: time of day)"), + span, unit.format(high.p50), Self.label(minute: high.minute), unit.format(low.p50), Self.label(minute: low.minute)) + } +} + +// MARK: - InsulinSplit + +/// Basal against bolus in one capsule: basal, then manual bolus, then automatic bolus. +struct InsulinSplitBar: View { + let split: InsulinSplit + /// A 6-point line for list headers. It is still tall enough to show the automatic bolus stripes. + var hairline = false + @Environment(\.colorScheme) private var scheme + + private enum Part: CaseIterable { case basal, manual, automatic } + + private func units(_ part: Part) -> Double { + switch part { + case .basal: return split.basal + // Unclassified bolus draws as plain bolus. + case .manual: return split.manualBolus + split.unclassifiedBolus + case .automatic: return split.automaticBolus + } + } + + var body: some View { + GeometryReader { geometry in + let parts = Part.allCases.filter { units($0) > 0 } + let gap: CGFloat = hairline ? 1.5 : 2.5 + let width = geometry.size.width - gap * CGFloat(max(0, parts.count - 1)) + let minimum: CGFloat = hairline ? 2 : 4 + let widths = parts.map { max(minimum, width * units($0) / max(split.total, .ulpOfOne)) } + // Take what the minimums add from the widest part, so the bar keeps its length. + let excess = widths.reduce(0, +) - width + let widest = widths.indices.max { widths[$0] < widths[$1] } + HStack(spacing: gap) { + ForEach(Array(parts.enumerated()), id: \.offset) { index, part in + segment(part).frame(width: max(0, widths[index] - (index == widest ? excess : 0))) + } + } + .clipShape(Capsule()) + } + .frame(height: hairline ? 6 : 14) + .accessibilityHidden(true) + } + + @ViewBuilder + private func segment(_ part: Part) -> some View { + switch part { + case .basal: + Rectangle().fill(LoopDesign.Palette.basal) + .overlay(alignment: .trailing) { + if !hairline { Rectangle().fill(LoopDesign.Palette.basalEdge).frame(width: 2) } + } + case .manual: + Rectangle().fill(LoopDesign.Palette.bolus) + case .automatic: + Rectangle().fill(Hatch.paint(LoopDesign.Palette.bolus, scheme: scheme)) + } + } +} + +/// The basal and bolus card body: the bar, units and shares, how the bolus divides, and the total. +struct InsulinSplitView: View { + let split: InsulinSplit + /// Today so far, rather than a whole day or a per-day average. + var partial = false + + var body: some View { + VStack(alignment: .leading, spacing: LoopDesign.Space.s3) { + InsulinSplitBar(split: split) + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + row(.basal, NSLocalizedString("Basal", comment: "Basal and bolus row: insulin delivered as basal"), split.basal, split.basalPercent) + row(.solid(LoopDesign.Palette.bolus), NSLocalizedString("Bolus", comment: "Basal and bolus row: insulin delivered as bolus, manual and automatic"), split.bolus, split.bolusPercent) + if split.isClassified && split.bolus > 0 { + FlowLayout(spacing: LoopDesign.Space.s3, lineSpacing: 2) { + part(.solid(LoopDesign.Palette.bolus), String(format: NSLocalizedString("Manual %1$@", comment: "Basal and bolus: bolus the person gave. (1: units, e.g. 16.2 U)"), LoopFormat.totalUnits(split.manualBolus))) + part(.hatch(LoopDesign.Palette.bolus), String(format: NSLocalizedString("Automatic %1$@", comment: "Basal and bolus: bolus Loop gave automatically. (1: units, e.g. 5.4 U)"), LoopFormat.totalUnits(split.automaticBolus))) + } + .font(LoopDesign.Typeface.footnote.monospacedDigit()) + .foregroundStyle(LoopDesign.Palette.inkMuted) + .padding(.leading, 10 + LoopDesign.Space.s2) + } + Divider().padding(.vertical, 2) + HStack(alignment: .firstTextBaseline) { + Text(partial + ? NSLocalizedString("Total so far", comment: "Basal and bolus total row for today, which isn't over yet") + : NSLocalizedString("Total", comment: "Basal and bolus total row: basal plus bolus")) + .font(LoopDesign.Typeface.subhead.weight(.semibold)) + .foregroundStyle(LoopDesign.Palette.ink) + Spacer() + Text(LoopFormat.totalUnits(split.total)) + .font(LoopDesign.Typeface.statValueSmall) + .foregroundStyle(LoopDesign.Palette.insulinInk) + } + } + } + .accessibilityElement(children: .ignore) + .accessibilityLabel(Self.accessibilityLabel(split, partial: partial)) + } + + private func row(_ swatch: LegendItem.Swatch, _ label: String, _ units: Double, _ percent: Int) -> some View { + HStack(alignment: .firstTextBaseline, spacing: LoopDesign.Space.s2) { + LegendSwatch(swatch: swatch).alignmentGuide(.firstTextBaseline) { $0[.bottom] - 1 } + Text(label) + .font(LoopDesign.Typeface.subhead) + .foregroundStyle(LoopDesign.Palette.ink) + Spacer() + Text(LoopFormat.totalUnits(units)) + .font(LoopDesign.Typeface.body.weight(.semibold).monospacedDigit()) + .foregroundStyle(LoopDesign.Palette.ink) + Text(verbatim: "\(percent)%") + .font(LoopDesign.Typeface.footnote.monospacedDigit()) + .foregroundStyle(LoopDesign.Palette.inkMuted) + .frame(minWidth: 38, alignment: .trailing) + } + } + + private func part(_ swatch: LegendItem.Swatch, _ text: String) -> some View { + HStack(spacing: LoopDesign.Space.s1) { + LegendSwatch(swatch: swatch) + Text(text) + } + } + + static func accessibilityLabel(_ split: InsulinSplit, partial: Bool) -> String { + var parts = [ + String(format: NSLocalizedString("Basal %1$@, %2$@ percent", comment: "VoiceOver: basal units and share. (1: units, 2: percentage number)"), LoopFormat.totalUnits(split.basal), "\(split.basalPercent)"), + String(format: NSLocalizedString("Bolus %1$@, %2$@ percent", comment: "VoiceOver: bolus units and share. (1: units, 2: percentage number)"), LoopFormat.totalUnits(split.bolus), "\(split.bolusPercent)"), + ] + if split.isClassified && split.bolus > 0 { + parts.append(String(format: NSLocalizedString("%1$@ manual, %2$@ automatic", comment: "VoiceOver: how the bolus divides. (1: manual units, 2: automatic units)"), + LoopFormat.totalUnits(split.manualBolus), LoopFormat.totalUnits(split.automaticBolus))) + } + parts.append(String(format: partial + ? NSLocalizedString("Total so far %1$@", comment: "VoiceOver: today's total insulin so far. (1: units)") + : NSLocalizedString("Total %1$@", comment: "VoiceOver: total insulin. (1: units)"), + LoopFormat.totalUnits(split.total))) + return parts.joined(separator: ". ") + } +} + +// MARK: - InsulinDailyTotals + +struct InsulinDailyTotalsView: View { + let totals: [LoopHistoryMath.DayTotal] + /// Weekly averages instead of days, for periods past 30 days. + let weekly: Bool + /// Calculated from the original daily totals, even when the bars are weekly averages. + let average: Double + var height: CGFloat = 170 + var onSelectDay: ((Date) -> Void)? = nil + @Environment(\.colorScheme) private var scheme + + private var yMax: Double { + let top = max(totals.map(\.total).max() ?? 0, average) + return max(10, IOBCOBChartView.niceStep(top / 4) * 5) + } + + var body: some View { + if totals.allSatisfy({ $0.total == 0 }) { + ChartPlaceholder(message: NSLocalizedString("No insulin recorded in this period", comment: "Trends Insulin per day chart placeholder when no insulin was delivered in the period"), height: height) + } else { + chart.frame(height: height) + } + } + + private var unitWidth: Calendar.Component { weekly ? .weekOfYear : .day } + + private var chart: some View { + Chart { + ForEach(totals) { day in + BarMark(x: .value("Day", day.day, unit: unitWidth), yStart: .value("Base", 0), yEnd: .value("Basal", day.basal), width: .ratio(0.7)) + .foregroundStyle(P.basal) + BarMark(x: .value("Day", day.day, unit: unitWidth), yStart: .value("Basal", max(0, day.basal - yMax * 0.006)), yEnd: .value("Edge", day.basal), width: .ratio(0.7)) + .foregroundStyle(P.basalEdge) + // A thin gap under each bolus part, so it never touches the part below it + let gap = yMax * 0.01 + let manualTop = day.basal + day.split.manualBolus + day.split.unclassifiedBolus + BarMark(x: .value("Day", day.day, unit: unitWidth), yStart: .value("Basal", day.basal + (manualTop - day.basal > gap * 2 ? gap : 0)), yEnd: .value("Bolus", manualTop), width: .ratio(0.7)) + .foregroundStyle(P.bolus) + if day.split.automaticBolus > 0 { + BarMark(x: .value("Day", day.day, unit: unitWidth), yStart: .value("Bolus", manualTop + (day.total - manualTop > gap * 2 ? gap : 0)), yEnd: .value("Total", day.total), width: .ratio(0.7)) + .foregroundStyle(Hatch.paint(P.bolus, scheme: scheme)) + } + } + RuleMark(y: .value("Average", average)) + .foregroundStyle(P.ink) + .lineStyle(StrokeStyle(lineWidth: 1, dash: [4, 3])) + .annotation(position: .top, alignment: .trailing, spacing: 2) { + Text(LoopFormat.number(average, decimals: 1)) + .font(LoopDesign.Typeface.caption.weight(.semibold)) + .foregroundStyle(P.ink) + } + } + .chartYScale(domain: 0...yMax) + .chartXAxis { + AxisMarks(values: axisDates) { value in + AxisValueLabel(centered: true) { + if let date = value.as(Date.self) { + Text(axisLabel(date)) + .multilineTextAlignment(.center) + } + } + .font(LoopDesign.Typeface.caption) + .foregroundStyle(P.inkMuted) + } + } + .chartYAxis { + AxisMarks(position: .leading) { _ in + AxisGridLine().foregroundStyle(P.grid) + AxisValueLabel().font(LoopDesign.Typeface.caption).foregroundStyle(P.inkMuted) + } + } + .chartOverlay { proxy in + GeometryReader { geometry in + Rectangle().fill(.clear).contentShape(Rectangle()) + .onTapGesture { location in + guard let onSelectDay, !weekly, let frame = proxy.plotFrame else { return } + let x = location.x - geometry[frame].origin.x + if let date: Date = proxy.value(atX: x), + let day = totals.min(by: { abs($0.day.addingTimeInterval(43200).timeIntervalSince(date)) < abs($1.day.addingTimeInterval(43200).timeIntervalSince(date)) }) { + onSelectDay(day.day) + } + } + } + } + .accessibilityElement(children: .ignore) + .accessibilityLabel(String(format: weekly + ? NSLocalizedString("Insulin per day, weekly average, average %1$@", comment: "VoiceOver label for the Trends Insulin per day chart when bars are weekly averages. (1: average insulin per day, e.g. 32.1 U)") + : NSLocalizedString("Insulin per day, average %1$@", comment: "VoiceOver label for the Trends Insulin per day chart. (1: average insulin per day, e.g. 32.1 U)"), + LoopFormat.totalUnits(average))) + } + + private var axisDates: [Date] { + if weekly { + let stride = max(1, Int(ceil(Double(totals.count) / 6))) + return totals.enumerated().compactMap { index, total in index % stride == 0 ? total.day : nil } + } + if totals.count > 14 { + return totals.map(\.day).filter { Calendar.current.component(.weekday, from: $0) == 2 } + } + return totals.map(\.day) + } + + private func axisLabel(_ date: Date) -> String { + if weekly { + return date.formatted(.dateTime.day().month(.abbreviated)) + } + let weekday = String(date.formatted(.dateTime.weekday(.narrow))) + if totals.count <= 14 { + let first = totals.first?.day == date, last = totals.last?.day == date + let isMonday = Calendar.current.component(.weekday, from: date) == 2 + return first || last || isMonday ? "\(weekday)\n\(date.formatted(.dateTime.day()))" : weekday + } + // 30 days: label every Monday only. + return Calendar.current.component(.weekday, from: date) == 2 ? date.formatted(.dateTime.day().month(.abbreviated)) : "" + } +} + +// MARK: - LoopModeBar + +struct LoopModeBarView: View { + let spans: [LoopModeSpan] + + private var total: TimeInterval { spans.reduce(0) { $0 + $1.duration } } + + static func color(_ mode: LoopModeSpan.Mode) -> Color { + switch mode { + case .closed: return P.statusAutomated + case .limited: return P.statusWarning + case .open: return P.inkMuted + } + } + + static func name(_ mode: LoopModeSpan.Mode) -> String { + switch mode { + case .closed: return NSLocalizedString("Closed loop", comment: "Trends Loop status: time Loop was running in closed loop") + case .limited: return NSLocalizedString("Limited", comment: "Trends Loop status: time closed loop was on but Loop wasn't completing runs") + case .open: return NSLocalizedString("Open loop / manual", comment: "Trends Loop status: time closed loop was switched off") + } + } + + private let modes: [LoopModeSpan.Mode] = [.closed, .limited, .open] + + var body: some View { + if total <= 0 { + ChartPlaceholder(message: NSLocalizedString("No loop runs recorded in this period", comment: "Trends Loop status placeholder when there is no Loop run history for the period"), height: 60) + } else { + VStack(spacing: LoopDesign.Space.s3) { + bar.frame(height: 10) + ForEach(modes, id: \.self) { mode in + row(mode) + } + } + } + } + + private func fraction(_ mode: LoopModeSpan.Mode) -> Double { + LoopHistoryMath.duration(of: mode, in: spans) / total + } + + private var bar: some View { + GeometryReader { geometry in + let minimum: CGFloat = 4 + let widths = modes.map { fraction($0) > 0 ? max(minimum, fraction($0) * geometry.size.width) : 0 } + let excess = widths.reduce(0, +) - geometry.size.width + HStack(spacing: 1) { + ForEach(Array(modes.enumerated()), id: \.offset) { index, mode in + if widths[index] > 0 { + Rectangle() + .fill(Self.color(mode)) + .frame(width: max(0, index == 0 ? widths[index] - excess - 2 : widths[index])) + } + } + } + .clipShape(Capsule()) + } + .accessibilityHidden(true) + } + + private func row(_ mode: LoopModeSpan.Mode) -> some View { + let duration = LoopHistoryMath.duration(of: mode, in: spans) + return HStack(spacing: LoopDesign.Space.s2) { + Circle().fill(Self.color(mode)).frame(width: 8, height: 8) + Text(Self.name(mode)) + .font(LoopDesign.Typeface.subhead) + .foregroundStyle(P.ink) + Text("· \(LoopFormat.duration(duration))") + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + Spacer() + Text(percentText(fraction(mode) * 100, decimals: 1)) + .font(LoopDesign.Typeface.statValueSmall) + .foregroundStyle(P.ink) + } + .accessibilityElement(children: .combine) + } +} + +struct LoopGapsList: View { + let spans: [LoopModeSpan] + + private var gaps: [LoopModeSpan] { + spans.filter { $0.mode != .closed }.sorted { $0.start > $1.start } + } + + var body: some View { + List { + if gaps.isEmpty { + Text(NSLocalizedString("No gaps: Loop ran closed loop all through this period.", comment: "Trends Gaps list when Loop was in closed loop for the whole period")) + .foregroundStyle(P.inkMuted) + } + ForEach(Array(gaps.enumerated()), id: \.offset) { _, gap in + HStack(alignment: .top, spacing: LoopDesign.Space.s3) { + Circle().fill(LoopModeBarView.color(gap.mode)).frame(width: 8, height: 8).padding(.top, 6) + VStack(alignment: .leading, spacing: 2) { + Text(gap.cause ?? LoopModeBarView.name(gap.mode)) + .font(LoopDesign.Typeface.body) + Text("\(gap.start.formatted(.dateTime.weekday(.abbreviated).day().month(.abbreviated).hour().minute())) · \(LoopFormat.duration(gap.duration))") + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + } + .accessibilityElement(children: .combine) + } + } + .navigationTitle(NSLocalizedString("Gaps", comment: "Trends Loop status button and screen title: list of times Loop wasn't in closed loop")) + .navigationBarTitleDisplayMode(.inline) + } +} diff --git a/Loop/Redesign/TrendsScreen.swift b/Loop/Redesign/TrendsScreen.swift new file mode 100644 index 0000000000..efb520874d --- /dev/null +++ b/Loop/Redesign/TrendsScreen.swift @@ -0,0 +1,555 @@ +// +// TrendsScreen.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import SwiftUI +import UIKit + +private typealias P = LoopDesign.Palette + +/// The period review: everything on the screen follows one RangeSelector period. +@MainActor +final class TrendsModel: ObservableObject { + + struct Summary { + var interval: DateInterval + var unit: GlucoseUnitStyle + var glucose: [GlucosePoint] = [] + var stats: LoopHistoryMath.GlucoseStats? + var cgmActive: Double = 0 + var timeInRange: [LoopHistoryMath.RangeBand: Double] = [:] + var agp: [LoopHistoryMath.AGPSlot] = [] + var agpDays = 0 + var totals: [LoopHistoryMath.DayTotal] = [] + /// Carbs and bolus counts cover only the days per-day figures divide by (see `dataDays`). + var carbGrams = 0.0 + var carbEntries = 0 + var carbsByDay: [Date: Double] = [:] + var manualBoluses = 0 + var automaticBoluses = 0 + var loopSpans: [LoopModeSpan] = [] + var targetRange: ClosedRange? + /// The whole calendar day, for a one-day period: drawn as one timeline. + var day: DayHistory? + var dayTargets: [TargetBand] = [] + + /// The days per-day figures divide by: insulin's averaging days, or complete days with CGM data when there is + /// no insulin (today only when no complete day has any), never the period length. + var dataDays = 0 + var days: Double { Double(max(1, dataDays)) } + var basalUnits: Double { LoopHistoryMath.averagingDays(totals).reduce(0) { $0 + $1.basal } } + var bolusUnits: Double { LoopHistoryMath.averagingDays(totals).reduce(0) { $0 + $1.bolus } } + var insulinPerDay: Double { LoopHistoryMath.averageDailyInsulin(totals) } + var basalShare: Double { basalUnits + bolusUnits > 0 ? basalUnits / (basalUnits + bolusUnits) : 0 } + /// Per day over the averaged days; a single day in progress when that is all there is. + var split: InsulinSplit { InsulinSplit.average(LoopHistoryMath.averagingDays(totals).map(\.split)) } + /// The split is today's total so far rather than an average of complete days. + var splitIsPartial: Bool { LoopHistoryMath.averagingDays(totals).allSatisfy { !$0.isComplete } } + } + + @Published var period: TrendsPeriod = .twoWeeks + @Published var endDay = Date() + @Published private(set) var summary: Summary? + @Published private(set) var loading = false + @Published private(set) var loadError: String? + + let provider: LoopHistoryProvider + private var loadTask: Task? + + init(provider: LoopHistoryProvider) { + self.provider = provider + } + + var interval: DateInterval { period.interval(endingOn: endDay) } + + func reload() { + loadTask?.cancel() + let interval = self.interval + let oneDay = period == .day + loading = true + summary = nil + loadError = nil + loadTask = Task { + do { + let result = try await Self.load(provider: provider, interval: interval, oneDay: oneDay) + guard !Task.isCancelled else { return } + self.summary = result + } catch { + guard !Task.isCancelled else { return } + self.loadError = error.localizedDescription + } + self.loading = false + } + } + + private static func load(provider: LoopHistoryProvider, interval: DateInterval, oneDay: Bool) async throws -> Summary { + var summary = Summary(interval: interval, unit: provider.unitStyle) + summary.targetRange = provider.glucoseTargetRange + + // A single day is read to midnight, as Today so far is, so the scheduled basal still to come is drawn. + let fetchWindow = oneDay ? DateInterval(start: interval.start, end: Calendar.current.date(byAdding: .day, value: 1, to: interval.start)!) : interval + async let historyFetch = provider.dayHistory(fetchWindow, includeOnBoard: false) + async let runsFetch = provider.loopRuns() + var history = try await historyFetch + let (runs, coverageStart) = try await runsFetch + if oneDay { + summary.day = history + summary.dayTargets = try await provider.glucoseTargetBands(in: fetchWindow) + history = history.clipped(to: interval) + } + + summary.glucose = history.glucose + summary.stats = LoopHistoryMath.glucoseStats(history.glucose) + summary.cgmActive = LoopHistoryMath.cgmActiveFraction(history.glucose, over: interval) + summary.timeInRange = LoopHistoryMath.timeInRange(history.glucose) + summary.agp = LoopHistoryMath.agp(history.glucose) + summary.agpDays = LoopHistoryMath.daysWithReadings(history.glucose) + + let calendar = Calendar.current + summary.totals = LoopHistoryMath.days(in: interval).map { day in + let basal = history.basal.compactMap { $0.clamped(to: day) }.reduce(0) { $0 + $1.units } + let boluses = history.boluses.filter { $0.date >= day.start && $0.date < day.end } + return LoopHistoryMath.DayTotal(day: day.start, split: InsulinSplit(basal: basal, boluses: boluses), + isComplete: calendar.date(byAdding: .day, value: 1, to: day.start) == day.end) + } + let glucoseDays = Set(history.glucose.map { calendar.startOfDay(for: $0.date) }) + // Carbs and bolus counts share insulin's days, so a period with years of CGM data but a few days of Loop + // isn't spread thin. Without insulin, fall back to days with CGM data. + let withData = summary.totals.filter { $0.total > 0 || glucoseDays.contains($0.day) } + let insulinDays = LoopHistoryMath.averagingDays(summary.totals) + let averaged = !insulinDays.isEmpty ? insulinDays + : withData.contains(where: \.isComplete) ? withData.filter(\.isComplete) : withData + let averagedDays = Set(averaged.map(\.day)) + let inAveragedDays: (Date) -> Bool = { averagedDays.contains(calendar.startOfDay(for: $0)) } + summary.dataDays = averaged.count + summary.carbsByDay = Dictionary(grouping: history.carbs) { calendar.startOfDay(for: $0.date) } + .mapValues { $0.reduce(0) { $0 + $1.grams } } + let carbs = history.carbs.filter { inAveragedDays($0.date) } + summary.carbGrams = carbs.reduce(0) { $0 + $1.grams } + summary.carbEntries = carbs.count + let boluses = history.boluses.filter { inAveragedDays($0.date) } + summary.manualBoluses = boluses.filter { !$0.automatic }.count + summary.automaticBoluses = boluses.filter(\.automatic).count + + summary.loopSpans = LoopHistoryMath.loopModeSpans(interval: interval, runs: runs, modeChanges: LoopModeHistory.shared.changes, + coverageStart: coverageStart) + return summary + } + + // MARK: Tiles + + func tiles(_ s: Summary) -> [StatTile] { + let unit = s.unit + var tiles: [StatTile] = [] + let averageLabel = NSLocalizedString("Average", comment: "Trends summary tile: mean glucose over the period") + let gmiLabel = NSLocalizedString("GMI", comment: "Trends summary tile: abbreviation for Glucose Management Indicator, an estimate of HbA1c from CGM data") + let loopActiveLabel = NSLocalizedString("Loop active", comment: "Trends summary tile: share of the period Loop spent in closed loop") + if let stats = s.stats { + tiles.append(StatTile(label: averageLabel, value: unit.format(stats.meanMgdl), unit: unit.unitName, + detail: String(format: NSLocalizedString("SD %1$@ · CV %2$@%%", comment: "Trends Average tile detail: standard deviation and coefficient of variation of glucose. (1: standard deviation in the glucose unit, 2: coefficient of variation as a percentage number)"), + unit.format(stats.sdMgdl), LoopFormat.number(stats.cv * 100, decimals: 0)))) + let enough = s.cgmActive * s.interval.duration / 86400 >= 10 + tiles.append(StatTile(label: gmiLabel, value: LoopFormat.number(stats.gmiPercent, decimals: 1), unit: "%", + detail: enough + ? String(format: NSLocalizedString("%1$@ mmol/mol", comment: "Trends GMI tile detail: the GMI in IFCC units. (1: value)"), + LoopFormat.number(stats.gmiMmolMol, decimals: 0)) + : NSLocalizedString("Needs 10 days of CGM", comment: "Trends GMI tile detail when fewer than 10 days of CGM data are available"))) + } else { + tiles.append(StatTile(label: averageLabel, value: "–", unit: unit.unitName, + detail: NSLocalizedString("No CGM data", comment: "Trends Average tile detail when the period has no glucose readings"))) + tiles.append(StatTile(label: gmiLabel, value: "–", unit: "%", detail: nil)) + } + let basalPercent = LoopFormat.number(s.basalShare * 100, decimals: 0) + let insulinDetail: String + if let note = daysNote(s) { + insulinDetail = String(format: NSLocalizedString("%1$@%% basal · %2$@", comment: "Trends Insulin per day tile detail when only some days are averaged. (1: basal share as a percentage number, 2: days averaged, e.g. 5 of 14 days)"), + basalPercent, note) + } else { + insulinDetail = String(format: NSLocalizedString("%1$@%% basal", comment: "Trends Insulin per day tile detail: share of insulin delivered as basal. (1: percentage number)"), + basalPercent) + } + // One day's figures are that day's own, so its tiles drop "per day" and show whole counts. + let oneDay = s.day != nil + let countDecimals = oneDay ? 0 : 1 + tiles.append(StatTile(label: oneDay + ? NSLocalizedString("Insulin", comment: "Trends summary tile for a one-day period: the day's total insulin") + : NSLocalizedString("Insulin per day", comment: "Trends tile and card title: average total insulin per day"), + value: LoopFormat.number(s.insulinPerDay, decimals: 1), + unit: NSLocalizedString("U", comment: "Abbreviation for insulin units"), + detail: insulinDetail, valueColor: P.insulinInk)) + tiles.append(StatTile(label: oneDay + ? NSLocalizedString("Carbs", comment: "Trends summary tile for a one-day period: the day's total carbohydrate") + : NSLocalizedString("Carbs per day", comment: "Trends summary tile: average grams of carbohydrate entered per day"), + value: LoopFormat.number(s.carbGrams / s.days, decimals: 0), + unit: NSLocalizedString("g", comment: "Abbreviation for grams"), + detail: oneDay + ? String(format: NSLocalizedString("%lld entries", comment: "Trends Carbs tile detail for a one-day period: how many carb entries were made that day. (1: number)"), s.carbEntries) + : String(format: NSLocalizedString("%1$@ entries", comment: "Trends Carbs per day tile detail: average number of carb entries per day, a fractional value such as 2.5, so not pluralised. (1: number with one decimal)"), + LoopFormat.number(Double(s.carbEntries) / s.days, decimals: 1)), + valueColor: P.carbsInk)) + tiles.append(StatTile(label: oneDay + ? NSLocalizedString("Boluses", comment: "Trends summary tile for a one-day period: how many manual boluses were given that day") + : NSLocalizedString("Boluses per day", comment: "Trends summary tile: average number of manual boluses per day"), + value: LoopFormat.number(Double(s.manualBoluses) / s.days, decimals: countDecimals), unit: nil, + detail: String(format: NSLocalizedString("+ %1$@ automatic", comment: "Trends Boluses per day tile detail: average number of automatic boluses per day on top of the manual ones, a fractional value such as 4.2. (1: number with one decimal)"), + LoopFormat.number(Double(s.automaticBoluses) / s.days, decimals: countDecimals)))) + let covered = s.loopSpans.reduce(0) { $0 + $1.duration } + if covered > 0 { + let closed = LoopHistoryMath.duration(of: .closed, in: s.loopSpans) + tiles.append(StatTile(label: loopActiveLabel, value: LoopFormat.number(closed / covered * 100, decimals: 0), unit: "%", + detail: String(format: NSLocalizedString("%1$@ of %2$@", comment: "Trends Loop active tile detail: time in closed loop out of the time Loop status is known. (1: duration in closed loop, e.g. 6d 4h, 2: duration covered, e.g. 7d)"), + LoopFormat.duration(closed), LoopFormat.duration(covered)))) + } else { + tiles.append(StatTile(label: loopActiveLabel, value: "–", unit: "%", + detail: NSLocalizedString("No loop runs kept", comment: "Trends Loop active tile detail when no Loop run history is available for the period"))) + } + return tiles + } + + /// "5 of 14 days" when only part of the period is averaged: days without data, or today while it runs. + private func daysNote(_ s: Summary) -> String? { + let insulinDays = LoopHistoryMath.averagingDays(s.totals).count + guard insulinDays < s.totals.count else { return nil } + let total = String.localizedStringWithFormat(NSLocalizedString("%lld days", comment: "A number of days, e.g. a Trends period or how many days are averaged. (1: number of days)"), s.totals.count) + return String(format: NSLocalizedString("%1$@ of %2$@", comment: "Trends: how many days of the period are averaged. (1: days averaged, a number; 2: days in the period, e.g. 14 days)"), LoopFormat.number(Double(insulinDays), decimals: 0), total) + } + + // MARK: Export + + static func exportCSV(_ s: Summary) throws -> URL { + // The CSV header and values are deliberately not localised: fixed English columns and '.' decimals for data interchange. + var lines = ["date,mean_glucose_\(s.unit.isMmol ? "mmol_l" : "mg_dl"),readings,time_in_range_pct,basal_u,bolus_u,total_u,carbs_g,manual_bolus_u,auto_bolus_u"] + let calendar = Calendar.current + let byDay = Dictionary(grouping: s.glucose) { calendar.startOfDay(for: $0.date) } + for total in s.totals { + let readings = byDay[total.day] ?? [] + let mean = readings.isEmpty ? "" : String(format: "%.*f", locale: Locale(identifier: "en_US_POSIX"), + s.unit.isMmol ? 1 : 0, s.unit.value(readings.reduce(0) { $0 + $1.mgdl } / Double(readings.count))) + let tir = readings.isEmpty ? "" : String(format: "%.0f", (LoopHistoryMath.timeInRange(readings)[.inRange] ?? 0) * 100) + let date = Date.ISO8601FormatStyle(timeZone: .current).year().month().day().format(total.day) + let carbs = String(format: "%.0f", s.carbsByDay[total.day] ?? 0) + // Left blank when some of the day's bolus doesn't say whether it was manual or automatic. + let bolusSplit = total.split.isClassified ? String(format: "%.2f,%.2f", total.split.manualBolus, total.split.automaticBolus) : "," + lines.append("\(date),\(mean),\(readings.count),\(tir),\(String(format: "%.2f,%.2f,%.2f", total.basal, total.bolus, total.total)),\(carbs),\(bolusSplit)") + } + let url = FileManager.default.temporaryDirectory.appendingPathComponent(Self.fileName(s.interval) + ".csv") + try lines.joined(separator: "\n").write(to: url, atomically: true, encoding: .utf8) + return url + } + + func exportPDF(_ s: Summary) throws -> URL { + let url = FileManager.default.temporaryDirectory.appendingPathComponent(Self.fileName(s.interval) + ".pdf") + let report = TrendsReport(summary: s, tiles: tiles(s)) + .frame(width: 560) + .environment(\.colorScheme, .light) + let renderer = ImageRenderer(content: report) + renderer.proposedSize = ProposedViewSize(width: 560, height: nil) + var thrown: Error? + renderer.render { size, draw in + var box = CGRect(x: 0, y: 0, width: 612, height: max(792, size.height + 52)) + guard let consumer = CGDataConsumer(url: url as CFURL), let context = CGContext(consumer: consumer, mediaBox: &box, nil) else { + thrown = CocoaError(.fileWriteUnknown) + return + } + context.beginPDFPage(nil) + context.translateBy(x: 26, y: box.height - size.height - 26) + draw(context) + context.endPDFPage() + context.closePDF() + } + if let thrown { throw thrown } + return url + } + + private static func fileName(_ interval: DateInterval) -> String { + String(format: NSLocalizedString("Loop trends %1$@", comment: "File name of an exported Trends report or CSV, without the extension. (1: date range of the period)"), + fileStamp(interval)) + } + + private static func fileStamp(_ interval: DateInterval) -> String { + LoopFormat.range(interval.start, interval.end.addingTimeInterval(-1)).replacingOccurrences(of: " – ", with: " to ") + } +} + +// MARK: - Screen + +struct TrendsView: View { + @StateObject private var model: TrendsModel + @State private var path: [TrendsRoute] = [] + @State private var exportURL: URL? + @State private var exportError: String? + + /// Closes Trends when it is presented on its own; nil where Trends has nowhere to return to. + private let done: (() -> Void)? + + enum TrendsRoute: Hashable { + case day(Date) + case gaps + } + + init(provider: LoopHistoryProvider, done: (() -> Void)? = nil) { + _model = StateObject(wrappedValue: TrendsModel(provider: provider)) + self.done = done + } + + var body: some View { + NavigationStack(path: $path) { + ScrollView { + LazyVStack(alignment: .leading, spacing: LoopDesign.Space.s6, pinnedViews: [.sectionHeaders]) { + Section { + content + } header: { + RangeSelectorView(period: $model.period, endDay: $model.endDay) + .padding(.vertical, LoopDesign.Space.s2) + .background(P.surface) + } + } + .padding(.horizontal, LoopDesign.Space.s4) + .padding(.bottom, LoopDesign.Space.s6) + } + .background(P.surface) + .navigationTitle(NSLocalizedString("Trends", comment: "Title of the Trends tab")) + .refreshable { model.reload() } + .toolbar { + if let done { + ToolbarItem(placement: .confirmationAction) { + Button(NSLocalizedString("Done", comment: "Button to close Trends"), action: done) + } + } + } + .navigationDestination(for: TrendsRoute.self) { route in + switch route { + case .day(let day): + DayDetailView(provider: model.provider, day: day) + case .gaps: + LoopGapsList(spans: model.summary?.loopSpans ?? []) + } + } + } + .onAppear { model.reload() } + .onChange(of: model.period) { _, _ in model.reload() } + .onChange(of: model.endDay) { _, _ in model.reload() } + .sheet(item: $exportURL) { url in + ShareSheet(items: [url]) + } + .alert(NSLocalizedString("Couldn't export", comment: "Alert title when exporting the Trends PDF or CSV fails"), + isPresented: Binding(get: { exportError != nil }, set: { if !$0 { exportError = nil } })) { + Button(NSLocalizedString("OK", comment: "Button to dismiss an alert"), role: .cancel) {} + } message: { + Text(exportError ?? "") + } + } + + @ViewBuilder + private var content: some View { + if let s = model.summary { + summaryCard(s) + // A single day leads with its timeline. + if let day = s.day { + dayCard(day, targets: s.dayTargets, unit: s.unit) + } + LoopCard { + CardHeader(title: NSLocalizedString("Time in range", comment: "Trends card title: share of glucose readings in each range")) { + Text(String(format: NSLocalizedString("%1$@%% CGM active", comment: "Trends Time in range card: share of the period with CGM readings. (1: percentage number)"), + LoopFormat.number(s.cgmActive * 100, decimals: 0))) + } + if s.glucose.isEmpty { + ChartPlaceholder(message: NSLocalizedString("No CGM data in this period", comment: "Trends chart placeholder when the period has no glucose readings")) + } else { + TimeInRangeView(fractions: s.timeInRange, unit: s.unit) + } + Text(String(format: NSLocalizedString("Aim: over 70%% in range, under 4%% low.", comment: "Trends Time in range card footnote: the consensus targets for time in range and time low"))) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + if s.day == nil { + patternCard(s) + } + LoopCard { + CardHeader(title: NSLocalizedString("Basal and bolus", comment: "Trends card title: insulin split into basal and bolus")) { + Text(s.splitIsPartial + ? NSLocalizedString("today so far", comment: "Trends Basal and bolus card subtitle when the figures are today's total so far") + : s.totals.count == 1 + ? NSLocalizedString("this day", comment: "Trends Basal and bolus card subtitle when the period is one complete day") + : NSLocalizedString("average per day", comment: "Trends Basal and bolus card subtitle when the figures are averaged per day over the period")) + } + if s.split.total > 0 { + InsulinSplitView(split: s.split, partial: s.splitIsPartial) + } else { + ChartPlaceholder(message: NSLocalizedString("No insulin recorded in this period", comment: "Trends Insulin per day chart placeholder when no insulin was delivered in the period"), height: 80) + } + } + // One day is one bar: the timeline and the Basal and bolus card already say it all. + if s.day == nil { + LoopCard { + CardHeader(title: NSLocalizedString("Insulin per day", comment: "Trends tile and card title: average total insulin per day")) { + Text(s.totals.count > 30 + ? NSLocalizedString("per day, weekly average", comment: "Trends Insulin per day card subtitle when each bar is a week's average daily total") + : NSLocalizedString("basal and bolus", comment: "Trends Insulin per day card subtitle when each bar is one day's basal and bolus")) + } + InsulinDailyTotalsView(totals: s.totals.count > 30 ? LoopHistoryMath.weekly(s.totals) : s.totals, + weekly: s.totals.count > 30, average: s.insulinPerDay) { day in + path.append(.day(day)) + } + ChartLegend(items: insulinLegend(s)) + } + } + LoopCard { + CardHeader(title: NSLocalizedString("Loop status", comment: "Trends card title: how long Loop spent in closed loop, limited or open loop")) { + Button(NSLocalizedString("Gaps", comment: "Trends Loop status button and screen title: list of times Loop wasn't in closed loop")) { path.append(.gaps) } + .foregroundStyle(P.accentInk) + .disabled(s.loopSpans.isEmpty) + } + LoopModeBarView(spans: s.loopSpans) + if let note = coverageNote(s) { + Text(note) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + } + } else if let error = model.loadError { + LoopCard { + ChartPlaceholder(message: String(format: NSLocalizedString("Couldn't load Trends: %1$@", comment: "Trends error message when the history can't be read. (1: error description)"), error)) + Button(NSLocalizedString("Retry", comment: "Button to try loading Trends again after an error")) { model.reload() }.foregroundStyle(P.accentInk) + } + } else { + ProgressView().frame(maxWidth: .infinity, minHeight: 300) + } + } + + /// The same timeline as Today so far: glucose, every bolus and basal change, and carbs, through the day. + private func dayCard(_ day: DayHistory, targets: [TargetBand], unit: GlucoseUnitStyle) -> some View { + let isToday = Calendar.current.isDateInToday(day.window.start) + return Button { + path.append(.day(day.window.start)) + } label: { + DayTimelineCard(title: isToday ? DayTimelineCard.todayTitle : LoopFormat.weekdayDay(day.window.start), + history: day, unit: unit, targetBands: targets, height: 260, showsTotals: false) + } + .buttonStyle(.plain) + .accessibilityHint(NSLocalizedString("Opens the full day", comment: "VoiceOver hint for the Today so far card on the Now screen")) + } + + private func patternCard(_ s: TrendsModel.Summary) -> some View { + LoopCard { + CardHeader(title: NSLocalizedString("Daily pattern", comment: "Trends card title: the ambulatory glucose profile, glucose by time of day across the period")) { Text(s.unit.unitName) } + TrendsAGPChart(slots: s.agp, readings: s.glucose, days: s.agpDays, unit: s.unit, targetRange: s.targetRange) + ChartLegend(items: [ + LegendItem(swatch: .solid(P.glucose), label: NSLocalizedString("Median", comment: "Trends Daily pattern legend: the median glucose line")), + LegendItem(swatch: .solid(P.agpInner), label: NSLocalizedString("25–75%", comment: "Trends Daily pattern legend: band between the 25th and 75th percentiles of glucose")), + LegendItem(swatch: .solid(P.agpOuter), label: NSLocalizedString("5–95%", comment: "Trends Daily pattern legend: band between the 5th and 95th percentiles of glucose")), + ]) + } + } + + private func summaryCard(_ s: TrendsModel.Summary) -> some View { + LoopCard { + CardHeader(title: NSLocalizedString("Summary", comment: "Trends card title: the summary statistics tiles")) { + Menu(NSLocalizedString("Export", comment: "Trends menu title: export the period as a PDF report or CSV file")) { + Button(NSLocalizedString("PDF report", comment: "Trends export menu item: share a PDF report of the period")) { export { try model.exportPDF(s) } } + Button(NSLocalizedString("CSV, one row per day", comment: "Trends export menu item: share a CSV spreadsheet file with one row for each day")) { export { try TrendsModel.exportCSV(s) } } + } + .foregroundStyle(P.accentInk) + } + StatGridView(tiles: model.tiles(s)) + .opacity(model.loading ? 0.5 : 1) + } + } + + private func export(_ make: () throws -> URL) { + do { + exportURL = try make() + } catch { + exportError = error.localizedDescription + } + } + + /// The figures are on the Basal and bolus card above, so the legend only names the marks. + private func insulinLegend(_ s: TrendsModel.Summary) -> [LegendItem] { + [ + LegendItem(swatch: .basal, label: NSLocalizedString("Basal", comment: "Chart legend: the basal part of each day's insulin")), + LegendItem(swatch: .solid(P.bolus), label: NSLocalizedString("Bolus", comment: "Chart legend: solid bars are manual boluses")), + LegendItem(swatch: .hatch(P.bolus), label: NSLocalizedString("Automatic", comment: "Chart legend: hatched bars are automatic boluses given by Loop")), + LegendItem(swatch: .dash(P.ink), + label: String(format: NSLocalizedString("Average %1$@ U", comment: "Trends Insulin per day legend: the dashed line showing average total insulin per day. (1: units)"), + LoopFormat.number(s.insulinPerDay, decimals: 1))), + ] + } + + private func coverageNote(_ s: TrendsModel.Summary) -> String? { + guard let start = s.loopSpans.first?.start else { + return NSLocalizedString("Loop status is recorded from when this version of Loop first ran.", comment: "Trends Loop status footnote when there is no Loop status history yet") + } + guard start > s.interval.start else { return nil } + return String(format: NSLocalizedString("Covers %1$@: Loop keeps 7 days of runs, and closed or open loop is recorded from when this version of Loop first ran.", comment: "Trends Loop status footnote when the status covers only part of the period. (1: date range covered)"), + LoopFormat.range(start, s.interval.end)) + } +} + +extension URL: @retroactive Identifiable { + public var id: String { absoluteString } +} + +struct ShareSheet: UIViewControllerRepresentable { + let items: [Any] + + func makeUIViewController(context: Context) -> UIActivityViewController { + UIActivityViewController(activityItems: items, applicationActivities: nil) + } + + func updateUIViewController(_ controller: UIActivityViewController, context: Context) {} +} + +// MARK: - PDF report + +/// The Trends cards laid out for paper. +private struct TrendsReport: View { + let summary: TrendsModel.Summary + let tiles: [StatTile] + + var body: some View { + VStack(alignment: .leading, spacing: LoopDesign.Space.s4) { + VStack(alignment: .leading, spacing: 2) { + Text(NSLocalizedString("Loop trends", comment: "Title of the exported Trends PDF report")).font(.title2.bold()) + Text(LoopFormat.range(summary.interval.start, summary.interval.end.addingTimeInterval(-1))) + .font(.subheadline) + .foregroundStyle(P.inkMuted) + } + StatGridView(tiles: tiles) + HStack(alignment: .top, spacing: LoopDesign.Space.s4) { + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + Text(NSLocalizedString("Time in range", comment: "Trends card title: share of glucose readings in each range")).font(.headline) + TimeInRangeView(fractions: summary.timeInRange, unit: summary.unit) + } + .frame(maxWidth: .infinity) + } + if let day = summary.day { + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + Text(LoopFormat.weekdayDay(day.window.start)).font(.headline) + InsulinTimelineView(history: day, unit: summary.unit, targetBands: summary.dayTargets, height: 220) + ChartLegend(items: DayTotalsTiles.legend) + } + } else { + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + Text(NSLocalizedString("Daily pattern", comment: "Trends card title: the ambulatory glucose profile, glucose by time of day across the period")).font(.headline) + TrendsAGPChart(slots: summary.agp, readings: summary.glucose, days: summary.agpDays, unit: summary.unit, + targetRange: summary.targetRange, height: 160) + } + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + Text(NSLocalizedString("Insulin per day", comment: "Trends tile and card title: average total insulin per day")).font(.headline) + InsulinDailyTotalsView(totals: summary.totals.count > 30 ? LoopHistoryMath.weekly(summary.totals) : summary.totals, + weekly: summary.totals.count > 30, average: summary.insulinPerDay, height: 140) + } + } + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + Text(NSLocalizedString("Loop status", comment: "Trends card title: how long Loop spent in closed loop, limited or open loop")).font(.headline) + LoopModeBarView(spans: summary.loopSpans) + } + } + .padding(LoopDesign.Space.s2) + .background(Color.white) + } +} diff --git a/Loop/View Models/SettingsViewModel.swift b/Loop/View Models/SettingsViewModel.swift index 626ae21e28..9762fe1efe 100644 --- a/Loop/View Models/SettingsViewModel.swift +++ b/Loop/View Models/SettingsViewModel.swift @@ -120,6 +120,8 @@ class SettingsViewModel { @ObservationIgnored var insulinDeliveryHistory: ((Int) async throws -> InsulinDeliveryHistory)? + @ObservationIgnored var historyProvider: LoopHistoryProvider? + @ObservationIgnored var setupHistory: (() async throws -> SetupHistory)? @ObservationIgnored var outcomesPackageBuilder: OutcomesPackageBuilder? diff --git a/Loop/Views/SettingsView.swift b/Loop/Views/SettingsView.swift index 70f6d26b4e..babad179cc 100644 --- a/Loop/Views/SettingsView.swift +++ b/Loop/Views/SettingsView.swift @@ -55,6 +55,7 @@ struct SettingsView: View { case favoriteFoods case presets + case trends } } @@ -156,6 +157,11 @@ struct SettingsView: View { } case .favoriteFoods: FavoriteFoodsView(insightsDelegate: viewModel.favoriteFoodInsightsDelegate) + case .trends: + if let historyProvider = viewModel.historyProvider { + // Trends brings its own navigation stack, so it is a sheet rather than a push. Done closes only Trends. + TrendsView(provider: historyProvider, done: { self.sheet = nil }) + } } } .environmentObject(displayGlucosePreference) @@ -358,6 +364,18 @@ extension SettingsView { ) .accessibilityIdentifier("settingsViewStatistics") } + if viewModel.historyProvider != nil { + LargeButton( + action: { sheet = .trends }, + imageView: Image(systemName: "chart.bar.doc.horizontal") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 30), + label: NSLocalizedString("Trends", comment: "Trends settings button text"), + descriptiveText: NSLocalizedString("Time in range, daily pattern, insulin per day and Loop status", comment: "Trends settings descriptive text") + ) + .accessibilityIdentifier("settingsViewTrends") + } NavigationLink(destination: CurrentSetupView(viewModel: CurrentSetupViewModel( selected: setupChange, loadHistory: { [viewModel] in try await viewModel.setupHistory?() ?? SetupHistory(settings: [], experiments: []) }, diff --git a/LoopTests/Managers/LoopDataManagerTests.swift b/LoopTests/Managers/LoopDataManagerTests.swift index fab964e866..e82fdd17ea 100644 --- a/LoopTests/Managers/LoopDataManagerTests.swift +++ b/LoopTests/Managers/LoopDataManagerTests.swift @@ -648,6 +648,62 @@ class LoopDataManagerTests: XCTestCase { try await assertMissingCarbScheduleThrows(.insulinSensitivitySchedule, preview: true) } + func testHistoricalTargetsUseStoredProfilesAndEndedPresets() async throws { + func target(_ low: Double, _ high: Double) -> ClosedRange { + LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: low)...LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: high) + } + let window = DateInterval(start: d(-.hours(3)), end: d(-.minutes(30))) + settingsProvider.targetRangeHistory = [ + AbsoluteScheduleValue(startDate: window.start, endDate: d(-.hours(2)), value: target(100, 110)), + AbsoluteScheduleValue(startDate: d(-.hours(2)), endDate: window.end, value: target(120, 130)), + ] + var preset = TemporaryScheduleOverride(context: .custom, + settings: TemporaryPresetSettings(targetRange: target(150, 160), insulinNeedsScaleFactor: 1), + startDate: d(-.minutes(150)), duration: .finite(.hours(2)), + enactTrigger: .local, syncIdentifier: UUID()) + preset.actualEnd = .early(d(-.minutes(90))) + temporaryPresetsManager.presetHistory.recordOverride(preset, at: d(-.minutes(150))) + + let bands = try await loopDataManager.glucoseTargetBands(in: window) + XCTAssertEqual(bands, [ + TargetBand(start: window.start, end: d(-.minutes(150)), range: 100...110), + TargetBand(start: d(-.minutes(150)), end: d(-.minutes(90)), range: 150...160), + TargetBand(start: d(-.minutes(90)), end: window.end, range: 120...130), + ]) + } + + func testTargetBandsAreUnknownBeforeTherapyIsConfigured() async throws { + settingsProvider.settings.glucoseTargetRangeSchedule = nil + let bands = try await loopDataManager.glucoseTargetBands(in: DateInterval(start: d(-.hours(3)), end: now)) + XCTAssertTrue(bands.isEmpty) + } + + func testHistoricalIOBUsesPresetAdjustedBasal() async throws { + settingsProvider.basalHistory = [AbsoluteScheduleValue(startDate: d(-.hours(6)), endDate: now, value: 1.0)] + let window = DateInterval(start: d(-.minutes(30)), end: now) + + for multiplier in [0.5, 1.5] { + let preset = TemporaryScheduleOverride( + context: .custom, + settings: TemporaryPresetSettings(targetRange: nil, insulinNeedsScaleFactor: multiplier), + startDate: d(-.hours(1)), + duration: .finite(.hours(2)), + enactTrigger: .local, + syncIdentifier: UUID() + ) + temporaryPresetsManager.presetHistory.recordOverride(preset, at: now) + doseStore.doseHistory = [DoseEntry(type: .tempBasal, startDate: window.start, endDate: window.end, + value: multiplier, unit: .unitsPerHour, decisionId: nil)] + + let timeline = try await loopDataManager.insulinOnBoardTimeline(in: window) + XCTAssertFalse(timeline.isEmpty) + for value in timeline { + XCTAssertEqual(value.value, 0, accuracy: 1e-9, + "Delivery matching a \(multiplier)× preset's basal must add no net IOB") + } + } + } + func testFetchDataWithHighInsulinNeedsPresetMitigation() async throws { var input = try await loopDataManager.fetchData(for: now) XCTAssertEqual(input.targetMgdl.count, 1) diff --git a/LoopTests/Models/LoopHistoryMathTests.swift b/LoopTests/Models/LoopHistoryMathTests.swift new file mode 100644 index 0000000000..b468c9dc42 --- /dev/null +++ b/LoopTests/Models/LoopHistoryMathTests.swift @@ -0,0 +1,340 @@ +// +// LoopHistoryMathTests.swift +// LoopTests +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import XCTest +import HealthKit +import LoopKit +@testable import Loop + +class LoopHistoryMathTests: XCTestCase { + + private let start = Date(timeIntervalSinceReferenceDate: 800_000_000) + private func at(_ minutes: Double) -> Date { start.addingTimeInterval(minutes * 60) } + + // MARK: Basal + + func testUnrecordedBasalCountsAtTheProgrammedRateOnlyAfterRecordsBegin() { + let window = DateInterval(start: at(0), end: at(240)) + let programmed = [LoopHistoryMath.ScheduleValue(start: at(0), end: at(240), rate: 1.2)] + let temp = LoopHistoryMath.BasalDelivery(start: at(120), end: at(150), rate: 0.4, kind: .temp, automatic: true) + let segments = LoopHistoryMath.basalSegments(window: window, schedule: programmed, deliveries: [temp], + programmedRates: programmed, recordsStart: at(60)) + // Nothing before the first record; 60–120 and 150–240 at 1.2 U/h; 120–150 at the temp rate. + XCTAssertNil(LoopHistoryMath.basalRate(at: at(30), in: segments)) + XCTAssertEqual(LoopHistoryMath.basalRate(at: at(90), in: segments)?.kind, .scheduled) + XCTAssertEqual(LoopHistoryMath.basalRate(at: at(135), in: segments)?.rate, 0.4) + XCTAssertEqual(segments.reduce(0) { $0 + $1.units }, 1.2 * 2.5 + 0.4 * 0.5, accuracy: 1e-9) + } + + func testBasalGapsDoNotInventDelivery() { + let window = DateInterval(start: at(0), end: at(120)) + let schedule = [LoopHistoryMath.ScheduleValue(start: at(-60), end: at(240), rate: 1.0)] + let temp = LoopHistoryMath.BasalDelivery(start: at(60), end: at(90), rate: 2.0, kind: .temp, automatic: true) + let segments = LoopHistoryMath.basalSegments(window: window, schedule: schedule, deliveries: [temp]) + XCTAssertEqual(segments.count, 1) + XCTAssertEqual(segments.reduce(0) { $0 + $1.units }, 1, accuracy: 1e-9) + XCTAssertEqual(segments[0].netUnits, 0.5, accuracy: 1e-9) + } + + func testSuspendDeliversNothingAndLaterRecordWins() { + let window = DateInterval(start: at(0), end: at(60)) + let schedule = [LoopHistoryMath.ScheduleValue(start: at(0), end: at(60), rate: 1.2)] + let deliveries = [ + LoopHistoryMath.BasalDelivery(start: at(0), end: at(60), rate: 3.0, kind: .temp, automatic: true), + LoopHistoryMath.BasalDelivery(start: at(20), end: at(40), rate: 0, kind: .suspended, automatic: false), + ] + let segments = LoopHistoryMath.basalSegments(window: window, schedule: schedule, deliveries: deliveries) + XCTAssertEqual(segments.map(\.kind), [.temp, .suspended, .temp]) + XCTAssertEqual(segments[1].units, 0) + XCTAssertEqual(segments[1].netUnits, -0.4, accuracy: 1e-9) + } + + func testNormalisedBasalPreservesDeliveredQuantityInPartialWindow() throws { + // The pump reports 1.05 U delivered, although the programmed rate implies 1.0625 U. + let dose = DoseEntry(type: .tempBasal, startDate: at(0), endDate: at(30), value: 2.125, + unit: .unitsPerHour, decisionId: nil, deliveredUnits: 1.05, automatic: true) + let delivery = try XCTUnwrap(LoopHistoryMath.BasalDelivery(dose: dose)) + for (end, expected) in [(30.0, 1.05), (15.0, 0.525)] { + let segments = LoopHistoryMath.basalSegments(window: DateInterval(start: at(0), end: at(end)), + schedule: [], deliveries: [delivery]) + XCTAssertEqual(segments.reduce(0) { $0 + $1.units }, expected, accuracy: 1e-9) + } + } + + func testHealthAndLocalPartitionsConserveBasalDelivery() throws { + let sample = HKQuantitySample(type: HKQuantityType(.insulinDelivery), + quantity: HKQuantity(unit: .internationalUnit(), doubleValue: 1), + start: at(0), end: at(60), + metadata: [HKMetadataKeyInsulinDeliveryReason: HKInsulinDeliveryReason.basal.rawValue]) + let left = try XCTUnwrap(BasalSegment(healthSample: sample, in: DateInterval(start: at(-30), end: at(30)))) + let right = try XCTUnwrap(BasalSegment(healthSample: sample, in: DateInterval(start: at(30), end: at(90)))) + XCTAssertEqual(left.units, 0.5, accuracy: 1e-9) + XCTAssertEqual(right.units, 0.5, accuracy: 1e-9, "Include basal beginning before the selected interval") + let dose = DoseEntry(type: .basal, startDate: at(0), endDate: at(60), value: 1, unit: .units, decisionId: nil, deliveredUnits: 1) + let local = LoopHistoryMath.basalSegments(window: DateInterval(start: at(30), end: at(90)), schedule: [], + deliveries: [try XCTUnwrap(LoopHistoryMath.BasalDelivery(dose: dose))]) + XCTAssertEqual(left.units + local.reduce(0) { $0 + $1.units }, 1, accuracy: 1e-9) + XCTAssertNil(BasalSegment(healthSample: sample, in: DateInterval(start: at(60), end: at(90)))) + + } + + func testBolusWindowIsHalfOpenAndPreservesDeliveredUnits() { + let window = DateInterval(start: at(0), end: at(60)) + for (minute, expected) in [(-1.0, false), (0.0, true), (30.0, true), (60.0, false)] { + let dose = DoseEntry(type: .bolus, startDate: at(minute), value: 1, unit: .units, decisionId: nil, deliveredUnits: 0.75) + let event = BolusEvent(dose: dose, in: window) + XCTAssertEqual(event != nil, expected, "Bolus at minute \(minute)") + if expected { XCTAssertEqual(event?.units, 0.75) } + } + } + + // MARK: Glucose + + func testTimeInRangeBandsAndRoundingSumTo100() { + let values: [Double] = [50, 60, 100, 100, 100, 200, 300] + let points = values.enumerated().map { GlucosePoint(date: at(Double($0.offset * 5)), mgdl: $0.element) } + let fractions = LoopHistoryMath.timeInRange(points) + XCTAssertEqual(fractions[.veryLow]!, 1 / 7, accuracy: 1e-9) + XCTAssertEqual(fractions[.low]!, 1 / 7, accuracy: 1e-9) + XCTAssertEqual(fractions[.inRange]!, 3 / 7, accuracy: 1e-9) + XCTAssertEqual(fractions[.high]!, 1 / 7, accuracy: 1e-9) + XCTAssertEqual(fractions[.veryHigh]!, 1 / 7, accuracy: 1e-9) + XCTAssertEqual(LoopHistoryMath.roundedPercentages(fractions).values.reduce(0, +), 100) + } + + func testBandEdgesFollowConsensusThresholds() { + XCTAssertEqual(LoopHistoryMath.RangeBand.band(for: 53.9), .veryLow) + XCTAssertEqual(LoopHistoryMath.RangeBand.band(for: 54), .low) + XCTAssertEqual(LoopHistoryMath.RangeBand.band(for: 70), .inRange) + XCTAssertEqual(LoopHistoryMath.RangeBand.band(for: 180), .inRange) + XCTAssertEqual(LoopHistoryMath.RangeBand.band(for: 181), .high) + XCTAssertEqual(LoopHistoryMath.RangeBand.band(for: 251), .veryHigh) + } + + func testGMIFromMean() { + let points = [GlucosePoint(date: at(0), mgdl: 150), GlucosePoint(date: at(5), mgdl: 150)] + let stats = LoopHistoryMath.glucoseStats(points)! + XCTAssertEqual(stats.gmiPercent, 3.31 + 0.02392 * 150, accuracy: 1e-9) + XCTAssertEqual(stats.gmiMmolMol, (stats.gmiPercent - 2.152) * 10.929, accuracy: 1e-9) + XCTAssertNil(LoopHistoryMath.glucoseStats([points[0]])) + } + + func testPercentileInterpolates() { + XCTAssertEqual(LoopHistoryMath.percentile([1, 2, 3, 4, 5], 0.5), 3) + XCTAssertEqual(LoopHistoryMath.percentile([1, 2, 3, 4], 0.5), 2.5) + XCTAssertEqual(LoopHistoryMath.percentile([10, 20], 0.25), 12.5) + } + + // MARK: Loop modes + + func testLoopModesStartAtFirstModeRecord() { + let interval = DateInterval(start: at(0), end: at(60)) + let runs = stride(from: 0.0, through: 60, by: 5).map { LoopHistoryMath.LoopRun(date: at($0), succeeded: true, error: nil) } + let spans = LoopHistoryMath.loopModeSpans(interval: interval, runs: runs, + modeChanges: [.init(date: at(30), closedLoop: true)], coverageStart: at(0)) + XCTAssertEqual(spans.first?.start, at(30), "Before the first record the mode is unknown and left out") + XCTAssertEqual(LoopHistoryMath.duration(of: .closed, in: spans), 30 * 60, accuracy: 1) + + XCTAssertTrue(LoopHistoryMath.loopModeSpans(interval: interval, runs: runs, modeChanges: [], coverageStart: at(0)).isEmpty) + } + + func testLoopModesClassifyOpenAndLimited() { + let interval = DateInterval(start: at(0), end: at(90)) + // Runs succeed until 20 min, then fail with an error, then stop. + var runs = stride(from: 0.0, through: 20, by: 5).map { LoopHistoryMath.LoopRun(date: at($0), succeeded: true, error: nil) } + runs.append(.init(date: at(25), succeeded: false, error: "Glucose too old")) + let changes: [LoopHistoryMath.ModeChange] = [.init(date: at(0), closedLoop: true), .init(date: at(60), closedLoop: false)] + + let spans = LoopHistoryMath.loopModeSpans(interval: interval, runs: runs, modeChanges: changes, coverageStart: at(0)) + XCTAssertEqual(spans.map(\.mode), [.closed, .limited, .open]) + XCTAssertEqual(spans[1].cause, "Glucose too old") + XCTAssertEqual(spans[1].start, at(35), "Limited starts once the last good run is over 15 minutes old") + XCTAssertEqual(spans[2].start, at(55), "The slot ending at the switch-off counts as open") + } + + // MARK: Calendar + + func testDaysAndQuarterMarksOnClockChangeDay() throws { + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = try XCTUnwrap(TimeZone(identifier: "Europe/London")) + // 25 Oct 2026: clocks go back an hour, so the day is 25 hours long. + let day = try XCTUnwrap(calendar.date(from: DateComponents(year: 2026, month: 10, day: 25))) + let next = try XCTUnwrap(calendar.date(byAdding: .day, value: 1, to: day)) + XCTAssertEqual(next.timeIntervalSince(day), 25 * 3600) + + let marks = DayAxis.quarterDayMarks(in: DateInterval(start: day, end: next), calendar: calendar) + XCTAssertEqual(marks.map { calendar.component(.hour, from: $0) }, [0, 6, 12, 18, 0]) + + let days = LoopHistoryMath.days(in: DateInterval(start: day, end: next.addingTimeInterval(3600)), calendar: calendar) + XCTAssertEqual(days.count, 2) + XCTAssertEqual(days[0].duration, 25 * 3600) + } + + func testWeeklyAveragesDays() { + let monday = Date(timeIntervalSinceReferenceDate: 815_270_400) // 9436 days after Monday 1 Jan 2001, so a Monday at midnight UTC + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(identifier: "UTC")! + let totals = (0..<14).map { LoopHistoryMath.DayTotal(day: monday.addingTimeInterval(Double($0) * 86400), basal: 10, bolus: Double($0)) } + let weeks = LoopHistoryMath.weekly(totals, calendar: calendar) + XCTAssertEqual(weeks.count, 2) + XCTAssertEqual(weeks[0].bolus, 3, accuracy: 1e-9) + XCTAssertEqual(weeks[1].bolus, 10, accuracy: 1e-9) + } + + func testDailyInsulinAverageLeavesOutTheDayInProgress() { + let day0 = Date(timeIntervalSinceReferenceDate: 815_270_400) + let complete = (0..<3).map { LoopHistoryMath.DayTotal(day: day0.addingTimeInterval(Double($0) * 86400), basal: 10, bolus: 20) } + let today = LoopHistoryMath.DayTotal(day: day0.addingTimeInterval(3 * 86400), basal: 3, bolus: 2, isComplete: false) + XCTAssertEqual(LoopHistoryMath.averageDailyInsulin(complete + [today]), 30, accuracy: 1e-9) + XCTAssertEqual(LoopHistoryMath.averagingDays(complete + [today]).count, 3) + // With nothing complete, the day in progress is all there is to show. + XCTAssertEqual(LoopHistoryMath.averageDailyInsulin([today]), 5, accuracy: 1e-9) + } + + func testInsulinSplitSeparatesManualAutomaticAndUnclassifiedBolus() { + let boluses = [ + BolusEvent(id: "m", date: at(0), units: 4, automatic: false, manuallyEntered: false), + BolusEvent(id: "a", date: at(5), units: 0.5, automatic: true, manuallyEntered: false), + BolusEvent(id: "u", date: at(10), units: 1, automatic: false, manuallyEntered: false, automaticKnown: false), + ] + let split = InsulinSplit(basal: 14.5, boluses: Array(boluses.prefix(2))) + XCTAssertEqual(split.manualBolus, 4, accuracy: 1e-9) + XCTAssertEqual(split.automaticBolus, 0.5, accuracy: 1e-9) + XCTAssertEqual(split.total, 19, accuracy: 1e-9) + XCTAssertTrue(split.isClassified) + // Shares round independently but always add up to 100. + XCTAssertEqual(split.basalPercent + split.bolusPercent, 100) + XCTAssertEqual(split.bolusPercent, 24) + + // A bolus that doesn't say who gave it still counts, but hides the manual and automatic detail. + let health = InsulinSplit(basal: 14.5, boluses: boluses) + XCTAssertEqual(health.bolus, 5.5, accuracy: 1e-9) + XCTAssertFalse(health.isClassified) + + let average = InsulinSplit.average([split, InsulinSplit(basal: 10.5, manualBolus: 2, automaticBolus: 1.5)]) + XCTAssertEqual(average.basal, 12.5, accuracy: 1e-9) + XCTAssertEqual(average.manualBolus, 3, accuracy: 1e-9) + XCTAssertEqual(average.automaticBolus, 1, accuracy: 1e-9) + } + + func testWeeklyInsulinUsesTheSummaryAveragingDays() { + let monday = Date(timeIntervalSinceReferenceDate: 815_270_400) + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(identifier: "UTC")! + let totals = [ + LoopHistoryMath.DayTotal(day: monday, basal: 0, bolus: 0), + LoopHistoryMath.DayTotal(day: monday.addingTimeInterval(86400), basal: 10, bolus: 20), + LoopHistoryMath.DayTotal(day: monday.addingTimeInterval(2 * 86400), basal: 10, bolus: 20), + LoopHistoryMath.DayTotal(day: monday.addingTimeInterval(3 * 86400), basal: 1, bolus: 2, isComplete: false), + ] + let weeks = LoopHistoryMath.weekly(totals, calendar: calendar) + XCTAssertEqual(weeks.count, 1) + XCTAssertEqual(weeks[0].basal, 10, accuracy: 1e-9) + XCTAssertEqual(weeks[0].bolus, 20, accuracy: 1e-9) + + // A current week with no complete day must still show the data it has. + let currentWeek = LoopHistoryMath.weekly([totals[3]], calendar: calendar) + XCTAssertEqual(currentWeek.first?.total, 3) + } + + func testGapMessageForADayWithoutReadings() { + let start = Date(timeIntervalSinceReferenceDate: 815_270_400) + let day = DateInterval(start: start, duration: 86400) + let empty = DayAxis.gaps(in: [], window: day, until: day.end.addingTimeInterval(3600)) + XCTAssertEqual(DayAxis.gapMessage(empty, window: day, until: day.end.addingTimeInterval(3600)), "No CGM data on this day") + let morning = start.addingTimeInterval(9 * 3600) + XCTAssertEqual(DayAxis.gapMessage(DayAxis.gaps(in: [], window: day, until: morning), window: day, until: morning), "No CGM data yet today") + let reading = [GlucosePoint(date: start.addingTimeInterval(12 * 3600), mgdl: 120)] + let partial = DayAxis.gaps(in: reading, window: day, until: day.end) + XCTAssertTrue(DayAxis.gapMessage(partial, window: day, until: day.end)?.hasSuffix("and 1 shorter gap") ?? false) + } + + // MARK: Formatting + + func testRangeFormatting() throws { + let calendar = Calendar.current + let a = try XCTUnwrap(calendar.date(from: DateComponents(year: 2026, month: 10, day: 12, hour: 12))) + let b = try XCTUnwrap(calendar.date(from: DateComponents(year: 2026, month: 10, day: 25, hour: 12))) + let c = try XCTUnwrap(calendar.date(from: DateComponents(year: 2026, month: 9, day: 28, hour: 12))) + // Ranges and durations come from the system's formatters, so they follow the user's locale; + // the test host runs in en_US. + XCTAssertEqual(LoopFormat.range(b, b), LoopFormat.day(b)) + let sameMonth = LoopFormat.range(a, b) + XCTAssertTrue(sameMonth.contains("12") && sameMonth.contains("25")) + XCTAssertEqual(sameMonth.components(separatedBy: "2026").count, 2, "the year appears once") + let twoMonths = LoopFormat.range(c, b) + XCTAssertTrue(twoMonths.contains("Sep") && twoMonths.contains("Oct")) + XCTAssertEqual(LoopFormat.duration(13 * 86400 + 11 * 3600), "13d 11h") + XCTAssertEqual(LoopFormat.duration(9 * 3600 + 45 * 60), "9h 45m") + XCTAssertEqual(LoopFormat.number(7.25, decimals: 1), 7.25.formatted(.number.precision(.fractionLength(1)).grouping(.never))) + XCTAssertEqual(LoopFormat.signed(-0.4), "−0.40") + XCTAssertEqual(LoopFormat.signed(0.0001), "+0.00") + } + + @MainActor + func testCSVExportsTenColumnsWithASCIINumbers() throws { + let day = Calendar.current.startOfDay(for: start) + let nextDay = day.addingTimeInterval(86400) + for isMmol in [true, false] { + var summary = TrendsModel.Summary(interval: DateInterval(start: day, duration: 2 * 86400), + unit: GlucoseUnitStyle(isMmol: isMmol)) + summary.glucose = [GlucosePoint(date: day, mgdl: 131.53286)] + summary.totals = [ + .init(day: day, split: InsulinSplit(basal: 1.25, manualBolus: 2, automaticBolus: 0.5)), + // From another app in Health: the bolus doesn't say whether it was manual or automatic. + .init(day: nextDay, split: InsulinSplit(basal: 1, unclassifiedBolus: 3)), + ] + summary.carbsByDay = [day: 45] + let url = try TrendsModel.exportCSV(summary) + defer { try? FileManager.default.removeItem(at: url) } + let rows = try String(contentsOf: url, encoding: .utf8).components(separatedBy: "\n") + XCTAssertEqual(rows.count, 3) + XCTAssertEqual(rows[0].components(separatedBy: ",").count, 10) + XCTAssertTrue(rows[0].hasSuffix(",carbs_g,manual_bolus_u,auto_bolus_u")) + let columns = rows[1].components(separatedBy: ",") + XCTAssertEqual(columns.count, 10) + XCTAssertEqual(Array(columns.dropFirst()), [isMmol ? "7.3" : "132", "1", "100", "1.25", "2.50", "3.75", "45", "2.00", "0.50"]) + XCTAssertEqual(Array(rows[2].components(separatedBy: ",").suffix(5)), ["3.00", "4.00", "0", "", ""]) + } + } + + func testModeHistoryRetainsTheModeBeforeTheRetentionWindow() throws { + let suite = "LoopHistoryMathTests-\(UUID().uuidString)" + let defaults = try XCTUnwrap(UserDefaults(suiteName: suite)) + defer { defaults.removePersistentDomain(forName: suite) } + let history = LoopModeHistory(defaults: defaults) + history.record(closedLoop: false, at: at(0)) + history.record(closedLoop: true, at: at(60)) + let switchDate = at(200 * 1440) + history.record(closedLoop: false, at: switchDate) + XCTAssertEqual(history.changes, [.init(date: at(60), closedLoop: true), .init(date: switchDate, closedLoop: false)]) + let interval = DateInterval(start: switchDate.addingTimeInterval(-3600), end: switchDate) + let spans = LoopHistoryMath.loopModeSpans(interval: interval, runs: [], modeChanges: history.changes, + coverageStart: interval.start) + XCTAssertEqual(spans.first?.start, interval.start, "Retaining the old mode must preserve recent coverage") + } + + func testIssueDescriptions() { + XCTAssertEqual(LoopHistoryProvider.describe(issue: "glucoseTooOld"), "Glucose too old") + XCTAssertEqual(LoopHistoryProvider.describe(issue: "pumpDataTooOld"), "Pump data too old") + } + + // MARK: Day history + + func testDayHistoryClippedToNowDropsLaterEventsAndCutsBasal() { + var history = DayHistory(window: DateInterval(start: at(0), end: at(1440))) + history.glucose = [GlucosePoint(date: at(10), mgdl: 100), GlucosePoint(date: at(700), mgdl: 120)] + history.boluses = [BolusEvent(id: "a", date: at(30), units: 1, automatic: false, manuallyEntered: false), + BolusEvent(id: "b", date: at(650), units: 2, automatic: true, manuallyEntered: false)] + history.basal = [BasalSegment(start: at(0), end: at(720), rate: 1, scheduledRate: 1, kind: .scheduled, automatic: false)] + let clipped = history.clipped(to: DateInterval(start: at(0), end: at(600))) + XCTAssertEqual(clipped.window.end, at(600)) + XCTAssertEqual(clipped.glucose.count, 1) + XCTAssertEqual(clipped.boluses.map(\.id), ["a"]) + XCTAssertEqual(clipped.basalUnits, 10, accuracy: 1e-9) + } +} From e6136415cf77663fc210c470be735e7b27021e31 Mon Sep 17 00:00:00 2001 From: Garry Casey Date: Sun, 1 Nov 2026 20:44:03 +0000 Subject: [PATCH 2/5] Bring what Statistics shows into Trends Trends now carries everything Statistics shows that it lacked, so it can take Statistics' place: - Consensus goals: average under 8.6 mmol/L (154 mg/dL), GMI under 7% and CV 36% or less under the summary tiles; time in range over 70%, under 25% above and 4% below, under 5% very high and 1% very low, and CGM active over 70% on the Time in range card. - Scheduled basal under Basal in Basal and bolus: the schedule that applied over the same days (the stored profile with presets), or today's schedule where the profile history doesn't reach. - "Usually X U by HH:MM": today's insulin against the median by this time over the last 14 days, from InsulinDeliveryHistory as on Statistics. Under Total so far on today's Day view, and under the Insulin per day chart on longer periods ending today. - "CGM data covers 5 of 14 days." under Summary when readings start partway through the period. - Typed-in (fingerstick) readings are left out of every figure, as on Statistics. The day timeline still draws them. Each new line reuses a pattern already on the screen: the Aim footnote, the indented line under Bolus, and the muted footnote. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01QHnVP7ynB62LNuuYMy3yvH --- Loop/Localizable.xcstrings | 805 ++++++++++++++++++-- Loop/Redesign/LoopHistoryMath.swift | 22 + Loop/Redesign/LoopHistoryModels.swift | 2 + Loop/Redesign/LoopHistoryProvider.swift | 18 +- Loop/Redesign/TrendsComponents.swift | 26 +- Loop/Redesign/TrendsScreen.swift | 94 ++- LoopTests/Models/LoopHistoryMathTests.swift | 16 + 7 files changed, 921 insertions(+), 62 deletions(-) diff --git a/Loop/Localizable.xcstrings b/Loop/Localizable.xcstrings index 7eaca9b0de..ddaba7520d 100644 --- a/Loop/Localizable.xcstrings +++ b/Loop/Localizable.xcstrings @@ -4180,145 +4180,288 @@ } } }, - "Aim: over 70%% in range, under 4%% low." : { - "comment" : "Trends Time in range card footnote: the consensus targets for time in range and time low", + "Aim: average under %1$@ %2$@, GMI under 7%%, CV 36%% or less." : { + "comment" : "Trends Summary footnote: the consensus targets for average glucose, GMI and glucose variability (CV). (1: glucose value, 2: glucose unit)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الهدف: أكثر من 70%% ضمن النطاق، وأقل من 4%% منخفض." + "value" : "الهدف: متوسط أقل من %1$@ %2$@، وGMI أقل من 7%%، وCV ‏36%% أو أقل." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Cíl: přes 70%% v rozsahu, pod 4%% nízko." + "value" : "Cíl: průměr pod %1$@ %2$@, GMI pod 7%%, CV nejvýše 36%%." } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Mål: over 70 %% i området, under 4 %% lavt." + "value" : "Mål: gennemsnit under %1$@ %2$@, GMI under 7 %%, CV højst 36 %%." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ziel: über 70 %% im Zielbereich, unter 4 %% niedrig." + "value" : "Ziel: Durchschnitt unter %1$@ %2$@, GMI unter 7 %%, VK höchstens 36 %%." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Objetivo: más del 70%% en rango, menos del 4%% bajo." + "value" : "Objetivo: promedio por debajo de %1$@ %2$@, GMI por debajo del 7%%, CV del 36%% o menos." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Tavoite: yli 70 %% tavoitealueella, alle 4 %% matalalla." + "value" : "Tavoite: keskiarvo alle %1$@ %2$@, GMI alle 7 %%, CV enintään 36 %%." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Objectif : plus de 70 %% dans la cible, moins de 4 %% en hypo." + "value" : "Objectif : moyenne sous %1$@ %2$@, GMI sous 7 %%, CV de 36 %% au plus." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "יעד: מעל 70%% בטווח, מתחת ל-4%% נמוך." + "value" : "יעד: ממוצע מתחת ל-%1$@ %2$@, GMI מתחת ל-7%%, CV ‏36%% או פחות." } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Cél: több mint 70%% tartományban, kevesebb mint 4%% alacsony." + "value" : "Cél: átlag %1$@ %2$@ alatt, GMI 7%% alatt, CV legfeljebb 36%%." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Obiettivo: oltre il 70%% nell'intervallo, meno del 4%% bassa." + "value" : "Obiettivo: media sotto %1$@ %2$@, GMI sotto il 7%%, CV al massimo 36%%." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "目標:範囲内70%%超、低値4%%未満。" + "value" : "目標:平均%1$@ %2$@未満、GMI 7%%未満、CV 36%%以下。" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Mål: over 70 %% i målområdet, under 4 %% lavt." + "value" : "Mål: gjennomsnitt under %1$@ %2$@, GMI under 7 %%, CV høyst 36 %%." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Doel: meer dan 70%% binnen bereik, minder dan 4%% laag." + "value" : "Doel: gemiddelde onder %1$@ %2$@, GMI onder 7%%, CV hoogstens 36%%." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Cel: ponad 70%% w zakresie, poniżej 4%% niskich." + "value" : "Cel: średnia poniżej %1$@ %2$@, GMI poniżej 7%%, CV najwyżej 36%%." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Meta: mais de 70%% no alvo, menos de 4%% baixo." + "value" : "Meta: média abaixo de %1$@ %2$@, GMI abaixo de 7%%, CV de no máximo 36%%." } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Țintă: peste 70%% în interval, sub 4%% scăzut." + "value" : "Țintă: medie sub %1$@ %2$@, GMI sub 7%%, CV de cel mult 36%%." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Цель: в диапазоне более 70%%, низкая — менее 4%%." + "value" : "Цель: среднее ниже %1$@ %2$@, GMI ниже 7%%, CV не более 36%%." } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Cieľ: nad 70%% v rozsahu, pod 4%% nízko." + "value" : "Cieľ: priemer pod %1$@ %2$@, GMI pod 7%%, CV najviac 36%%." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Mål: över 70 %% inom målområdet, under 4 %% lågt." + "value" : "Mål: genomsnitt under %1$@ %2$@, GMI under 7 %%, CV högst 36 %%." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Hedef: aralıkta %%70'in üzeri, düşükte %%4'ün altı." + "value" : "Hedef: ortalama %1$@ %2$@ altı, GMI %%7 altı, CV en fazla %%36." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Мета: у діапазоні понад 70%%, низька — менше 4%%." + "value" : "Мета: середнє нижче %1$@ %2$@, GMI нижче 7%%, CV не більше 36%%." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mục tiêu: trên 70%% trong khoảng, dưới 4%% thấp." + "value" : "Mục tiêu: trung bình dưới %1$@ %2$@, GMI dưới 7%%, CV từ 36%% trở xuống." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "目标:范围内超过70%%,低血糖低于4%%。" + "value" : "目标:平均值低于 %1$@ %2$@,GMI 低于 7%%,CV 不超过 36%%。" + } + } + } + }, + "Aim: over 70%% in range; under 25%% above and 4%% below; under 5%% very high and 1%% very low; CGM active over 70%%." : { + "comment" : "Trends Time in range card footnote: the consensus targets for time in range, above and below range, very high, very low and CGM wear", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الهدف: أكثر من 70%% ضمن النطاق؛ أقل من 25%% فوق و4%% تحت؛ أقل من 5%% مرتفع جدًا و1%% منخفض جدًا؛ وCGM نشط أكثر من 70%%." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cíl: přes 70%% v rozsahu; pod 25%% nad a 4%% pod; pod 5%% velmi vysoko a 1%% velmi nízko; CGM aktivní přes 70%%." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: over 70 %% i området; under 25 %% over og 4 %% under; under 5 %% meget højt og 1 %% meget lavt; CGM aktiv over 70 %%." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ziel: über 70 %% im Zielbereich; unter 25 %% darüber und 4 %% darunter; unter 5 %% sehr hoch und 1 %% sehr niedrig; CGM über 70 %% aktiv." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objetivo: más del 70%% en rango; menos del 25%% por encima y del 4%% por debajo; menos del 5%% muy alto y del 1%% muy bajo; CGM activo más del 70%%." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tavoite: yli 70 %% tavoitealueella; alle 25 %% yläpuolella ja 4 %% alapuolella; alle 5 %% erittäin korkealla ja 1 %% erittäin matalalla; CGM aktiivinen yli 70 %%." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objectif : plus de 70 %% dans la cible ; moins de 25 %% au-dessus et 4 %% en dessous ; moins de 5 %% très haut et 1 %% très bas ; CGM actif plus de 70 %%." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "יעד: מעל 70%% בטווח; מתחת ל-25%% מעל ו-4%% מתחת; מתחת ל-5%% גבוה מאוד ו-1%% נמוך מאוד; CGM פעיל מעל 70%%." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cél: több mint 70%% tartományban; kevesebb mint 25%% felette és 4%% alatta; kevesebb mint 5%% nagyon magas és 1%% nagyon alacsony; CGM aktív több mint 70%%." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obiettivo: oltre il 70%% nell'intervallo; meno del 25%% sopra e del 4%% sotto; meno del 5%% molto alta e dell'1%% molto bassa; CGM attivo oltre il 70%%." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "目標:範囲内70%%超、範囲上25%%未満・範囲下4%%未満、非常に高い5%%未満・非常に低い1%%未満、CGM稼働70%%超。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: over 70 %% i målområdet; under 25 %% over og 4 %% under; under 5 %% svært høyt og 1 %% svært lavt; CGM aktiv over 70 %%." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doel: meer dan 70%% binnen bereik; minder dan 25%% erboven en 4%% eronder; minder dan 5%% zeer hoog en 1%% zeer laag; CGM meer dan 70%% actief." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cel: ponad 70%% w zakresie; poniżej 25%% powyżej i 4%% poniżej; poniżej 5%% bardzo wysokich i 1%% bardzo niskich; CGM aktywny ponad 70%%." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meta: mais de 70%% no alvo; menos de 25%% acima e 4%% abaixo; menos de 5%% muito alto e 1%% muito baixo; CGM ativo mais de 70%%." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Țintă: peste 70%% în interval; sub 25%% peste și 4%% sub; sub 5%% foarte ridicat și 1%% foarte scăzut; CGM activ peste 70%%." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цель: в диапазоне более 70%%; выше — менее 25%%, ниже — менее 4%%; очень высокая — менее 5%%, очень низкая — менее 1%%; CGM активен более 70%%." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cieľ: nad 70%% v rozsahu; pod 25%% nad a 4%% pod; pod 5%% veľmi vysoko a 1%% veľmi nízko; CGM aktívny nad 70%%." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: över 70 %% inom målområdet; under 25 %% över och 4 %% under; under 5 %% mycket högt och 1 %% mycket lågt; CGM aktiv över 70 %%." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hedef: aralıkta %%70'in üzeri; üstünde %%25'in, altında %%4'ün altı; çok yüksekte %%5'in, çok düşükte %%1'in altı; CGM aktif %%70'in üzeri." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Мета: у діапазоні понад 70%%; вище — менше 25%%, нижче — менше 4%%; дуже висока — менше 5%%, дуже низька — менше 1%%; CGM активний понад 70%%." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mục tiêu: trên 70%% trong khoảng; dưới 25%% trên khoảng và 4%% dưới khoảng; dưới 5%% rất cao và 1%% rất thấp; CGM hoạt động trên 70%%." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "目标:范围内超过70%%;高于范围低于25%%,低于范围低于4%%;极高低于5%%,极低低于1%%;CGM 在线超过70%%。" } } } @@ -6468,6 +6611,149 @@ } } }, + "CGM data covers %1$@." : { + "comment" : "Trends Summary note when glucose readings start partway through the period. (1: days covered, e.g. 5 of 14 days)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "بيانات CGM تغطي %1$@." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data CGM pokrývají %1$@." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM-data dækker %1$@." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM-Daten decken %1$@ ab." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los datos de CGM cubren %1$@." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM-data kattaa %1$@." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les données CGM couvrent %1$@." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "נתוני CGM מכסים %1$@." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM-adat: %1$@." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "I dati CGM coprono %1$@." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGMデータ:%1$@。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM-data dekker %1$@." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM-gegevens beslaan %1$@." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dane CGM obejmują %1$@." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Os dados de CGM cobrem %1$@." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datele CGM acoperă %1$@." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Данные CGM: %1$@." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Údaje CGM pokrývajú %1$@." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM-data täcker %1$@." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM verisi: %1$@." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дані CGM: %1$@." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dữ liệu CGM bao phủ %1$@." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM 数据覆盖 %1$@。" + } + } + } + }, "Closed loop" : { "comment" : "Trends Loop status: time Loop was running in closed loop", "localizations" : { @@ -16055,115 +16341,258 @@ } } }, - "SD %1$@ · CV %2$@%%" : { - "comment" : "Trends Average tile detail: standard deviation and coefficient of variation of glucose. (1: standard deviation in the glucose unit, 2: coefficient of variation as a percentage number)", + "Scheduled %1$@" : { + "comment" : "Basal and bolus: what the basal schedule delivers over the same time, under the basal row. (1: units, e.g. 18.0 U)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@%%" + "value" : "المجدول %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@%%" + "value" : "Plánovaný %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@ %%" + "value" : "Planlagt %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · VK %2$@ %%" + "value" : "Geplant %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "DE %1$@ · CV %2$@%%" + "value" : "Programado %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@ %%" + "value" : "Ohjelmoitu %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "ET %1$@ · CV %2$@ %%" + "value" : "Programmé %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@%%" + "value" : "מתוזמן %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@%%" + "value" : "Ütemezett %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "DS %1$@ · CV %2$@%%" + "value" : "Programmato %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@%%" + "value" : "スケジュール %1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@ %%" + "value" : "Planlagt %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@%%" + "value" : "Gepland %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@%%" + "value" : "Zaplanowane %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "DP %1$@ · CV %2$@%%" + "value" : "Programada %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@%%" + "value" : "Programat %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@%%" + "value" : "Запланировано %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "SD %1$@ · CV %2$@%%" + "value" : "Plánovaný %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schemalagd %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Planlanan %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "За розкладом %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Theo lịch %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "预设 %1$@" + } + } + } + }, + "SD %1$@ · CV %2$@%%" : { + "comment" : "Trends Average tile detail: standard deviation and coefficient of variation of glucose. (1: standard deviation in the glucose unit, 2: coefficient of variation as a percentage number)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@ %%" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · VK %2$@ %%" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "DE %1$@ · CV %2$@%%" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@ %%" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ET %1$@ · CV %2$@ %%" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "DS %1$@ · CV %2$@%%" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@ %%" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "DP %1$@ · CV %2$@%%" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "SD %1$@ · CV %2$@%%" } }, "sv" : { @@ -17485,6 +17914,149 @@ } } }, + "Today so far %1$@, usually %2$@ by %3$@." : { + "comment" : "Trends Insulin per day footnote: today's insulin so far against the median by this time of day over the last 14 days. (1: today's units, 2: usual units, 3: time of day)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اليوم حتى الآن %1$@، وعادةً %2$@ بحلول %3$@." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dnes zatím %1$@, obvykle %2$@ do %3$@." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "I dag indtil nu %1$@, normalt %2$@ kl. %3$@." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Heute bisher %1$@, üblicherweise %2$@ bis %3$@." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoy hasta ahora %1$@, normalmente %2$@ a las %3$@." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tänään tähän mennessä %1$@, yleensä %2$@ klo %3$@ mennessä." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aujourd'hui jusqu'ici %1$@, d'habitude %2$@ à %3$@." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "היום עד כה %1$@, בדרך כלל %2$@ עד %3$@." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ma eddig %1$@, általában %2$@ ilyenkorra (%3$@)." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oggi finora %1$@, di solito %2$@ entro le %3$@." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "今日のこれまで%1$@、通常は%3$@までに%2$@。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "I dag så langt %1$@, vanligvis %2$@ innen %3$@." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vandaag tot nu toe %1$@, meestal %2$@ om %3$@." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dziś do tej pory %1$@, zwykle %2$@ do %3$@." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoje até agora %1$@, normalmente %2$@ até %3$@." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azi până acum %1$@, de obicei %2$@ până la %3$@." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сегодня на данный момент %1$@, обычно %2$@ к %3$@." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dnes zatiaľ %1$@, zvyčajne %2$@ do %3$@." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hittills i dag %1$@, vanligtvis %2$@ vid %3$@." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bugün şu ana kadar %1$@, genelde %3$@ itibarıyla %2$@." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сьогодні наразі %1$@, зазвичай %2$@ до %3$@." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hôm nay đến giờ %1$@, thường %2$@ trước %3$@." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "今日至今 %1$@,通常到 %3$@ 为 %2$@。" + } + } + } + }, "Total" : { "comment" : "Basal and bolus total row: basal plus bolus", "localizations" : { @@ -18629,6 +19201,149 @@ } } }, + "Usually %1$@ by %2$@" : { + "comment" : "Trends: the median insulin delivered by this time of day over the last 14 days. (1: units, e.g. 28.4 U, 2: time of day)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عادةً %1$@ بحلول %2$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obvykle %1$@ do %2$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Normalt %1$@ kl. %2$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Üblicherweise %1$@ bis %2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Normalmente %1$@ a las %2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yleensä %1$@ klo %2$@ mennessä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "D'habitude %1$@ à %2$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בדרך כלל %1$@ עד %2$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Általában %1$@ ilyenkorra (%2$@)" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Di solito %1$@ entro le %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "通常は%2$@までに%1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vanligvis %1$@ innen %2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meestal %1$@ om %2$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zwykle %1$@ do %2$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Normalmente %1$@ até %2$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "De obicei %1$@ până la %2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Обычно %1$@ к %2$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zvyčajne %1$@ do %2$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vanligtvis %1$@ vid %2$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Genelde %2$@ itibarıyla %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Зазвичай %1$@ до %2$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thường %1$@ trước %2$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "通常到 %2$@ 为 %1$@" + } + } + } + }, "Very high" : { "comment" : "Time in range band: glucose above 250 mg/dL (13.9 mmol/L) Time-in-range band name: glucose above 250 mg/dL (13.9 mmol/L)", "localizations" : { diff --git a/Loop/Redesign/LoopHistoryMath.swift b/Loop/Redesign/LoopHistoryMath.swift index 0bc1080785..cfdca173dc 100644 --- a/Loop/Redesign/LoopHistoryMath.swift +++ b/Loop/Redesign/LoopHistoryMath.swift @@ -126,6 +126,28 @@ enum LoopHistoryMath { Set(points.map { calendar.startOfDay(for: $0.date) }).count } + /// Whole days from the first reading to the end of `interval`, when that falls short of the period by more than a + /// day; nil when the readings span the period, or there are none. + static func cgmCoveredDays(_ points: [GlucosePoint], in interval: DateInterval, periodDays: Int) -> Int? { + guard periodDays > 1, let first = points.map(\.date).min() else { return nil } + let available = interval.end.timeIntervalSince(first) / 86400 + guard available + 1 < Double(periodDays) else { return nil } + return max(1, Int(available.rounded())) + } + + /// Units the schedule would deliver over `span`, or nil where the points don't cover all of it. + static func scheduledUnits(_ points: [ScheduledBasalPoint], over span: DateInterval) -> Double? { + var units = 0.0 + var covered: TimeInterval = 0 + for point in points { + let overlap = min(point.end, span.end).timeIntervalSince(max(point.start, span.start)) + guard overlap > 0 else { continue } + units += point.rate * overlap / 3600 + covered += overlap + } + return covered >= span.duration - 60 ? units : nil + } + // MARK: Days /// Midnight-to-midnight intervals covering `interval`, oldest first, the last one cut off at `interval.end`. diff --git a/Loop/Redesign/LoopHistoryModels.swift b/Loop/Redesign/LoopHistoryModels.swift index e1acc34c89..3fc71c5b47 100644 --- a/Loop/Redesign/LoopHistoryModels.swift +++ b/Loop/Redesign/LoopHistoryModels.swift @@ -16,6 +16,8 @@ import LoopAlgorithm struct GlucosePoint: Equatable { let date: Date let mgdl: Double + /// A fingerstick or other reading typed in: drawn on charts, but left out of CGM statistics. + var userEntered = false } struct BolusEvent: Equatable, Identifiable { diff --git a/Loop/Redesign/LoopHistoryProvider.swift b/Loop/Redesign/LoopHistoryProvider.swift index 5bdc47d9f6..0c5236808a 100644 --- a/Loop/Redesign/LoopHistoryProvider.swift +++ b/Loop/Redesign/LoopHistoryProvider.swift @@ -59,6 +59,19 @@ final class LoopHistoryProvider { return range.lowerBound.doubleValue(for: .milligramsPerDeciliter)...range.upperBound.doubleValue(for: .milligramsPerDeciliter) } + /// What the basal schedule delivers in a day, for days the stored profile history doesn't reach. + @MainActor + var scheduledBasalPerDay: Double? { + loopDataManager.settingsProvider.settings.basalRateSchedule?.total() + } + + /// The median insulin delivered from midnight to this time of day over the last 14 days, as on Statistics. + func typicalInsulinByNow() async -> (typical: TypicalInsulinDelivery, at: Date)? { + guard let history = try? await loopDataManager.insulinDeliveryHistory(days: 14), + let typical = history.typicalDelivery(by: history.end) else { return nil } + return (typical, history.end) + } + /// The targets that actually applied, including dated presets and changes to the stored profile. func glucoseTargetBands(in interval: DateInterval) async throws -> [TargetBand] { if let store = settingsManager.settingsStore { @@ -203,7 +216,7 @@ final class LoopHistoryProvider { let samples = try await glucoseStore.getGlucoseSamples(start: interval.start, end: interval.end) return samples .filter { !$0.isDisplayOnly } - .map { GlucosePoint(date: $0.startDate, mgdl: $0.quantity.doubleValue(for: .milligramsPerDeciliter)) } + .map { GlucosePoint(date: $0.startDate, mgdl: $0.quantity.doubleValue(for: .milligramsPerDeciliter), userEntered: $0.wasUserEntered) } } catch { os_log("Glucose fetch failed: %{public}@", log: log, type: .error, String(describing: error)) throw error @@ -320,7 +333,8 @@ final class LoopHistoryProvider { for sample in samples where !sample.isDisplayOnly && (FeatureFlags.observeHealthKitGlucoseSamplesFromOtherApps || sample.sourceRevision.source.bundleIdentifier == own) { if let last = points.last, sample.startDate.timeIntervalSince(last.date) < 150 { continue } - points.append(GlucosePoint(date: sample.startDate, mgdl: sample.quantity.doubleValue(for: .milligramsPerDeciliter))) + points.append(GlucosePoint(date: sample.startDate, mgdl: sample.quantity.doubleValue(for: .milligramsPerDeciliter), + userEntered: sample.wasUserEntered)) } return points } diff --git a/Loop/Redesign/TrendsComponents.swift b/Loop/Redesign/TrendsComponents.swift index 5f93573c36..c98336a72c 100644 --- a/Loop/Redesign/TrendsComponents.swift +++ b/Loop/Redesign/TrendsComponents.swift @@ -460,12 +460,22 @@ struct InsulinSplitView: View { let split: InsulinSplit /// Today so far, rather than a whole day or a per-day average. var partial = false + /// What the basal schedule delivers over the same time, shown under the basal row. + var scheduledBasal: Double? + /// Today against a usual day, e.g. "Usually 28.4 U by 19:46", under the total. + var usual: String? var body: some View { VStack(alignment: .leading, spacing: LoopDesign.Space.s3) { InsulinSplitBar(split: split) VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { row(.basal, NSLocalizedString("Basal", comment: "Basal and bolus row: insulin delivered as basal"), split.basal, split.basalPercent) + if let scheduledBasal { + part(.dash(LoopDesign.Palette.basalScheduled), String(format: NSLocalizedString("Scheduled %1$@", comment: "Basal and bolus: what the basal schedule delivers over the same time, under the basal row. (1: units, e.g. 18.0 U)"), LoopFormat.totalUnits(scheduledBasal))) + .font(LoopDesign.Typeface.footnote.monospacedDigit()) + .foregroundStyle(LoopDesign.Palette.inkMuted) + .padding(.leading, 10 + LoopDesign.Space.s2) + } row(.solid(LoopDesign.Palette.bolus), NSLocalizedString("Bolus", comment: "Basal and bolus row: insulin delivered as bolus, manual and automatic"), split.bolus, split.bolusPercent) if split.isClassified && split.bolus > 0 { FlowLayout(spacing: LoopDesign.Space.s3, lineSpacing: 2) { @@ -488,10 +498,16 @@ struct InsulinSplitView: View { .font(LoopDesign.Typeface.statValueSmall) .foregroundStyle(LoopDesign.Palette.insulinInk) } + if let usual { + Text(usual) + .font(LoopDesign.Typeface.footnote.monospacedDigit()) + .foregroundStyle(LoopDesign.Palette.inkMuted) + .frame(maxWidth: .infinity, alignment: .trailing) + } } } .accessibilityElement(children: .ignore) - .accessibilityLabel(Self.accessibilityLabel(split, partial: partial)) + .accessibilityLabel(Self.accessibilityLabel(split, partial: partial, scheduledBasal: scheduledBasal, usual: usual)) } private func row(_ swatch: LegendItem.Swatch, _ label: String, _ units: Double, _ percent: Int) -> some View { @@ -518,7 +534,7 @@ struct InsulinSplitView: View { } } - static func accessibilityLabel(_ split: InsulinSplit, partial: Bool) -> String { + static func accessibilityLabel(_ split: InsulinSplit, partial: Bool, scheduledBasal: Double? = nil, usual: String? = nil) -> String { var parts = [ String(format: NSLocalizedString("Basal %1$@, %2$@ percent", comment: "VoiceOver: basal units and share. (1: units, 2: percentage number)"), LoopFormat.totalUnits(split.basal), "\(split.basalPercent)"), String(format: NSLocalizedString("Bolus %1$@, %2$@ percent", comment: "VoiceOver: bolus units and share. (1: units, 2: percentage number)"), LoopFormat.totalUnits(split.bolus), "\(split.bolusPercent)"), @@ -531,6 +547,12 @@ struct InsulinSplitView: View { ? NSLocalizedString("Total so far %1$@", comment: "VoiceOver: today's total insulin so far. (1: units)") : NSLocalizedString("Total %1$@", comment: "VoiceOver: total insulin. (1: units)"), LoopFormat.totalUnits(split.total))) + if let scheduledBasal { + parts.insert(String(format: NSLocalizedString("Scheduled %1$@", comment: "Basal and bolus: what the basal schedule delivers over the same time, under the basal row. (1: units, e.g. 18.0 U)"), LoopFormat.totalUnits(scheduledBasal)), at: 1) + } + if let usual { + parts.append(usual) + } return parts.joined(separator: ". ") } } diff --git a/Loop/Redesign/TrendsScreen.swift b/Loop/Redesign/TrendsScreen.swift index efb520874d..9f8c5960a6 100644 --- a/Loop/Redesign/TrendsScreen.swift +++ b/Loop/Redesign/TrendsScreen.swift @@ -17,7 +17,10 @@ final class TrendsModel: ObservableObject { struct Summary { var interval: DateInterval var unit: GlucoseUnitStyle + /// CGM readings only: typed-in fingersticks are left out of every figure, as on Statistics. var glucose: [GlucosePoint] = [] + /// Days from the first reading, when that is short of the period; nil when the readings span it. + var cgmCoveredDays: Int? var stats: LoopHistoryMath.GlucoseStats? var cgmActive: Double = 0 var timeInRange: [LoopHistoryMath.RangeBand: Double] = [:] @@ -35,6 +38,10 @@ final class TrendsModel: ObservableObject { /// The whole calendar day, for a one-day period: drawn as one timeline. var day: DayHistory? var dayTargets: [TargetBand] = [] + /// What the basal schedule delivers over the same days the split averages. + var scheduledBasal: Double? + /// The median insulin by this time of day over the last 14 days, when the period ends now. + var typicalByNow: (typical: TypicalInsulinDelivery, at: Date)? /// The days per-day figures divide by: insulin's averaging days, or complete days with CGM data when there is /// no insulin (today only when no complete day has any), never the period length. @@ -101,12 +108,15 @@ final class TrendsModel: ObservableObject { history = history.clipped(to: interval) } - summary.glucose = history.glucose - summary.stats = LoopHistoryMath.glucoseStats(history.glucose) - summary.cgmActive = LoopHistoryMath.cgmActiveFraction(history.glucose, over: interval) - summary.timeInRange = LoopHistoryMath.timeInRange(history.glucose) - summary.agp = LoopHistoryMath.agp(history.glucose) - summary.agpDays = LoopHistoryMath.daysWithReadings(history.glucose) + let cgm = history.glucose.filter { !$0.userEntered } + summary.glucose = cgm + summary.stats = LoopHistoryMath.glucoseStats(cgm) + summary.cgmActive = LoopHistoryMath.cgmActiveFraction(cgm, over: interval) + summary.timeInRange = LoopHistoryMath.timeInRange(cgm) + summary.agp = LoopHistoryMath.agp(cgm) + summary.agpDays = LoopHistoryMath.daysWithReadings(cgm) + summary.cgmCoveredDays = LoopHistoryMath.cgmCoveredDays(cgm, in: interval, + periodDays: LoopHistoryMath.days(in: interval).count) let calendar = Calendar.current summary.totals = LoopHistoryMath.days(in: interval).map { day in @@ -134,6 +144,20 @@ final class TrendsModel: ObservableObject { summary.manualBoluses = boluses.filter { !$0.automatic }.count summary.automaticBoluses = boluses.filter(\.automatic).count + // Days the stored profile history doesn't reach (older ones, read from Health) use today's schedule. + let spans = Dictionary(uniqueKeysWithValues: LoopHistoryMath.days(in: interval).map { ($0.start, $0) }) + let perDay = provider.scheduledBasalPerDay + let scheduled = insulinDays.compactMap { day -> Double? in + guard let span = spans[day.day] else { return nil } + return LoopHistoryMath.scheduledUnits(history.scheduledBasal, over: span) ?? perDay.map { $0 * span.duration / 86400 } + } + if !scheduled.isEmpty, scheduled.count == insulinDays.count { + summary.scheduledBasal = scheduled.reduce(0, +) / Double(scheduled.count) + } + if interval.end.timeIntervalSinceNow > -300 { + summary.typicalByNow = await provider.typicalInsulinByNow() + } + summary.loopSpans = LoopHistoryMath.loopModeSpans(interval: interval, runs: runs, modeChanges: LoopModeHistory.shared.changes, coverageStart: coverageStart) return summary @@ -361,9 +385,7 @@ struct TrendsView: View { } else { TimeInRangeView(fractions: s.timeInRange, unit: s.unit) } - Text(String(format: NSLocalizedString("Aim: over 70%% in range, under 4%% low.", comment: "Trends Time in range card footnote: the consensus targets for time in range and time low"))) - .font(LoopDesign.Typeface.footnote) - .foregroundStyle(P.inkMuted) + Footnote(String(format: NSLocalizedString("Aim: over 70%% in range; under 25%% above and 4%% below; under 5%% very high and 1%% very low; CGM active over 70%%.", comment: "Trends Time in range card footnote: the consensus targets for time in range, above and below range, very high, very low and CGM wear"))) } if s.day == nil { patternCard(s) @@ -377,7 +399,8 @@ struct TrendsView: View { : NSLocalizedString("average per day", comment: "Trends Basal and bolus card subtitle when the figures are averaged per day over the period")) } if s.split.total > 0 { - InsulinSplitView(split: s.split, partial: s.splitIsPartial) + InsulinSplitView(split: s.split, partial: s.splitIsPartial, scheduledBasal: s.scheduledBasal, + usual: s.splitIsPartial ? usualByNow(s) : nil) } else { ChartPlaceholder(message: NSLocalizedString("No insulin recorded in this period", comment: "Trends Insulin per day chart placeholder when no insulin was delivered in the period"), height: 80) } @@ -395,6 +418,9 @@ struct TrendsView: View { path.append(.day(day)) } ChartLegend(items: insulinLegend(s)) + if let today = todaySoFar(s) { + Footnote(today) + } } } LoopCard { @@ -405,9 +431,7 @@ struct TrendsView: View { } LoopModeBarView(spans: s.loopSpans) if let note = coverageNote(s) { - Text(note) - .font(LoopDesign.Typeface.footnote) - .foregroundStyle(P.inkMuted) + Footnote(note) } } } else if let error = model.loadError { @@ -454,11 +478,39 @@ struct TrendsView: View { } .foregroundStyle(P.accentInk) } + if let covered = s.cgmCoveredDays { + Footnote(String(format: NSLocalizedString("CGM data covers %1$@.", comment: "Trends Summary note when glucose readings start partway through the period. (1: days covered, e.g. 5 of 14 days)"), + daysOf(covered, s.totals.count))) + } StatGridView(tiles: model.tiles(s)) .opacity(model.loading ? 0.5 : 1) + if s.stats != nil { + Footnote(String(format: NSLocalizedString("Aim: average under %1$@ %2$@, GMI under 7%%, CV 36%% or less.", comment: "Trends Summary footnote: the consensus targets for average glucose, GMI and glucose variability (CV). (1: glucose value, 2: glucose unit)"), + s.unit.isMmol ? LoopFormat.number(8.6, decimals: 1) : "154", s.unit.unitName)) + } } } + /// "5 of 14 days". + private func daysOf(_ count: Int, _ total: Int) -> String { + let days = String.localizedStringWithFormat(NSLocalizedString("%lld days", comment: "A number of days, e.g. a Trends period or how many days are averaged. (1: number of days)"), total) + return String(format: NSLocalizedString("%1$@ of %2$@", comment: "Trends: how many days of the period are averaged. (1: days averaged, a number; 2: days in the period, e.g. 14 days)"), LoopFormat.number(Double(count), decimals: 0), days) + } + + /// "Usually 28.4 U by 19:46": today's insulin against the last 14 days at the same time. + private func usualByNow(_ s: TrendsModel.Summary) -> String? { + guard let usual = s.typicalByNow else { return nil } + return String(format: NSLocalizedString("Usually %1$@ by %2$@", comment: "Trends: the median insulin delivered by this time of day over the last 14 days. (1: units, e.g. 28.4 U, 2: time of day)"), + LoopFormat.totalUnits(usual.typical.median), LoopFormat.time(usual.at)) + } + + /// Today's bar is still growing, so say where it stands against a usual day. + private func todaySoFar(_ s: TrendsModel.Summary) -> String? { + guard let today = s.totals.last, !today.isComplete, today.total > 0, let usual = s.typicalByNow else { return nil } + return String(format: NSLocalizedString("Today so far %1$@, usually %2$@ by %3$@.", comment: "Trends Insulin per day footnote: today's insulin so far against the median by this time of day over the last 14 days. (1: today's units, 2: usual units, 3: time of day)"), + LoopFormat.totalUnits(today.total), LoopFormat.totalUnits(usual.typical.median), LoopFormat.time(usual.at)) + } + private func export(_ make: () throws -> URL) { do { exportURL = try make() @@ -503,6 +555,22 @@ struct ShareSheet: UIViewControllerRepresentable { func updateUIViewController(_ controller: UIActivityViewController, context: Context) {} } +/// A card's quiet closing line: a target, a caveat or a comparison. +private struct Footnote: View { + let text: String + + init(_ text: String) { + self.text = text + } + + var body: some View { + Text(text) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + .fixedSize(horizontal: false, vertical: true) + } +} + // MARK: - PDF report /// The Trends cards laid out for paper. diff --git a/LoopTests/Models/LoopHistoryMathTests.swift b/LoopTests/Models/LoopHistoryMathTests.swift index b468c9dc42..baa7b68960 100644 --- a/LoopTests/Models/LoopHistoryMathTests.swift +++ b/LoopTests/Models/LoopHistoryMathTests.swift @@ -123,6 +123,22 @@ class LoopHistoryMathTests: XCTestCase { XCTAssertNil(LoopHistoryMath.glucoseStats([points[0]])) } + func testCGMCoverageNotedOnlyWhenReadingsStartLate() { + let interval = DateInterval(start: at(0), end: at(14 * 1440)) + let late = [GlucosePoint(date: at(9 * 1440), mgdl: 120), GlucosePoint(date: at(10 * 1440), mgdl: 130)] + XCTAssertEqual(LoopHistoryMath.cgmCoveredDays(late, in: interval, periodDays: 14), 5) + // Within a day of the full period, or a one-day period, says nothing. + XCTAssertNil(LoopHistoryMath.cgmCoveredDays([GlucosePoint(date: at(600), mgdl: 120)], in: interval, periodDays: 14)) + XCTAssertNil(LoopHistoryMath.cgmCoveredDays(late, in: DateInterval(start: at(0), end: at(1440)), periodDays: 1)) + XCTAssertNil(LoopHistoryMath.cgmCoveredDays([], in: interval, periodDays: 14)) + } + + func testScheduledUnitsNeedTheWholeSpanCovered() { + let points = [ScheduledBasalPoint(start: at(0), end: at(120), rate: 1.0), ScheduledBasalPoint(start: at(120), end: at(240), rate: 0.5)] + XCTAssertEqual(try XCTUnwrap(LoopHistoryMath.scheduledUnits(points, over: DateInterval(start: at(60), end: at(180)))), 1.5, accuracy: 1e-9) + XCTAssertNil(LoopHistoryMath.scheduledUnits(points, over: DateInterval(start: at(60), end: at(360)))) + } + func testPercentileInterpolates() { XCTAssertEqual(LoopHistoryMath.percentile([1, 2, 3, 4, 5], 0.5), 3) XCTAssertEqual(LoopHistoryMath.percentile([1, 2, 3, 4], 0.5), 2.5) From 71df867ac5bba474084bfa1d884942cca5ba614b Mon Sep 17 00:00:00 2001 From: Garry Casey Date: Sun, 1 Nov 2026 13:53:41 +0000 Subject: [PATCH 3/5] Add a Logbook of the last 7 days The Logbook lists Loop's 7 days newest first, a section per day with its insulin and carb totals and the day's basal and bolus split. Rows cover manual and automatic boluses, carbs, basal adjustments, suspends and resumes, site changes and pump alarms. Runs of automatic boluses fold into one row that opens the list of doses, and a day's basal adjustments open as their own list. Filters narrow it to boluses, basal, carbs or what Loop did automatically. Carb entries can be edited or deleted with a swipe when Loop's own carb editor would allow it, and doses can be deleted. Editing opens Loop's existing carb entry screen in a sheet, which keeps the bolus screen that can follow it clear of anything underneath. The Logbook opens from Settings, beside Trends, as a sheet with its own navigation and a Done button. This is part 2 of 6 splitting #2621, and builds on part 1. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01QHnVP7ynB62LNuuYMy3yvH --- Loop.xcodeproj/project.pbxproj | 4 + .../critical-ink.colorset/Contents.json | 38 + Loop/Localizable.xcstrings | 10100 +++++++++++++--- Loop/Redesign/LogbookScreen.swift | 642 + Loop/Redesign/LoopDesign.swift | 64 + Loop/Redesign/LoopHistoryMath.swift | 2 +- Loop/Redesign/LoopHistoryModels.swift | 12 + Loop/Redesign/LoopHistoryProvider.swift | 66 + Loop/Redesign/TrendsComponents.swift | 37 + Loop/Views/SettingsView.swift | 15 + LoopTests/Models/LoopHistoryMathTests.swift | 31 +- 11 files changed, 9231 insertions(+), 1780 deletions(-) create mode 100644 Loop/DerivedAssetsBase.xcassets/critical-ink.colorset/Contents.json create mode 100644 Loop/Redesign/LogbookScreen.swift diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index f87be065c4..62f600c001 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -732,6 +732,7 @@ 2ADBB92A8ABB74360D18EEFA /* LoopHistoryModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 471FFD525A4EB897745608BE /* LoopHistoryModels.swift */; }; A743DC26D066EE5AC88C2F45 /* LoopHistoryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */; }; 6A6C1E1C7A26EC88AFB4FAB1 /* DayDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */; }; + 23B98F0A53A093412229364D /* LogbookScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEDC42672A7A8C57546EA0C /* LogbookScreen.swift */; }; 591F13AF435B37B4818E2F2B /* TrendsComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */; }; 5CD0888FF33D6F844A81E638 /* TrendsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF0FD64B2B3E9C9AA8764684 /* TrendsScreen.swift */; }; 97CF13197936D2794766C84A /* LoopHistoryMathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B08644C3031D9E815F9892 /* LoopHistoryMathTests.swift */; }; @@ -1611,6 +1612,7 @@ 471FFD525A4EB897745608BE /* LoopHistoryModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopHistoryModels.swift; sourceTree = ""; }; 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopHistoryProvider.swift; sourceTree = ""; }; E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayDetailView.swift; sourceTree = ""; }; + 7AEDC42672A7A8C57546EA0C /* LogbookScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogbookScreen.swift; sourceTree = ""; }; 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendsComponents.swift; sourceTree = ""; }; AF0FD64B2B3E9C9AA8764684 /* TrendsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendsScreen.swift; sourceTree = ""; }; 42B08644C3031D9E815F9892 /* LoopHistoryMathTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopHistoryMathTests.swift; sourceTree = ""; }; @@ -3017,6 +3019,7 @@ children = ( AF0FD64B2B3E9C9AA8764684 /* TrendsScreen.swift */, 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */, + 7AEDC42672A7A8C57546EA0C /* LogbookScreen.swift */, E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */, 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */, 471FFD525A4EB897745608BE /* LoopHistoryModels.swift */, @@ -3687,6 +3690,7 @@ files = ( 5CD0888FF33D6F844A81E638 /* TrendsScreen.swift in Sources */, 591F13AF435B37B4818E2F2B /* TrendsComponents.swift in Sources */, + 23B98F0A53A093412229364D /* LogbookScreen.swift in Sources */, 6A6C1E1C7A26EC88AFB4FAB1 /* DayDetailView.swift in Sources */, A743DC26D066EE5AC88C2F45 /* LoopHistoryProvider.swift in Sources */, 2ADBB92A8ABB74360D18EEFA /* LoopHistoryModels.swift in Sources */, diff --git a/Loop/DerivedAssetsBase.xcassets/critical-ink.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/critical-ink.colorset/Contents.json new file mode 100644 index 0000000000..2f18c32cc1 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/critical-ink.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "0.769", + "green": "0.188", + "blue": "0.169", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "1.000", + "green": "0.412", + "blue": "0.380", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/Localizable.xcstrings b/Loop/Localizable.xcstrings index ddaba7520d..848e10750e 100644 --- a/Loop/Localizable.xcstrings +++ b/Loop/Localizable.xcstrings @@ -299,6 +299,292 @@ } } }, + "%1$@ for %2$@" : { + "comment" : "A temp basal: its rate and how long it ran. (1: rate, e.g. 0.50 U/h; 2: duration, e.g. 30m)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ لمدة %2$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ po dobu %2$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ i %2$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ für %2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ durante %2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ ajan" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ pendant %2$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ למשך %2$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ ideig" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ per %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@(%2$@間)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ i %2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ gedurende %2$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ przez %2$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ por %2$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ timp de %2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ на %2$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ počas %2$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ i %2$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%2$@ boyunca %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ на %2$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ trong %2$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@,持续%2$@" + } + } + } + }, + "%1$@ for %2$@ (scheduled %3$@)" : { + "comment" : "A temp basal: its rate, how long it ran and the scheduled rate it replaced. (1: rate, e.g. 0.50 U/h; 2: duration, e.g. 30m; 3: scheduled rate number, e.g. 0.80)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ لمدة %2$@ (المجدول %3$@)" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ po dobu %2$@ (dle plánu %3$@)" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ i %2$@ (planlagt %3$@)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ für %2$@ (geplant %3$@)" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ durante %2$@ (programada %3$@)" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ ajan (ohjelmoitu %3$@)" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ pendant %2$@ (programmé %3$@)" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ למשך %2$@ (מתוזמן %3$@)" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@, %2$@ ideig (ütemezett: %3$@)" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ per %2$@ (programmata %3$@)" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@(%2$@間、予定 %3$@)" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ i %2$@ (planlagt %3$@)" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ gedurende %2$@ (gepland %3$@)" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ przez %2$@ (wg planu %3$@)" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ por %2$@ (programada %3$@)" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ timp de %2$@ (programat %3$@)" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ на %2$@ (по плану %3$@)" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ počas %2$@ (podľa plánu %3$@)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ i %2$@ (schemalagd %3$@)" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%2$@ boyunca %1$@ (planlanan %3$@)" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ на %2$@ (за планом %3$@)" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ trong %2$@ (theo lịch %3$@)" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@,持续%2$@(预设 %3$@)" + } + } + } + }, "%1$@ g" : { "comment" : "Format string for an amount of carbohydrate in grams. (1: grams)", "localizations" : { @@ -2399,8 +2685,8 @@ } } }, - "%lld entries" : { - "comment" : "Trends Carbs tile detail for a one-day period: how many carb entries were made that day. (1: number)", + "%lld doses" : { + "comment" : "Logbook folded automatic boluses row: how many automatic boluses it holds. (1: number)", "localizations" : { "ar" : { "variations" : { @@ -2408,37 +2694,37 @@ "zero" : { "stringUnit" : { "state" : "translated", - "value" : "%lld إدخالات" + "value" : "%lld جرعة" } }, "one" : { "stringUnit" : { "state" : "translated", - "value" : "إدخال واحد" + "value" : "جرعة واحدة" } }, "two" : { "stringUnit" : { "state" : "translated", - "value" : "إدخالان" + "value" : "جرعتان" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld إدخالات" + "value" : "%lld جرعات" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld إدخالًا" + "value" : "%lld جرعة" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld إدخال" + "value" : "%lld جرعة" } } } @@ -2450,25 +2736,25 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld záznam" + "value" : "%lld dávka" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld záznamy" + "value" : "%lld dávky" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld záznamu" + "value" : "%lld dávky" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld záznamů" + "value" : "%lld dávek" } } } @@ -2480,13 +2766,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld registrering" + "value" : "%lld dosis" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld registreringer" + "value" : "%lld doser" } } } @@ -2498,13 +2784,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld Eintrag" + "value" : "%lld Dosis" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld Einträge" + "value" : "%lld Dosen" } } } @@ -2516,13 +2802,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld entry" + "value" : "%lld dose" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld entries" + "value" : "%lld doses" } } } @@ -2534,19 +2820,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld entrada" + "value" : "%lld dosis" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld entradas" + "value" : "%lld dosis" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld entradas" + "value" : "%lld dosis" } } } @@ -2558,13 +2844,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kirjaus" + "value" : "%lld annos" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kirjausta" + "value" : "%lld annosta" } } } @@ -2576,19 +2862,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld entrée" + "value" : "%lld dose" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld entrées" + "value" : "%lld doses" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld entrées" + "value" : "%lld doses" } } } @@ -2600,19 +2886,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "רשומה אחת" + "value" : "מנה %lld" } }, "two" : { "stringUnit" : { "state" : "translated", - "value" : "%lld רשומות" + "value" : "%lld מנות" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld רשומות" + "value" : "%lld מנות" } } } @@ -2624,13 +2910,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld bejegyzés" + "value" : "%lld adag" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld bejegyzés" + "value" : "%lld adag" } } } @@ -2642,19 +2928,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld inserimento" + "value" : "%lld dose" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld inserimenti" + "value" : "%lld dosi" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld inserimenti" + "value" : "%lld dosi" } } } @@ -2666,7 +2952,7 @@ "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld件" + "value" : "%lld回" } } } @@ -2678,13 +2964,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld registrering" + "value" : "%lld dose" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld registreringer" + "value" : "%lld doser" } } } @@ -2696,13 +2982,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld registratie" + "value" : "%lld dosis" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld registraties" + "value" : "%lld doses" } } } @@ -2714,25 +3000,25 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld wpis" + "value" : "%lld dawka" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld wpisy" + "value" : "%lld dawki" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld wpisów" + "value" : "%lld dawek" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld wpisu" + "value" : "%lld dawki" } } } @@ -2744,19 +3030,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld registro" + "value" : "%lld dose" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld registros" + "value" : "%lld doses" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld registros" + "value" : "%lld doses" } } } @@ -2768,19 +3054,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld intrare" + "value" : "%lld doză" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld intrări" + "value" : "%lld doze" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld de intrări" + "value" : "%lld de doze" } } } @@ -2792,25 +3078,25 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld запись" + "value" : "%lld доза" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld записи" + "value" : "%lld дозы" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld записей" + "value" : "%lld доз" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld записи" + "value" : "%lld дозы" } } } @@ -2822,25 +3108,25 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld záznam" + "value" : "%lld dávka" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld záznamy" + "value" : "%lld dávky" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld záznamu" + "value" : "%lld dávky" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld záznamov" + "value" : "%lld dávok" } } } @@ -2852,13 +3138,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld registrering" + "value" : "%lld dos" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld registreringar" + "value" : "%lld doser" } } } @@ -2870,13 +3156,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld giriş" + "value" : "%lld doz" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld giriş" + "value" : "%lld doz" } } } @@ -2888,25 +3174,25 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld запис" + "value" : "%lld доза" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld записи" + "value" : "%lld дози" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld записів" + "value" : "%lld доз" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld запису" + "value" : "%lld дози" } } } @@ -2918,7 +3204,7 @@ "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld lần nhập" + "value" : "%lld liều" } } } @@ -2930,7 +3216,7 @@ "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld条" + "value" : "%lld 次" } } } @@ -2938,64 +3224,46 @@ } } }, - "%lld shorter gaps" : { - "comment" : "Chart note: how many other, shorter gaps in glucose readings there were; follows the longest gap. (1: number of gaps)", + "%lld entries" : { + "comment" : "Trends Carbs tile detail for a one-day period: how many carb entries were made that day. (1: number)", "localizations" : { - "en" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld shorter gap" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld shorter gaps" - } - } - } - } - }, "ar" : { "variations" : { "plural" : { "zero" : { "stringUnit" : { "state" : "translated", - "value" : "%lld فجوة أقصر" + "value" : "%lld إدخالات" } }, "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld فجوة أقصر" + "value" : "إدخال واحد" } }, "two" : { "stringUnit" : { "state" : "translated", - "value" : "%lld فجوة أقصر" + "value" : "إدخالان" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld فجوات أقصر" + "value" : "%lld إدخالات" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld فجوة أقصر" + "value" : "%lld إدخالًا" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld فجوة أقصر" + "value" : "%lld إدخال" } } } @@ -3007,25 +3275,25 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kratší výpadek" + "value" : "%lld záznam" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kratší výpadky" + "value" : "%lld záznamy" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kratšího výpadku" + "value" : "%lld záznamu" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kratších výpadků" + "value" : "%lld záznamů" } } } @@ -3037,13 +3305,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kortere hul" + "value" : "%lld registrering" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kortere huller" + "value" : "%lld registreringer" } } } @@ -3055,13 +3323,31 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kürzere Lücke" + "value" : "%lld Eintrag" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kürzere Lücken" + "value" : "%lld Einträge" + } + } + } + } + }, + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld entry" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld entries" } } } @@ -3073,13 +3359,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld interrupción más corta" + "value" : "%lld entrada" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld entradas" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld interrupciones más cortas" + "value" : "%lld entradas" } } } @@ -3091,13 +3383,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld lyhyempi katko" + "value" : "%lld kirjaus" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld lyhyempää katkoa" + "value" : "%lld kirjausta" } } } @@ -3109,19 +3401,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld interruption plus courte" + "value" : "%lld entrée" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld interruptions plus courtes" + "value" : "%lld entrées" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld interruptions plus courtes" + "value" : "%lld entrées" } } } @@ -3133,25 +3425,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld פער קצר יותר" + "value" : "רשומה אחת" } }, "two" : { "stringUnit" : { "state" : "translated", - "value" : "%lld פערים קצרים יותר" - } - }, - "many" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld פערים קצרים יותר" + "value" : "%lld רשומות" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld פערים קצרים יותר" + "value" : "%lld רשומות" } } } @@ -3163,13 +3449,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld rövidebb kimaradás" + "value" : "%lld bejegyzés" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld rövidebb kimaradás" + "value" : "%lld bejegyzés" } } } @@ -3181,13 +3467,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld interruzione più breve" + "value" : "%lld inserimento" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld inserimenti" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld interruzioni più brevi" + "value" : "%lld inserimenti" } } } @@ -3199,7 +3491,7 @@ "other" : { "stringUnit" : { "state" : "translated", - "value" : "ほか短い欠損%lld件" + "value" : "%lld件" } } } @@ -3211,13 +3503,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kortere hull" + "value" : "%lld registrering" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kortere hull" + "value" : "%lld registreringer" } } } @@ -3229,13 +3521,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kortere onderbreking" + "value" : "%lld registratie" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kortere onderbrekingen" + "value" : "%lld registraties" } } } @@ -3247,25 +3539,25 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld krótsza przerwa" + "value" : "%lld wpis" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld krótsze przerwy" + "value" : "%lld wpisy" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld krótszych przerw" + "value" : "%lld wpisów" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld krótszej przerwy" + "value" : "%lld wpisu" } } } @@ -3277,13 +3569,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld interrupção mais curta" + "value" : "%lld registro" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld registros" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld interrupções mais curtas" + "value" : "%lld registros" } } } @@ -3295,19 +3593,19 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld întrerupere mai scurtă" + "value" : "%lld intrare" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld întreruperi mai scurte" + "value" : "%lld intrări" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld de întreruperi mai scurte" + "value" : "%lld de intrări" } } } @@ -3319,25 +3617,25 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld короткий пропуск" + "value" : "%lld запись" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld коротких пропуска" + "value" : "%lld записи" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld коротких пропусков" + "value" : "%lld записей" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld коротких пропуска" + "value" : "%lld записи" } } } @@ -3349,25 +3647,25 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kratší výpadok" + "value" : "%lld záznam" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kratšie výpadky" + "value" : "%lld záznamy" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kratšieho výpadku" + "value" : "%lld záznamu" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kratších výpadkov" + "value" : "%lld záznamov" } } } @@ -3379,13 +3677,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kortare lucka" + "value" : "%lld registrering" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld kortare luckor" + "value" : "%lld registreringar" } } } @@ -3397,13 +3695,13 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld daha kısa boşluk" + "value" : "%lld giriş" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld daha kısa boşluk" + "value" : "%lld giriş" } } } @@ -3415,25 +3713,25 @@ "one" : { "stringUnit" : { "state" : "translated", - "value" : "%lld коротший пропуск" + "value" : "%lld запис" } }, "few" : { "stringUnit" : { "state" : "translated", - "value" : "%lld коротші пропуски" + "value" : "%lld записи" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "%lld коротших пропусків" + "value" : "%lld записів" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld коротшого пропуску" + "value" : "%lld запису" } } } @@ -3445,7 +3743,7 @@ "other" : { "stringUnit" : { "state" : "translated", - "value" : "%lld khoảng trống ngắn hơn" + "value" : "%lld lần nhập" } } } @@ -3457,7 +3755,7 @@ "other" : { "stringUnit" : { "state" : "translated", - "value" : "另有%lld段较短中断" + "value" : "%lld条" } } } @@ -3465,8731 +3763,12931 @@ } } }, - "+ %1$@ automatic" : { - "comment" : "Trends Boluses per day tile detail: average number of automatic boluses per day on top of the manual ones, a fractional value such as 4.2. (1: number with one decimal)", + "%lld shorter gaps" : { + "comment" : "Chart note: how many other, shorter gaps in glucose readings there were; follows the longest gap. (1: number of gaps)", "localizations" : { - "ar" : { - "stringUnit" : { - "state" : "translated", - "value" : "+ %1$@ تلقائي" + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld shorter gap" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld shorter gaps" + } + } + } } }, - "cs" : { + "ar" : { + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوة أقصر" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوة أقصر" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوة أقصر" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوات أقصر" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوة أقصر" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld فجوة أقصر" + } + } + } + } + }, + "cs" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratší výpadek" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratší výpadky" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratšího výpadku" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratších výpadků" + } + } + } + } + }, + "da" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere hul" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere huller" + } + } + } + } + }, + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kürzere Lücke" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kürzere Lücken" + } + } + } + } + }, + "es" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interrupción más corta" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interrupciones más cortas" + } + } + } + } + }, + "fi" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld lyhyempi katko" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld lyhyempää katkoa" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interruption plus courte" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interruptions plus courtes" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interruptions plus courtes" + } + } + } + } + }, + "he" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld פער קצר יותר" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld פערים קצרים יותר" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld פערים קצרים יותר" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld פערים קצרים יותר" + } + } + } + } + }, + "hu" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld rövidebb kimaradás" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld rövidebb kimaradás" + } + } + } + } + }, + "it" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interruzione più breve" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interruzioni più brevi" + } + } + } + } + }, + "ja" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "ほか短い欠損%lld件" + } + } + } + } + }, + "nb" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere hull" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere hull" + } + } + } + } + }, + "nl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere onderbreking" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortere onderbrekingen" + } + } + } + } + }, + "pl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld krótsza przerwa" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld krótsze przerwy" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld krótszych przerw" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld krótszej przerwy" + } + } + } + } + }, + "pt-BR" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interrupção mais curta" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld interrupções mais curtas" + } + } + } + } + }, + "ro" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld întrerupere mai scurtă" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld întreruperi mai scurte" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld de întreruperi mai scurte" + } + } + } + } + }, + "ru" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld короткий пропуск" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротких пропуска" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротких пропусков" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротких пропуска" + } + } + } + } + }, + "sk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratší výpadok" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratšie výpadky" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratšieho výpadku" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kratších výpadkov" + } + } + } + } + }, + "sv" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortare lucka" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kortare luckor" + } + } + } + } + }, + "tr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld daha kısa boşluk" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld daha kısa boşluk" + } + } + } + } + }, + "uk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротший пропуск" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротші пропуски" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротших пропусків" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld коротшого пропуску" + } + } + } + } + }, + "vi" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld khoảng trống ngắn hơn" + } + } + } + } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "另有%lld段较短中断" + } + } + } + } + } + } + }, + "+ %1$@ automatic" : { + "comment" : "Trends Boluses per day tile detail: average number of automatic boluses per day on top of the manual ones, a fractional value such as 4.2. (1: number with one decimal)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ تلقائي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automat." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatiske" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatisch" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automáticos" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automaattista" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatiques" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ אוטומטיים" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatikus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatici" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ 自動 %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatiske" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatisch" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automat." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automáticos" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automate" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ авто" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automat." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ automatiska" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ otomatik" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ авто" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ %1$@ tự động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "+ 自动 %1$@" + } + } + } + }, + "25–75%" : { + "comment" : "Trends Daily pattern legend: band between the 25th and 75th percentiles of glucose", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75 %" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75 %" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75 %" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75 %" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%25–75" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "25–75%" + } + } + } + }, + "5–95%" : { + "comment" : "Trends Daily pattern legend: band between the 5th and 95th percentiles of glucose", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95 %" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95 %" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95 %" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95 %" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%5–95" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "5–95%" + } + } + } + }, + "absorbs over %1$@ h" : { + "comment" : "Logbook carb row detail: carb absorption time in hours. (1: hours, e.g. 3 or 2.5)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يُمتص خلال %1$@ س" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "vstřebání za %1$@ h" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "optages over %1$@ t" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aufnahme über %1$@ Std" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "se absorbe en %1$@ h" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "imeytyy %1$@ h:ssa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "absorption sur %1$@ h" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "נספג במשך %1$@ שע׳" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ óra alatt szívódik fel" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "assorbimento in %1$@ h" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@時間で吸収" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "absorberes over %1$@ t" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "opname in %1$@ u" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "wchłanianie przez %1$@ godz." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "absorve em %1$@ h" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "se absoarbe în %1$@ h" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "усваивается за %1$@ ч" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "vstrebanie za %1$@ h" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "absorberas under %1$@ h" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ sa içinde emilir" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "засвоюється за %1$@ год" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "hấp thu trong %1$@ giờ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@小时内吸收" + } + } + } + }, + "Active insulin %1$@, active carbs %2$@" : { + "comment" : "VoiceOver summary of the active insulin and carbs chart. (1: insulin on board, e.g. 1.25 U, or unknown; 2: carbs on board, e.g. 20 g, or unknown)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الأنسولين النشط %1$@، الكارب النشط %2$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivní inzulín %1$@, aktivní sacharidy %2$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin %1$@, aktive kulhydrater %2$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Insulin %1$@, aktive KH %2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina activa %1$@, carbohidratos activos %2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akt. insuliini %1$@, akt. hiilari %2$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuline active %1$@, glucides actifs %2$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אינסולין פעיל %1$@, פחמימות פעילות %2$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktív inzulin %1$@, aktív szénhidrát %2$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina attiva %1$@, carboidrati attivi %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "残存インスリン %1$@、残存糖質 %2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin %1$@, aktive karbohydrater %2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve insuline %1$@, actieve koolhydraten %2$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktywna insulina %1$@, aktywne węglowodany %2$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina ativa %1$@, carboidratos ativos %2$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulină activă %1$@, carbohidrați activi %2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активный инсулин %1$@, активные углеводы %2$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktívny inzulín %1$@, aktívne sacharidy %2$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin %1$@, aktiva kolhydrater %2$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktif insülin %1$@, aktif karb %2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активний інсулін %1$@, активні вуглеводи %2$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin còn hoạt động %1$@, Carbs còn hoạt động %2$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "活性胰岛素 %1$@,活性碳水 %2$@" + } + } + } + }, + "Active insulin and carbs" : { + "comment" : "Title of the day page card charting insulin and carbs on board", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الأنسولين والكارب النشطان" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivní inzulín a sacharidy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin og kulhydrater" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktives Insulin und KH" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina y carbohidratos activos" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktiivinen insuliini ja hiilihydraatit" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuline et glucides actifs" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אינסולין ופחמימות פעילים" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktív inzulin és szénhidrát" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina e carboidrati attivi" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "残存インスリンと残存糖質" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin og karbohydrater" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve insuline en koolhydraten" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktywna insulina i węglowodany" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina e carboidratos ativos" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulină activă și carbohidrați activi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активный инсулин и углеводы" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktívny inzulín a sacharidy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivt insulin och kolhydrater" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktif insülin ve karb" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активний інсулін і вуглеводи" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin và Carbs còn hoạt động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "活性胰岛素和碳水" + } + } + } + }, + "Aim: average under %1$@ %2$@, GMI under 7%%, CV 36%% or less." : { + "comment" : "Trends Summary footnote: the consensus targets for average glucose, GMI and glucose variability (CV). (1: glucose value, 2: glucose unit)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الهدف: متوسط أقل من %1$@ %2$@، وGMI أقل من 7%%، وCV ‏36%% أو أقل." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cíl: průměr pod %1$@ %2$@, GMI pod 7%%, CV nejvýše 36%%." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: gennemsnit under %1$@ %2$@, GMI under 7 %%, CV højst 36 %%." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ziel: Durchschnitt unter %1$@ %2$@, GMI unter 7 %%, VK höchstens 36 %%." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objetivo: promedio por debajo de %1$@ %2$@, GMI por debajo del 7%%, CV del 36%% o menos." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tavoite: keskiarvo alle %1$@ %2$@, GMI alle 7 %%, CV enintään 36 %%." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objectif : moyenne sous %1$@ %2$@, GMI sous 7 %%, CV de 36 %% au plus." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "יעד: ממוצע מתחת ל-%1$@ %2$@, GMI מתחת ל-7%%, CV ‏36%% או פחות." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cél: átlag %1$@ %2$@ alatt, GMI 7%% alatt, CV legfeljebb 36%%." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obiettivo: media sotto %1$@ %2$@, GMI sotto il 7%%, CV al massimo 36%%." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "目標:平均%1$@ %2$@未満、GMI 7%%未満、CV 36%%以下。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: gjennomsnitt under %1$@ %2$@, GMI under 7 %%, CV høyst 36 %%." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doel: gemiddelde onder %1$@ %2$@, GMI onder 7%%, CV hoogstens 36%%." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cel: średnia poniżej %1$@ %2$@, GMI poniżej 7%%, CV najwyżej 36%%." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meta: média abaixo de %1$@ %2$@, GMI abaixo de 7%%, CV de no máximo 36%%." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Țintă: medie sub %1$@ %2$@, GMI sub 7%%, CV de cel mult 36%%." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цель: среднее ниже %1$@ %2$@, GMI ниже 7%%, CV не более 36%%." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cieľ: priemer pod %1$@ %2$@, GMI pod 7%%, CV najviac 36%%." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: genomsnitt under %1$@ %2$@, GMI under 7 %%, CV högst 36 %%." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hedef: ortalama %1$@ %2$@ altı, GMI %%7 altı, CV en fazla %%36." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Мета: середнє нижче %1$@ %2$@, GMI нижче 7%%, CV не більше 36%%." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mục tiêu: trung bình dưới %1$@ %2$@, GMI dưới 7%%, CV từ 36%% trở xuống." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "目标:平均值低于 %1$@ %2$@,GMI 低于 7%%,CV 不超过 36%%。" + } + } + } + }, + "Aim: over 70%% in range; under 25%% above and 4%% below; under 5%% very high and 1%% very low; CGM active over 70%%." : { + "comment" : "Trends Time in range card footnote: the consensus targets for time in range, above and below range, very high, very low and CGM wear", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الهدف: أكثر من 70%% ضمن النطاق؛ أقل من 25%% فوق و4%% تحت؛ أقل من 5%% مرتفع جدًا و1%% منخفض جدًا؛ وCGM نشط أكثر من 70%%." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cíl: přes 70%% v rozsahu; pod 25%% nad a 4%% pod; pod 5%% velmi vysoko a 1%% velmi nízko; CGM aktivní přes 70%%." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: over 70 %% i området; under 25 %% over og 4 %% under; under 5 %% meget højt og 1 %% meget lavt; CGM aktiv over 70 %%." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ziel: über 70 %% im Zielbereich; unter 25 %% darüber und 4 %% darunter; unter 5 %% sehr hoch und 1 %% sehr niedrig; CGM über 70 %% aktiv." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objetivo: más del 70%% en rango; menos del 25%% por encima y del 4%% por debajo; menos del 5%% muy alto y del 1%% muy bajo; CGM activo más del 70%%." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tavoite: yli 70 %% tavoitealueella; alle 25 %% yläpuolella ja 4 %% alapuolella; alle 5 %% erittäin korkealla ja 1 %% erittäin matalalla; CGM aktiivinen yli 70 %%." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objectif : plus de 70 %% dans la cible ; moins de 25 %% au-dessus et 4 %% en dessous ; moins de 5 %% très haut et 1 %% très bas ; CGM actif plus de 70 %%." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "יעד: מעל 70%% בטווח; מתחת ל-25%% מעל ו-4%% מתחת; מתחת ל-5%% גבוה מאוד ו-1%% נמוך מאוד; CGM פעיל מעל 70%%." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cél: több mint 70%% tartományban; kevesebb mint 25%% felette és 4%% alatta; kevesebb mint 5%% nagyon magas és 1%% nagyon alacsony; CGM aktív több mint 70%%." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obiettivo: oltre il 70%% nell'intervallo; meno del 25%% sopra e del 4%% sotto; meno del 5%% molto alta e dell'1%% molto bassa; CGM attivo oltre il 70%%." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "目標:範囲内70%%超、範囲上25%%未満・範囲下4%%未満、非常に高い5%%未満・非常に低い1%%未満、CGM稼働70%%超。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: over 70 %% i målområdet; under 25 %% over og 4 %% under; under 5 %% svært høyt og 1 %% svært lavt; CGM aktiv over 70 %%." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doel: meer dan 70%% binnen bereik; minder dan 25%% erboven en 4%% eronder; minder dan 5%% zeer hoog en 1%% zeer laag; CGM meer dan 70%% actief." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cel: ponad 70%% w zakresie; poniżej 25%% powyżej i 4%% poniżej; poniżej 5%% bardzo wysokich i 1%% bardzo niskich; CGM aktywny ponad 70%%." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meta: mais de 70%% no alvo; menos de 25%% acima e 4%% abaixo; menos de 5%% muito alto e 1%% muito baixo; CGM ativo mais de 70%%." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Țintă: peste 70%% în interval; sub 25%% peste și 4%% sub; sub 5%% foarte ridicat și 1%% foarte scăzut; CGM activ peste 70%%." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цель: в диапазоне более 70%%; выше — менее 25%%, ниже — менее 4%%; очень высокая — менее 5%%, очень низкая — менее 1%%; CGM активен более 70%%." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cieľ: nad 70%% v rozsahu; pod 25%% nad a 4%% pod; pod 5%% veľmi vysoko a 1%% veľmi nízko; CGM aktívny nad 70%%." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål: över 70 %% inom målområdet; under 25 %% över och 4 %% under; under 5 %% mycket högt och 1 %% mycket lågt; CGM aktiv över 70 %%." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hedef: aralıkta %%70'in üzeri; üstünde %%25'in, altında %%4'ün altı; çok yüksekte %%5'in, çok düşükte %%1'in altı; CGM aktif %%70'in üzeri." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Мета: у діапазоні понад 70%%; вище — менше 25%%, нижче — менше 4%%; дуже висока — менше 5%%, дуже низька — менше 1%%; CGM активний понад 70%%." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mục tiêu: trên 70%% trong khoảng; dưới 25%% trên khoảng và 4%% dưới khoảng; dưới 5%% rất cao và 1%% rất thấp; CGM hoạt động trên 70%%." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "目标:范围内超过70%%;高于范围低于25%%,低于范围低于4%%;极高低于5%%,极低低于1%%;CGM 在线超过70%%。" + } + } + } + }, + "All" : { + "comment" : "Logbook filter chip: show every entry", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الكل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vše" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alle" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alle" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Todo" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kaikki" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tout" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הכל" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mind" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tutto" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "すべて" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alle" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alles" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wszystko" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tudo" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toate" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Все" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Všetko" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alla" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tümü" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Усі" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tất cả" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "全部" + } + } + } + }, + "Automatic" : { + "comment" : "Chart legend: hatched bars are automatic boluses given by Loop", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تلقائي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatické" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaattinen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatique" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אוטומטי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatikus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatico" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "自動" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatyczne" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматические" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatické" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otomatik" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматичні" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tự động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自动" + } + } + } + }, + "Automatic %1$@" : { + "comment" : "Basal and bolus: bolus Loop gave automatically. (1: units, e.g. 5.4 U)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تلقائي %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaticky %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaattinen %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatique %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אוטומטי %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatikus %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatico %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "自動 %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatycznie %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automat %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматически %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaticky %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otomatik %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматично %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tự động %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自动 %1$@" + } + } + } + }, + "Automatic bolus" : { + "comment" : "Chart callout label for a bolus Loop delivered automatically near the touched time Logbook row title: a bolus Loop delivered automatically", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus تلقائي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatický bolus" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk bolus" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatischer Bolus" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolo automático" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaattinen bolus" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus automatique" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus אוטומטי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatikus bólus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolo automatico" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "自動ボーラス" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk bolus" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatische bolus" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus automatyczny" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus automático" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus automat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматический болюс" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatický bolus" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisk bolus" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otomatik bolus" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматичний болюс" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus tự động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自动大剂量" + } + } + } + }, + "Automatic boluses" : { + "comment" : "Logbook row title: several automatic boluses in a row, folded into one row that lists them", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "جرعات تلقائية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatické bolusy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatiske boluser" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatische Boli" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolos automáticos" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaattiset boluset" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus automatiques" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בולוסים אוטומטיים" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatikus bóluszok" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boli automatici" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "自動ボーラス" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatiske boluser" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatische bolussen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusy automatyczne" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus automáticos" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolusuri automate" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматические болюсы" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatické bolusy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatiska bolusdoser" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otomatik boluslar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Автоматичні болюси" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bolus tự động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自动大剂量" + } + } + } + }, + "Average %1$@ U" : { + "comment" : "Trends Insulin per day legend: the dashed line showing average total insulin per day. (1: units)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المتوسط %1$@ وحدة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Průměr %1$@ U" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gennemsnit %1$@ E" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durchschnitt %1$@ IE" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Promedio %1$@ U" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keskiarvo %1$@ U" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Moyenne %1$@ U" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ממוצע U %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Átlag %1$@ U" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Media %1$@ U" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均 %1$@ 単位" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gjennomsnitt %1$@ E" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gemiddeld %1$@ E" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Średnio %1$@ J" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Média %1$@ U" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medie %1$@ U" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Среднее %1$@ Ед." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Priemer %1$@ j" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medel %1$@ E" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ortalama %1$@ Ü" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Середнє %1$@ U" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trung bình %1$@ U" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均 %1$@ U" + } + } + } + }, + "average per day" : { + "comment" : "Trends Basal and bolus card subtitle when the figures are averaged per day over the period", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المتوسط اليومي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "průměr za den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "gennemsnit pr. dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durchschnitt pro Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "promedio por día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "keskiarvo päivässä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "moyenne par jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ממוצע ליום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "napi átlag" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "media al giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "1日あたりの平均" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "gjennomsnitt per dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "gemiddeld per dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "średnio dziennie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "média por dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "medie pe zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "в среднем за день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "priemer za deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "medel per dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "günlük ortalama" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "у середньому за день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "trung bình mỗi ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日平均" + } + } + } + }, + "Basal" : { + "comment" : "Chart callout label for the basal rate at the touched time Label of a tile showing the current basal rate (Now screen) or the day's basal insulin total (Today so far totals) Logbook filter chip: show only basal changes", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القاعدية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaalinen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basale" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサル" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaal" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podstawowy" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazală" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazálna" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazal" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础" + } + } + } + }, + "Basal %1$@, %2$@ percent" : { + "comment" : "VoiceOver: basal units and share. (1: units, 2: percentage number)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القاعدية %1$@، %2$@ بالمئة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál %1$@, %2$@ procent" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ procent" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ Prozent" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ por ciento" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaali %1$@, %2$@ prosenttia" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ pour cent" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ אחוז" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ százalék" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basale %1$@, %2$@ percento" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサル %1$@、%2$@ パーセント" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ prosent" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaal %1$@, %2$@ procent" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podstawowy %1$@, %2$@ procent" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ por cento" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazală %1$@, %2$@ la sută" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал %1$@, %2$@ процентов" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál %1$@, %2$@ percent" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ procent" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazal %1$@, yüzde %2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал %1$@, %2$@ відсотків" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal %1$@, %2$@ phần trăm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础 %1$@,百分之 %2$@" + } + } + } + }, + "Basal adjustments" : { + "comment" : "Logbook row title summarising the temp basals Loop set in a day Title of the list of every temp basal Loop set in a day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعديلات القاعدية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Úpravy bazálu" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaljusteringer" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basalanpassungen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes de basal" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaalisäädöt" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustements du basal" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "התאמות Basal" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal-módosítások" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Regolazioni basale" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサル調整" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaljusteringer" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaalaanpassingen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zmiany bazy" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes de basal" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustări bazală" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Изменения базала" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Úpravy bazálu" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaljusteringar" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazal ayarlamaları" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Зміни базалу" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Điều chỉnh Basal" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础率调整" + } + } + } + }, + "Basal and bolus" : { + "comment" : "Trends and day page card title: insulin split into basal and bolus", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القاعدية والبلعة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál a bolus" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal og bolus" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal und Bolus" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal y bolo" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaali ja bolus" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal et bolus" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal ו-Bolus" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal és bólus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basale e bolo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサルとボーラス" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal og bolus" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaal en bolus" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina podstawowa i bolus" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal e bolus" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazală și bolus" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал и болюс" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál a bolus" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal och bolus" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazal ve bolus" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал і болюс" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal và bolus" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础与大剂量" + } + } + } + }, + "basal and bolus" : { + "comment" : "Trends Insulin per day card subtitle when each bar is one day's basal and bolus", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القاعدية و Bolus" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazál a bolus" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal og bolus" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal und Bolus" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal y bolo" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "basaali ja bolus" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal et bolus" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal ו-Bolus" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal és bólus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "basale e bolo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサルとボーラス" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal og bolus" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "basaal en bolus" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "baza i bolus" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal e bolus" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazală și bolus" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "базал и болюс" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazál a bolus" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "basal och bolus" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "bazal ve bolus" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "базал і болюс" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal và Bolus" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础和大剂量" + } + } + } + }, + "Basal given" : { + "comment" : "Chart legend: filled area is basal insulin actually delivered", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القاعدية المُعطاة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podaný bazál" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Givet basal" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abgegebene Basalrate" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal administrada" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annettu basaali" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal administré" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal שניתן" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leadott basal" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basale erogata" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "注入済みベーサル" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gitt basal" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toegediend basaal" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podana baza" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal entregue" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazală administrată" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поданный базал" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podaný bazál" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Given basal" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verilen bazal" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поданий базал" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal đã tiêm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已输注基础" + } + } + } + }, + "Basal resumed" : { + "comment" : "Logbook row title: insulin delivery resumed after a suspend", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "استئناف القاعدية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál obnoven" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal genoptaget" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal fortgesetzt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal reanudada" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaali jatkuu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal repris" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal חודש" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal folytatva" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basale ripresa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサル再開" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal gjenopptatt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaal hervat" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Baza wznowiona" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal retomada" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazală reluată" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал возобновлён" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál obnovený" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal återupptagen" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazal devam ettirildi" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал відновлено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã tiếp tục Basal" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础已恢复" + } + } + } + }, + "Basal suspended" : { + "comment" : "Logbook row title: insulin delivery was suspended", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إيقاف القاعدية مؤقتًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál pozastaven" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal suspenderet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal unterbrochen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal suspendida" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaali pysäytetty" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal suspendu" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal הושהה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal felfüggesztve" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basale sospesa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ベーサル一時停止" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal stanset" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basaal onderbroken" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Baza wstrzymana" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal suspensa" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazală suspendată" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал приостановлен" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazál pozastavený" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal pausad" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazal askıya alındı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Базал призупинено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã tạm ngưng Basal" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "基础已暂停" + } + } + } + }, + "Bolus %1$@, %2$@ percent" : { + "comment" : "VoiceOver: bolus units and share. (1: units, 2: percentage number)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "البلعة %1$@، %2$@ بالمئة" + } + }, + "cs" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automat." + "value" : "Bolus %1$@, %2$@ procent" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automatiske" + "value" : "Bolus %1$@, %2$@ procent" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automatisch" + "value" : "Bolus %1$@, %2$@ Prozent" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automáticos" + "value" : "Bolo %1$@, %2$@ por ciento" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automaattista" + "value" : "Bolus %1$@, %2$@ prosenttia" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automatiques" + "value" : "Bolus %1$@, %2$@ pour cent" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ אוטומטיים" + "value" : "Bolus %1$@, %2$@ אחוז" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automatikus" + "value" : "Bólus %1$@, %2$@ százalék" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automatici" + "value" : "Bolo %1$@, %2$@ percento" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "+ 自動 %1$@" + "value" : "ボーラス %1$@、%2$@ パーセント" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automatiske" + "value" : "Bolus %1$@, %2$@ prosent" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automatisch" + "value" : "Bolus %1$@, %2$@ procent" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automat." + "value" : "Bolus %1$@, %2$@ procent" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automáticos" + "value" : "Bolus %1$@, %2$@ por cento" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automate" + "value" : "Bolus %1$@, %2$@ la sută" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ авто" + "value" : "Болюс %1$@, %2$@ процентов" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automat." + "value" : "Bolus %1$@, %2$@ percent" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ automatiska" + "value" : "Bolus %1$@, %2$@ procent" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ otomatik" + "value" : "Bolus %1$@, yüzde %2$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ авто" + "value" : "Болюс %1$@, %2$@ відсотків" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "+ %1$@ tự động" + "value" : "Bolus %1$@, %2$@ phần trăm" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "+ 自动 %1$@" + "value" : "大剂量 %1$@,百分之 %2$@" } } } }, - "25–75%" : { - "comment" : "Trends Daily pattern legend: band between the 25th and 75th percentiles of glucose", + "Boluses" : { + "comment" : "Trends summary tile for a one-day period: how many manual boluses were given that day", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "الجرعات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Bolusy" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "25–75 %" + "value" : "Boluser" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "25–75 %" + "value" : "Boli" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Bolos" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Boluksia" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "25–75 %" + "value" : "Bolus" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "בולוסים" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Bóluszok" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Boli" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "ボーラス" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "25–75 %" + "value" : "Boluser" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Bolussen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Bolusy" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Bolus" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Bolusuri" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Болюсы" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Bolusy" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Bolusdoser" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "%25–75" + "value" : "Bolus" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Болюси" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "Liều bolus" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "25–75%" + "value" : "大剂量次数" } } } }, - "5–95%" : { - "comment" : "Trends Daily pattern legend: band between the 5th and 95th percentiles of glucose", + "Boluses per day" : { + "comment" : "Trends summary tile: average number of manual boluses per day", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "جرعات Bolus يوميًا" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Bolusů denně" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "5–95 %" + "value" : "Boluser pr. dag" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "5–95 %" + "value" : "Boli pro Tag" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Bolos por día" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Boluksia päivässä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "5–95 %" + "value" : "Bolus par jour" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Bolus ליום" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Bólusok naponta" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Boli al giorno" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "1日のボーラス回数" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "5–95 %" + "value" : "Boluser per dag" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Bolussen per dag" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Bolusy dziennie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Bolus por dia" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Bolusuri pe zi" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Болюсов в день" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Bolusy za deň" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Bolusar per dag" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "%5–95" + "value" : "Günlük bolus sayısı" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Болюсів на день" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "Số lần Bolus mỗi ngày" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "5–95%" + "value" : "每日大剂量次数" } } } }, - "Active insulin %1$@, active carbs %2$@" : { - "comment" : "VoiceOver summary of the active insulin and carbs chart. (1: insulin on board, e.g. 1.25 U, or unknown; 2: carbs on board, e.g. 20 g, or unknown)", + "Boluses, basal changes, carbs and pump events from the last 7 days" : { + "comment" : "Logbook settings descriptive text", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الأنسولين النشط %1$@، الكارب النشط %2$@" + "value" : "جرعات Bolus وتغييرات القاعدية والكارب وأحداث المضخة خلال آخر 7 أيام" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Aktivní inzulín %1$@, aktivní sacharidy %2$@" + "value" : "Bolusy, změny bazálu, sacharidy a události pumpy za posledních 7 dní" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Aktivt insulin %1$@, aktive kulhydrater %2$@" + "value" : "Bolusser, basalændringer, kulhydrater og pumpehændelser fra de seneste 7 dage" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aktives Insulin %1$@, aktive KH %2$@" + "value" : "Boli, Basaländerungen, KH und Pumpenereignisse der letzten 7 Tage" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina activa %1$@, carbohidratos activos %2$@" + "value" : "Bolos, cambios de basal, carbohidratos y eventos de la bomba de los últimos 7 días" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Akt. insuliini %1$@, akt. hiilari %2$@" + "value" : "Bolukset, basaalimuutokset, hiilihydraatit ja pumpun tapahtumat viimeisiltä 7 päivältä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Insuline active %1$@, glucides actifs %2$@" + "value" : "Bolus, changements de basal, glucides et événements de la pompe des 7 derniers jours" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אינסולין פעיל %1$@, פחמימות פעילות %2$@" + "value" : "בולוסים, שינויי בזאלי, פחמימות ואירועי משאבה מ-7 הימים האחרונים" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Aktív inzulin %1$@, aktív szénhidrát %2$@" + "value" : "Bólusok, bázisváltozások, szénhidrátok és pumpaesemények az utolsó 7 napból" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina attiva %1$@, carboidrati attivi %2$@" + "value" : "Boli, modifiche della basale, carboidrati ed eventi del microinfusore degli ultimi 7 giorni" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "残存インスリン %1$@、残存糖質 %2$@" + "value" : "直近7日間のボーラス、ベーサル変更、カーボ、ポンプのイベント" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Aktivt insulin %1$@, aktive karbohydrater %2$@" + "value" : "Boluser, basalendringer, karbohydrater og pumpehendelser fra de siste 7 dagene" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Actieve insuline %1$@, actieve koolhydraten %2$@" + "value" : "Bolussen, basaalwijzigingen, koolhydraten en pompgebeurtenissen van de laatste 7 dagen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Aktywna insulina %1$@, aktywne węglowodany %2$@" + "value" : "Bolusy, zmiany bazy, węglowodany i zdarzenia pompy z ostatnich 7 dni" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina ativa %1$@, carboidratos ativos %2$@" + "value" : "Bolus, mudanças de basal, carbs e eventos da bomba dos últimos 7 dias" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Insulină activă %1$@, carbohidrați activi %2$@" + "value" : "Bolusuri, modificări ale bazalei, carbohidrați și evenimente ale pompei din ultimele 7 zile" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Активный инсулин %1$@, активные углеводы %2$@" + "value" : "Болюсы, изменения базала, углеводы и события помпы за последние 7 дней" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Aktívny inzulín %1$@, aktívne sacharidy %2$@" + "value" : "Bolusy, zmeny bazálu, sacharidy a udalosti pumpy za posledných 7 dní" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Aktivt insulin %1$@, aktiva kolhydrater %2$@" + "value" : "Bolusar, basaländringar, kolhydrater och pumphändelser från de senaste 7 dagarna" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Aktif insülin %1$@, aktif karb %2$@" + "value" : "Son 7 günün bolusları, bazal değişiklikleri, karbonhidratları ve pompa olayları" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Активний інсулін %1$@, активні вуглеводи %2$@" + "value" : "Болюси, зміни базалу, вуглеводи та події помпи за останні 7 днів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin còn hoạt động %1$@, Carbs còn hoạt động %2$@" + "value" : "Bolus, thay đổi basal, Carbs và sự kiện của bơm trong 7 ngày gần nhất" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "活性胰岛素 %1$@,活性碳水 %2$@" + "value" : "最近 7 天的大剂量、基础率变化、碳水和泵事件" } } } }, - "Active insulin and carbs" : { - "comment" : "Title of the day page card charting insulin and carbs on board", + "Carbs" : { + "comment" : "Label of a tile showing the day's total carbohydrate Logbook filter chip: show only carb entries Logbook row title: a carb entry", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الأنسولين والكارب النشطان" + "value" : "Carbs" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Aktivní inzulín a sacharidy" + "value" : "Sacharidy" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Aktivt insulin og kulhydrater" + "value" : "Kulhydrater" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aktives Insulin und KH" + "value" : "KH" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina y carbohidratos activos" + "value" : "Carbohidratos" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Aktiivinen insuliini ja hiilihydraatit" + "value" : "Hiilihydraatit" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Insuline et glucides actifs" + "value" : "Glucides" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אינסולין ופחמימות פעילים" + "value" : "Carbs" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Aktív inzulin és szénhidrát" + "value" : "Szénhidrát" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina e carboidrati attivi" + "value" : "Carb." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "残存インスリンと残存糖質" + "value" : "カーボ" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Aktivt insulin og karbohydrater" + "value" : "Karbohydrater" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Actieve insuline en koolhydraten" + "value" : "Koolhydraten" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Aktywna insulina i węglowodany" + "value" : "Węglowodany" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina e carboidratos ativos" + "value" : "Carbs" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Insulină activă și carbohidrați activi" + "value" : "Carbohidrați" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Активный инсулин и углеводы" + "value" : "Углеводы" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Aktívny inzulín a sacharidy" + "value" : "Sacharidy" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Aktivt insulin och kolhydrater" + "value" : "Kolhydrater" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Aktif insülin ve karb" + "value" : "Karb" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Активний інсулін і вуглеводи" + "value" : "Вуглеводи" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin và Carbs còn hoạt động" + "value" : "Carbs" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "活性胰岛素和碳水" + "value" : "碳水化合物" } } } }, - "Aim: average under %1$@ %2$@, GMI under 7%%, CV 36%% or less." : { - "comment" : "Trends Summary footnote: the consensus targets for average glucose, GMI and glucose variability (CV). (1: glucose value, 2: glucose unit)", + "Carbs per day" : { + "comment" : "Trends summary tile: average grams of carbohydrate entered per day", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الهدف: متوسط أقل من %1$@ %2$@، وGMI أقل من 7%%، وCV ‏36%% أو أقل." + "value" : "الكارب يوميًا" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Cíl: průměr pod %1$@ %2$@, GMI pod 7%%, CV nejvýše 36%%." + "value" : "Sacharidy denně" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Mål: gennemsnit under %1$@ %2$@, GMI under 7 %%, CV højst 36 %%." + "value" : "Kulhydrater pr. dag" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ziel: Durchschnitt unter %1$@ %2$@, GMI unter 7 %%, VK höchstens 36 %%." + "value" : "KH pro Tag" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Objetivo: promedio por debajo de %1$@ %2$@, GMI por debajo del 7%%, CV del 36%% o menos." + "value" : "Carbohidratos por día" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Tavoite: keskiarvo alle %1$@ %2$@, GMI alle 7 %%, CV enintään 36 %%." + "value" : "Hiilihydraatit päivässä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Objectif : moyenne sous %1$@ %2$@, GMI sous 7 %%, CV de 36 %% au plus." + "value" : "Glucides par jour" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "יעד: ממוצע מתחת ל-%1$@ %2$@, GMI מתחת ל-7%%, CV ‏36%% או פחות." + "value" : "פחמימות ליום" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Cél: átlag %1$@ %2$@ alatt, GMI 7%% alatt, CV legfeljebb 36%%." + "value" : "Szénhidrát naponta" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Obiettivo: media sotto %1$@ %2$@, GMI sotto il 7%%, CV al massimo 36%%." + "value" : "Carboidrati al giorno" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "目標:平均%1$@ %2$@未満、GMI 7%%未満、CV 36%%以下。" + "value" : "1日のカーボ" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Mål: gjennomsnitt under %1$@ %2$@, GMI under 7 %%, CV høyst 36 %%." + "value" : "Karbohydrater per dag" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Doel: gemiddelde onder %1$@ %2$@, GMI onder 7%%, CV hoogstens 36%%." + "value" : "Koolhydraten per dag" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Cel: średnia poniżej %1$@ %2$@, GMI poniżej 7%%, CV najwyżej 36%%." + "value" : "Węglowodany dziennie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Meta: média abaixo de %1$@ %2$@, GMI abaixo de 7%%, CV de no máximo 36%%." + "value" : "Carbs por dia" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Țintă: medie sub %1$@ %2$@, GMI sub 7%%, CV de cel mult 36%%." + "value" : "Carbohidrați pe zi" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Цель: среднее ниже %1$@ %2$@, GMI ниже 7%%, CV не более 36%%." + "value" : "Углеводов в день" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Cieľ: priemer pod %1$@ %2$@, GMI pod 7%%, CV najviac 36%%." + "value" : "Sacharidy za deň" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Mål: genomsnitt under %1$@ %2$@, GMI under 7 %%, CV högst 36 %%." + "value" : "Kolhydrater per dag" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Hedef: ortalama %1$@ %2$@ altı, GMI %%7 altı, CV en fazla %%36." + "value" : "Günlük karb" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Мета: середнє нижче %1$@ %2$@, GMI нижче 7%%, CV не більше 36%%." + "value" : "Вуглеводів на день" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mục tiêu: trung bình dưới %1$@ %2$@, GMI dưới 7%%, CV từ 36%% trở xuống." + "value" : "Carbs mỗi ngày" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "目标:平均值低于 %1$@ %2$@,GMI 低于 7%%,CV 不超过 36%%。" + "value" : "每日碳水" } } } }, - "Aim: over 70%% in range; under 25%% above and 4%% below; under 5%% very high and 1%% very low; CGM active over 70%%." : { - "comment" : "Trends Time in range card footnote: the consensus targets for time in range, above and below range, very high, very low and CGM wear", + "CGM data covers %1$@." : { + "comment" : "Trends Summary note when glucose readings start partway through the period. (1: days covered, e.g. 5 of 14 days)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الهدف: أكثر من 70%% ضمن النطاق؛ أقل من 25%% فوق و4%% تحت؛ أقل من 5%% مرتفع جدًا و1%% منخفض جدًا؛ وCGM نشط أكثر من 70%%." + "value" : "بيانات CGM تغطي %1$@." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Cíl: přes 70%% v rozsahu; pod 25%% nad a 4%% pod; pod 5%% velmi vysoko a 1%% velmi nízko; CGM aktivní přes 70%%." + "value" : "Data CGM pokrývají %1$@." } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Mål: over 70 %% i området; under 25 %% over og 4 %% under; under 5 %% meget højt og 1 %% meget lavt; CGM aktiv over 70 %%." + "value" : "CGM-data dækker %1$@." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ziel: über 70 %% im Zielbereich; unter 25 %% darüber und 4 %% darunter; unter 5 %% sehr hoch und 1 %% sehr niedrig; CGM über 70 %% aktiv." + "value" : "CGM-Daten decken %1$@ ab." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Objetivo: más del 70%% en rango; menos del 25%% por encima y del 4%% por debajo; menos del 5%% muy alto y del 1%% muy bajo; CGM activo más del 70%%." + "value" : "Los datos de CGM cubren %1$@." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Tavoite: yli 70 %% tavoitealueella; alle 25 %% yläpuolella ja 4 %% alapuolella; alle 5 %% erittäin korkealla ja 1 %% erittäin matalalla; CGM aktiivinen yli 70 %%." + "value" : "CGM-data kattaa %1$@." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Objectif : plus de 70 %% dans la cible ; moins de 25 %% au-dessus et 4 %% en dessous ; moins de 5 %% très haut et 1 %% très bas ; CGM actif plus de 70 %%." + "value" : "Les données CGM couvrent %1$@." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "יעד: מעל 70%% בטווח; מתחת ל-25%% מעל ו-4%% מתחת; מתחת ל-5%% גבוה מאוד ו-1%% נמוך מאוד; CGM פעיל מעל 70%%." + "value" : "נתוני CGM מכסים %1$@." } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Cél: több mint 70%% tartományban; kevesebb mint 25%% felette és 4%% alatta; kevesebb mint 5%% nagyon magas és 1%% nagyon alacsony; CGM aktív több mint 70%%." + "value" : "CGM-adat: %1$@." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Obiettivo: oltre il 70%% nell'intervallo; meno del 25%% sopra e del 4%% sotto; meno del 5%% molto alta e dell'1%% molto bassa; CGM attivo oltre il 70%%." + "value" : "I dati CGM coprono %1$@." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "目標:範囲内70%%超、範囲上25%%未満・範囲下4%%未満、非常に高い5%%未満・非常に低い1%%未満、CGM稼働70%%超。" + "value" : "CGMデータ:%1$@。" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Mål: over 70 %% i målområdet; under 25 %% over og 4 %% under; under 5 %% svært høyt og 1 %% svært lavt; CGM aktiv over 70 %%." + "value" : "CGM-data dekker %1$@." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Doel: meer dan 70%% binnen bereik; minder dan 25%% erboven en 4%% eronder; minder dan 5%% zeer hoog en 1%% zeer laag; CGM meer dan 70%% actief." + "value" : "CGM-gegevens beslaan %1$@." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Cel: ponad 70%% w zakresie; poniżej 25%% powyżej i 4%% poniżej; poniżej 5%% bardzo wysokich i 1%% bardzo niskich; CGM aktywny ponad 70%%." + "value" : "Dane CGM obejmują %1$@." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Meta: mais de 70%% no alvo; menos de 25%% acima e 4%% abaixo; menos de 5%% muito alto e 1%% muito baixo; CGM ativo mais de 70%%." + "value" : "Os dados de CGM cobrem %1$@." } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Țintă: peste 70%% în interval; sub 25%% peste și 4%% sub; sub 5%% foarte ridicat și 1%% foarte scăzut; CGM activ peste 70%%." + "value" : "Datele CGM acoperă %1$@." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Цель: в диапазоне более 70%%; выше — менее 25%%, ниже — менее 4%%; очень высокая — менее 5%%, очень низкая — менее 1%%; CGM активен более 70%%." + "value" : "Данные CGM: %1$@." } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Cieľ: nad 70%% v rozsahu; pod 25%% nad a 4%% pod; pod 5%% veľmi vysoko a 1%% veľmi nízko; CGM aktívny nad 70%%." + "value" : "Údaje CGM pokrývajú %1$@." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Mål: över 70 %% inom målområdet; under 25 %% över och 4 %% under; under 5 %% mycket högt och 1 %% mycket lågt; CGM aktiv över 70 %%." + "value" : "CGM-data täcker %1$@." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Hedef: aralıkta %%70'in üzeri; üstünde %%25'in, altında %%4'ün altı; çok yüksekte %%5'in, çok düşükte %%1'in altı; CGM aktif %%70'in üzeri." + "value" : "CGM verisi: %1$@." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Мета: у діапазоні понад 70%%; вище — менше 25%%, нижче — менше 4%%; дуже висока — менше 5%%, дуже низька — менше 1%%; CGM активний понад 70%%." + "value" : "Дані CGM: %1$@." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mục tiêu: trên 70%% trong khoảng; dưới 25%% trên khoảng và 4%% dưới khoảng; dưới 5%% rất cao và 1%% rất thấp; CGM hoạt động trên 70%%." + "value" : "Dữ liệu CGM bao phủ %1$@." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "目标:范围内超过70%%;高于范围低于25%%,低于范围低于4%%;极高低于5%%,极低低于1%%;CGM 在线超过70%%。" + "value" : "CGM 数据覆盖 %1$@。" } } } }, - "Automatic" : { - "comment" : "Chart legend: hatched bars are automatic boluses given by Loop", + "Closed loop" : { + "comment" : "Trends Loop status: time Loop was running in closed loop", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تلقائي" + "value" : "حلقة مغلقة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Automatické" + "value" : "Uzavřená smyčka" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisk" + "value" : "Lukket Loop" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisch" + "value" : "Closed Loop" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Automático" + "value" : "Circuito cerrado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Automaattinen" + "value" : "Suljettu säätö" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Automatique" + "value" : "Boucle fermée" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אוטומטי" + "value" : "Closed Loop" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Automatikus" + "value" : "Zárt hurok" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Automatico" + "value" : "Loop chiuso" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "自動" + "value" : "クローズドループ" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisk" + "value" : "Lukket Loop" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisch" + "value" : "Actieve loop" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Automatyczne" + "value" : "Pętla zamknięta" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Automático" + "value" : "Ciclo fechado" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Automat" + "value" : "Buclă închisă" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Автоматические" + "value" : "Замкнутый цикл" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Automatické" + "value" : "Uzavretá slučka" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisk" + "value" : "Sluten loop" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Otomatik" + "value" : "Kapalı döngü" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Автоматичні" + "value" : "Замкнутий цикл" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tự động" + "value" : "Vòng lặp kín" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "自动" + "value" : "闭环" } } } }, - "Automatic %1$@" : { - "comment" : "Basal and bolus: bolus Loop gave automatically. (1: units, e.g. 5.4 U)", + "Closed loop off" : { + "comment" : "Reason shown for a stretch of time when Loop was in open loop because closed loop was turned off", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تلقائي %1$@" + "value" : "الحلقة المغلقة متوقفة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Automaticky %1$@" + "value" : "Uzavřená smyčka vypnuta" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisk %1$@" + "value" : "Lukket Loop slået fra" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisch %1$@" + "value" : "Closed Loop aus" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Automático %1$@" + "value" : "Circuito cerrado desactivado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Automaattinen %1$@" + "value" : "Suljettu säätö pois" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Automatique %1$@" + "value" : "Boucle fermée désactivée" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אוטומטי %1$@" + "value" : "Closed Loop כבוי" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Automatikus %1$@" + "value" : "Zárt hurok kikapcsolva" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Automatico %1$@" + "value" : "Loop chiuso disattivato" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "自動 %1$@" + "value" : "クローズドループ オフ" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisk %1$@" + "value" : "Lukket Loop av" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisch %1$@" + "value" : "Actieve loop uit" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Automatycznie %1$@" + "value" : "Pętla zamknięta wyłączona" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Automático %1$@" + "value" : "Ciclo fechado desligado" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Automat %1$@" + "value" : "Buclă închisă oprită" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Автоматически %1$@" + "value" : "Замкнутый цикл выключен" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Automaticky %1$@" + "value" : "Uzavretá slučka vypnutá" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisk %1$@" + "value" : "Sluten loop av" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Otomatik %1$@" + "value" : "Kapalı döngü kapalı" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Автоматично %1$@" + "value" : "Замкнутий цикл вимкнено" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tự động %1$@" + "value" : "Vòng lặp kín tắt" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "自动 %1$@" + "value" : "闭环已关闭" } } } }, - "Automatic bolus" : { - "comment" : "Chart callout label for a bolus Loop delivered automatically near the touched time Logbook row title: a bolus Loop delivered automatically", + "COB" : { + "comment" : "Abbreviation for carbs on board (active carbohydrate) Abbreviation for carbs on board; label of a tile on the Now screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus تلقائي" + "value" : "COB" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Automatický bolus" + "value" : "COB" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisk bolus" + "value" : "COB" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Automatischer Bolus" + "value" : "COB" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Bolo automático" + "value" : "COB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Automaattinen bolus" + "value" : "COB" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus automatique" + "value" : "GA" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus אוטומטי" + "value" : "COB" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Automatikus bólus" + "value" : "COB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Bolo automatico" + "value" : "COB" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "自動ボーラス" + "value" : "COB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisk bolus" + "value" : "COB" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Automatische bolus" + "value" : "COB" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus automatyczny" + "value" : "COB" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus automático" + "value" : "COB" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus automat" + "value" : "COB" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Автоматический болюс" + "value" : "COB" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Automatický bolus" + "value" : "COB" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisk bolus" + "value" : "COB" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Otomatik bolus" + "value" : "COB" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Автоматичний болюс" + "value" : "COB" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus tự động" + "value" : "COB" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "自动大剂量" + "value" : "COB" } } } }, - "Average %1$@ U" : { - "comment" : "Trends Insulin per day legend: the dashed line showing average total insulin per day. (1: units)", + "COB (g, right)" : { + "comment" : "Chart legend: carbs on board in grams, on the right axis", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "المتوسط %1$@ وحدة" + "value" : "COB (g، يمين)" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Průměr %1$@ U" + "value" : "COB (g, vpravo)" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Gennemsnit %1$@ E" + "value" : "COB (g, højre)" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Durchschnitt %1$@ IE" + "value" : "COB (g, rechts)" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Promedio %1$@ U" + "value" : "COB (g, derecha)" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Keskiarvo %1$@ U" + "value" : "COB (g, oikea)" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Moyenne %1$@ U" + "value" : "GA (g, droite)" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ממוצע U %1$@" + "value" : "COB (g, ימין)" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Átlag %1$@ U" + "value" : "COB (g, jobb)" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Media %1$@ U" + "value" : "COB (g, destra)" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "平均 %1$@ 単位" + "value" : "COB(g、右)" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Gjennomsnitt %1$@ E" + "value" : "COB (g, høyre)" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Gemiddeld %1$@ E" + "value" : "COB (g, rechts)" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Średnio %1$@ J" + "value" : "COB (g, prawa oś)" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Média %1$@ U" + "value" : "COB (g, direita)" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Medie %1$@ U" + "value" : "COB (g, dreapta)" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Среднее %1$@ Ед." + "value" : "COB (г, справа)" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Priemer %1$@ j" + "value" : "COB (g, vpravo)" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Medel %1$@ E" + "value" : "COB (g, höger)" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Ortalama %1$@ Ü" + "value" : "COB (gr, sağ)" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Середнє %1$@ U" + "value" : "COB (г, праворуч)" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Trung bình %1$@ U" + "value" : "COB (g, bên phải)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "平均 %1$@ U" + "value" : "COB(克,右轴)" } } } }, - "average per day" : { - "comment" : "Trends Basal and bolus card subtitle when the figures are averaged per day over the period", + "Configuration error" : { + "comment" : "Reason Loop could not run: its settings are incomplete", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "المتوسط اليومي" + "value" : "خطأ في الإعدادات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "průměr za den" + "value" : "Chyba konfigurace" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "gennemsnit pr. dag" + "value" : "Konfigurationsfejl" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Durchschnitt pro Tag" + "value" : "Konfigurationsfehler" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "promedio por día" + "value" : "Error de configuración" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "keskiarvo päivässä" + "value" : "Määritysvirhe" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "moyenne par jour" + "value" : "Erreur de configuration" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ממוצע ליום" + "value" : "שגיאת תצורה" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "napi átlag" + "value" : "Konfigurációs hiba" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "media al giorno" + "value" : "Errore di configurazione" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "1日あたりの平均" + "value" : "設定エラー" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "gjennomsnitt per dag" + "value" : "Konfigurasjonsfeil" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "gemiddeld per dag" + "value" : "Configuratiefout" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "średnio dziennie" + "value" : "Błąd konfiguracji" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "média por dia" + "value" : "Erro de configuração" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "medie pe zi" + "value" : "Eroare de configurare" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "в среднем за день" + "value" : "Ошибка настроек" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "priemer za deň" + "value" : "Chyba konfigurácie" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "medel per dag" + "value" : "Konfigurationsfel" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "günlük ortalama" + "value" : "Yapılandırma hatası" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "у середньому за день" + "value" : "Помилка налаштувань" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "trung bình mỗi ngày" + "value" : "Lỗi cấu hình" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日平均" + "value" : "配置错误" } } } }, - "Basal" : { - "comment" : "Chart callout label for the basal rate at the touched time Label of a tile showing the current basal rate (Now screen) or the day's basal insulin total (Today so far totals) Logbook filter chip: show only basal changes", + "Connection error" : { + "comment" : "Reason Loop could not run: no connected devices, or a device connection failed", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "القاعدية" + "value" : "خطأ في الاتصال" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Bazál" + "value" : "Chyba připojení" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Basal" + "value" : "Forbindelsesfejl" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Basal" + "value" : "Verbindungsfehler" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Basal" + "value" : "Error de conexión" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Basaalinen" + "value" : "Yhteysvirhe" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Basal" + "value" : "Erreur de connexion" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Basal" + "value" : "שגיאת חיבור" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Basal" + "value" : "Kapcsolódási hiba" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Basale" + "value" : "Errore di connessione" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ベーサル" + "value" : "接続エラー" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Basal" + "value" : "Tilkoblingsfeil" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Basaal" + "value" : "Verbindingsfout" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Podstawowy" + "value" : "Błąd połączenia" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Basal" + "value" : "Erro de conexão" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Bazală" + "value" : "Eroare de conexiune" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Базал" + "value" : "Ошибка подключения" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Bazálna" + "value" : "Chyba pripojenia" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Basal" + "value" : "Anslutningsfel" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bazal" + "value" : "Bağlantı hatası" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Базал" + "value" : "Помилка підключення" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Basal" + "value" : "Lỗi kết nối" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "基础" + "value" : "连接错误" } } } }, - "Basal %1$@, %2$@ percent" : { - "comment" : "VoiceOver: basal units and share. (1: units, 2: percentage number)", + "Couldn't delete" : { + "comment" : "Alert title when deleting a Logbook entry fails", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "القاعدية %1$@، %2$@ بالمئة" + "value" : "تعذّر الحذف" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Bazál %1$@, %2$@ procent" + "value" : "Nelze smazat" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Basal %1$@, %2$@ procent" + "value" : "Kunne ikke slette" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Basal %1$@, %2$@ Prozent" + "value" : "Löschen fehlgeschlagen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Basal %1$@, %2$@ por ciento" + "value" : "No se pudo eliminar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Basaali %1$@, %2$@ prosenttia" + "value" : "Poisto epäonnistui" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Basal %1$@, %2$@ pour cent" + "value" : "Suppression impossible" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Basal %1$@, %2$@ אחוז" + "value" : "לא ניתן היה למחוק" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Basal %1$@, %2$@ százalék" + "value" : "Nem sikerült törölni" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Basale %1$@, %2$@ percento" + "value" : "Impossibile eliminare" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ベーサル %1$@、%2$@ パーセント" + "value" : "削除できませんでした" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Basal %1$@, %2$@ prosent" + "value" : "Kunne ikke slette" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Basaal %1$@, %2$@ procent" + "value" : "Verwijderen mislukt" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Podstawowy %1$@, %2$@ procent" + "value" : "Nie udało się usunąć" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Basal %1$@, %2$@ por cento" + "value" : "Não foi possível apagar" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Bazală %1$@, %2$@ la sută" + "value" : "Nu s-a putut șterge" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Базал %1$@, %2$@ процентов" + "value" : "Не удалось удалить" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Bazál %1$@, %2$@ percent" + "value" : "Nedá sa odstrániť" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Basal %1$@, %2$@ procent" + "value" : "Kunde inte ta bort" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bazal %1$@, yüzde %2$@" + "value" : "Silinemedi" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Базал %1$@, %2$@ відсотків" + "value" : "Не вдалося видалити" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Basal %1$@, %2$@ phần trăm" + "value" : "Không thể xóa" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "基础 %1$@,百分之 %2$@" + "value" : "无法删除" } } } }, - "Basal and bolus" : { - "comment" : "Trends and day page card title: insulin split into basal and bolus", + "Couldn't export" : { + "comment" : "Alert title when exporting the Trends PDF or CSV fails", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "القاعدية والبلعة" + "value" : "تعذّر التصدير" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Bazál a bolus" + "value" : "Nelze exportovat" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Basal og bolus" + "value" : "Kunne ikke eksportere" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Basal und Bolus" + "value" : "Export fehlgeschlagen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Basal y bolo" + "value" : "No se pudo exportar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Basaali ja bolus" + "value" : "Vienti epäonnistui" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Basal et bolus" + "value" : "Exportation impossible" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Basal ו-Bolus" + "value" : "לא ניתן היה לייצא" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Basal és bólus" + "value" : "Nem sikerült exportálni" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Basale e bolo" + "value" : "Impossibile esportare" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ベーサルとボーラス" + "value" : "書き出せませんでした" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Basal og bolus" + "value" : "Kunne ikke eksportere" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Basaal en bolus" + "value" : "Exporteren mislukt" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina podstawowa i bolus" + "value" : "Nie udało się wyeksportować" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Basal e bolus" + "value" : "Não foi possível exportar" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Bazală și bolus" + "value" : "Nu s-a putut exporta" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Базал и болюс" + "value" : "Не удалось экспортировать" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Bazál a bolus" + "value" : "Nedá sa exportovať" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Basal och bolus" + "value" : "Kunde inte exportera" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bazal ve bolus" + "value" : "Dışa aktarılamadı" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Базал і болюс" + "value" : "Не вдалося експортувати" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Basal và bolus" + "value" : "Không thể xuất" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "基础与大剂量" + "value" : "无法导出" } } } }, - "basal and bolus" : { - "comment" : "Trends Insulin per day card subtitle when each bar is one day's basal and bolus", + "Couldn't load Logbook: %1$@" : { + "comment" : "Logbook error when its history can't be read. (1: system error description)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "القاعدية و Bolus" + "value" : "تعذّر تحميل السجل: %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "bazál a bolus" + "value" : "Nelze načíst deník: %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "basal og bolus" + "value" : "Kunne ikke indlæse logbogen: %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Basal und Bolus" + "value" : "Logbuch konnte nicht geladen werden: %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "basal y bolo" + "value" : "No se pudo cargar el registro: %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "basaali ja bolus" + "value" : "Lokikirjaa ei voitu ladata: %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "basal et bolus" + "value" : "Impossible de charger le journal : %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Basal ו-Bolus" + "value" : "לא ניתן היה לטעון את היומן: %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "basal és bólus" + "value" : "Nem sikerült betölteni a naplót: %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "basale e bolo" + "value" : "Impossibile caricare il diario: %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ベーサルとボーラス" + "value" : "ログブックを読み込めませんでした:%1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "basal og bolus" + "value" : "Kunne ikke laste inn loggboken: %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "basaal en bolus" + "value" : "Logboek kan niet worden geladen: %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "baza i bolus" + "value" : "Nie udało się wczytać dziennika: %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "basal e bolus" + "value" : "Não foi possível carregar o diário: %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "bazală și bolus" + "value" : "Nu s-a putut încărca jurnalul: %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "базал и болюс" + "value" : "Не удалось загрузить журнал: %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "bazál a bolus" + "value" : "Denník sa nedá načítať: %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "basal och bolus" + "value" : "Kunde inte läsa in loggboken: %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "bazal ve bolus" + "value" : "Kayıt Defteri yüklenemedi: %1$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "базал і болюс" + "value" : "Не вдалося завантажити журнал: %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Basal và Bolus" + "value" : "Không thể tải Nhật ký: %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "基础和大剂量" + "value" : "无法载入日志:%1$@" } } } }, - "Basal given" : { - "comment" : "Chart legend: filled area is basal insulin actually delivered", + "Couldn't load this day: %1$@" : { + "comment" : "Day page error when the day's history can't be read. (1: system error description)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "القاعدية المُعطاة" + "value" : "تعذّر تحميل هذا اليوم: %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Podaný bazál" + "value" : "Nelze načíst tento den: %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Givet basal" + "value" : "Kunne ikke indlæse denne dag: %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Abgegebene Basalrate" + "value" : "Dieser Tag konnte nicht geladen werden: %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Basal administrada" + "value" : "No se pudo cargar este día: %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Annettu basaali" + "value" : "Päivää ei voitu ladata: %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Basal administré" + "value" : "Impossible de charger cette journée : %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Basal שניתן" + "value" : "לא ניתן היה לטעון יום זה: %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Leadott basal" + "value" : "Nem sikerült betölteni ezt a napot: %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Basale erogata" + "value" : "Impossibile caricare questo giorno: %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "注入済みベーサル" + "value" : "この日を読み込めませんでした:%1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Gitt basal" + "value" : "Kunne ikke laste inn denne dagen: %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Toegediend basaal" + "value" : "Deze dag kan niet worden geladen: %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Podana baza" + "value" : "Nie udało się wczytać tego dnia: %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Basal entregue" + "value" : "Não foi possível carregar este dia: %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Bazală administrată" + "value" : "Nu s-a putut încărca această zi: %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Поданный базал" + "value" : "Не удалось загрузить этот день: %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Podaný bazál" + "value" : "Tento deň sa nedá načítať: %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Given basal" + "value" : "Kunde inte läsa in dagen: %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Verilen bazal" + "value" : "Bu gün yüklenemedi: %1$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Поданий базал" + "value" : "Не вдалося завантажити цей день: %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Basal đã tiêm" + "value" : "Không thể tải ngày này: %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "已输注基础" + "value" : "无法载入这一天:%1$@" } } } }, - "Bolus %1$@, %2$@ percent" : { - "comment" : "VoiceOver: bolus units and share. (1: units, 2: percentage number)", + "Couldn't load Trends: %1$@" : { + "comment" : "Trends error message when the history can't be read. (1: error description)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "البلعة %1$@، %2$@ بالمئة" + "value" : "تعذّر تحميل الاتجاهات: %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ procent" + "value" : "Nelze načíst trendy: %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ procent" + "value" : "Kunne ikke indlæse Tendenser: %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ Prozent" + "value" : "Trends konnten nicht geladen werden: %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Bolo %1$@, %2$@ por ciento" + "value" : "No se pudieron cargar las tendencias: %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ prosenttia" + "value" : "Trendejä ei voitu ladata: %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ pour cent" + "value" : "Impossible de charger les tendances : %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ אחוז" + "value" : "לא ניתן היה לטעון מגמות: %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Bólus %1$@, %2$@ százalék" + "value" : "Nem sikerült betölteni a trendeket: %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Bolo %1$@, %2$@ percento" + "value" : "Impossibile caricare le tendenze: %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ボーラス %1$@、%2$@ パーセント" + "value" : "トレンドを読み込めませんでした:%1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ prosent" + "value" : "Kunne ikke laste inn Trender: %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ procent" + "value" : "Trends kunnen niet worden geladen: %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ procent" + "value" : "Nie udało się wczytać trendów: %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ por cento" + "value" : "Não foi possível carregar as tendências: %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ la sută" + "value" : "Nu s-au putut încărca tendințele: %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Болюс %1$@, %2$@ процентов" + "value" : "Не удалось загрузить тренды: %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ percent" + "value" : "Trendy sa nedajú načítať: %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ procent" + "value" : "Kunde inte läsa in Trender: %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, yüzde %2$@" + "value" : "Eğilimler yüklenemedi: %1$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Болюс %1$@, %2$@ відсотків" + "value" : "Не вдалося завантажити тренди: %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus %1$@, %2$@ phần trăm" + "value" : "Không thể tải Xu hướng: %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "大剂量 %1$@,百分之 %2$@" + "value" : "无法载入趋势:%1$@" } } } }, - "Boluses" : { - "comment" : "Trends summary tile for a one-day period: how many manual boluses were given that day", + "Covers %1$@: Loop keeps 7 days of runs, and closed or open loop is recorded from when this version of Loop first ran." : { + "comment" : "Trends Loop status footnote when the status covers only part of the period. (1: date range covered)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الجرعات" + "value" : "يغطي %1$@: يحتفظ Loop بسجل تشغيلات 7 أيام، وتُسجَّل الحلقة المغلقة أو المفتوحة منذ أول تشغيل لهذا الإصدار من Loop." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Bolusy" + "value" : "Pokrývá %1$@: Loop uchovává běhy za 7 dní a uzavřená či otevřená smyčka se zaznamenává od prvního spuštění této verze Loop." } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Boluser" + "value" : "Dækker %1$@: Loop gemmer kørsler i 7 dage, og lukket eller åbent Loop registreres fra første gang denne version af Loop kørte." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Boli" + "value" : "Umfasst %1$@: Loop speichert Durchläufe 7 Tage lang, und Closed oder Open Loop wird erst ab dem ersten Start dieser Loop-Version aufgezeichnet." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Bolos" + "value" : "Cubre %1$@: Loop guarda 7 días de ejecuciones, y el circuito cerrado o abierto se registra desde la primera vez que se ejecutó esta versión de Loop." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Boluksia" + "value" : "Kattaa %1$@: Loop säilyttää 7 päivän ajot, ja suljettu tai avoin säätö tallennetaan siitä lähtien, kun tämä Loopin versio käynnistettiin ensimmäisen kerran." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus" + "value" : "Couvre %1$@ : Loop conserve 7 jours d'exécutions, et la boucle fermée ou ouverte est enregistrée depuis le premier lancement de cette version de Loop." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "בולוסים" + "value" : "מכסה %1$@: Loop שומר 7 ימים של הרצות, ו-Closed Loop או Open Loop נרשמים מאז ההפעלה הראשונה של גרסה זו של Loop." } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Bóluszok" + "value" : "Lefedett időszak: %1$@. A Loop 7 napnyi futást őriz meg, a zárt vagy nyitott hurok pedig a Loop ezen verziójának első futtatásától kerül rögzítésre." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Boli" + "value" : "Copre %1$@: Loop conserva 7 giorni di esecuzioni e il loop chiuso o aperto è registrato dal primo avvio di questa versione di Loop." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ボーラス" + "value" : "対象期間 %1$@:Loopは実行記録を7日間保持し、クローズド/オープンループはこのバージョンのLoopを初めて起動した時点から記録されます。" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Boluser" + "value" : "Dekker %1$@: Loop lagrer kjøringer i 7 dager, og lukket eller åpen Loop registreres fra første gang denne versjonen av Loop kjørte." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Bolussen" + "value" : "Omvat %1$@: Loop bewaart 7 dagen aan loop-runs, en actieve of open loop wordt vastgelegd vanaf de eerste keer dat deze versie van Loop draaide." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Bolusy" + "value" : "Obejmuje %1$@: Loop przechowuje cykle z 7 dni, a pętla zamknięta lub otwarta jest zapisywana od pierwszego uruchomienia tej wersji Loop." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus" + "value" : "Cobre %1$@: o Loop guarda 7 dias de execuções, e o ciclo fechado ou aberto é registrado desde a primeira vez que esta versão do Loop foi executada." } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Bolusuri" + "value" : "Acoperă %1$@: Loop păstrează 7 zile de rulări, iar bucla închisă sau deschisă este înregistrată de la prima rulare a acestei versiuni de Loop." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Болюсы" + "value" : "Охвачено: %1$@. Loop хранит запуски за 7 дней, а замкнутый или открытый цикл записывается с первого запуска этой версии Loop." } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Bolusy" + "value" : "Pokrýva %1$@: Loop uchováva behy za 7 dní a uzavretá či otvorená slučka sa zaznamenáva od prvého spustenia tejto verzie Loop." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Bolusdoser" + "value" : "Omfattar %1$@: Loop sparar 7 dagars körningar, och sluten eller öppen loop registreras från när den här versionen av Loop först kördes." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus" + "value" : "Kapsanan dönem %1$@: Loop 7 günlük çalışma kaydı tutar; kapalı veya açık döngü, Loop'un bu sürümünün ilk çalıştığı andan itibaren kaydedilir." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Болюси" + "value" : "Охоплено: %1$@. Loop зберігає запуски за 7 днів, а замкнутий чи відкритий цикл записується з першого запуску цієї версії Loop." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Liều bolus" + "value" : "Bao gồm %1$@: Loop lưu các lần chạy trong 7 ngày, và trạng thái vòng lặp kín hoặc mở được ghi lại từ khi phiên bản Loop này chạy lần đầu." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "大剂量次数" + "value" : "覆盖 %1$@:Loop 保留 7 天的运行记录,闭环或开环状态从此版本 Loop 首次运行时开始记录。" } } } }, - "Boluses per day" : { - "comment" : "Trends summary tile: average number of manual boluses per day", + "CSV, one row per day" : { + "comment" : "Trends export menu item: share a CSV spreadsheet file with one row for each day", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "جرعات Bolus يوميًا" + "value" : "CSV، صف واحد لكل يوم" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Bolusů denně" + "value" : "CSV, jeden řádek na den" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Boluser pr. dag" + "value" : "CSV, én række pr. dag" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Boli pro Tag" + "value" : "CSV, eine Zeile pro Tag" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Bolos por día" + "value" : "CSV, una fila por día" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Boluksia päivässä" + "value" : "CSV, yksi rivi päivää kohden" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus par jour" + "value" : "CSV, une ligne par jour" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus ליום" + "value" : "CSV, שורה אחת לכל יום" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Bólusok naponta" + "value" : "CSV, naponta egy sor" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Boli al giorno" + "value" : "CSV, una riga al giorno" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "1日のボーラス回数" + "value" : "CSV(1日1行)" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Boluser per dag" + "value" : "CSV, én rad per dag" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Bolussen per dag" + "value" : "CSV, één rij per dag" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Bolusy dziennie" + "value" : "CSV, jeden wiersz na dzień" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Bolus por dia" + "value" : "CSV, uma linha por dia" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Bolusuri pe zi" + "value" : "CSV, un rând pe zi" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Болюсов в день" + "value" : "CSV, одна строка на день" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Bolusy za deň" + "value" : "CSV, jeden riadok na deň" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Bolusar per dag" + "value" : "CSV, en rad per dag" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Günlük bolus sayısı" + "value" : "CSV, gün başına bir satır" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Болюсів на день" + "value" : "CSV, один рядок на день" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Số lần Bolus mỗi ngày" + "value" : "CSV, mỗi ngày một dòng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日大剂量次数" + "value" : "CSV,每天一行" } } } }, - "Carbs" : { - "comment" : "Label of a tile showing the day's total carbohydrate Logbook filter chip: show only carb entries Logbook row title: a carb entry", + "Daily pattern" : { + "comment" : "Trends card title: the ambulatory glucose profile, glucose by time of day across the period", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "Carbs" + "value" : "النمط اليومي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Sacharidy" + "value" : "Denní profil" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Kulhydrater" + "value" : "Døgnmønster" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "KH" + "value" : "Tagesprofil" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Carbohidratos" + "value" : "Patrón diario" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Hiilihydraatit" + "value" : "Vuorokausiprofiili" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Glucides" + "value" : "Profil journalier" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Carbs" + "value" : "דפוס יומי" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Szénhidrát" + "value" : "Napi mintázat" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Carb." + "value" : "Andamento giornaliero" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "カーボ" + "value" : "日内パターン" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Karbohydrater" + "value" : "Døgnmønster" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Koolhydraten" + "value" : "Dagpatroon" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Węglowodany" + "value" : "Profil dobowy" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Carbs" + "value" : "Padrão diário" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Carbohidrați" + "value" : "Tipar zilnic" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Углеводы" + "value" : "Суточный профиль" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Sacharidy" + "value" : "Denný profil" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kolhydrater" + "value" : "Dygnsmönster" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Karb" + "value" : "Günlük örüntü" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Вуглеводи" + "value" : "Добовий профіль" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Carbs" + "value" : "Mẫu hằng ngày" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "碳水化合物" + "value" : "每日模式" } } } }, - "Carbs per day" : { - "comment" : "Trends summary tile: average grams of carbohydrate entered per day", + "Daily pattern over %1$@. Median highest %2$@ at %3$@, lowest %4$@ at %5$@" : { + "comment" : "VoiceOver label for the Trends Daily pattern chart. (1: days with readings, e.g. 14 days; 2: highest median glucose; 3: time of day; 4: lowest median glucose; 5: time of day)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الكارب يوميًا" + "value" : "النمط اليومي خلال %1$@. أعلى وسيط %2$@ عند %3$@، وأدنى وسيط %4$@ عند %5$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Sacharidy denně" + "value" : "Denní profil za %1$@. Nejvyšší medián %2$@ v %3$@, nejnižší %4$@ v %5$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Kulhydrater pr. dag" + "value" : "Døgnmønster over %1$@. Median højest %2$@ kl. %3$@, lavest %4$@ kl. %5$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "KH pro Tag" + "value" : "Tagesprofil über %1$@. Median am höchsten %2$@ um %3$@, am niedrigsten %4$@ um %5$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Carbohidratos por día" + "value" : "Patrón diario de %1$@. Mediana más alta %2$@ a las %3$@, más baja %4$@ a las %5$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Hiilihydraatit päivässä" + "value" : "Vuorokausiprofiili %1$@ ajalta. Korkein mediaani %2$@ klo %3$@, matalin %4$@ klo %5$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Glucides par jour" + "value" : "Profil journalier sur %1$@. Médiane la plus haute %2$@ à %3$@, la plus basse %4$@ à %5$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "פחמימות ליום" + "value" : "דפוס יומי במשך %1$@. חציון גבוה ביותר %2$@ ב-%3$@, נמוך ביותר %4$@ ב-%5$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Szénhidrát naponta" + "value" : "Napi mintázat, %1$@. Legmagasabb medián %2$@ ekkor: %3$@, legalacsonyabb %4$@ ekkor: %5$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Carboidrati al giorno" + "value" : "Andamento giornaliero su %1$@. Mediana più alta %2$@ alle %3$@, più bassa %4$@ alle %5$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "1日のカーボ" + "value" : "%1$@の日内パターン。中央値の最高は%3$@の%2$@、最低は%5$@の%4$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Karbohydrater per dag" + "value" : "Døgnmønster over %1$@. Median høyest %2$@ kl. %3$@, lavest %4$@ kl. %5$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Koolhydraten per dag" + "value" : "Dagpatroon over %1$@. Mediaan hoogst %2$@ om %3$@, laagst %4$@ om %5$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Węglowodany dziennie" + "value" : "Profil dobowy z %1$@. Najwyższa mediana %2$@ o %3$@, najniższa %4$@ o %5$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Carbs por dia" + "value" : "Padrão diário em %1$@. Mediana mais alta %2$@ às %3$@, mais baixa %4$@ às %5$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Carbohidrați pe zi" + "value" : "Tipar zilnic pe %1$@. Mediana cea mai mare %2$@ la %3$@, cea mai mică %4$@ la %5$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Углеводов в день" + "value" : "Суточный профиль за %1$@. Наибольшая медиана %2$@ в %3$@, наименьшая %4$@ в %5$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Sacharidy za deň" + "value" : "Denný profil za %1$@. Najvyšší medián %2$@ o %3$@, najnižší %4$@ o %5$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kolhydrater per dag" + "value" : "Dygnsmönster över %1$@. Högsta median %2$@ kl. %3$@, lägsta %4$@ kl. %5$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Günlük karb" + "value" : "%1$@ boyunca günlük örüntü. En yüksek medyan %3$@ saatinde %2$@, en düşük %5$@ saatinde %4$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Вуглеводів на день" + "value" : "Добовий профіль за %1$@. Найвища медіана %2$@ о %3$@, найнижча %4$@ о %5$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Carbs mỗi ngày" + "value" : "Mẫu hằng ngày trong %1$@. Trung vị cao nhất %2$@ lúc %3$@, thấp nhất %4$@ lúc %5$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日碳水" + "value" : "%1$@的每日模式。中位数最高 %2$@(%3$@),最低 %4$@(%5$@)" } } } - }, - "CGM data covers %1$@." : { - "comment" : "Trends Summary note when glucose readings start partway through the period. (1: days covered, e.g. 5 of 14 days)", + }, + "Daily pattern: not enough days" : { + "comment" : "VoiceOver label for the Trends Daily pattern chart when fewer than 5 days have readings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "بيانات CGM تغطي %1$@." + "value" : "النمط اليومي: لا توجد أيام كافية" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Data CGM pokrývají %1$@." + "value" : "Denní profil: málo dní" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "CGM-data dækker %1$@." + "value" : "Døgnmønster: ikke nok dage" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "CGM-Daten decken %1$@ ab." + "value" : "Tagesprofil: nicht genug Tage" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Los datos de CGM cubren %1$@." + "value" : "Patrón diario: no hay suficientes días" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "CGM-data kattaa %1$@." + "value" : "Vuorokausiprofiili: liian vähän päiviä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Les données CGM couvrent %1$@." + "value" : "Profil journalier : pas assez de jours" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נתוני CGM מכסים %1$@." + "value" : "דפוס יומי: אין מספיק ימים" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "CGM-adat: %1$@." + "value" : "Napi mintázat: nincs elég nap" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "I dati CGM coprono %1$@." + "value" : "Andamento giornaliero: giorni insufficienti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "CGMデータ:%1$@。" + "value" : "日内パターン:日数が不足しています" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "CGM-data dekker %1$@." + "value" : "Døgnmønster: ikke nok dager" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "CGM-gegevens beslaan %1$@." + "value" : "Dagpatroon: niet genoeg dagen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dane CGM obejmują %1$@." + "value" : "Profil dobowy: za mało dni" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Os dados de CGM cobrem %1$@." + "value" : "Padrão diário: dias insuficientes" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Datele CGM acoperă %1$@." + "value" : "Tipar zilnic: prea puține zile" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Данные CGM: %1$@." + "value" : "Суточный профиль: недостаточно дней" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Údaje CGM pokrývajú %1$@." + "value" : "Denný profil: málo dní" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "CGM-data täcker %1$@." + "value" : "Dygnsmönster: för få dagar" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "CGM verisi: %1$@." + "value" : "Günlük örüntü: yeterli gün yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Дані CGM: %1$@." + "value" : "Добовий профіль: замало днів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Dữ liệu CGM bao phủ %1$@." + "value" : "Mẫu hằng ngày: không đủ số ngày" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "CGM 数据覆盖 %1$@。" + "value" : "每日模式:天数不足" } } } }, - "Closed loop" : { - "comment" : "Trends Loop status: time Loop was running in closed loop", + "Day" : { + "comment" : "Appearance option and app icon name: the light (daytime) version", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حلقة مغلقة" + "value" : "نهار" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Uzavřená smyčka" + "value" : "Den" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Lukket Loop" + "value" : "Dag" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Closed Loop" + "value" : "Tag" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Circuito cerrado" + "value" : "Día" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Suljettu säätö" + "value" : "Päivä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Boucle fermée" + "value" : "Jour" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Closed Loop" + "value" : "יום" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Zárt hurok" + "value" : "Nap" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Loop chiuso" + "value" : "Giorno" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "クローズドループ" + "value" : "日" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Lukket Loop" + "value" : "Dag" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Actieve loop" + "value" : "Dag" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Pętla zamknięta" + "value" : "Dzień" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ciclo fechado" + "value" : "Dia" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Buclă închisă" + "value" : "Zi" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Замкнутый цикл" + "value" : "День" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Uzavretá slučka" + "value" : "Deň" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Sluten loop" + "value" : "Dag" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Kapalı döngü" + "value" : "Gündüz" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Замкнутий цикл" + "value" : "День" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Vòng lặp kín" + "value" : "Ngày" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "闭环" + "value" : "日" } } } }, - "Closed loop off" : { - "comment" : "Reason shown for a stretch of time when Loop was in open loop because closed loop was turned off", + "Day (Trends period)" : { + "comment" : "Trends period picker: a period of one calendar day (not daytime)", "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Day" + } + }, "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الحلقة المغلقة متوقفة" + "value" : "يوم" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Uzavřená smyčka vypnuta" + "value" : "Den" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Lukket Loop slået fra" + "value" : "Dag" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Closed Loop aus" + "value" : "Tag" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Circuito cerrado desactivado" + "value" : "Día" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Suljettu säätö pois" + "value" : "Päivä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Boucle fermée désactivée" + "value" : "Jour" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Closed Loop כבוי" + "value" : "יום" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Zárt hurok kikapcsolva" + "value" : "Nap" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Loop chiuso disattivato" + "value" : "Giorno" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "クローズドループ オフ" + "value" : "日" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Lukket Loop av" + "value" : "Dag" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Actieve loop uit" + "value" : "Dag" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Pętla zamknięta wyłączona" + "value" : "Dzień" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ciclo fechado desligado" + "value" : "Dia" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Buclă închisă oprită" + "value" : "Zi" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Замкнутый цикл выключен" + "value" : "День" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Uzavretá slučka vypnutá" + "value" : "Deň" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Sluten loop av" + "value" : "Dag" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Kapalı döngü kapalı" + "value" : "Gün" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Замкнутий цикл вимкнено" + "value" : "День" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Vòng lặp kín tắt" + "value" : "Ngày" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "闭环已关闭" + "value" : "日" } } } }, - "COB" : { - "comment" : "Abbreviation for carbs on board (active carbohydrate) Abbreviation for carbs on board; label of a tile on the Now screen", + "Edit" : { + "comment" : "Swipe action to edit a Logbook carb entry", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "تعديل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Upravit" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Rediger" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Bearbeiten" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Editar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Muokkaa" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "GA" + "value" : "Modifier" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "ערוך" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Szerkesztés" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Modifica" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "編集" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Rediger" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Wijzig" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Edytuj" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Editar" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Editați" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Изменить" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Upraviť" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Redigera" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Düzenle" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Змінити" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "Sửa" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "COB" + "value" : "编辑" } } } }, - "COB (g, right)" : { - "comment" : "Chart legend: carbs on board in grams, on the right axis", + "Edit Carb Entry" : { + "comment" : "Title of the screen for editing a carb entry from the Logbook", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g، يمين)" + "value" : "تعديل إدخال الكارب" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, vpravo)" + "value" : "Upravit záznam sacharidů" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, højre)" + "value" : "Rediger kulhydrater" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, rechts)" + "value" : "KH-Eintrag bearbeiten" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, derecha)" + "value" : "Editar entrada de carbohidratos" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, oikea)" + "value" : "Muokkaa hiilihydraatteja" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "GA (g, droite)" + "value" : "Modifier les glucides" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, ימין)" + "value" : "עריכת פחמימות" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, jobb)" + "value" : "Szénhidrát szerkesztése" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, destra)" + "value" : "Modifica carboidrati inseriti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "COB(g、右)" + "value" : "糖質の記入を編集" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, høyre)" + "value" : "Rediger karbohydrater" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, rechts)" + "value" : "Koolhydraatinvoer wijzigen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, prawa oś)" + "value" : "Edytuj wprowadzone węglowodany" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, direita)" + "value" : "Editar carbs" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, dreapta)" + "value" : "Editați intrarea de carbohidrați" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "COB (г, справа)" + "value" : "Редактировать запись углеводов" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, vpravo)" + "value" : "Upraviť záznam sacharidov" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, höger)" + "value" : "Redigera kolhydrater" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "COB (gr, sağ)" + "value" : "Karb Girişini Düzenle" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "COB (г, праворуч)" + "value" : "Редагувати запис вуглеводів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "COB (g, bên phải)" + "value" : "Sửa khai báo Carb" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "COB(克,右轴)" + "value" : "编辑碳水记录" } } } }, - "Configuration error" : { - "comment" : "Reason Loop could not run: its settings are incomplete", + "Export" : { + "comment" : "Trends menu title: export the period as a PDF report or CSV file", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "خطأ في الإعدادات" + "value" : "تصدير" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Chyba konfigurace" + "value" : "Exportovat" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Konfigurationsfejl" + "value" : "Eksporter" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Konfigurationsfehler" + "value" : "Exportieren" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Error de configuración" + "value" : "Exportar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Määritysvirhe" + "value" : "Vie" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Erreur de configuration" + "value" : "Exporter" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שגיאת תצורה" + "value" : "ייצוא" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Konfigurációs hiba" + "value" : "Exportálás" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Errore di configurazione" + "value" : "Esporta" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "設定エラー" + "value" : "書き出し" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Konfigurasjonsfeil" + "value" : "Eksporter" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Configuratiefout" + "value" : "Exporteer" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Błąd konfiguracji" + "value" : "Eksportuj" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Erro de configuração" + "value" : "Exportar" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Eroare de configurare" + "value" : "Exportați" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Ошибка настроек" + "value" : "Экспорт" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Chyba konfigurácie" + "value" : "Exportovať" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Konfigurationsfel" + "value" : "Exportera" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Yapılandırma hatası" + "value" : "Dışa Aktar" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Помилка налаштувань" + "value" : "Експорт" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Lỗi cấu hình" + "value" : "Xuất" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "配置错误" + "value" : "导出" } } } }, - "Connection error" : { - "comment" : "Reason Loop could not run: no connected devices, or a device connection failed", + "From Apple Health: Loop keeps 7 days, so this day has no temp basal or schedule detail." : { + "comment" : "Day page note when the day is older than Loop's 7-day history and was read from Apple Health", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "خطأ في الاتصال" + "value" : "من Apple Health: يحتفظ Loop بـ 7 أيام فقط، لذا لا توجد تفاصيل Temp Basal أو الجدول لهذا اليوم." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Chyba připojení" + "value" : "Z aplikace Zdraví: Loop uchovává 7 dní, takže pro tento den chybí podrobnosti o dočasném bazálu a plánu." } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Forbindelsesfejl" + "value" : "Fra Apple Sundhed: Loop gemmer 7 dage, så denne dag har ingen detaljer om midlertidig basal eller tidsplan." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbindungsfehler" + "value" : "Aus Apple Health: Loop speichert 7 Tage, daher gibt es für diesen Tag keine Details zu temporären Basalraten oder zum Zeitplan." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Error de conexión" + "value" : "De Apple Salud: Loop guarda 7 días, así que este día no tiene detalle de basal temporal ni de programación." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Yhteysvirhe" + "value" : "Apple Terveydestä: Loop säilyttää 7 päivää, joten tältä päivältä ei ole tilapäisen basaalin tai ohjelman tietoja." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Erreur de connexion" + "value" : "Depuis Apple Santé : Loop conserve 7 jours, donc ce jour n'a pas de détail de basal temp. ni de programmation." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שגיאת חיבור" + "value" : "מ-Apple Health: Loop שומר 7 ימים, ולכן אין ליום זה פרטי Temp Basal או תוכנית." } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Kapcsolódási hiba" + "value" : "Az Apple Egészség appból: a Loop 7 napot őriz meg, így ehhez a naphoz nincsenek ideiglenes basal- és ütemezési részletek." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Errore di connessione" + "value" : "Da Apple Salute: Loop conserva 7 giorni, quindi questo giorno non ha dettagli su basale temporanea o programmazione." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "接続エラー" + "value" : "Appleヘルスケアから取得:Loopは7日間のみ保持するため、この日には一時ベーサルやスケジュールの詳細がありません。" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Tilkoblingsfeil" + "value" : "Fra Apple Helse: Loop lagrer 7 dager, så denne dagen har ingen detaljer om midlertidig basal eller tidsplan." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbindingsfout" + "value" : "Uit Apple Health: Loop bewaart 7 dagen, dus deze dag heeft geen details over tijdelijk basaal of het schema." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Błąd połączenia" + "value" : "Z aplikacji Zdrowie: Loop przechowuje 7 dni, więc dla tego dnia brak szczegółów bazy tymczasowej i harmonogramu." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Erro de conexão" + "value" : "Do app Saúde: o Loop guarda 7 dias, então este dia não tem detalhes de basal temporária nem da programação." } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Eroare de conexiune" + "value" : "Din Apple Sănătate: Loop păstrează 7 zile, așa că această zi nu are detalii despre bazala temporară sau program." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Ошибка подключения" + "value" : "Из приложения «Здоровье»: Loop хранит 7 дней, поэтому для этого дня нет данных о временном базале и расписании." } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Chyba pripojenia" + "value" : "Z aplikácie Zdravie: Loop uchováva 7 dní, preto pre tento deň chýbajú podrobnosti o dočasnom bazáli a pláne." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Anslutningsfel" + "value" : "Från Apple Hälsa: Loop sparar 7 dagar, så den här dagen saknar detaljer om temporär basal och schema." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bağlantı hatası" + "value" : "Apple Health'ten: Loop 7 gün saklar, bu nedenle bu gün için geçici bazal veya program ayrıntısı yok." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Помилка підключення" + "value" : "З програми «Здоровʼя»: Loop зберігає 7 днів, тому для цього дня немає даних про тимчасовий базал і розклад." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Lỗi kết nối" + "value" : "Từ Apple Health: Loop chỉ lưu 7 ngày, nên ngày này không có chi tiết Temp Basal hoặc lịch biểu." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "连接错误" + "value" : "来自 Apple 健康:Loop 只保留 7 天,因此这一天没有临时基础率或预设详情。" } } } }, - "Couldn't export" : { - "comment" : "Alert title when exporting the Trends PDF or CSV fails", + "Gaps" : { + "comment" : "Trends Loop status button and screen title: list of times Loop wasn't in closed loop", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تعذّر التصدير" + "value" : "الفجوات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nelze exportovat" + "value" : "Výpadky" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Kunne ikke eksportere" + "value" : "Huller" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Export fehlgeschlagen" + "value" : "Lücken" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No se pudo exportar" + "value" : "Interrupciones" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Vienti epäonnistui" + "value" : "Katkot" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Exportation impossible" + "value" : "Interruptions" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "לא ניתן היה לייצא" + "value" : "פערים" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nem sikerült exportálni" + "value" : "Kimaradások" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Impossibile esportare" + "value" : "Interruzioni" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "書き出せませんでした" + "value" : "中断" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kunne ikke eksportere" + "value" : "Hull" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Exporteren mislukt" + "value" : "Onderbrekingen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Nie udało się wyeksportować" + "value" : "Przerwy" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Não foi possível exportar" + "value" : "Interrupções" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Nu s-a putut exporta" + "value" : "Întreruperi" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Не удалось экспортировать" + "value" : "Пропуски" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Nedá sa exportovať" + "value" : "Výpadky" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kunde inte exportera" + "value" : "Luckor" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Dışa aktarılamadı" + "value" : "Boşluklar" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Не вдалося експортувати" + "value" : "Пропуски" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không thể xuất" + "value" : "Gián đoạn" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "无法导出" + "value" : "中断" } } } }, - "Couldn't load this day: %1$@" : { - "comment" : "Day page error when the day's history can't be read. (1: system error description)", + "glucose %1$@" : { + "comment" : "Logbook row detail: glucose when the dose was given. (1: glucose value, e.g. 7.5)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تعذّر تحميل هذا اليوم: %1$@" + "value" : "السكر %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nelze načíst tento den: %1$@" + "value" : "glukóza %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Kunne ikke indlæse denne dag: %1$@" + "value" : "glukose %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Dieser Tag konnte nicht geladen werden: %1$@" + "value" : "Blutzucker %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No se pudo cargar este día: %1$@" + "value" : "glucosa %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Päivää ei voitu ladata: %1$@" + "value" : "glukoosi %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Impossible de charger cette journée : %1$@" + "value" : "glycémie %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "לא ניתן היה לטעון יום זה: %1$@" + "value" : "סוכר %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nem sikerült betölteni ezt a napot: %1$@" + "value" : "glükóz %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Impossibile caricare questo giorno: %1$@" + "value" : "glicemia %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "この日を読み込めませんでした:%1$@" + "value" : "血糖値 %1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kunne ikke laste inn denne dagen: %1$@" + "value" : "blodsukker %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Deze dag kan niet worden geladen: %1$@" + "value" : "glucose %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Nie udało się wczytać tego dnia: %1$@" + "value" : "glukoza %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Não foi possível carregar este dia: %1$@" + "value" : "glicose %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Nu s-a putut încărca această zi: %1$@" + "value" : "glicemie %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Не удалось загрузить этот день: %1$@" + "value" : "глюкоза %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Tento deň sa nedá načítať: %1$@" + "value" : "glykémia %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kunde inte läsa in dagen: %1$@" + "value" : "glukos %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bu gün yüklenemedi: %1$@" + "value" : "KŞ %1$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Не вдалося завантажити цей день: %1$@" + "value" : "глюкоза %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không thể tải ngày này: %1$@" + "value" : "đường huyết %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "无法载入这一天:%1$@" + "value" : "血糖 %1$@" } } } }, - "Couldn't load Trends: %1$@" : { - "comment" : "Trends error message when the history can't be read. (1: error description)", + "glucose %1$@ falling" : { + "comment" : "Logbook row detail: glucose when the dose was given, and falling. (1: glucose value, e.g. 11.0)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تعذّر تحميل الاتجاهات: %1$@" + "value" : "السكر %1$@ ينخفض" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nelze načíst trendy: %1$@" + "value" : "glukóza %1$@, klesá" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Kunne ikke indlæse Tendenser: %1$@" + "value" : "glukose %1$@ faldende" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Trends konnten nicht geladen werden: %1$@" + "value" : "Blutzucker %1$@ fallend" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No se pudieron cargar las tendencias: %1$@" + "value" : "glucosa %1$@ bajando" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Trendejä ei voitu ladata: %1$@" + "value" : "glukoosi %1$@ laskeva" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Impossible de charger les tendances : %1$@" + "value" : "glycémie %1$@ en baisse" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "לא ניתן היה לטעון מגמות: %1$@" + "value" : "סוכר %1$@ יורד" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nem sikerült betölteni a trendeket: %1$@" + "value" : "glükóz %1$@, csökkenő" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Impossibile caricare le tendenze: %1$@" + "value" : "glicemia %1$@ in discesa" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "トレンドを読み込めませんでした:%1$@" + "value" : "血糖値 %1$@ 下降中" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Kunne ikke laste inn Trender: %1$@" + "value" : "blodsukker %1$@ fallende" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Trends kunnen niet worden geladen: %1$@" + "value" : "glucose %1$@ dalend" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Nie udało się wczytać trendów: %1$@" + "value" : "glukoza %1$@, spada" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Não foi possível carregar as tendências: %1$@" + "value" : "glicose %1$@ caindo" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Nu s-au putut încărca tendințele: %1$@" + "value" : "glicemie %1$@ în scădere" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Не удалось загрузить тренды: %1$@" + "value" : "глюкоза %1$@, падает" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Trendy sa nedajú načítať: %1$@" + "value" : "glykémia %1$@, klesá" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kunde inte läsa in Trender: %1$@" + "value" : "glukos %1$@ sjunkande" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Eğilimler yüklenemedi: %1$@" + "value" : "KŞ %1$@ düşüyor" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Не вдалося завантажити тренди: %1$@" + "value" : "глюкоза %1$@, падає" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không thể tải Xu hướng: %1$@" + "value" : "đường huyết %1$@ đang giảm" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "无法载入趋势:%1$@" + "value" : "血糖 %1$@ 下降中" } } } }, - "Covers %1$@: Loop keeps 7 days of runs, and closed or open loop is recorded from when this version of Loop first ran." : { - "comment" : "Trends Loop status footnote when the status covers only part of the period. (1: date range covered)", + "glucose %1$@ rising" : { + "comment" : "Logbook row detail: glucose when the dose was given, and rising. (1: glucose value, e.g. 11.0)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يغطي %1$@: يحتفظ Loop بسجل تشغيلات 7 أيام، وتُسجَّل الحلقة المغلقة أو المفتوحة منذ أول تشغيل لهذا الإصدار من Loop." + "value" : "السكر %1$@ يرتفع" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Pokrývá %1$@: Loop uchovává běhy za 7 dní a uzavřená či otevřená smyčka se zaznamenává od prvního spuštění této verze Loop." + "value" : "glukóza %1$@, stoupá" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Dækker %1$@: Loop gemmer kørsler i 7 dage, og lukket eller åbent Loop registreres fra første gang denne version af Loop kørte." + "value" : "glukose %1$@ stigende" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Umfasst %1$@: Loop speichert Durchläufe 7 Tage lang, und Closed oder Open Loop wird erst ab dem ersten Start dieser Loop-Version aufgezeichnet." + "value" : "Blutzucker %1$@ steigend" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cubre %1$@: Loop guarda 7 días de ejecuciones, y el circuito cerrado o abierto se registra desde la primera vez que se ejecutó esta versión de Loop." + "value" : "glucosa %1$@ subiendo" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Kattaa %1$@: Loop säilyttää 7 päivän ajot, ja suljettu tai avoin säätö tallennetaan siitä lähtien, kun tämä Loopin versio käynnistettiin ensimmäisen kerran." + "value" : "glukoosi %1$@ nouseva" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Couvre %1$@ : Loop conserve 7 jours d'exécutions, et la boucle fermée ou ouverte est enregistrée depuis le premier lancement de cette version de Loop." + "value" : "glycémie %1$@ en hausse" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מכסה %1$@: Loop שומר 7 ימים של הרצות, ו-Closed Loop או Open Loop נרשמים מאז ההפעלה הראשונה של גרסה זו של Loop." + "value" : "סוכר %1$@ עולה" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Lefedett időszak: %1$@. A Loop 7 napnyi futást őriz meg, a zárt vagy nyitott hurok pedig a Loop ezen verziójának első futtatásától kerül rögzítésre." + "value" : "glükóz %1$@, emelkedő" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Copre %1$@: Loop conserva 7 giorni di esecuzioni e il loop chiuso o aperto è registrato dal primo avvio di questa versione di Loop." + "value" : "glicemia %1$@ in salita" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "対象期間 %1$@:Loopは実行記録を7日間保持し、クローズド/オープンループはこのバージョンのLoopを初めて起動した時点から記録されます。" + "value" : "血糖値 %1$@ 上昇中" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Dekker %1$@: Loop lagrer kjøringer i 7 dager, og lukket eller åpen Loop registreres fra første gang denne versjonen av Loop kjørte." + "value" : "blodsukker %1$@ stigende" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Omvat %1$@: Loop bewaart 7 dagen aan loop-runs, en actieve of open loop wordt vastgelegd vanaf de eerste keer dat deze versie van Loop draaide." + "value" : "glucose %1$@ stijgend" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Obejmuje %1$@: Loop przechowuje cykle z 7 dni, a pętla zamknięta lub otwarta jest zapisywana od pierwszego uruchomienia tej wersji Loop." + "value" : "glukoza %1$@, rośnie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Cobre %1$@: o Loop guarda 7 dias de execuções, e o ciclo fechado ou aberto é registrado desde a primeira vez que esta versão do Loop foi executada." + "value" : "glicose %1$@ subindo" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Acoperă %1$@: Loop păstrează 7 zile de rulări, iar bucla închisă sau deschisă este înregistrată de la prima rulare a acestei versiuni de Loop." + "value" : "glicemie %1$@ în creștere" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Охвачено: %1$@. Loop хранит запуски за 7 дней, а замкнутый или открытый цикл записывается с первого запуска этой версии Loop." + "value" : "глюкоза %1$@, растёт" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Pokrýva %1$@: Loop uchováva behy za 7 dní a uzavretá či otvorená slučka sa zaznamenáva od prvého spustenia tejto verzie Loop." + "value" : "glykémia %1$@, stúpa" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Omfattar %1$@: Loop sparar 7 dagars körningar, och sluten eller öppen loop registreras från när den här versionen av Loop först kördes." + "value" : "glukos %1$@ stigande" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Kapsanan dönem %1$@: Loop 7 günlük çalışma kaydı tutar; kapalı veya açık döngü, Loop'un bu sürümünün ilk çalıştığı andan itibaren kaydedilir." + "value" : "KŞ %1$@ yükseliyor" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Охоплено: %1$@. Loop зберігає запуски за 7 днів, а замкнутий чи відкритий цикл записується з першого запуску цієї версії Loop." + "value" : "глюкоза %1$@, зростає" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Bao gồm %1$@: Loop lưu các lần chạy trong 7 ngày, và trạng thái vòng lặp kín hoặc mở được ghi lại từ khi phiên bản Loop này chạy lần đầu." + "value" : "đường huyết %1$@ đang tăng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "覆盖 %1$@:Loop 保留 7 天的运行记录,闭环或开环状态从此版本 Loop 首次运行时开始记录。" + "value" : "血糖 %1$@ 上升中" } } } }, - "CSV, one row per day" : { - "comment" : "Trends export menu item: share a CSV spreadsheet file with one row for each day", + "Glucose too old" : { + "comment" : "Reason Loop could not run: the latest glucose reading was too old", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "CSV، صف واحد لكل يوم" + "value" : "قراءة السكر قديمة جدًا" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, jeden řádek na den" + "value" : "Glukóza je příliš stará" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, én række pr. dag" + "value" : "Glukosemåling for gammel" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, eine Zeile pro Tag" + "value" : "Blutzuckerwert zu alt" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, una fila por día" + "value" : "Glucosa demasiado antigua" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, yksi rivi päivää kohden" + "value" : "Glukoosi liian vanha" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, une ligne par jour" + "value" : "Glycémie trop ancienne" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, שורה אחת לכל יום" + "value" : "קריאת הסוכר ישנה מדי" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, naponta egy sor" + "value" : "Túl régi glükózérték" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, una riga al giorno" + "value" : "Glicemia troppo vecchia" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "CSV(1日1行)" + "value" : "血糖値が古すぎます" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, én rad per dag" + "value" : "Blodsukkermåling for gammel" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, één rij per dag" + "value" : "Glucosewaarde te oud" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, jeden wiersz na dzień" + "value" : "Glukoza zbyt stara" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, uma linha por dia" + "value" : "Glicose muito antiga" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, un rând pe zi" + "value" : "Glicemie prea veche" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, одна строка на день" + "value" : "Данные глюкозы устарели" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, jeden riadok na deň" + "value" : "Glykémia je príliš stará" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, en rad per dag" + "value" : "Glukosvärdet är för gammalt" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, gün başına bir satır" + "value" : "Kan şekeri verisi çok eski" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, один рядок на день" + "value" : "Дані глюкози застаріли" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "CSV, mỗi ngày một dòng" + "value" : "Dữ liệu đường huyết quá cũ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "CSV,每天一行" + "value" : "血糖数据过旧" } } } }, - "Daily pattern" : { - "comment" : "Trends card title: the ambulatory glucose profile, glucose by time of day across the period", + "Health" : { + "comment" : "Logbook carb row detail: the entry came from Apple Health", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "النمط اليومي" + "value" : "Health" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Denní profil" + "value" : "Zdraví" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster" + "value" : "Sundhed" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tagesprofil" + "value" : "Health" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Patrón diario" + "value" : "Salud" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Vuorokausiprofiili" + "value" : "Terveys" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Profil journalier" + "value" : "Santé" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "דפוס יומי" + "value" : "Health" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Napi mintázat" + "value" : "Egészség" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Andamento giornaliero" + "value" : "Salute" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "日内パターン" + "value" : "ヘルスケア" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster" + "value" : "Helse" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dagpatroon" + "value" : "Gezondheid" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Profil dobowy" + "value" : "Zdrowie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Padrão diário" + "value" : "Saúde" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Tipar zilnic" + "value" : "Sănătate" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Суточный профиль" + "value" : "Здоровье" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Denný profil" + "value" : "Zdravie" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Dygnsmönster" + "value" : "Hälsa" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Günlük örüntü" + "value" : "Health" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Добовий профіль" + "value" : "Здоровʼя" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mẫu hằng ngày" + "value" : "Apple Health" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日模式" + "value" : "健康" } } } }, - "Daily pattern over %1$@. Median highest %2$@ at %3$@, lowest %4$@ at %5$@" : { - "comment" : "VoiceOver label for the Trends Daily pattern chart. (1: days with readings, e.g. 14 days; 2: highest median glucose; 3: time of day; 4: lowest median glucose; 5: time of day)", + "In range" : { + "comment" : "Time in range band: glucose 70–180 mg/dL (3.9–10.0 mmol/L) Time-in-range band name: glucose 70–180 mg/dL (3.9–10.0 mmol/L)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "النمط اليومي خلال %1$@. أعلى وسيط %2$@ عند %3$@، وأدنى وسيط %4$@ عند %5$@" + "value" : "ضمن النطاق" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Denní profil za %1$@. Nejvyšší medián %2$@ v %3$@, nejnižší %4$@ v %5$@" + "value" : "V rozsahu" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster over %1$@. Median højest %2$@ kl. %3$@, lavest %4$@ kl. %5$@" + "value" : "I området" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tagesprofil über %1$@. Median am höchsten %2$@ um %3$@, am niedrigsten %4$@ um %5$@" + "value" : "Im Zielbereich" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Patrón diario de %1$@. Mediana más alta %2$@ a las %3$@, más baja %4$@ a las %5$@" + "value" : "En rango" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Vuorokausiprofiili %1$@ ajalta. Korkein mediaani %2$@ klo %3$@, matalin %4$@ klo %5$@" + "value" : "Tavoitealueella" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Profil journalier sur %1$@. Médiane la plus haute %2$@ à %3$@, la plus basse %4$@ à %5$@" + "value" : "Dans la cible" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "דפוס יומי במשך %1$@. חציון גבוה ביותר %2$@ ב-%3$@, נמוך ביותר %4$@ ב-%5$@" + "value" : "בטווח" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Napi mintázat, %1$@. Legmagasabb medián %2$@ ekkor: %3$@, legalacsonyabb %4$@ ekkor: %5$@" + "value" : "Tartományban" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Andamento giornaliero su %1$@. Mediana più alta %2$@ alle %3$@, più bassa %4$@ alle %5$@" + "value" : "Nell'intervallo" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@の日内パターン。中央値の最高は%3$@の%2$@、最低は%5$@の%4$@" + "value" : "範囲内" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster over %1$@. Median høyest %2$@ kl. %3$@, lavest %4$@ kl. %5$@" + "value" : "I målområdet" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dagpatroon over %1$@. Mediaan hoogst %2$@ om %3$@, laagst %4$@ om %5$@" + "value" : "Binnen bereik" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Profil dobowy z %1$@. Najwyższa mediana %2$@ o %3$@, najniższa %4$@ o %5$@" + "value" : "W zakresie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Padrão diário em %1$@. Mediana mais alta %2$@ às %3$@, mais baixa %4$@ às %5$@" + "value" : "No alvo" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Tipar zilnic pe %1$@. Mediana cea mai mare %2$@ la %3$@, cea mai mică %4$@ la %5$@" + "value" : "În interval" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Суточный профиль за %1$@. Наибольшая медиана %2$@ в %3$@, наименьшая %4$@ в %5$@" + "value" : "В диапазоне" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Denný profil za %1$@. Najvyšší medián %2$@ o %3$@, najnižší %4$@ o %5$@" + "value" : "V rozsahu" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Dygnsmönster över %1$@. Högsta median %2$@ kl. %3$@, lägsta %4$@ kl. %5$@" + "value" : "Inom målområdet" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@ boyunca günlük örüntü. En yüksek medyan %3$@ saatinde %2$@, en düşük %5$@ saatinde %4$@" + "value" : "Aralıkta" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Добовий профіль за %1$@. Найвища медіана %2$@ о %3$@, найнижча %4$@ о %5$@" + "value" : "У діапазоні" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mẫu hằng ngày trong %1$@. Trung vị cao nhất %2$@ lúc %3$@, thấp nhất %4$@ lúc %5$@" + "value" : "Trong khoảng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@的每日模式。中位数最高 %2$@(%3$@),最低 %4$@(%5$@)" + "value" : "范围内" } } } }, - "Daily pattern: not enough days" : { - "comment" : "VoiceOver label for the Trends Daily pattern chart when fewer than 5 days have readings", + "Infusion set changed" : { + "comment" : "Pump event in the history: the infusion set was replaced", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "النمط اليومي: لا توجد أيام كافية" + "value" : "تم تغيير مجموعة التسريب" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Denní profil: málo dní" + "value" : "Infuzní set vyměněn" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster: ikke nok dage" + "value" : "Infusionssæt skiftet" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tagesprofil: nicht genug Tage" + "value" : "Infusionsset gewechselt" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Patrón diario: no hay suficientes días" + "value" : "Equipo de infusión cambiado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Vuorokausiprofiili: liian vähän päiviä" + "value" : "Infuusiosetti vaihdettu" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Profil journalier : pas assez de jours" + "value" : "Cathéter changé" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "דפוס יומי: אין מספיק ימים" + "value" : "סט העירוי הוחלף" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Napi mintázat: nincs elég nap" + "value" : "Infúziós szerelék cserélve" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Andamento giornaliero: giorni insufficienti" + "value" : "Set di infusione cambiato" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "日内パターン:日数が不足しています" + "value" : "インフュージョンセット交換" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster: ikke nok dager" + "value" : "Infusjonssett byttet" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dagpatroon: niet genoeg dagen" + "value" : "Infuusset vervangen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Profil dobowy: za mało dni" + "value" : "Wymieniono zestaw infuzyjny" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Padrão diário: dias insuficientes" + "value" : "Conjunto de infusão trocado" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Tipar zilnic: prea puține zile" + "value" : "Set de infuzie schimbat" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Суточный профиль: недостаточно дней" + "value" : "Инфузионный набор заменён" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Denný profil: málo dní" + "value" : "Infúzny set vymenený" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Dygnsmönster: för få dagar" + "value" : "Infusionsset bytt" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Günlük örüntü: yeterli gün yok" + "value" : "İnfüzyon seti değiştirildi" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Добовий профіль: замало днів" + "value" : "Інфузійний набір замінено" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mẫu hằng ngày: không đủ số ngày" + "value" : "Đã thay bộ truyền" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日模式:天数不足" + "value" : "已更换输注管路" } } } }, - "Day" : { - "comment" : "Appearance option and app icon name: the light (daytime) version", + "Insulin per day" : { + "comment" : "Trends tile and card title: average total insulin per day", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "نهار" + "value" : "الأنسولين يوميًا" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Den" + "value" : "Inzulín za den" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Insulin pr. dag" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tag" + "value" : "Insulin pro Tag" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Día" + "value" : "Insulina por día" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Päivä" + "value" : "Insuliini päivässä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Jour" + "value" : "Insuline par jour" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "יום" + "value" : "אינסולין ליום" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nap" + "value" : "Inzulin naponta" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Giorno" + "value" : "Insulina al giorno" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "日" + "value" : "1日のインスリン量" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Insulin per dag" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Insuline per dag" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dzień" + "value" : "Insulina dziennie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Dia" + "value" : "Insulina por dia" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Zi" + "value" : "Insulină pe zi" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "День" + "value" : "Инсулин в день" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Deň" + "value" : "Inzulín za deň" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Insulin per dag" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Gündüz" + "value" : "Günlük insülin" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "День" + "value" : "Інсулін на день" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Ngày" + "value" : "Insulin mỗi ngày" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "日" + "value" : "每日胰岛素" } } } }, - "Day (Trends period)" : { - "comment" : "Trends period picker: a period of one calendar day (not daytime)", + "Insulin per day, average %1$@" : { + "comment" : "VoiceOver label for the Trends Insulin per day chart. (1: average insulin per day, e.g. 32.1 U)", "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Day" - } - }, "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يوم" + "value" : "الأنسولين يوميًا، المتوسط %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Den" + "value" : "Inzulín za den, průměr %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Insulin pr. dag, gennemsnit %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tag" + "value" : "Insulin pro Tag, Durchschnitt %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Día" + "value" : "Insulina por día, promedio %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Päivä" + "value" : "Insuliini päivässä, keskiarvo %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Jour" + "value" : "Insuline par jour, moyenne %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "יום" + "value" : "אינסולין ליום, ממוצע %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nap" + "value" : "Inzulin naponta, átlag %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Giorno" + "value" : "Insulina al giorno, media %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "日" + "value" : "1日のインスリン量、平均 %1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Insulin per dag, gjennomsnitt %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Insuline per dag, gemiddeld %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dzień" + "value" : "Insulina dziennie, średnio %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Dia" + "value" : "Insulina por dia, média %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Zi" + "value" : "Insulină pe zi, medie %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "День" + "value" : "Инсулин в день, в среднем %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Deň" + "value" : "Inzulín za deň, priemer %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Insulin per dag, medel %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Gün" + "value" : "Günlük insülin, ortalama %1$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "День" + "value" : "Інсулін на день, у середньому %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Ngày" + "value" : "Insulin mỗi ngày, trung bình %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "日" + "value" : "每日胰岛素,平均 %1$@" } } } }, - "Export" : { - "comment" : "Trends menu title: export the period as a PDF report or CSV file", + "Insulin per day, weekly average, average %1$@" : { + "comment" : "VoiceOver label for the Trends Insulin per day chart when bars are weekly averages. (1: average insulin per day, e.g. 32.1 U)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تصدير" + "value" : "الأنسولين يوميًا، متوسط أسبوعي، المتوسط %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Exportovat" + "value" : "Inzulín za den, týdenní průměr, průměr %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Eksporter" + "value" : "Insulin pr. dag, ugegennemsnit, gennemsnit %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Exportieren" + "value" : "Insulin pro Tag, Wochendurchschnitt, Durchschnitt %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Exportar" + "value" : "Insulina por día, promedio semanal, promedio %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Vie" + "value" : "Insuliini päivässä, viikkokeskiarvo, keskiarvo %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Exporter" + "value" : "Insuline par jour, moyenne hebdomadaire, moyenne %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ייצוא" + "value" : "אינסולין ליום, ממוצע שבועי, ממוצע %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Exportálás" + "value" : "Inzulin naponta, heti átlag, átlag %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Esporta" + "value" : "Insulina al giorno, media settimanale, media %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "書き出し" + "value" : "1日のインスリン量、週平均、平均 %1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Eksporter" + "value" : "Insulin per dag, ukesgjennomsnitt, gjennomsnitt %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Exporteer" + "value" : "Insuline per dag, weekgemiddelde, gemiddeld %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Eksportuj" + "value" : "Insulina dziennie, średnia tygodniowa, średnio %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Exportar" + "value" : "Insulina por dia, média semanal, média %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Exportați" + "value" : "Insulină pe zi, medie săptămânală, medie %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Экспорт" + "value" : "Инсулин в день, среднее за неделю, в среднем %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Exportovať" + "value" : "Inzulín za deň, týždenný priemer, priemer %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Exportera" + "value" : "Insulin per dag, veckomedel, medel %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Dışa Aktar" + "value" : "Günlük insülin, haftalık ortalama, ortalama %1$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Експорт" + "value" : "Інсулін на день, середнє за тиждень, у середньому %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Xuất" + "value" : "Insulin mỗi ngày, trung bình theo tuần, trung bình %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "导出" + "value" : "每日胰岛素,按周平均,平均 %1$@" } } } }, - "From Apple Health: Loop keeps 7 days, so this day has no temp basal or schedule detail." : { - "comment" : "Day page note when the day is older than Loop's 7-day history and was read from Apple Health", + "Insulin timeline, %1$@, bolus %2$@, basal %3$@, carbs %4$@" : { + "comment" : "VoiceOver summary of the day's insulin timeline chart with no glucose readings. (1: weekday and date; 2: bolus total, e.g. 12.5 U; 3: basal total; 4: carbs total, e.g. 140 g)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "من Apple Health: يحتفظ Loop بـ 7 أيام فقط، لذا لا توجد تفاصيل Temp Basal أو الجدول لهذا اليوم." + "value" : "مخطط الأنسولين، %1$@، Bolus %2$@، القاعدية %3$@، الكارب %4$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Z aplikace Zdraví: Loop uchovává 7 dní, takže pro tento den chybí podrobnosti o dočasném bazálu a plánu." + "value" : "Časová osa inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Fra Apple Sundhed: Loop gemmer 7 dage, så denne dag har ingen detaljer om midlertidig basal eller tidsplan." + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kulhydrater %4$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aus Apple Health: Loop speichert 7 Tage, daher gibt es für diesen Tag keine Details zu temporären Basalraten oder zum Zeitplan." + "value" : "Insulin-Zeitleiste, %1$@, Bolus %2$@, Basal %3$@, KH %4$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "De Apple Salud: Loop guarda 7 días, así que este día no tiene detalle de basal temporal ni de programación." + "value" : "Cronología de insulina, %1$@, bolo %2$@, basal %3$@, carbohidratos %4$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Apple Terveydestä: Loop säilyttää 7 päivää, joten tältä päivältä ei ole tilapäisen basaalin tai ohjelman tietoja." + "value" : "Insuliinin aikajana, %1$@, bolus %2$@, basaali %3$@, hiilihydraatit %4$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Depuis Apple Santé : Loop conserve 7 jours, donc ce jour n'a pas de détail de basal temp. ni de programmation." + "value" : "Chronologie de l'insuline, %1$@, bolus %2$@, basal %3$@, glucides %4$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מ-Apple Health: Loop שומר 7 ימים, ולכן אין ליום זה פרטי Temp Basal או תוכנית." + "value" : "ציר זמן אינסולין, %1$@, Bolus %2$@, Basal %3$@, פחמימות %4$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Az Apple Egészség appból: a Loop 7 napot őriz meg, így ehhez a naphoz nincsenek ideiglenes basal- és ütemezési részletek." + "value" : "Inzulin idővonal, %1$@, bólus %2$@, basal %3$@, szénhidrát %4$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Da Apple Salute: Loop conserva 7 giorni, quindi questo giorno non ha dettagli su basale temporanea o programmazione." + "value" : "Cronologia insulina, %1$@, bolo %2$@, basale %3$@, carboidrati %4$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Appleヘルスケアから取得:Loopは7日間のみ保持するため、この日には一時ベーサルやスケジュールの詳細がありません。" + "value" : "インスリンのタイムライン、%1$@、ボーラス %2$@、ベーサル %3$@、カーボ %4$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Fra Apple Helse: Loop lagrer 7 dager, så denne dagen har ingen detaljer om midlertidig basal eller tidsplan." + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, karbohydrater %4$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Uit Apple Health: Loop bewaart 7 dagen, dus deze dag heeft geen details over tijdelijk basaal of het schema." + "value" : "Insulinetijdlijn, %1$@, bolus %2$@, basaal %3$@, koolhydraten %4$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Z aplikacji Zdrowie: Loop przechowuje 7 dni, więc dla tego dnia brak szczegółów bazy tymczasowej i harmonogramu." + "value" : "Oś czasu insuliny, %1$@, bolus %2$@, baza %3$@, węglowodany %4$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Do app Saúde: o Loop guarda 7 dias, então este dia não tem detalhes de basal temporária nem da programação." + "value" : "Linha do tempo de insulina, %1$@, bolus %2$@, basal %3$@, carbs %4$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Din Apple Sănătate: Loop păstrează 7 zile, așa că această zi nu are detalii despre bazala temporară sau program." + "value" : "Cronologie insulină, %1$@, bolus %2$@, bazală %3$@, carbohidrați %4$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Из приложения «Здоровье»: Loop хранит 7 дней, поэтому для этого дня нет данных о временном базале и расписании." + "value" : "Хронология инсулина, %1$@, болюс %2$@, базал %3$@, углеводы %4$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Z aplikácie Zdravie: Loop uchováva 7 dní, preto pre tento deň chýbajú podrobnosti o dočasnom bazáli a pláne." + "value" : "Časová os inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Från Apple Hälsa: Loop sparar 7 dagar, så den här dagen saknar detaljer om temporär basal och schema." + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kolhydrater %4$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Apple Health'ten: Loop 7 gün saklar, bu nedenle bu gün için geçici bazal veya program ayrıntısı yok." + "value" : "İnsülin zaman çizelgesi, %1$@, bolus %2$@, bazal %3$@, karb %4$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "З програми «Здоровʼя»: Loop зберігає 7 днів, тому для цього дня немає даних про тимчасовий базал і розклад." + "value" : "Хронологія інсуліну, %1$@, болюс %2$@, базал %3$@, вуглеводи %4$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Từ Apple Health: Loop chỉ lưu 7 ngày, nên ngày này không có chi tiết Temp Basal hoặc lịch biểu." + "value" : "Dòng thời gian insulin, %1$@, Bolus %2$@, Basal %3$@, Carbs %4$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "来自 Apple 健康:Loop 只保留 7 天,因此这一天没有临时基础率或预设详情。" + "value" : "胰岛素时间线,%1$@,大剂量 %2$@,基础 %3$@,碳水 %4$@" } } } }, - "Gaps" : { - "comment" : "Trends Loop status button and screen title: list of times Loop wasn't in closed loop", + "Insulin timeline, %1$@, bolus %2$@, basal %3$@, carbs %4$@, latest glucose %5$@ %6$@" : { + "comment" : "VoiceOver summary of the day's insulin timeline chart. (1: weekday and date; 2: bolus total, e.g. 12.5 U; 3: basal total; 4: carbs total, e.g. 140 g; 5: latest glucose value; 6: glucose unit, mmol/L or mg/dL)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الفجوات" + "value" : "مخطط الأنسولين، %1$@، Bolus %2$@، القاعدية %3$@، الكارب %4$@، آخر قراءة سكر %5$@ %6$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Výpadky" + "value" : "Časová osa inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@, poslední glukóza %5$@ %6$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Huller" + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kulhydrater %4$@, seneste glukose %5$@ %6$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Lücken" + "value" : "Insulin-Zeitleiste, %1$@, Bolus %2$@, Basal %3$@, KH %4$@, letzter Blutzucker %5$@ %6$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Interrupciones" + "value" : "Cronología de insulina, %1$@, bolo %2$@, basal %3$@, carbohidratos %4$@, última glucosa %5$@ %6$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Katkot" + "value" : "Insuliinin aikajana, %1$@, bolus %2$@, basaali %3$@, hiilihydraatit %4$@, viimeisin glukoosi %5$@ %6$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Interruptions" + "value" : "Chronologie de l'insuline, %1$@, bolus %2$@, basal %3$@, glucides %4$@, dernière glycémie %5$@ %6$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "פערים" + "value" : "ציר זמן אינסולין, %1$@, Bolus %2$@, Basal %3$@, פחמימות %4$@, סוכר אחרון %5$@ %6$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Kimaradások" + "value" : "Inzulin idővonal, %1$@, bólus %2$@, basal %3$@, szénhidrát %4$@, legutóbbi glükóz %5$@ %6$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Interruzioni" + "value" : "Cronologia insulina, %1$@, bolo %2$@, basale %3$@, carboidrati %4$@, ultima glicemia %5$@ %6$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "中断" + "value" : "インスリンのタイムライン、%1$@、ボーラス %2$@、ベーサル %3$@、カーボ %4$@、最新の血糖値 %5$@ %6$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Hull" + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, karbohydrater %4$@, siste blodsukker %5$@ %6$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Onderbrekingen" + "value" : "Insulinetijdlijn, %1$@, bolus %2$@, basaal %3$@, koolhydraten %4$@, laatste glucose %5$@ %6$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Przerwy" + "value" : "Oś czasu insuliny, %1$@, bolus %2$@, baza %3$@, węglowodany %4$@, ostatnia glukoza %5$@ %6$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Interrupções" + "value" : "Linha do tempo de insulina, %1$@, bolus %2$@, basal %3$@, carbs %4$@, última glicose %5$@ %6$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Întreruperi" + "value" : "Cronologie insulină, %1$@, bolus %2$@, bazală %3$@, carbohidrați %4$@, ultima glicemie %5$@ %6$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Пропуски" + "value" : "Хронология инсулина, %1$@, болюс %2$@, базал %3$@, углеводы %4$@, последняя глюкоза %5$@ %6$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Výpadky" + "value" : "Časová os inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@, posledná glykémia %5$@ %6$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Luckor" + "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kolhydrater %4$@, senaste glukos %5$@ %6$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Boşluklar" + "value" : "İnsülin zaman çizelgesi, %1$@, bolus %2$@, bazal %3$@, karb %4$@, son KŞ %5$@ %6$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Пропуски" + "value" : "Хронологія інсуліну, %1$@, болюс %2$@, базал %3$@, вуглеводи %4$@, остання глюкоза %5$@ %6$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Gián đoạn" + "value" : "Dòng thời gian insulin, %1$@, Bolus %2$@, Basal %3$@, Carbs %4$@, đường huyết mới nhất %5$@ %6$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "中断" + "value" : "胰岛素时间线,%1$@,大剂量 %2$@,基础 %3$@,碳水 %4$@,最新血糖 %5$@ %6$@" } } } }, - "Glucose too old" : { - "comment" : "Reason Loop could not run: the latest glucose reading was too old", + "Invalid future glucose" : { + "comment" : "Reason Loop could not run: a glucose reading had a timestamp in the future", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "قراءة السكر قديمة جدًا" + "value" : "قراءة سكر مستقبلية غير صالحة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Glukóza je příliš stará" + "value" : "Glukóza s budoucím časem" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Glukosemåling for gammel" + "value" : "Ugyldig glukosemåling i fremtiden" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Blutzuckerwert zu alt" + "value" : "Ungültiger Blutzuckerwert in der Zukunft" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Glucosa demasiado antigua" + "value" : "Glucosa futura no válida" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Glukoosi liian vanha" + "value" : "Virheellinen tulevaisuuden glukoosiarvo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Glycémie trop ancienne" + "value" : "Glycémie future non valide" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קריאת הסוכר ישנה מדי" + "value" : "קריאת סוכר עתידית לא תקינה" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Túl régi glükózérték" + "value" : "Érvénytelen, jövőbeli glükózérték" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Glicemia troppo vecchia" + "value" : "Glicemia futura non valida" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "血糖値が古すぎます" + "value" : "未来時刻の無効な血糖値" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Blodsukkermåling for gammel" + "value" : "Ugyldig blodsukkermåling i fremtiden" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Glucosewaarde te oud" + "value" : "Ongeldige glucosewaarde in de toekomst" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Glukoza zbyt stara" + "value" : "Glukoza z przyszłą datą" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Glicose muito antiga" + "value" : "Glicose futura inválida" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Glicemie prea veche" + "value" : "Glicemie invalidă, din viitor" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Данные глюкозы устарели" + "value" : "Глюкоза с временем в будущем" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Glykémia je príliš stará" + "value" : "Glykémia s budúcim časom" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Glukosvärdet är för gammalt" + "value" : "Ogiltigt glukosvärde i framtiden" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Kan şekeri verisi çok eski" + "value" : "Geçersiz, ileri tarihli KŞ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Дані глюкози застаріли" + "value" : "Глюкоза з часом у майбутньому" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Dữ liệu đường huyết quá cũ" + "value" : "Đường huyết có thời gian trong tương lai không hợp lệ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "血糖数据过旧" + "value" : "无效的未来血糖" } } } }, - "In range" : { - "comment" : "Time in range band: glucose 70–180 mg/dL (3.9–10.0 mmol/L) Time-in-range band name: glucose 70–180 mg/dL (3.9–10.0 mmol/L)", + "IOB" : { + "comment" : "Abbreviation for insulin on board (active insulin) Abbreviation for insulin on board; label of a tile on the Now screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ضمن النطاق" + "value" : "IOB" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "V rozsahu" + "value" : "IOB" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "I området" + "value" : "IOB" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Im Zielbereich" + "value" : "IOB" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "En rango" + "value" : "IOB" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Tavoitealueella" + "value" : "IOB" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Dans la cible" + "value" : "IA" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "בטווח" + "value" : "IOB" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Tartományban" + "value" : "IOB" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nell'intervallo" + "value" : "IOB" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "範囲内" + "value" : "IOB" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "I målområdet" + "value" : "IOB" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Binnen bereik" + "value" : "IOB" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "W zakresie" + "value" : "IOB" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "No alvo" + "value" : "IOB" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "În interval" + "value" : "IOB" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "В диапазоне" + "value" : "IOB" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "V rozsahu" + "value" : "IOB" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inom målområdet" + "value" : "IOB" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Aralıkta" + "value" : "IOB" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "У діапазоні" + "value" : "IOB" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Trong khoảng" + "value" : "IOB" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "范围内" + "value" : "IOB" } } } }, - "Insulin per day" : { - "comment" : "Trends tile and card title: average total insulin per day", + "IOB (U, left)" : { + "comment" : "Chart legend: insulin on board in units, on the left axis", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الأنسولين يوميًا" + "value" : "IOB (وحدة، يسار)" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Inzulín za den" + "value" : "IOB (U, vlevo)" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin pr. dag" + "value" : "IOB (E, venstre)" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin pro Tag" + "value" : "IOB (IE, links)" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina por día" + "value" : "IOB (U, izquierda)" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Insuliini päivässä" + "value" : "IOB (U, vasen)" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Insuline par jour" + "value" : "IA (U, gauche)" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אינסולין ליום" + "value" : "IOB (U, שמאל)" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Inzulin naponta" + "value" : "IOB (U, bal)" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina al giorno" + "value" : "IOB (U, sinistra)" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "1日のインスリン量" + "value" : "IOB(U、左)" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin per dag" + "value" : "IOB (E, venstre)" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Insuline per dag" + "value" : "IOB (E, links)" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina dziennie" + "value" : "IOB (J, lewa oś)" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina por dia" + "value" : "IOB (U, esquerda)" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Insulină pe zi" + "value" : "IOB (U, stânga)" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Инсулин в день" + "value" : "IOB (Ед, слева)" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Inzulín za deň" + "value" : "IOB (j, vľavo)" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin per dag" + "value" : "IOB (E, vänster)" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Günlük insülin" + "value" : "IOB (Ü, sol)" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Інсулін на день" + "value" : "IOB (U, ліворуч)" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin mỗi ngày" + "value" : "IOB (U, bên trái)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日胰岛素" + "value" : "IOB(U,左轴)" } } } }, - "Insulin per day, average %1$@" : { - "comment" : "VoiceOver label for the Trends Insulin per day chart. (1: average insulin per day, e.g. 32.1 U)", + "Limited" : { + "comment" : "Trends Loop status: time closed loop was on but Loop wasn't completing runs", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الأنسولين يوميًا، المتوسط %1$@" + "value" : "محدود" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Inzulín za den, průměr %1$@" + "value" : "Omezeno" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin pr. dag, gennemsnit %1$@" + "value" : "Begrænset" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin pro Tag, Durchschnitt %1$@" + "value" : "Eingeschränkt" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina por día, promedio %1$@" + "value" : "Limitado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Insuliini päivässä, keskiarvo %1$@" + "value" : "Rajoitettu" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Insuline par jour, moyenne %1$@" + "value" : "Limitée" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אינסולין ליום, ממוצע %1$@" + "value" : "מוגבל" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Inzulin naponta, átlag %1$@" + "value" : "Korlátozott" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina al giorno, media %1$@" + "value" : "Limitato" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "1日のインスリン量、平均 %1$@" + "value" : "制限あり" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin per dag, gjennomsnitt %1$@" + "value" : "Begrenset" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Insuline per dag, gemiddeld %1$@" + "value" : "Beperkt" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina dziennie, średnio %1$@" + "value" : "Ograniczona" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina por dia, média %1$@" + "value" : "Limitado" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Insulină pe zi, medie %1$@" + "value" : "Limitat" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Инсулин в день, в среднем %1$@" + "value" : "Ограничен" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Inzulín za deň, priemer %1$@" + "value" : "Obmedzená" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin per dag, medel %1$@" + "value" : "Begränsad" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Günlük insülin, ortalama %1$@" + "value" : "Sınırlı" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Інсулін на день, у середньому %1$@" + "value" : "Обмежено" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin mỗi ngày, trung bình %1$@" + "value" : "Hạn chế" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日胰岛素,平均 %1$@" + "value" : "受限" } } } }, - "Insulin per day, weekly average, average %1$@" : { - "comment" : "VoiceOver label for the Trends Insulin per day chart when bars are weekly averages. (1: average insulin per day, e.g. 32.1 U)", + "Logbook" : { + "comment" : "Title of the Logbook tab Title of the Logbook tab in the redesigned interface, listing past events", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الأنسولين يوميًا، متوسط أسبوعي، المتوسط %1$@" + "value" : "السجل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Inzulín za den, týdenní průměr, průměr %1$@" + "value" : "Deník" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin pr. dag, ugegennemsnit, gennemsnit %1$@" + "value" : "Logbog" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin pro Tag, Wochendurchschnitt, Durchschnitt %1$@" + "value" : "Logbuch" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina por día, promedio semanal, promedio %1$@" + "value" : "Registro" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Insuliini päivässä, viikkokeskiarvo, keskiarvo %1$@" + "value" : "Lokikirja" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Insuline par jour, moyenne hebdomadaire, moyenne %1$@" + "value" : "Journal" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אינסולין ליום, ממוצע שבועי, ממוצע %1$@" + "value" : "יומן" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Inzulin naponta, heti átlag, átlag %1$@" + "value" : "Napló" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina al giorno, media settimanale, media %1$@" + "value" : "Diario" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "1日のインスリン量、週平均、平均 %1$@" + "value" : "ログブック" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin per dag, ukesgjennomsnitt, gjennomsnitt %1$@" + "value" : "Loggbok" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Insuline per dag, weekgemiddelde, gemiddeld %1$@" + "value" : "Logboek" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina dziennie, średnia tygodniowa, średnio %1$@" + "value" : "Dziennik" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Insulina por dia, média semanal, média %1$@" + "value" : "Diário" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Insulină pe zi, medie săptămânală, medie %1$@" + "value" : "Jurnal" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Инсулин в день, среднее за неделю, в среднем %1$@" + "value" : "Журнал" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Inzulín za deň, týždenný priemer, priemer %1$@" + "value" : "Denník" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin per dag, veckomedel, medel %1$@" + "value" : "Loggbok" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Günlük insülin, haftalık ortalama, ortalama %1$@" + "value" : "Kayıt Defteri" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Інсулін на день, середнє за тиждень, у середньому %1$@" + "value" : "Журнал" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin mỗi ngày, trung bình theo tuần, trung bình %1$@" + "value" : "Nhật ký" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日胰岛素,按周平均,平均 %1$@" + "value" : "日志" } } } }, - "Insulin timeline, %1$@, bolus %2$@, basal %3$@, carbs %4$@" : { - "comment" : "VoiceOver summary of the day's insulin timeline chart with no glucose readings. (1: weekday and date; 2: bolus total, e.g. 12.5 U; 3: basal total; 4: carbs total, e.g. 140 g)", + "logged" : { + "comment" : "Logbook bolus row detail: the dose was logged by hand rather than delivered by the pump", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "مخطط الأنسولين، %1$@، Bolus %2$@، القاعدية %3$@، الكارب %4$@" + "value" : "مُسجَّل يدويًا" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Časová osa inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@" + "value" : "zadáno ručně" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kulhydrater %4$@" + "value" : "registreret manuelt" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin-Zeitleiste, %1$@, Bolus %2$@, Basal %3$@, KH %4$@" + "value" : "eingetragen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cronología de insulina, %1$@, bolo %2$@, basal %3$@, carbohidratos %4$@" + "value" : "registrado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Insuliinin aikajana, %1$@, bolus %2$@, basaali %3$@, hiilihydraatit %4$@" + "value" : "kirjattu" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Chronologie de l'insuline, %1$@, bolus %2$@, basal %3$@, glucides %4$@" + "value" : "saisi" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ציר זמן אינסולין, %1$@, Bolus %2$@, Basal %3$@, פחמימות %4$@" + "value" : "נרשם ידנית" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Inzulin idővonal, %1$@, bólus %2$@, basal %3$@, szénhidrát %4$@" + "value" : "naplózva" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cronologia insulina, %1$@, bolo %2$@, basale %3$@, carboidrati %4$@" + "value" : "registrato" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "インスリンのタイムライン、%1$@、ボーラス %2$@、ベーサル %3$@、カーボ %4$@" + "value" : "手動記録" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, karbohydrater %4$@" + "value" : "registrert manuelt" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Insulinetijdlijn, %1$@, bolus %2$@, basaal %3$@, koolhydraten %4$@" + "value" : "handmatig vastgelegd" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Oś czasu insuliny, %1$@, bolus %2$@, baza %3$@, węglowodany %4$@" + "value" : "wpisany ręcznie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Linha do tempo de insulina, %1$@, bolus %2$@, basal %3$@, carbs %4$@" + "value" : "registrado" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Cronologie insulină, %1$@, bolus %2$@, bazală %3$@, carbohidrați %4$@" + "value" : "înregistrat manual" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Хронология инсулина, %1$@, болюс %2$@, базал %3$@, углеводы %4$@" + "value" : "внесено вручную" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Časová os inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@" + "value" : "zadané ručne" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kolhydrater %4$@" + "value" : "loggad" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "İnsülin zaman çizelgesi, %1$@, bolus %2$@, bazal %3$@, karb %4$@" + "value" : "elle kaydedildi" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Хронологія інсуліну, %1$@, болюс %2$@, базал %3$@, вуглеводи %4$@" + "value" : "внесено вручну" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Dòng thời gian insulin, %1$@, Bolus %2$@, Basal %3$@, Carbs %4$@" + "value" : "ghi tay" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "胰岛素时间线,%1$@,大剂量 %2$@,基础 %3$@,碳水 %4$@" + "value" : "手动记录" } } } }, - "Insulin timeline, %1$@, bolus %2$@, basal %3$@, carbs %4$@, latest glucose %5$@ %6$@" : { - "comment" : "VoiceOver summary of the day's insulin timeline chart. (1: weekday and date; 2: bolus total, e.g. 12.5 U; 3: basal total; 4: carbs total, e.g. 140 g; 5: latest glucose value; 6: glucose unit, mmol/L or mg/dL)", + "Loop" : { + "comment" : "Logbook filter chip: show only what Loop did automatically (Loop is the app's name) Logbook row detail: Loop made this change automatically (Loop is the app's name)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "مخطط الأنسولين، %1$@، Bolus %2$@، القاعدية %3$@، الكارب %4$@، آخر قراءة سكر %5$@ %6$@" + "value" : "Loop" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Časová osa inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@, poslední glukóza %5$@ %6$@" + "value" : "Loop" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kulhydrater %4$@, seneste glukose %5$@ %6$@" + "value" : "Loop" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Insulin-Zeitleiste, %1$@, Bolus %2$@, Basal %3$@, KH %4$@, letzter Blutzucker %5$@ %6$@" + "value" : "Loop" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cronología de insulina, %1$@, bolo %2$@, basal %3$@, carbohidratos %4$@, última glucosa %5$@ %6$@" + "value" : "Loop" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Insuliinin aikajana, %1$@, bolus %2$@, basaali %3$@, hiilihydraatit %4$@, viimeisin glukoosi %5$@ %6$@" + "value" : "Loop" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Chronologie de l'insuline, %1$@, bolus %2$@, basal %3$@, glucides %4$@, dernière glycémie %5$@ %6$@" + "value" : "Loop" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ציר זמן אינסולין, %1$@, Bolus %2$@, Basal %3$@, פחמימות %4$@, סוכר אחרון %5$@ %6$@" + "value" : "Loop" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Inzulin idővonal, %1$@, bólus %2$@, basal %3$@, szénhidrát %4$@, legutóbbi glükóz %5$@ %6$@" + "value" : "Loop" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cronologia insulina, %1$@, bolo %2$@, basale %3$@, carboidrati %4$@, ultima glicemia %5$@ %6$@" + "value" : "Loop" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "インスリンのタイムライン、%1$@、ボーラス %2$@、ベーサル %3$@、カーボ %4$@、最新の血糖値 %5$@ %6$@" + "value" : "Loop" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, karbohydrater %4$@, siste blodsukker %5$@ %6$@" + "value" : "Loop" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Insulinetijdlijn, %1$@, bolus %2$@, basaal %3$@, koolhydraten %4$@, laatste glucose %5$@ %6$@" + "value" : "Loop" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Oś czasu insuliny, %1$@, bolus %2$@, baza %3$@, węglowodany %4$@, ostatnia glukoza %5$@ %6$@" + "value" : "Loop" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Linha do tempo de insulina, %1$@, bolus %2$@, basal %3$@, carbs %4$@, última glicose %5$@ %6$@" + "value" : "Loop" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Cronologie insulină, %1$@, bolus %2$@, bazală %3$@, carbohidrați %4$@, ultima glicemie %5$@ %6$@" + "value" : "Loop" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Хронология инсулина, %1$@, болюс %2$@, базал %3$@, углеводы %4$@, последняя глюкоза %5$@ %6$@" + "value" : "Loop" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Časová os inzulínu, %1$@, bolus %2$@, bazál %3$@, sacharidy %4$@, posledná glykémia %5$@ %6$@" + "value" : "Loop" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Insulintidslinje, %1$@, bolus %2$@, basal %3$@, kolhydrater %4$@, senaste glukos %5$@ %6$@" + "value" : "Loop" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "İnsülin zaman çizelgesi, %1$@, bolus %2$@, bazal %3$@, karb %4$@, son KŞ %5$@ %6$@" + "value" : "Loop" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Хронологія інсуліну, %1$@, болюс %2$@, базал %3$@, вуглеводи %4$@, остання глюкоза %5$@ %6$@" + "value" : "Loop" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Dòng thời gian insulin, %1$@, Bolus %2$@, Basal %3$@, Carbs %4$@, đường huyết mới nhất %5$@ %6$@" + "value" : "Loop" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "胰岛素时间线,%1$@,大剂量 %2$@,基础 %3$@,碳水 %4$@,最新血糖 %5$@ %6$@" + "value" : "Loop" } } } }, - "Invalid future glucose" : { - "comment" : "Reason Loop could not run: a glucose reading had a timestamp in the future", + "Loop active" : { + "comment" : "Trends summary tile: share of the period Loop spent in closed loop", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "قراءة سكر مستقبلية غير صالحة" + "value" : "Loop نشط" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Glukóza s budoucím časem" + "value" : "Loop aktivní" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Ugyldig glukosemåling i fremtiden" + "value" : "Loop aktiv" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ungültiger Blutzuckerwert in der Zukunft" + "value" : "Loop aktiv" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Glucosa futura no válida" + "value" : "Loop activo" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Virheellinen tulevaisuuden glukoosiarvo" + "value" : "Loop aktiivinen" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Glycémie future non valide" + "value" : "Loop actif" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קריאת סוכר עתידית לא תקינה" + "value" : "Loop פעיל" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Érvénytelen, jövőbeli glükózérték" + "value" : "Loop aktív" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Glicemia futura non valida" + "value" : "Loop attivo" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "未来時刻の無効な血糖値" + "value" : "Loop稼働" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ugyldig blodsukkermåling i fremtiden" + "value" : "Loop aktiv" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ongeldige glucosewaarde in de toekomst" + "value" : "Loop actief" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Glukoza z przyszłą datą" + "value" : "Loop aktywny" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Glicose futura inválida" + "value" : "Loop ativo" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Glicemie invalidă, din viitor" + "value" : "Loop activ" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Глюкоза с временем в будущем" + "value" : "Loop активен" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Glykémia s budúcim časom" + "value" : "Loop aktívny" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ogiltigt glukosvärde i framtiden" + "value" : "Loop aktiv" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Geçersiz, ileri tarihli KŞ" + "value" : "Loop aktif" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Глюкоза з часом у майбутньому" + "value" : "Loop активний" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đường huyết có thời gian trong tương lai không hợp lệ" + "value" : "Loop hoạt động" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "无效的未来血糖" + "value" : "Loop 运行" } } } }, - "IOB" : { - "comment" : "Abbreviation for insulin on board (active insulin) Abbreviation for insulin on board; label of a tile on the Now screen", + "Loop didn't run" : { + "comment" : "Reason shown for a stretch of time when Loop did not complete a run and recorded no error", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "لم يعمل Loop" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop neproběhl" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop kørte ikke" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop ist nicht gelaufen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop no se ejecutó" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop ei ajanut" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "IA" + "value" : "Loop ne s'est pas exécuté" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop לא רץ" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "A Loop nem futott" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop non eseguito" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loopが実行されませんでした" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop kjørte ikke" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop heeft niet gedraaid" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop nie działał" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "O Loop não executou" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop nu a rulat" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop не запускался" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop neprebehol" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop kördes inte" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop çalışmadı" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop не запускався" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop không chạy" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "IOB" + "value" : "Loop 未运行" } } } }, - "IOB (U, left)" : { - "comment" : "Chart legend: insulin on board in units, on the left axis", + "Loop keeps the last 7 days on this phone. Older doses and carbs are in Apple Health and on Trends." : { + "comment" : "Logbook footer explaining why only 7 days are listed", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (وحدة، يسار)" + "value" : "يحتفظ Loop بآخر 7 أيام على هذا الهاتف. الجرعات والكارب الأقدم موجودة في Apple Health وفي الاتجاهات." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (U, vlevo)" + "value" : "Loop uchovává posledních 7 dní v tomto telefonu. Starší dávky a sacharidy najdete v aplikaci Zdraví a v Trendech." } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (E, venstre)" + "value" : "Loop gemmer de seneste 7 dage på denne telefon. Ældre doser og kulhydrater findes i Apple Sundhed og under Tendenser." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (IE, links)" + "value" : "Loop speichert die letzten 7 Tage auf diesem iPhone. Ältere Dosen und KH findest Du in Apple Health und unter Trends." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (U, izquierda)" + "value" : "Loop guarda los últimos 7 días en este teléfono. Las dosis y carbohidratos anteriores están en Apple Salud y en Tendencias." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (U, vasen)" + "value" : "Loop säilyttää viimeiset 7 päivää tässä puhelimessa. Vanhemmat annokset ja hiilihydraatit ovat Apple Terveydessä ja Trendeissä." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "IA (U, gauche)" + "value" : "Loop conserve les 7 derniers jours sur ce téléphone. Les doses et glucides plus anciens sont dans Apple Santé et dans Tendances." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (U, שמאל)" + "value" : "Loop שומר את 7 הימים האחרונים בטלפון זה. מנות ופחמימות ישנות יותר נמצאות ב-Apple Health ובמגמות." } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (U, bal)" + "value" : "A Loop az utolsó 7 napot őrzi meg ezen a telefonon. A régebbi adagok és szénhidrátok az Apple Egészség appban és a Trendekben találhatók." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (U, sinistra)" + "value" : "Loop conserva gli ultimi 7 giorni su questo telefono. Dosi e carboidrati precedenti sono in Apple Salute e in Tendenze." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "IOB(U、左)" + "value" : "Loopはこの端末に直近7日間を保持します。それより前の投与とカーボはAppleヘルスケアとトレンドにあります。" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (E, venstre)" + "value" : "Loop lagrer de siste 7 dagene på denne telefonen. Eldre doser og karbohydrater finner du i Apple Helse og under Trender." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (E, links)" + "value" : "Loop bewaart de laatste 7 dagen op deze telefoon. Oudere doses en koolhydraten vind je in Apple Health en bij Trends." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (J, lewa oś)" + "value" : "Loop przechowuje ostatnie 7 dni na tym telefonie. Starsze dawki i węglowodany są w aplikacji Zdrowie i w Trendach." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (U, esquerda)" + "value" : "O Loop guarda os últimos 7 dias neste iPhone. Doses e carbs mais antigos estão no app Saúde e em Tendências." } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (U, stânga)" + "value" : "Loop păstrează ultimele 7 zile pe acest telefon. Dozele și carbohidrații mai vechi sunt în Apple Sănătate și în Tendințe." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (Ед, слева)" + "value" : "Loop хранит последние 7 дней на этом телефоне. Более ранние дозы и углеводы — в приложении «Здоровье» и в разделе «Тренды»." } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (j, vľavo)" + "value" : "Loop uchováva posledných 7 dní v tomto telefóne. Staršie dávky a sacharidy nájdete v aplikácii Zdravie a v Trendoch." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (E, vänster)" + "value" : "Loop sparar de senaste 7 dagarna på den här telefonen. Äldre doser och kolhydrater finns i Apple Hälsa och under Trender." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (Ü, sol)" + "value" : "Loop son 7 günü bu telefonda saklar. Daha eski dozlar ve karbonhidratlar Apple Health'te ve Eğilimler'dedir." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (U, ліворуч)" + "value" : "Loop зберігає останні 7 днів на цьому телефоні. Старіші дози та вуглеводи є в програмі «Здоровʼя» і в розділі «Тренди»." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "IOB (U, bên trái)" + "value" : "Loop lưu 7 ngày gần nhất trên điện thoại này. Các liều và Carbs cũ hơn có trong Apple Health và trong Xu hướng." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "IOB(U,左轴)" + "value" : "Loop 在此手机上保留最近 7 天的数据。更早的剂量和碳水可在 Apple 健康和“趋势”中查看。" } } } }, - "Limited" : { - "comment" : "Trends Loop status: time closed loop was on but Loop wasn't completing runs", + "Loop only works out active insulin and carbs for the days it keeps" : { + "comment" : "Day page placeholder for active insulin and carbs on a day older than Loop's 7-day history", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "محدود" + "value" : "يحسب Loop الأنسولين والكارب النشطين فقط للأيام التي يحتفظ بها" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Omezeno" + "value" : "Loop počítá aktivní inzulín a sacharidy jen pro dny, které uchovává" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Begrænset" + "value" : "Loop beregner kun aktivt insulin og kulhydrater for de dage, den gemmer" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Eingeschränkt" + "value" : "Loop berechnet aktives Insulin und aktive KH nur für die gespeicherten Tage" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Limitado" + "value" : "Loop solo calcula la insulina y los carbohidratos activos de los días que guarda" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Rajoitettu" + "value" : "Loop laskee aktiivisen insuliinin ja hiilihydraatit vain säilyttämilleen päiville" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Limitée" + "value" : "Loop calcule l'insuline et les glucides actifs uniquement pour les jours qu'il conserve" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מוגבל" + "value" : "Loop מחשב אינסולין ופחמימות פעילים רק עבור הימים שהוא שומר" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Korlátozott" + "value" : "A Loop csak a megőrzött napokra számolja ki az aktív inzulint és szénhidrátot" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Limitato" + "value" : "Loop calcola insulina e carboidrati attivi solo per i giorni che conserva" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "制限あり" + "value" : "Loopが残存インスリンと残存糖質を計算するのは、保持している日のみです" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Begrenset" + "value" : "Loop beregner bare aktivt insulin og karbohydrater for dagene den lagrer" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Beperkt" + "value" : "Loop berekent actieve insuline en koolhydraten alleen voor de dagen die het bewaart" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ograniczona" + "value" : "Loop oblicza aktywną insulinę i węglowodany tylko dla przechowywanych dni" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Limitado" + "value" : "O Loop só calcula a insulina e os carboidratos ativos dos dias que guarda" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Limitat" + "value" : "Loop calculează insulina și carbohidrații activi doar pentru zilele pe care le păstrează" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Ограничен" + "value" : "Loop рассчитывает активный инсулин и углеводы только за хранимые дни" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Obmedzená" + "value" : "Loop počíta aktívny inzulín a sacharidy len pre dni, ktoré uchováva" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Begränsad" + "value" : "Loop beräknar bara aktivt insulin och kolhydrater för de dagar som sparas" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Sınırlı" + "value" : "Loop aktif insülini ve karbı yalnızca sakladığı günler için hesaplar" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Обмежено" + "value" : "Loop розраховує активний інсулін і вуглеводи лише за дні, які зберігає" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hạn chế" + "value" : "Loop chỉ tính insulin và Carbs còn hoạt động cho những ngày nó lưu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "受限" + "value" : "Loop 只为其保留的日期计算活性胰岛素和碳水" } } } }, - "Loop active" : { - "comment" : "Trends summary tile: share of the period Loop spent in closed loop", + "Loop status" : { + "comment" : "Trends card title: how long Loop spent in closed loop, limited or open loop", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "Loop نشط" + "value" : "حالة Loop" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktivní" + "value" : "Stav Loop" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktiv" + "value" : "Loop-status" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktiv" + "value" : "Loop-Status" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Loop activo" + "value" : "Estado de Loop" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktiivinen" + "value" : "Loopin tila" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop actif" + "value" : "État de Loop" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Loop פעיל" + "value" : "מצב Loop" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktív" + "value" : "Loop állapota" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Loop attivo" + "value" : "Stato di Loop" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loop稼働" + "value" : "Loopの状態" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktiv" + "value" : "Loop-status" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop actief" + "value" : "Loopstatus" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktywny" + "value" : "Stan Loop" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Loop ativo" + "value" : "Status do Loop" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Loop activ" + "value" : "Starea Loop" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Loop активен" + "value" : "Статус Loop" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktívny" + "value" : "Stav Loop" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktiv" + "value" : "Loopstatus" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktif" + "value" : "Loop durumu" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop активний" + "value" : "Стан Loop" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop hoạt động" + "value" : "Trạng thái Loop" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 运行" + "value" : "Loop 状态" } } } }, - "Loop didn't run" : { - "comment" : "Reason shown for a stretch of time when Loop did not complete a run and recorded no error", + "Loop status is recorded from when this version of Loop first ran." : { + "comment" : "Trends Loop status footnote when there is no Loop status history yet", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لم يعمل Loop" + "value" : "تُسجَّل حالة Loop منذ أول تشغيل لهذا الإصدار من Loop." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Loop neproběhl" + "value" : "Stav Loop se zaznamenává od prvního spuštění této verze Loop." } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop kørte ikke" + "value" : "Loop-status registreres fra første gang denne version af Loop kørte." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Loop ist nicht gelaufen" + "value" : "Der Loop-Status wird ab dem ersten Start dieser Loop-Version aufgezeichnet." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Loop no se ejecutó" + "value" : "El estado de Loop se registra desde la primera vez que se ejecutó esta versión de Loop." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop ei ajanut" + "value" : "Loopin tila tallennetaan siitä lähtien, kun tämä Loopin versio käynnistettiin ensimmäisen kerran." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop ne s'est pas exécuté" + "value" : "L'état de Loop est enregistré depuis le premier lancement de cette version de Loop." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Loop לא רץ" + "value" : "מצב Loop נרשם מאז ההפעלה הראשונה של גרסה זו של Loop." } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "A Loop nem futott" + "value" : "A Loop állapota a Loop ezen verziójának első futtatásától kerül rögzítésre." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Loop non eseguito" + "value" : "Lo stato di Loop è registrato dal primo avvio di questa versione di Loop." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loopが実行されませんでした" + "value" : "Loopの状態は、このバージョンのLoopを初めて起動した時点から記録されます。" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop kjørte ikke" + "value" : "Loop-status registreres fra første gang denne versjonen av Loop kjørte." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop heeft niet gedraaid" + "value" : "De loopstatus wordt vastgelegd vanaf de eerste keer dat deze versie van Loop draaide." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop nie działał" + "value" : "Stan Loop jest zapisywany od pierwszego uruchomienia tej wersji Loop." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "O Loop não executou" + "value" : "O status do Loop é registrado desde a primeira vez que esta versão do Loop foi executada." } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Loop nu a rulat" + "value" : "Starea Loop este înregistrată de la prima rulare a acestei versiuni de Loop." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Loop не запускался" + "value" : "Статус Loop записывается с первого запуска этой версии Loop." } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop neprebehol" + "value" : "Stav Loop sa zaznamenáva od prvého spustenia tejto verzie Loop." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loop kördes inte" + "value" : "Loopstatus registreras från när den här versionen av Loop först kördes." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop çalışmadı" + "value" : "Loop durumu, Loop'un bu sürümünün ilk çalıştığı andan itibaren kaydedilir." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop не запускався" + "value" : "Стан Loop записується з першого запуску цієї версії Loop." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop không chạy" + "value" : "Trạng thái Loop được ghi lại từ khi phiên bản Loop này chạy lần đầu." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 未运行" + "value" : "Loop 状态从此版本 Loop 首次运行时开始记录。" } } } }, - "Loop only works out active insulin and carbs for the days it keeps" : { - "comment" : "Day page placeholder for active insulin and carbs on a day older than Loop's 7-day history", + "Loop trends" : { + "comment" : "Title of the exported Trends PDF report", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يحسب Loop الأنسولين والكارب النشطين فقط للأيام التي يحتفظ بها" + "value" : "اتجاهات Loop" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Loop počítá aktivní inzulín a sacharidy jen pro dny, které uchovává" + "value" : "Trendy Loop" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop beregner kun aktivt insulin og kulhydrater for de dage, den gemmer" + "value" : "Loop-tendenser" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Loop berechnet aktives Insulin und aktive KH nur für die gespeicherten Tage" + "value" : "Loop-Trends" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Loop solo calcula la insulina y los carbohidratos activos de los días que guarda" + "value" : "Tendencias de Loop" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop laskee aktiivisen insuliinin ja hiilihydraatit vain säilyttämilleen päiville" + "value" : "Loop-trendit" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop calcule l'insuline et les glucides actifs uniquement pour les jours qu'il conserve" + "value" : "Tendances Loop" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Loop מחשב אינסולין ופחמימות פעילים רק עבור הימים שהוא שומר" + "value" : "מגמות Loop" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "A Loop csak a megőrzött napokra számolja ki az aktív inzulint és szénhidrátot" + "value" : "Loop-trendek" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Loop calcola insulina e carboidrati attivi solo per i giorni che conserva" + "value" : "Tendenze Loop" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loopが残存インスリンと残存糖質を計算するのは、保持している日のみです" + "value" : "Loopトレンド" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop beregner bare aktivt insulin og karbohydrater for dagene den lagrer" + "value" : "Loop-trender" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop berekent actieve insuline en koolhydraten alleen voor de dagen die het bewaart" + "value" : "Loop-trends" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop oblicza aktywną insulinę i węglowodany tylko dla przechowywanych dni" + "value" : "Trendy Loop" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "O Loop só calcula a insulina e os carboidratos ativos dos dias que guarda" + "value" : "Tendências do Loop" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Loop calculează insulina și carbohidrații activi doar pentru zilele pe care le păstrează" + "value" : "Tendințe Loop" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Loop рассчитывает активный инсулин и углеводы только за хранимые дни" + "value" : "Тренды Loop" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop počíta aktívny inzulín a sacharidy len pre dni, ktoré uchováva" + "value" : "Trendy Loop" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loop beräknar bara aktivt insulin och kolhydrater för de dagar som sparas" + "value" : "Loop-trender" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktif insülini ve karbı yalnızca sakladığı günler için hesaplar" + "value" : "Loop eğilimleri" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop розраховує активний інсулін і вуглеводи лише за дні, які зберігає" + "value" : "Тренди Loop" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop chỉ tính insulin và Carbs còn hoạt động cho những ngày nó lưu" + "value" : "Xu hướng Loop" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 只为其保留的日期计算活性胰岛素和碳水" + "value" : "Loop 趋势" } } } }, - "Loop status" : { - "comment" : "Trends card title: how long Loop spent in closed loop, limited or open loop", + "Loop trends %1$@" : { + "comment" : "File name of an exported Trends report or CSV, without the extension. (1: date range of the period)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حالة Loop" + "value" : "اتجاهات Loop %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Stav Loop" + "value" : "Trendy Loop %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-status" + "value" : "Loop-tendenser %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-Status" + "value" : "Loop-Trends %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Estado de Loop" + "value" : "Tendencias de Loop %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loopin tila" + "value" : "Loop-trendit %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "État de Loop" + "value" : "Tendances Loop %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מצב Loop" + "value" : "מגמות Loop %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Loop állapota" + "value" : "Loop-trendek %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Stato di Loop" + "value" : "Tendenze Loop %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loopの状態" + "value" : "Loopトレンド %1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-status" + "value" : "Loop-trender %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loopstatus" + "value" : "Loop-trends %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Stan Loop" + "value" : "Trendy Loop %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Status do Loop" + "value" : "Tendências do Loop %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Starea Loop" + "value" : "Tendințe Loop %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Статус Loop" + "value" : "Тренды Loop %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Stav Loop" + "value" : "Trendy Loop %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loopstatus" + "value" : "Loop-trender %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop durumu" + "value" : "Loop eğilimleri %1$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Стан Loop" + "value" : "Тренди Loop %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Trạng thái Loop" + "value" : "Xu hướng Loop %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 状态" + "value" : "Loop 趋势 %1$@" } } } }, - "Loop status is recorded from when this version of Loop first ran." : { - "comment" : "Trends Loop status footnote when there is no Loop status history yet", + "Loop · %1$@ · tap to list" : { + "comment" : "Logbook folded automatic boluses row subtitle: Loop gave them, the time span they cover, and tapping lists them. (1: time range, e.g. 15:08 – 16:14)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تُسجَّل حالة Loop منذ أول تشغيل لهذا الإصدار من Loop." + "value" : "Loop · %1$@ · اضغط للعرض" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Stav Loop se zaznamenává od prvního spuštění této verze Loop." + "value" : "Loop · %1$@ · klepnutím zobrazíte" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-status registreres fra første gang denne version af Loop kørte." + "value" : "Loop · %1$@ · tryk for at se listen" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Der Loop-Status wird ab dem ersten Start dieser Loop-Version aufgezeichnet." + "value" : "Loop · %1$@ · zum Auflisten tippen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "El estado de Loop se registra desde la primera vez que se ejecutó esta versión de Loop." + "value" : "Loop · %1$@ · toca para ver la lista" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loopin tila tallennetaan siitä lähtien, kun tämä Loopin versio käynnistettiin ensimmäisen kerran." + "value" : "Loop · %1$@ · napauta nähdäksesi" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "L'état de Loop est enregistré depuis le premier lancement de cette version de Loop." + "value" : "Loop · %1$@ · touchez pour la liste" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מצב Loop נרשם מאז ההפעלה הראשונה של גרסה זו של Loop." + "value" : "Loop · %1$@ · הקש להצגה" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "A Loop állapota a Loop ezen verziójának első futtatásától kerül rögzítésre." + "value" : "Loop · %1$@ · koppintson a listához" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Lo stato di Loop è registrato dal primo avvio di questa versione di Loop." + "value" : "Loop · %1$@ · tocca per l'elenco" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loopの状態は、このバージョンのLoopを初めて起動した時点から記録されます。" + "value" : "Loop · %1$@ · タップして一覧" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-status registreres fra første gang denne versjonen av Loop kjørte." + "value" : "Loop · %1$@ · trykk for å se listen" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De loopstatus wordt vastgelegd vanaf de eerste keer dat deze versie van Loop draaide." + "value" : "Loop · %1$@ · tik voor lijst" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Stan Loop jest zapisywany od pierwszego uruchomienia tej wersji Loop." + "value" : "Loop · %1$@ · stuknij, aby wyświetlić" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "O status do Loop é registrado desde a primeira vez que esta versão do Loop foi executada." + "value" : "Loop · %1$@ · toque para ver a lista" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Starea Loop este înregistrată de la prima rulare a acestei versiuni de Loop." + "value" : "Loop · %1$@ · atingeți pentru listă" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Статус Loop записывается с первого запуска этой версии Loop." + "value" : "Loop · %1$@ · нажмите для списка" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Stav Loop sa zaznamenáva od prvého spustenia tejto verzie Loop." + "value" : "Loop · %1$@ · ťuknutím zobrazíte" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loopstatus registreras från när den här versionen av Loop först kördes." + "value" : "Loop · %1$@ · tryck för att visa" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop durumu, Loop'un bu sürümünün ilk çalıştığı andan itibaren kaydedilir." + "value" : "Loop · %1$@ · listelemek için dokunun" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Стан Loop записується з першого запуску цієї версії Loop." + "value" : "Loop · %1$@ · торкніться для списку" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Trạng thái Loop được ghi lại từ khi phiên bản Loop này chạy lần đầu." + "value" : "Loop · %1$@ · chạm để xem" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 状态从此版本 Loop 首次运行时开始记录。" + "value" : "Loop · %1$@ · 轻点查看列表" } } } }, - "Loop trends" : { - "comment" : "Title of the exported Trends PDF report", + "Loop · %lld changes · tap to list" : { + "comment" : "Logbook basal adjustments row subtitle: how many temp basals Loop set in the day; tapping lists them. (1: number of changes)", "localizations" : { + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld change · tap to list" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld changes · tap to list" + } + } + } + } + }, "ar" : { - "stringUnit" : { - "state" : "translated", - "value" : "اتجاهات Loop" + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغيير · اضغط للعرض" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغيير · اضغط للعرض" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغيير · اضغط للعرض" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغييرات · اضغط للعرض" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغييرًا · اضغط للعرض" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغيير · اضغط للعرض" + } + } + } } }, "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Trendy Loop" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld změna · klepněte pro seznam" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld změny · klepněte pro seznam" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld změny · klepněte pro seznam" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld změn · klepněte pro seznam" + } + } + } } }, "da" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop-tendenser" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld ændring · tryk for liste" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld ændringer · tryk for liste" + } + } + } } }, "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop-Trends" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld Änderung · tippen für Liste" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld Änderungen · tippen für Liste" + } + } + } } }, "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tendencias de Loop" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld cambio · toca para ver" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld cambios · toca para ver" + } + } + } } }, "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop-trendit" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld muutos · näytä napauttamalla" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld muutosta · näytä napauttamalla" + } + } + } } }, "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tendances Loop" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modification · touchez pour la liste" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modifications · touchez pour la liste" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modifications · touchez pour la liste" + } + } + } } }, "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "מגמות Loop" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld שינוי · הקש לרשימה" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld שינויים · הקש לרשימה" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld שינויים · הקש לרשימה" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld שינויים · הקש לרשימה" + } + } + } } }, "hu" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop-trendek" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld módosítás · koppints a listához" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld módosítás · koppints a listához" + } + } + } } }, "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tendenze Loop" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modifica · tocca per l'elenco" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modifiche · tocca per l'elenco" + } + } + } } }, "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loopトレンド" + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld件の変更 · タップで一覧表示" + } + } + } } }, "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop-trender" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld endring · trykk for liste" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld endringer · trykk for liste" + } + } + } } }, "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop-trends" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld wijziging · tik voor lijst" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld wijzigingen · tik voor lijst" + } + } + } } }, "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Trendy Loop" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmiana · stuknij, aby wyświetlić" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmiany · stuknij, aby wyświetlić" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmian · stuknij, aby wyświetlić" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmiany · stuknij, aby wyświetlić" + } + } + } } }, "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tendências do Loop" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld alteração · toque para listar" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld alterações · toque para listar" + } + } + } } }, "ro" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tendințe Loop" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modificare · atingeți pentru listă" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modificări · atingeți pentru listă" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld de modificări · atingeți pentru listă" + } + } + } } }, "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Тренды Loop" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld изменение · нажмите для списка" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld изменения · нажмите для списка" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld изменений · нажмите для списка" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld изменения · нажмите для списка" + } + } + } } }, "sk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Trendy Loop" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmena · klepnite pre zoznam" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmeny · klepnite pre zoznam" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmeny · klepnite pre zoznam" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmien · klepnite pre zoznam" + } + } + } } }, "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop-trender" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld ändring · tryck för lista" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld ändringar · tryck för lista" + } + } + } } }, "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop eğilimleri" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld değişiklik · listelemek için dokunun" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld değişiklik · listelemek için dokunun" + } + } + } } }, "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Тренди Loop" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld зміна · торкніться для списку" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld зміни · торкніться для списку" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld змін · торкніться для списку" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld зміни · торкніться для списку" + } + } + } } }, "vi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Xu hướng Loop" + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld thay đổi · chạm để xem danh sách" + } + } + } } }, "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop 趋势" + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld次更改 · 轻点查看列表" + } + } + } } } } }, - "Loop trends %1$@" : { - "comment" : "File name of an exported Trends report or CSV, without the extension. (1: date range of the period)", + "Manual %1$@" : { + "comment" : "Basal and bolus: bolus the person gave. (1: units, e.g. 16.2 U)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "اتجاهات Loop %1$@" + "value" : "يدوي %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Trendy Loop %1$@" + "value" : "Ručně %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-tendenser %1$@" + "value" : "Manuel %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-Trends %1$@" + "value" : "Manuell %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Tendencias de Loop %1$@" + "value" : "Manual %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trendit %1$@" + "value" : "Manuaalinen %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Tendances Loop %1$@" + "value" : "Manuel %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מגמות Loop %1$@" + "value" : "ידני %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trendek %1$@" + "value" : "Kézi %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Tendenze Loop %1$@" + "value" : "Manuale %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loopトレンド %1$@" + "value" : "手動 %1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trender %1$@" + "value" : "Manuell %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trends %1$@" + "value" : "Handmatig %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Trendy Loop %1$@" + "value" : "Ręcznie %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Tendências do Loop %1$@" + "value" : "Manual %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Tendințe Loop %1$@" + "value" : "Manual %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Тренды Loop %1$@" + "value" : "Вручную %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Trendy Loop %1$@" + "value" : "Ručne %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trender %1$@" + "value" : "Manuell %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop eğilimleri %1$@" + "value" : "Manuel %1$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Тренди Loop %1$@" + "value" : "Вручну %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Xu hướng Loop %1$@" + "value" : "Thủ công %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 趋势 %1$@" + "value" : "手动 %1$@" } } } }, - "Manual %1$@" : { - "comment" : "Basal and bolus: bolus the person gave. (1: units, e.g. 16.2 U)", + "Manual · %1$@" : { + "comment" : "Logbook temp basal row subtitle: set by hand, then the rate and how long. (1: description, e.g. 0.50 U/h for 30m (scheduled 0.80))", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يدوي %1$@" + "value" : "يدوي · %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Ručně %1$@" + "value" : "Ručně · %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Manuel %1$@" + "value" : "Manuel · %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Manuell %1$@" + "value" : "Manuell · %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Manual %1$@" + "value" : "Manual · %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Manuaalinen %1$@" + "value" : "Manuaalinen · %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Manuel %1$@" + "value" : "Manuel · %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ידני %1$@" + "value" : "ידני · %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Kézi %1$@" + "value" : "Kézi · %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Manuale %1$@" + "value" : "Manuale · %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "手動 %1$@" + "value" : "手動 · %1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Manuell %1$@" + "value" : "Manuell · %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Handmatig %1$@" + "value" : "Handmatig · %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ręcznie %1$@" + "value" : "Ręcznie · %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Manual %1$@" + "value" : "Manual · %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Manual %1$@" + "value" : "Manual · %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Вручную %1$@" + "value" : "Вручную · %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Ručne %1$@" + "value" : "Ručne · %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Manuell %1$@" + "value" : "Manuell · %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Manuel %1$@" + "value" : "Manuel · %1$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Вручну %1$@" + "value" : "Вручну · %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thủ công %1$@" + "value" : "Thủ công · %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "手动 %1$@" + "value" : "手动 · %1$@" } } } @@ -14625,6 +19123,435 @@ } } }, + "Nothing logged" : { + "comment" : "Logbook day with no entries", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا شيء مسجَّل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nic zaznamenáno" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intet registreret" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nichts eingetragen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nada registrado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei kirjauksia" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune saisie" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא נרשם דבר" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs bejegyzés" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna registrazione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "記録なし" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingenting registrert" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niets vastgelegd" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak wpisów" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nada registrado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nimic înregistrat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет записей" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nič zaznamenané" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inget loggat" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kayıt yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає записів" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có ghi chép" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无记录" + } + } + } + }, + "Nothing logged in the last 7 days." : { + "comment" : "Logbook empty state when there are no entries at all", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا شيء مسجَّل في آخر 7 أيام." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Za posledních 7 dní nic zaznamenáno." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intet registreret de seneste 7 dage." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "In den letzten 7 Tagen nichts eingetragen." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nada registrado en los últimos 7 días." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei kirjauksia viimeisen 7 päivän aikana." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune saisie ces 7 derniers jours." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא נרשם דבר ב-7 הימים האחרונים." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs bejegyzés az elmúlt 7 napban." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna registrazione negli ultimi 7 giorni." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "過去7日間の記録はありません。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingenting registrert de siste 7 dagene." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niets vastgelegd in de afgelopen 7 dagen." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak wpisów z ostatnich 7 dni." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nada registrado nos últimos 7 dias." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nimic înregistrat în ultimele 7 zile." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет записей за последние 7 дней." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Za posledných 7 dní nič zaznamenané." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inget loggat de senaste 7 dagarna." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Son 7 günde kayıt yok." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає записів за останні 7 днів." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có ghi chép nào trong 7 ngày qua." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "过去 7 天没有记录。" + } + } + } + }, + "Nothing to show for this filter" : { + "comment" : "Logbook day with no entries matching the selected filter", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا شيء لعرضه لهذا الفلتر" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pro tento filtr nic k zobrazení" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intet at vise for dette filter" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Einträge für diesen Filter" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nada que mostrar con este filtro" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei näytettävää tällä suodattimella" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rien à afficher pour ce filtre" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין מה להציג עבור מסנן זה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs megjeleníthető elem ehhez a szűrőhöz" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niente da mostrare per questo filtro" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "このフィルタに該当する項目はありません" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingenting å vise for dette filteret" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niets te tonen voor dit filter" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak wpisów dla tego filtra" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nada para mostrar neste filtro" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nimic de afișat pentru acest filtru" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет записей для этого фильтра" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pre tento filter nič na zobrazenie" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inget att visa för detta filter" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bu filtre için gösterilecek bir şey yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає записів для цього фільтра" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có mục nào cho bộ lọc này" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "此筛选条件下没有内容" + } + } + } + }, "Now" : { "comment" : "Title at the top of the Now screen, showing current glucose and Loop status Title of the Now tab in the redesigned interface, showing current glucose and Loop status", "localizations" : { @@ -15340,6 +20267,149 @@ } } }, + "Pod changed" : { + "comment" : "Pump event in the history: the pump or pod was replaced", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم تغيير Pod" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod vyměněn" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod skiftet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod gewechselt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod cambiado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod vaihdettu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod changé" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ה-Pod הוחלף" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod cserélve" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod cambiato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod交換" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod byttet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod vervangen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wymieniono poda" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod trocado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod schimbat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Под заменён" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod vymenený" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod bytt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pod değiştirildi" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Под замінено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã thay Pod" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已更换 Pod" + } + } + } + }, "Previous day" : { "comment" : "Accessibility label for the day page button that steps back a day", "localizations" : { @@ -15626,6 +20696,149 @@ } } }, + "Primed" : { + "comment" : "Pump event in the history: the pump primed the tubing or cannula", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تمت التعبئة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Naplněno" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fyldt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gefüllt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cebado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Täytetty" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Amorcé" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בוצע תיחול" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Feltöltve" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caricamento eseguito" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "プライミング" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fylt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gevuld" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Napełniono" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preenchido" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Amorsat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заполнение выполнено" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Naplnené" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Primning klar" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dolum yapıldı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заповнення виконано" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã mồi" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已充注" + } + } + } + }, "Projected" : { "comment" : "Chart legend: dashed lines show Loop's projection of insulin and carbs on board", "localizations" : { @@ -15769,6 +20982,149 @@ } } }, + "Pump alarm" : { + "comment" : "Pump event in the history: an alarm whose pump gave no title", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إنذار المضخة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alarm pumpy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpealarm" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpenalarm" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alarma de la microinfusadora" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpun hälytys" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alarme de la pompe" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "התראת משאבה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpariasztás" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allarme pompa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ポンプアラーム" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpealarm" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompalarm" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alarm pompy" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alarme da bomba" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alarmă pompă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тревога помпы" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alarm pumpy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumplarm" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompa alarmı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тривога помпи" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cảnh báo bơm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "胰岛素泵警报" + } + } + } + }, "Pump data too old" : { "comment" : "Reason Loop could not run: the latest pump data was too old", "localizations" : { @@ -16341,6 +21697,292 @@ } } }, + "Reservoir changed" : { + "comment" : "Pump event in the history: the insulin reservoir was replaced", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم تغيير الخزان" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zásobník vyměněn" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservoir skiftet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservoir gewechselt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservorio cambiado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Säiliö vaihdettu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réservoir changé" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "המאגר הוחלף" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tartály cserélve" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serbatoio cambiato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "リザーバ交換" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservoar byttet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservoir vervangen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wymieniono zbiorniczek" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservatório trocado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rezervor schimbat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Резервуар заменён" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zásobník vymenený" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservoar bytt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rezervuar değiştirildi" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Резервуар замінено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã thay bình chứa" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已更换储药器" + } + } + } + }, + "Reservoir rewound" : { + "comment" : "Pump event in the history: the pump rewound its reservoir", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم إرجاع الخزان" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zásobník převinut" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservoir spolet tilbage" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservoir zurückgespult" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservorio rebobinado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Säiliö kelattu taakse" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réservoir rembobiné" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "המאגר הוחזר לאחור" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tartály visszatekerve" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Serbatoio riavvolto" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "リザーバ巻き戻し" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservoar spolt tilbake" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservoir teruggespoeld" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cofnięto tłok zbiorniczka" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservatório rebobinado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rezervor rebobinat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Резервуар перемотан" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zásobník pretočený" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reservoar tillbakaspolad" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rezervuar geri sarıldı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Резервуар перемотано" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã tua lại bình chứa" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "储药器已回位" + } + } + } + }, "Scheduled %1$@" : { "comment" : "Basal and bolus: what the basal schedule delivers over the same time, under the basal row. (1: units, e.g. 18.0 U)", "localizations" : { @@ -16484,6 +22126,149 @@ } } }, + "Scheduled basal only" : { + "comment" : "Logbook day where only the scheduled basal ran, with nothing else logged", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القاعدية المجدولة فقط" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jen plánovaný bazál" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kun planlagt basal" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nur geplante Basalrate" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solo basal programada" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vain ohjelmoitu basaali" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal programmé uniquement" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal מתוזמן בלבד" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Csak ütemezett basal" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solo basale programmata" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "スケジュールのベーサルのみ" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bare planlagt basal" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alleen gepland basaal" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tylko zaplanowana baza" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Somente basal programada" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doar bazala programată" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Только базал по расписанию" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Len plánovaný bazál" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Endast schemalagd basal" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yalnızca planlanan bazal" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Лише базал за розкладом" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chỉ Basal theo lịch" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "仅预设基础率" + } + } + } + }, "SD %1$@ · CV %2$@%%" : { "comment" : "Trends Average tile detail: standard deviation and coefficient of variation of glucose. (1: standard deviation in the glucose unit, 2: coefficient of variation as a percentage number)", "localizations" : { @@ -16913,6 +22698,149 @@ } } }, + "Temp basal" : { + "comment" : "Logbook row title: a temporary basal rate", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temp Basal" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dočasný bazál" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Midlertidig basal" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temporäre Basalrate" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal temporal" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temp Basal" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basal temp." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temp Basal" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temp Basal" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basale temporanea" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "一時ベーサル" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Midlertidig basal" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tijdelijk basaal" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Baza tymczasowa" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temp Basal" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bazală temporară" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Врем. базал" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dočasný bazál" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temporär basal" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geçici bazal oranı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тимчасовий базал" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temp Basal" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "临时基础率" + } + } + } + }, "this day" : { "comment" : "Trends Basal and bolus card subtitle when the period is one complete day", "localizations" : { @@ -17628,8 +23556,8 @@ } } }, - "today so far" : { - "comment" : "Trends Basal and bolus card subtitle when the figures are today's total so far", + "Today so far" : { + "comment" : "Title of the card on the Now screen showing today's insulin and carbs", "localizations" : { "ar" : { "stringUnit" : { @@ -17640,37 +23568,37 @@ "cs" : { "stringUnit" : { "state" : "translated", - "value" : "dnes zatím" + "value" : "Dnes zatím" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "i dag indtil nu" + "value" : "I dag indtil nu" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "heute bisher" + "value" : "Heute bisher" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "hoy hasta ahora" + "value" : "Hoy hasta ahora" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "tänään tähän mennessä" + "value" : "Tänään tähän mennessä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "aujourd'hui jusqu'ici" + "value" : "Aujourd'hui jusqu'ici" } }, "he" : { @@ -17682,13 +23610,13 @@ "hu" : { "stringUnit" : { "state" : "translated", - "value" : "ma eddig" + "value" : "Ma eddig" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "oggi finora" + "value" : "Oggi finora" } }, "ja" : { @@ -17700,67 +23628,67 @@ "nb" : { "stringUnit" : { "state" : "translated", - "value" : "i dag så langt" + "value" : "I dag så langt" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "vandaag tot nu toe" + "value" : "Vandaag tot nu toe" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "dziś do tej pory" + "value" : "Dziś do tej pory" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "hoje até agora" + "value" : "Hoje até agora" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "azi până acum" + "value" : "Azi până acum" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "сегодня на данный момент" + "value" : "Сегодня на данный момент" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "dnes zatiaľ" + "value" : "Dnes zatiaľ" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "hittills i dag" + "value" : "Hittills i dag" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "bugün şu ana kadar" + "value" : "Bugün şu ana kadar" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "сьогодні наразі" + "value" : "Сьогодні наразі" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "hôm nay đến giờ" + "value" : "Hôm nay đến giờ" } }, "zh-Hans" : { @@ -17771,8 +23699,8 @@ } } }, - "Today so far" : { - "comment" : "Title of the card on the Now screen showing today's insulin and carbs", + "today so far" : { + "comment" : "Trends Basal and bolus card subtitle when the figures are today's total so far", "localizations" : { "ar" : { "stringUnit" : { @@ -17783,37 +23711,37 @@ "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Dnes zatím" + "value" : "dnes zatím" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "I dag indtil nu" + "value" : "i dag indtil nu" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Heute bisher" + "value" : "heute bisher" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Hoy hasta ahora" + "value" : "hoy hasta ahora" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Tänään tähän mennessä" + "value" : "tänään tähän mennessä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aujourd'hui jusqu'ici" + "value" : "aujourd'hui jusqu'ici" } }, "he" : { @@ -17825,13 +23753,13 @@ "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Ma eddig" + "value" : "ma eddig" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Oggi finora" + "value" : "oggi finora" } }, "ja" : { @@ -17843,67 +23771,67 @@ "nb" : { "stringUnit" : { "state" : "translated", - "value" : "I dag så langt" + "value" : "i dag så langt" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vandaag tot nu toe" + "value" : "vandaag tot nu toe" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dziś do tej pory" + "value" : "dziś do tej pory" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Hoje até agora" + "value" : "hoje até agora" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Azi până acum" + "value" : "azi până acum" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Сегодня на данный момент" + "value" : "сегодня на данный момент" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Dnes zatiaľ" + "value" : "dnes zatiaľ" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Hittills i dag" + "value" : "hittills i dag" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bugün şu ana kadar" + "value" : "bugün şu ana kadar" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Сьогодні наразі" + "value" : "сьогодні наразі" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hôm nay đến giờ" + "value" : "hôm nay đến giờ" } }, "zh-Hans" : { @@ -18057,6 +23985,149 @@ } } }, + "Today · %1$@" : { + "comment" : "Logbook day header for today. (1: weekday and date, e.g. Sun 25 Oct)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اليوم · %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dnes · %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "I dag · %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Heute · %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoy · %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tänään · %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aujourd'hui · %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "היום · %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ma · %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oggi · %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "今日 · %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "I dag · %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vandaag · %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dziś · %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoje · %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azi · %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сегодня · %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dnes · %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "I dag · %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bugün · %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сьогодні · %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hôm nay · %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "今天 · %1$@" + } + } + } + }, "Total" : { "comment" : "Basal and bolus total row: basal plus bolus", "localizations" : { @@ -19630,6 +25701,292 @@ } } }, + "vs plan" : { + "comment" : "Logbook basal adjustments row: the insulin amount is the difference from the scheduled basal", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مقارنة بالمجدول" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "oproti plánu" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "ift. plan" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "ggü. Plan" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "vs. lo programado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "vs. ohjelmoitu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "vs prévu" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לעומת התוכנית" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "a tervhez képest" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "vs programmata" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "予定比" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "mot plan" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "t.o.v. gepland" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "wzgl. planu" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "vs. programado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "față de program" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "к плану" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "oproti plánu" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "mot schema" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "plana göre" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "до плану" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "so với lịch" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "较预设" + } + } + } + }, + "Yesterday · %1$@" : { + "comment" : "Logbook day header for yesterday. (1: weekday and date, e.g. Sat 24 Oct)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أمس · %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Včera · %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "I går · %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gestern · %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ayer · %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eilen · %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hier · %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אתמול · %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tegnap · %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ieri · %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "昨日 · %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "I går · %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gisteren · %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wczoraj · %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ontem · %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ieri · %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вчера · %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Včera · %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "I går · %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dün · %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Учора · %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hôm qua · %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "昨天 · %1$@" + } + } + } + }, " - " : { }, @@ -35602,6 +41959,30 @@ "state" : "translated", "value" : "删除" } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Smazat" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Törlés" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "削除" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Видалити" + } } } }, @@ -47563,7 +53944,146 @@ } }, "Manual" : { - + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يدوي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ručně" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuaalinen" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ידני" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kézi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuale" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "手動" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Handmatig" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ręcznie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вручную" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ručne" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вручну" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thủ công" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "手动" + } + } + } }, "Manual Dose: %1$@ %2$@" : { "comment" : "Description of a bolus dose entry (1: value (? if no value) in bold, 2: unit)", @@ -53151,6 +59671,30 @@ "state" : "translated", "value" : "胰岛素泵" } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpa" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpa" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpa" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Помпа" + } } } }, diff --git a/Loop/Redesign/LogbookScreen.swift b/Loop/Redesign/LogbookScreen.swift new file mode 100644 index 0000000000..852337c7cd --- /dev/null +++ b/Loop/Redesign/LogbookScreen.swift @@ -0,0 +1,642 @@ +// +// LogbookScreen.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import SwiftUI +import LoopKit +import LoopKitUI + +private typealias P = LoopDesign.Palette + +/// One row of the logbook. +struct LogEntry: Identifiable { + enum Filter: String, CaseIterable, Identifiable { + case all = "All" + case bolus = "Bolus" + case basal = "Basal" + case carbs = "Carbs" + case loop = "Loop" + var id: String { rawValue } + + var title: String { + switch self { + case .all: return NSLocalizedString("All", comment: "Logbook filter chip: show every entry") + case .bolus: return NSLocalizedString("Bolus", comment: "Logbook filter chip: show only boluses") + case .basal: return NSLocalizedString("Basal", comment: "Logbook filter chip: show only basal changes") + case .carbs: return NSLocalizedString("Carbs", comment: "Logbook filter chip: show only carb entries") + case .loop: return NSLocalizedString("Loop", comment: "Logbook filter chip: show only what Loop did automatically (Loop is the app's name)") + } + } + } + + enum Action { + case deleteCarbs(StoredCarbEntry) + case deleteDose(DoseEntry) + } + + let id: String + let date: Date + let title: String + let subtitle: String + let amount: String + let amountColor: Color + let detail: String + let icon: String + let iconStyle: IconTile.Style + let filters: Set + /// Loop's own small, frequent doses: the amount is set lighter so the person's own entries stand out. + var quiet = false + var basalChanges: [BasalSegment] = [] + var edit: StoredCarbEntry? = nil + var delete: Action? = nil + /// Units of a single automatic bolus, so runs of them can be folded into one row. + var automaticUnits: Double? = nil + /// The automatic boluses a folded row stands for, newest first. + var bolusRun: [LogEntry] = [] + + func matches(_ filter: Filter) -> Bool { + filter == .all || filters.contains(filter) + } +} + +struct LogDay: Identifiable { + let day: Date + let entries: [LogEntry] + let insulin: InsulinSplit + let carbs: Double + var id: Date { day } +} + +@MainActor +final class LogbookModel: ObservableObject { + @Published private(set) var days: [LogDay] = [] + @Published private(set) var loaded = false + @Published var filter: LogEntry.Filter = .all + @Published var error: String? + @Published private(set) var loadError: String? + @Published private(set) var carbEditingAllowed = false + + let provider: LoopHistoryProvider + private var loadTask: Task? + + init(provider: LoopHistoryProvider) { + self.provider = provider + } + + func reload() { + loadTask?.cancel() + carbEditingAllowed = provider.allowsCarbEditing + loadError = nil + loadTask = Task { + do { + let result = try await Self.build(provider: provider) + guard !Task.isCancelled else { return } + self.days = result + } catch { + guard !Task.isCancelled else { return } + self.days = [] + self.loadError = error.localizedDescription + } + self.loaded = true + } + } + + func perform(_ action: LogEntry.Action) { + Task { + do { + switch action { + case .deleteCarbs(let entry): + guard provider.allowsCarbEditing else { return } + try await provider.delete(carb: entry) + case .deleteDose(let dose): try await provider.delete(dose: dose) + } + reload() + } catch { + self.error = error.localizedDescription + } + } + } + + // MARK: Building rows + + private static func build(provider: LoopHistoryProvider, now: Date = Date()) async throws -> [LogDay] { + let calendar = Calendar.current + let start = calendar.startOfDay(for: provider.localHistoryStart.addingTimeInterval(86400)) + let interval = DateInterval(start: start, end: now) + async let historyFetch = provider.dayHistory(interval, includeOnBoard: false) + async let pumpFetch = provider.pumpEvents(since: start) + let history = try await historyFetch + let pumpEvents = try await pumpFetch + let unit = provider.unitStyle + let canDelete = FeatureFlags.entryDeletionEnabled + + return LoopHistoryMath.days(in: interval).reversed().map { day in + var entries: [LogEntry] = [] + let inDay: (Date) -> Bool = { $0 >= day.start && $0 < day.end } + let carbsToday = history.carbs.filter { inDay($0.date) } + + for bolus in history.boluses where inDay(bolus.date) { + let glucose = glucoseContext(at: bolus.date, history.glucose, unit: unit, withTrend: bolus.automatic) + var parts = [bolus.automatic ? sourceLoop : sourceManual] + if !bolus.automatic, let meal = carbsToday.first(where: { abs($0.date.timeIntervalSince(bolus.date)) <= 15 * 60 }) { + parts.append(LoopFormat.grams(meal.grams)) + } + if let glucose { parts.append(glucose) } + if bolus.manuallyEntered { parts.append(NSLocalizedString("logged", comment: "Logbook bolus row detail: the dose was logged by hand rather than delivered by the pump")) } + entries.append(LogEntry( + id: "bolus-\(bolus.id)", date: bolus.date, + title: bolus.automatic + ? NSLocalizedString("Automatic bolus", comment: "Logbook row title: a bolus Loop delivered automatically") + : NSLocalizedString("Bolus", comment: "Logbook row title: a bolus"), + subtitle: joined(parts), + amount: LoopFormat.doseUnits(bolus.units), amountColor: P.insulinInk, + detail: LoopFormat.time(bolus.date), + icon: bolus.automatic ? "arrow.triangle.2.circlepath" : "syringe.fill", + iconStyle: bolus.automatic ? .hatch(P.bolus) : .solid(P.bolus), + filters: bolus.automatic ? [.bolus, .loop] : [.bolus], + quiet: bolus.automatic, + delete: canDelete && bolus.manuallyEntered ? bolus.dose.map { .deleteDose($0) } : nil, + automaticUnits: bolus.automatic && !bolus.manuallyEntered ? bolus.units : nil)) + } + + for carb in carbsToday { + var parts = [carb.createdByLoop ? sourceManual : NSLocalizedString("Health", comment: "Logbook carb row detail: the entry came from Apple Health")] + if let food = carb.foodType, !food.isEmpty { parts.append(food) } + if let hours = carb.absorptionHours { parts.append(String(format: NSLocalizedString("absorbs over %1$@ h", comment: "Logbook carb row detail: carb absorption time in hours. (1: hours, e.g. 3 or 2.5)"), hours.formatted(.number.precision(.fractionLength(0...1))))) } + let editable = carb.createdByLoop && provider.allowsCarbEditing ? carb.entry : nil + entries.append(LogEntry( + id: "carbs-\(carb.id)", date: carb.date, + title: NSLocalizedString("Carbs", comment: "Logbook row title: a carb entry"), subtitle: joined(parts), + amount: LoopFormat.grams(carb.grams), amountColor: P.carbsInk, + detail: LoopFormat.time(carb.date), + icon: "fork.knife", iconStyle: .solid(P.carbs), filters: [.carbs], + edit: editable, + delete: canDelete ? editable.map { .deleteCarbs($0) } : nil)) + } + + let segments = history.basal.compactMap { $0.clamped(to: day) } + let automaticTemps = segments.filter { $0.kind == .temp && $0.automatic } + if let latest = automaticTemps.last { + let net = automaticTemps.reduce(0) { $0 + $1.netUnits } + let count = automaticTemps.count + entries.append(LogEntry( + id: "basal-\(day.start.timeIntervalSince1970)", date: latest.start, + title: NSLocalizedString("Basal adjustments", comment: "Logbook row title summarising the temp basals Loop set in a day"), + subtitle: String.localizedStringWithFormat(NSLocalizedString("Loop · %lld changes · tap to list", comment: "Logbook basal adjustments row subtitle: how many temp basals Loop set in the day; tapping lists them. (1: number of changes)"), count), + amount: String(format: NSLocalizedString("%1$@ U", comment: "Format string for an amount of insulin in units. (1: amount)"), LoopFormat.signed(net, decimals: 1)), amountColor: P.ink, + detail: NSLocalizedString("vs plan", comment: "Logbook basal adjustments row: the insulin amount is the difference from the scheduled basal"), + icon: "waveform.path.ecg", iconStyle: .basal, filters: [.basal, .loop], + basalChanges: automaticTemps.reversed())) + } + for temp in segments where temp.kind == .temp && !temp.automatic { + entries.append(LogEntry( + id: "temp-\(temp.start.timeIntervalSince1970)", date: temp.start, + title: NSLocalizedString("Temp basal", comment: "Logbook row title: a temporary basal rate"), + subtitle: String(format: NSLocalizedString("Manual · %1$@", comment: "Logbook temp basal row subtitle: set by hand, then the rate and how long. (1: description, e.g. 0.50 U/h for 30m (scheduled 0.80))"), describe(temp)), + amount: LoopFormat.rate(temp.rate), amountColor: P.ink, + detail: LoopFormat.time(temp.start), + icon: "waveform.path.ecg", iconStyle: .basal, filters: [.basal])) + } + for suspend in segments where suspend.kind == .suspended { + var parts = [suspend.automatic ? sourceLoop : sourceManual, LoopFormat.duration(suspend.end.timeIntervalSince(suspend.start))] + if let glucose = glucoseContext(at: suspend.start, history.glucose, unit: unit, withTrend: false) { parts.append(glucose) } + entries.append(LogEntry( + id: "suspend-\(suspend.start.timeIntervalSince1970)", date: suspend.start, + title: NSLocalizedString("Basal suspended", comment: "Logbook row title: insulin delivery was suspended"), subtitle: joined(parts), + amount: String(format: NSLocalizedString("%1$@ U/h", comment: "Format string for an insulin rate in units per hour. (1: rate)"), LoopFormat.number(0, decimals: 0)), amountColor: P.ink, + detail: LoopFormat.time(suspend.start), + icon: "pause.fill", iconStyle: .solid(P.statusWarning), + filters: suspend.automatic ? [.basal, .loop] : [.basal])) + } + + for event in pumpEvents where inDay(event.date) { + let title: String + switch event.kind { + // Site change and alarm names come from the pump plugin as it reports them. + case .siteChange(let name): title = name + case .alarm(let name): title = name + case .resume: title = NSLocalizedString("Basal resumed", comment: "Logbook row title: insulin delivery resumed after a suspend") + } + entries.append(LogEntry( + id: "pump-\(event.id)", date: event.date, + title: title, subtitle: NSLocalizedString("Pump", comment: "Logbook row subtitle: the event was reported by the insulin pump"), + amount: "", amountColor: P.ink, + detail: LoopFormat.time(event.date), + icon: "circle.dashed", iconStyle: .solid(P.inkMuted.opacity(0.5)), filters: [])) + } + + let insulin = InsulinSplit(basal: segments.reduce(0) { $0 + $1.units }, boluses: history.boluses.filter { inDay($0.date) }) + return LogDay(day: day.start, entries: foldingAutomaticBoluses(entries.sorted { $0.date > $1.date }), insulin: insulin, + carbs: carbsToday.reduce(0) { $0 + $1.grams }) + } + } + + /// Loop gives many small automatic boluses; two or more in a row, with nothing else logged between them, become + /// one row that lists them. The day's Basal adjustments summary row is dated by its latest change, not where it + /// falls, so it doesn't break a run. + static func foldingAutomaticBoluses(_ entries: [LogEntry]) -> [LogEntry] { + var result: [LogEntry] = [] + var run: [LogEntry] = [] + var summaries: [LogEntry] = [] + + func flush() { + if run.count >= 2, let newest = run.first, let oldest = run.last { + let units = run.reduce(0) { $0 + ($1.automaticUnits ?? 0) } + result.append(LogEntry( + id: "autorun-\(newest.id)", date: newest.date, + title: NSLocalizedString("Automatic boluses", comment: "Logbook row title: several automatic boluses in a row, folded into one row that lists them"), + subtitle: String(format: NSLocalizedString("Loop · %1$@ · tap to list", comment: "Logbook folded automatic boluses row subtitle: Loop gave them, the time span they cover, and tapping lists them. (1: time range, e.g. 15:08 – 16:14)"), + "\(LoopFormat.time(oldest.date))–\(LoopFormat.time(newest.date))"), + amount: LoopFormat.doseUnits(units), amountColor: P.insulinInk, + detail: String.localizedStringWithFormat(NSLocalizedString("%lld doses", comment: "Logbook folded automatic boluses row: how many automatic boluses it holds. (1: number)"), run.count), + icon: "arrow.triangle.2.circlepath", iconStyle: .hatch(P.bolus), + filters: [.bolus, .loop], quiet: true, + bolusRun: run)) + } else { + result.append(contentsOf: run) + } + run = [] + } + + for entry in entries { + if entry.automaticUnits != nil { + run.append(entry) + } else if !entry.basalChanges.isEmpty { + summaries.append(entry) + } else { + flush() + result.append(entry) + } + } + flush() + // Put the summary rows back in date order. + return (result + summaries).sorted { $0.date > $1.date } + } + + private static var sourceLoop: String { + NSLocalizedString("Loop", comment: "Logbook row detail: Loop made this change automatically (Loop is the app's name)") + } + + private static var sourceManual: String { + NSLocalizedString("Manual", comment: "Logbook row detail: the user made this entry or change by hand") + } + + /// Row details are a list of independent facts ("Manual", "45 g", "glucose 7.5"), shown with a middle-dot separator. + private static func joined(_ parts: [String]) -> String { + parts.joined(separator: " · ") + } + + static func describe(_ segment: BasalSegment) -> String { + let rate = LoopFormat.rate(segment.rate) + let duration = LoopFormat.duration(segment.end.timeIntervalSince(segment.start)) + if let scheduled = segment.scheduledRate { + return String(format: NSLocalizedString("%1$@ for %2$@ (scheduled %3$@)", comment: "A temp basal: its rate, how long it ran and the scheduled rate it replaced. (1: rate, e.g. 0.50 U/h; 2: duration, e.g. 30m; 3: scheduled rate number, e.g. 0.80)"), rate, duration, LoopFormat.number(scheduled, decimals: 2)) + } + return String(format: NSLocalizedString("%1$@ for %2$@", comment: "A temp basal: its rate and how long it ran. (1: rate, e.g. 0.50 U/h; 2: duration, e.g. 30m)"), rate, duration) + } + + /// "glucose 7.5", or "glucose 11.0 rising" for Loop's own doses. + private static func glucoseContext(at date: Date, _ readings: [GlucosePoint], unit: GlucoseUnitStyle, withTrend: Bool) -> String? { + guard let reading = readings.last(where: { $0.date <= date.addingTimeInterval(60) }), date.timeIntervalSince(reading.date) <= 10 * 60 else { + return nil + } + let value = unit.format(reading.mgdl) + if withTrend, let earlier = readings.last(where: { $0.date <= reading.date.addingTimeInterval(-14 * 60) }), + reading.date.timeIntervalSince(earlier.date) <= 20 * 60 { + let perFifteen = (reading.mgdl - earlier.mgdl) / reading.date.timeIntervalSince(earlier.date) * 900 + if perFifteen > 10 { + return String(format: NSLocalizedString("glucose %1$@ rising", comment: "Logbook row detail: glucose when the dose was given, and rising. (1: glucose value, e.g. 11.0)"), value) + } else if perFifteen < -10 { + return String(format: NSLocalizedString("glucose %1$@ falling", comment: "Logbook row detail: glucose when the dose was given, and falling. (1: glucose value, e.g. 11.0)"), value) + } + } + return String(format: NSLocalizedString("glucose %1$@", comment: "Logbook row detail: glucose when the dose was given. (1: glucose value, e.g. 7.5)"), value) + } +} + +// MARK: - Screen + +struct LogbookView: View { + @StateObject private var model: LogbookModel + @State private var path: [Route] = [] + /// Editing is a sheet, as adding carbs is, with its own navigation for the bolus screen that can follow. + @State private var editingCarbs: CarbEditRoute? + @Environment(\.dynamicTypeSize) private var typeSize + private let deviceManager: DeviceDataManager + + enum Route: Hashable { + case basal(String) + case boluses(String) + } + + struct CarbEditRoute: Identifiable { + let id = UUID() + let entry: StoredCarbEntry + } + + /// Closes the Logbook when it is presented on its own; nil where it has nowhere to return to. + private let done: (() -> Void)? + + init(provider: LoopHistoryProvider, deviceManager: DeviceDataManager, done: (() -> Void)? = nil) { + _model = StateObject(wrappedValue: LogbookModel(provider: provider)) + self.deviceManager = deviceManager + self.done = done + } + + private var visibleDays: [LogDay] { + model.days.map { day in + LogDay(day: day.day, entries: day.entries.filter { $0.matches(model.filter) }, insulin: day.insulin, carbs: day.carbs) + } + } + + var body: some View { + NavigationStack(path: $path) { + List { + Section { + chips + .listRowInsets(EdgeInsets()) + .listRowBackground(Color.clear) + } + if let error = model.loadError { + Text(String(format: NSLocalizedString("Couldn't load Logbook: %1$@", comment: "Logbook error when its history can't be read. (1: system error description)"), error)).foregroundStyle(P.criticalInk) + Button(NSLocalizedString("Retry", comment: "Button to try loading the Logbook again")) { model.reload() } + } else if model.loaded && model.days.allSatisfy({ $0.entries.isEmpty }) { + Text(NSLocalizedString("Nothing logged in the last 7 days.", comment: "Logbook empty state when there are no entries at all")) + .foregroundStyle(P.inkMuted) + } + ForEach(visibleDays) { day in + Section { + if day.entries.isEmpty { + Text(model.filter != .all + ? NSLocalizedString("Nothing to show for this filter", comment: "Logbook day with no entries matching the selected filter") + : day.insulin.total > 0 + ? NSLocalizedString("Scheduled basal only", comment: "Logbook day where only the scheduled basal ran, with nothing else logged") + : NSLocalizedString("Nothing logged", comment: "Logbook day with no entries")) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + ForEach(day.entries) { entry in + row(entry) + } + } header: { + dayHeader(day) + } + } + Section { + EmptyView() + } footer: { + Text(NSLocalizedString("Loop keeps the last 7 days on this phone. Older doses and carbs are in Apple Health and on Trends.", comment: "Logbook footer explaining why only 7 days are listed")) + } + } + .listStyle(.insetGrouped) + .scrollContentBackground(.hidden) + .background(P.surface) + .navigationTitle(NSLocalizedString("Logbook", comment: "Title of the Logbook tab")) + .refreshable { model.reload() } + .toolbar { + if let done { + ToolbarItem(placement: .confirmationAction) { + Button(NSLocalizedString("Done", comment: "Button to close the Logbook"), action: done) + } + } + } + .overlay { + if !model.loaded { ProgressView() } + } + .navigationDestination(for: Route.self) { route in + destination(route) + } + } + .sheet(item: $editingCarbs) { route in + NavigationStack { + LogbookCarbEditor(entry: route.entry, deviceManager: deviceManager, provider: model.provider) { + editingCarbs = nil + model.reload() + } + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button(NSLocalizedString("Cancel", comment: "Button label for cancel")) { editingCarbs = nil } + } + } + } + } + .onAppear { model.reload() } + .onReceive(NotificationCenter.default.publisher(for: .LoopDataUpdated).debounce(for: .seconds(2), scheduler: RunLoop.main)) { _ in + model.reload() + } + .alert(NSLocalizedString("Couldn't delete", comment: "Alert title when deleting a Logbook entry fails"), isPresented: Binding(get: { model.error != nil }, set: { if !$0 { model.error = nil } })) { + Button(NSLocalizedString("OK", comment: "Button to dismiss the Logbook delete error alert"), role: .cancel) {} + } message: { + Text(model.error ?? "") + } + } + + private var chips: some View { + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: LoopDesign.Space.s2) { + ForEach(LogEntry.Filter.allCases) { filter in + Button { + model.filter = filter + } label: { + Text(filter.title) + .font(LoopDesign.Typeface.footnote.weight(.semibold)) + .padding(.horizontal, 12) + .padding(.vertical, 6) + .foregroundStyle(model.filter == filter ? Color(uiColor: .systemBackground) : P.ink) + .background(model.filter == filter ? P.ink : P.surfaceSunken, in: Capsule()) + } + .buttonStyle(.plain) + .accessibilityAddTraits(model.filter == filter ? .isSelected : []) + } + } + .padding(.vertical, LoopDesign.Space.s1) + } + } + + private func dayHeader(_ day: LogDay) -> some View { + let calendar = Calendar.current + let date = LoopFormat.weekdayDay(day.day) + let title = calendar.isDateInToday(day.day) + ? String(format: NSLocalizedString("Today · %1$@", comment: "Logbook day header for today. (1: weekday and date, e.g. Sun 25 Oct)"), date) + : calendar.isDateInYesterday(day.day) + ? String(format: NSLocalizedString("Yesterday · %1$@", comment: "Logbook day header for yesterday. (1: weekday and date, e.g. Sat 24 Oct)"), date) + : date + return VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + let totals = HStack(spacing: LoopDesign.Space.s3) { + if day.insulin.total > 0 { + Text(LoopFormat.totalUnits(day.insulin.total)) + .foregroundStyle(P.insulinInk) + } + Text(LoopFormat.grams(day.carbs)) + .foregroundStyle(P.carbsInk) + } + .font(LoopDesign.Typeface.footnote.monospacedDigit()) + .fixedSize() + // The totals drop under the date when both won't fit on one line, rather than breaking numbers apart. + ViewThatFits(in: .horizontal) { + HStack(alignment: .firstTextBaseline) { + Text(title) + .font(LoopDesign.Typeface.headline) + .foregroundStyle(P.ink) + .fixedSize() + Spacer() + totals + } + VStack(alignment: .leading, spacing: LoopDesign.Space.s1) { + Text(title) + .font(LoopDesign.Typeface.headline) + .foregroundStyle(P.ink) + totals + } + } + if day.insulin.total > 0 { + // The day's total sits on the line above, so the key keeps to one line. + InsulinSplitLine(split: day.insulin, partial: calendar.isDateInToday(day.day)) + } + } + .padding(.bottom, 2) + .textCase(nil) + .accessibilityElement(children: .combine) + } + + @ViewBuilder + private func row(_ entry: LogEntry) -> some View { + // At accessibility sizes the amount moves under the title, so neither column squeezes the other into fragments. + let stacked = typeSize.isAccessibilitySize + let titles = VStack(alignment: .leading, spacing: 2) { + Text(entry.title) + .font(LoopDesign.Typeface.body) + .foregroundStyle(P.ink) + Text(entry.subtitle) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + let amounts = VStack(alignment: stacked ? .leading : .trailing, spacing: 2) { + if !entry.amount.isEmpty { + Text(entry.amount) + .font(LoopDesign.Typeface.body.weight(entry.quiet ? .regular : .semibold).monospacedDigit()) + .foregroundStyle(entry.amountColor) + } + Text(entry.detail) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + let content = HStack(alignment: stacked ? .top : .center, spacing: LoopDesign.Space.s3) { + IconTile(systemName: entry.icon, style: entry.iconStyle) + if stacked { + VStack(alignment: .leading, spacing: LoopDesign.Space.s1) { + titles + amounts + } + Spacer(minLength: 0) + } else { + titles + Spacer(minLength: LoopDesign.Space.s2) + amounts + } + } + .accessibilityElement(children: .combine) + + if !entry.basalChanges.isEmpty { + NavigationLink(value: Route.basal(entry.id)) { content } + } else if !entry.bolusRun.isEmpty { + NavigationLink(value: Route.boluses(entry.id)) { content } + } else { + content + .swipeActions(edge: .trailing, allowsFullSwipe: false) { + if let action = entry.delete, canPerform(action) { + Button(role: .destructive) { model.perform(action) } label: { Label(NSLocalizedString("Delete", comment: "Swipe action to delete a Logbook entry"), systemImage: "trash") } + } + if let carb = entry.edit, model.carbEditingAllowed { + Button { + guard model.provider.allowsCarbEditing else { return } + editingCarbs = CarbEditRoute(entry: carb) + } label: { Label(NSLocalizedString("Edit", comment: "Swipe action to edit a Logbook carb entry"), systemImage: "pencil") } + .tint(P.accent) + } + } + } + } + + private func canPerform(_ action: LogEntry.Action) -> Bool { + switch action { + case .deleteCarbs: return model.carbEditingAllowed + case .deleteDose: return true + } + } + + @ViewBuilder + private func destination(_ route: Route) -> some View { + switch route { + case .basal(let id): + if let entry = model.days.flatMap(\.entries).first(where: { $0.id == id }) { + BasalChangesList(changes: entry.basalChanges) + } + case .boluses(let id): + if let entry = model.days.flatMap(\.entries).first(where: { $0.id == id }) { + List(entry.bolusRun) { bolus in + row(bolus) + } + .navigationTitle(NSLocalizedString("Automatic boluses", comment: "Logbook row title: several automatic boluses in a row, folded into one row that lists them")) + .navigationBarTitleDisplayMode(.inline) + } + } + } +} + +/// Own the draft independently of rows that refresh while the editor or its bolus screen is open. +private struct LogbookCarbEditor: View { + @StateObject private var viewModel: CarbEntryViewModel + let deviceManager: DeviceDataManager + let provider: LoopHistoryProvider + let dismiss: () -> Void + + init(entry: StoredCarbEntry, deviceManager: DeviceDataManager, provider: LoopHistoryProvider, dismiss: @escaping () -> Void) { + _viewModel = StateObject(wrappedValue: provider.carbEntryViewModel(editing: entry)) + self.deviceManager = deviceManager + self.provider = provider + self.dismiss = dismiss + } + + var body: some View { + CarbEntryView(viewModel: viewModel) + .environmentObject(deviceManager.displayGlucosePreference) + .environment(\.dismissAction, dismiss) + .navigationTitle(NSLocalizedString("Edit Carb Entry", comment: "Title of the screen for editing a carb entry from the Logbook")) + .navigationBarTitleDisplayMode(.inline) + .onAppear { if !provider.allowsCarbEditing { dismiss() } } + // Loop posts this from its data queue; SwiftUI faults on publishers that emit off the main thread. + .onReceive(NotificationCenter.default.publisher(for: .LoopDataUpdated).receive(on: RunLoop.main)) { _ in + if !provider.allowsCarbEditing { dismiss() } + } + } +} + +/// Every basal change Loop made in a day. +struct BasalChangesList: View { + let changes: [BasalSegment] + + var body: some View { + List(Array(changes.enumerated()), id: \.offset) { _, change in + HStack { + VStack(alignment: .leading, spacing: 2) { + Text(LoopFormat.time(change.start)) + .font(LoopDesign.Typeface.body) + Text(LogbookModel.describe(change)) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + Spacer() + Text(String(format: NSLocalizedString("%1$@ U", comment: "Format string for an amount of insulin in units. (1: amount)"), LoopFormat.signed(change.netUnits))) + .font(LoopDesign.Typeface.body.weight(.semibold).monospacedDigit()) + .foregroundStyle(P.insulinInk) + } + .accessibilityElement(children: .combine) + } + .navigationTitle(NSLocalizedString("Basal adjustments", comment: "Title of the list of every temp basal Loop set in a day")) + .navigationBarTitleDisplayMode(.inline) + } +} diff --git a/Loop/Redesign/LoopDesign.swift b/Loop/Redesign/LoopDesign.swift index 0fb74edffd..3e9b50299e 100644 --- a/Loop/Redesign/LoopDesign.swift +++ b/Loop/Redesign/LoopDesign.swift @@ -30,7 +30,9 @@ enum LoopDesign { // Text static let ink = Color(uiColor: .label) static let inkMuted = Color("ink-muted") + static let accent = Color("accent") static let accentInk = Color("accent-ink") + static let onFill = Color.black // Quantities static let glucose = Color("glucose") @@ -61,6 +63,7 @@ enum LoopDesign { // Loop state static let statusAutomated = Color("fresh") static let statusWarning = Color("warning") + static let criticalInk = Color("critical-ink") } enum Space { @@ -301,6 +304,67 @@ struct ValueTile: View { } } +/// The square icon tile at the start of logbook rows. +/// +/// Insulin tiles are drawn the way the charts draw the same insulin, so a row and its mark read alike: manual bolus +/// solid, automatic bolus hatched, basal pale with its edge. +struct IconTile: View { + enum Style { + case solid(Color) + case hatch(Color) + case basal + } + + let systemName: String + let style: Style + @Environment(\.colorScheme) private var scheme + + init(systemName: String, style: Style) { + self.systemName = systemName + self.style = style + } + + private var shape: RoundedRectangle { RoundedRectangle(cornerRadius: LoopDesign.Radius.sm, style: .continuous) } + + var body: some View { + Image(systemName: systemName) + .font(.system(size: isHatched ? 11 : 13, weight: .bold)) + .foregroundStyle(glyph) + // Stripes behind a glyph blur it, so the hatched tile sets its glyph on a plain disc. + .frame(width: isHatched ? 18 : 28, height: isHatched ? 18 : 28) + .background { if isHatched { Circle().fill(LoopDesign.Palette.surfaceRaised) } } + .frame(width: 28, height: 28) + .background { background } + .accessibilityHidden(true) + } + + private var isHatched: Bool { + if case .hatch = style { return true } + return false + } + + private var glyph: Color { + switch style { + case .solid: return LoopDesign.Palette.onFill + case .hatch, .basal: return LoopDesign.Palette.insulinInk + } + } + + @ViewBuilder + private var background: some View { + switch style { + case .solid(let fill): + shape.fill(fill) + case .hatch(let color): + shape.fill(Hatch.paint(color, scheme: scheme)) + .overlay(shape.strokeBorder(color, lineWidth: 1.5)) + case .basal: + shape.fill(LoopDesign.Palette.basal) + .overlay(shape.strokeBorder(LoopDesign.Palette.basalEdge, lineWidth: 1.5)) + } + } +} + /// The round previous and next buttons beside a date or period. struct StepButton: View { let systemName: String diff --git a/Loop/Redesign/LoopHistoryMath.swift b/Loop/Redesign/LoopHistoryMath.swift index cfdca173dc..45ffaf8297 100644 --- a/Loop/Redesign/LoopHistoryMath.swift +++ b/Loop/Redesign/LoopHistoryMath.swift @@ -7,7 +7,7 @@ import Foundation -/// Pure calculations behind the Trends screen. Nothing here touches a store. +/// Pure calculations behind the Trends and Logbook screens. Nothing here touches a store. enum LoopHistoryMath { // MARK: Glucose statistics diff --git a/Loop/Redesign/LoopHistoryModels.swift b/Loop/Redesign/LoopHistoryModels.swift index 3fc71c5b47..8835c7b839 100644 --- a/Loop/Redesign/LoopHistoryModels.swift +++ b/Loop/Redesign/LoopHistoryModels.swift @@ -104,6 +104,18 @@ struct TimedValue: Equatable { let value: Double } +struct PumpLogEvent: Equatable, Identifiable { + enum Kind: Equatable { + case siteChange(String) + case alarm(String) + case resume + } + + let id: String + let date: Date + let kind: Kind +} + /// Everything known about one day or reporting period, for InsulinTimeline. struct DayHistory { var window: DateInterval diff --git a/Loop/Redesign/LoopHistoryProvider.swift b/Loop/Redesign/LoopHistoryProvider.swift index 0c5236808a..824bd692d0 100644 --- a/Loop/Redesign/LoopHistoryProvider.swift +++ b/Loop/Redesign/LoopHistoryProvider.swift @@ -48,6 +48,12 @@ final class LoopHistoryProvider { GlucoseUnitStyle(unit: deviceManager.displayGlucosePreference.unit) } + /// Match the classic carb editor's eligibility rule, including mode changes while a screen is open. + @MainActor + var allowsCarbEditing: Bool { + settingsManager.dosingEnabled || !FeatureFlags.simpleBolusCalculatorEnabled + } + /// The start of what Loop keeps locally. var localHistoryStart: Date { Date().addingTimeInterval(-Bundle.main.localCacheDuration) @@ -209,6 +215,66 @@ final class LoopHistoryProvider { return sentence.prefix(1).uppercased() + sentence.dropFirst() } + // MARK: - Pump events + + func pumpEvents(since start: Date) async throws -> [PumpLogEvent] { + let events = try await doseStore.getPumpEventValues(since: start) + let items: [PumpLogEvent] = events.compactMap { event in + let id = event.objectIDURL.absoluteString + switch event.type { + case .replaceComponent(let component)?: + let name: String + switch component { + case .pump: name = NSLocalizedString("Pod changed", comment: "Pump event in the history: the pump or pod was replaced") + case .reservoir: name = NSLocalizedString("Reservoir changed", comment: "Pump event in the history: the insulin reservoir was replaced") + case .infusionSet: name = NSLocalizedString("Infusion set changed", comment: "Pump event in the history: the infusion set was replaced") + } + return PumpLogEvent(id: id, date: event.date, kind: .siteChange(name)) + case .rewind?: + return PumpLogEvent(id: id, date: event.date, kind: .siteChange(NSLocalizedString("Reservoir rewound", comment: "Pump event in the history: the pump rewound its reservoir"))) + case .prime?: + return PumpLogEvent(id: id, date: event.date, kind: .siteChange(NSLocalizedString("Primed", comment: "Pump event in the history: the pump primed the tubing or cannula"))) + case .resume?: + return PumpLogEvent(id: id, date: event.date, kind: .resume) + case .alarm?: + // `title` is a debug description; only a pump's own alarm text is meant for people. + let pumpText: String? + if case .other(let details)? = event.alarmType, !details.isEmpty { pumpText = details } else { pumpText = nil } + return PumpLogEvent(id: id, date: event.date, kind: .alarm(pumpText ?? NSLocalizedString("Pump alarm", comment: "Pump event in the history: an alarm whose pump gave no title"))) + default: + return nil + } + } + return items + } + + // MARK: - Editing + + /// Edits an entry the way Loop's carb absorption screen does. + @MainActor + func carbEntryViewModel(editing entry: StoredCarbEntry) -> CarbEntryViewModel { + let viewModel = CarbEntryViewModel(delegate: loopDataManager, originalCarbEntry: entry) + viewModel.analyticsServicesManager = loopDataManager.analyticsServicesManager + viewModel.deliveryDelegate = deviceManager + return viewModel + } + + func delete(carb entry: StoredCarbEntry) async throws { + _ = try await loopDataManager.deleteCarbEntry(entry) + } + + func delete(dose: DoseEntry) async throws { + try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + doseStore.deleteDose(dose) { error in + if let error { + continuation.resume(throwing: error) + } else { + continuation.resume() + } + } + } + } + // MARK: - Loop's stores private func localGlucose(_ interval: DateInterval) async throws -> [GlucosePoint] { diff --git a/Loop/Redesign/TrendsComponents.swift b/Loop/Redesign/TrendsComponents.swift index c98336a72c..0a2c29feae 100644 --- a/Loop/Redesign/TrendsComponents.swift +++ b/Loop/Redesign/TrendsComponents.swift @@ -557,6 +557,43 @@ struct InsulinSplitView: View { } } +/// A day's basal and bolus under a list header: the line, then each part keyed the way the charts and rows draw it. +/// The header shows the day's total itself. +struct InsulinSplitLine: View { + let split: InsulinSplit + var partial = false + + var body: some View { + VStack(alignment: .leading, spacing: 6) { + InsulinSplitBar(split: split, hairline: true) + FlowLayout(spacing: LoopDesign.Space.s3, lineSpacing: 2) { + part(.basal, Text(NSLocalizedString("Basal", comment: "Basal and bolus row: insulin delivered as basal")) + Text(verbatim: " ") + Text(LoopFormat.number(split.basal, decimals: 1))) + if split.isClassified { + if split.manualBolus > 0 { + part(.solid(LoopDesign.Palette.bolus), Text(String(format: NSLocalizedString("Manual %1$@", comment: "Basal and bolus: bolus the person gave. (1: units, e.g. 16.2 U)"), LoopFormat.number(split.manualBolus, decimals: 1)))) + } + if split.automaticBolus > 0 { + part(.hatch(LoopDesign.Palette.bolus), Text(String(format: NSLocalizedString("Automatic %1$@", comment: "Basal and bolus: bolus Loop gave automatically. (1: units, e.g. 5.4 U)"), LoopFormat.number(split.automaticBolus, decimals: 1)))) + } + } else { + part(.solid(LoopDesign.Palette.bolus), Text(NSLocalizedString("Bolus", comment: "Basal and bolus row: insulin delivered as bolus, manual and automatic")) + Text(verbatim: " ") + Text(LoopFormat.number(split.bolus, decimals: 1))) + } + } + .font(LoopDesign.Typeface.footnote.monospacedDigit()) + .foregroundStyle(LoopDesign.Palette.inkMuted) + } + .accessibilityElement(children: .ignore) + .accessibilityLabel(InsulinSplitView.accessibilityLabel(split, partial: partial)) + } + + private func part(_ swatch: LegendItem.Swatch, _ text: Text) -> some View { + HStack(spacing: LoopDesign.Space.s1) { + LegendSwatch(swatch: swatch) + text + } + } +} + // MARK: - InsulinDailyTotals struct InsulinDailyTotalsView: View { diff --git a/Loop/Views/SettingsView.swift b/Loop/Views/SettingsView.swift index babad179cc..71d2b71345 100644 --- a/Loop/Views/SettingsView.swift +++ b/Loop/Views/SettingsView.swift @@ -56,6 +56,7 @@ struct SettingsView: View { case favoriteFoods case presets case trends + case logbook } } @@ -162,6 +163,10 @@ struct SettingsView: View { // Trends brings its own navigation stack, so it is a sheet rather than a push. Done closes only Trends. TrendsView(provider: historyProvider, done: { self.sheet = nil }) } + case .logbook: + if let historyProvider = viewModel.historyProvider, let deviceManager = viewModel.deviceManager { + LogbookView(provider: historyProvider, deviceManager: deviceManager, done: { self.sheet = nil }) + } } } .environmentObject(displayGlucosePreference) @@ -375,6 +380,16 @@ extension SettingsView { descriptiveText: NSLocalizedString("Time in range, daily pattern, insulin per day and Loop status", comment: "Trends settings descriptive text") ) .accessibilityIdentifier("settingsViewTrends") + LargeButton( + action: { sheet = .logbook }, + imageView: Image(systemName: "list.bullet.rectangle") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 30), + label: NSLocalizedString("Logbook", comment: "Logbook settings button text"), + descriptiveText: NSLocalizedString("Boluses, basal changes, carbs and pump events from the last 7 days", comment: "Logbook settings descriptive text") + ) + .accessibilityIdentifier("settingsViewLogbook") } NavigationLink(destination: CurrentSetupView(viewModel: CurrentSetupViewModel( selected: setupChange, diff --git a/LoopTests/Models/LoopHistoryMathTests.swift b/LoopTests/Models/LoopHistoryMathTests.swift index baa7b68960..4c4628ca36 100644 --- a/LoopTests/Models/LoopHistoryMathTests.swift +++ b/LoopTests/Models/LoopHistoryMathTests.swift @@ -339,7 +339,7 @@ class LoopHistoryMathTests: XCTestCase { XCTAssertEqual(LoopHistoryProvider.describe(issue: "pumpDataTooOld"), "Pump data too old") } - // MARK: Day history + // MARK: Day history and Logbook folding func testDayHistoryClippedToNowDropsLaterEventsAndCutsBasal() { var history = DayHistory(window: DateInterval(start: at(0), end: at(1440))) @@ -353,4 +353,33 @@ class LoopHistoryMathTests: XCTestCase { XCTAssertEqual(clipped.boluses.map(\.id), ["a"]) XCTAssertEqual(clipped.basalUnits, 10, accuracy: 1e-9) } + + private func logRow(_ id: String, _ minutes: Double, automaticUnits: Double? = nil, basalSummary: Bool = false) -> LogEntry { + LogEntry(id: id, date: at(minutes), title: id, subtitle: "", amount: "", amountColor: .primary, detail: "", + icon: "plus", iconStyle: .solid(.primary), filters: [], + basalChanges: basalSummary ? [BasalSegment(start: at(0), end: at(30), rate: 0, scheduledRate: 1, kind: .temp, automatic: true)] : [], + automaticUnits: automaticUnits) + } + + @MainActor + func testLogbookFoldsRunsOfAutomaticBolusesBetweenOtherEntries() { + // Newest first, as the Logbook lists them. + let rows = [ + logRow("auto5", 100, automaticUnits: 0.3), + logRow("auto4", 95, automaticUnits: 0.2), + logRow("basal", 90, basalSummary: true), + logRow("auto3", 85, automaticUnits: 0.1), + logRow("carbs", 60), + logRow("auto2", 30, automaticUnits: 0.5), + logRow("manual", 20), + logRow("auto1", 10, automaticUnits: 0.1), + ] + let folded = LogbookModel.foldingAutomaticBoluses(rows) + // auto5, auto4 and auto3 fold together: the basal summary doesn't break a run, the carbs do. + XCTAssertEqual(folded.map(\.id), ["autorun-auto5", "basal", "carbs", "auto2", "manual", "auto1"]) + XCTAssertEqual(folded[0].bolusRun.map(\.id), ["auto5", "auto4", "auto3"]) + XCTAssertEqual(folded[0].date, at(100)) + XCTAssertTrue(folded[0].matches(.loop) && folded[0].matches(.bolus)) + XCTAssertEqual(folded[0].amount, LoopFormat.doseUnits(0.6)) + } } From 2a92d656d1e329b1373ee07a6d1eaf157efa6d27 Mon Sep 17 00:00:00 2001 From: Garry Casey Date: Sun, 1 Nov 2026 14:03:27 +0000 Subject: [PATCH 4/5] Add the Now home screen with Now, Trends, Logbook and Settings tabs Now replaces the classic status screen's HUD and charts with one screen: the current glucose with its trend and target, Loop's state in words, the pump and CGM, IOB, COB and the current basal against plan, and Add carbs, Bolus and Presets. Under it, the next three hours with Loop's prediction and today so far on one timeline. Device and Loop problems are written out as notices that open the screen that fixes them. Now is StatusTableViewController's table with the new rows, so every existing flow keeps working from it: bolus progress and cancel, suspend and resume, alerts, presets, the insulin delivery log, carb absorption, the prediction breakdown, pump and CGM settings, the last Loop error, deep links and activity restoration. The last-error check compares with Loop's last completion rather than the HUD's, as Now has no HUD. Trends and the Logbook become tabs. Settings stays a sheet: its tab selects Now and presents Settings from there, as device and service screens open through StatusTableViewController. Deep links select Now first. A New Interface toggle in Settings switches back to the classic status screen with its action tabs, where Statistics, Trends and the Logbook stay in Settings. The new interface is on by default. This is part 3 of 6 splitting #2621, and builds on parts 1 and 2. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01QHnVP7ynB62LNuuYMy3yvH --- Loop.xcodeproj/project.pbxproj | 8 + .../status-critical.colorset/Contents.json | 38 + Loop/Localizable.xcstrings | 8900 ++++++++++++++--- Loop/Managers/LoopAppManager.swift | 79 +- Loop/Redesign/DayCharts.swift | 112 + Loop/Redesign/DayDetailView.swift | 2 +- Loop/Redesign/LoopDesign.swift | 26 +- Loop/Redesign/LoopHistoryModels.swift | 12 +- Loop/Redesign/LoopHistoryProvider.swift | 13 + Loop/Redesign/MainTabBarController.swift | 82 + Loop/Redesign/NowScreen.swift | 770 ++ Loop/Redesign/TrendsScreen.swift | 4 +- .../StatusTableViewController.swift | 237 +- Loop/Views/SettingsView.swift | 47 +- Loop/Views/StatusTableView.swift | 48 +- 15 files changed, 9032 insertions(+), 1346 deletions(-) create mode 100644 Loop/DerivedAssetsBase.xcassets/status-critical.colorset/Contents.json create mode 100644 Loop/Redesign/MainTabBarController.swift create mode 100644 Loop/Redesign/NowScreen.swift diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index 62f600c001..3000aa0db2 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -733,6 +733,8 @@ A743DC26D066EE5AC88C2F45 /* LoopHistoryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */; }; 6A6C1E1C7A26EC88AFB4FAB1 /* DayDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */; }; 23B98F0A53A093412229364D /* LogbookScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEDC42672A7A8C57546EA0C /* LogbookScreen.swift */; }; + 3ED693ACBF8F9E416055BD56 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BECBB6357021979B9443F /* MainTabBarController.swift */; }; + 7705483CFFCF0B89F8F1A08E /* NowScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0874B7B679DB9CE661CCE152 /* NowScreen.swift */; }; 591F13AF435B37B4818E2F2B /* TrendsComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */; }; 5CD0888FF33D6F844A81E638 /* TrendsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF0FD64B2B3E9C9AA8764684 /* TrendsScreen.swift */; }; 97CF13197936D2794766C84A /* LoopHistoryMathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42B08644C3031D9E815F9892 /* LoopHistoryMathTests.swift */; }; @@ -1613,6 +1615,8 @@ 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopHistoryProvider.swift; sourceTree = ""; }; E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayDetailView.swift; sourceTree = ""; }; 7AEDC42672A7A8C57546EA0C /* LogbookScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogbookScreen.swift; sourceTree = ""; }; + 844BECBB6357021979B9443F /* MainTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = ""; }; + 0874B7B679DB9CE661CCE152 /* NowScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowScreen.swift; sourceTree = ""; }; 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendsComponents.swift; sourceTree = ""; }; AF0FD64B2B3E9C9AA8764684 /* TrendsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendsScreen.swift; sourceTree = ""; }; 42B08644C3031D9E815F9892 /* LoopHistoryMathTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopHistoryMathTests.swift; sourceTree = ""; }; @@ -3019,6 +3023,8 @@ children = ( AF0FD64B2B3E9C9AA8764684 /* TrendsScreen.swift */, 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */, + 0874B7B679DB9CE661CCE152 /* NowScreen.swift */, + 844BECBB6357021979B9443F /* MainTabBarController.swift */, 7AEDC42672A7A8C57546EA0C /* LogbookScreen.swift */, E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */, 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */, @@ -3690,6 +3696,8 @@ files = ( 5CD0888FF33D6F844A81E638 /* TrendsScreen.swift in Sources */, 591F13AF435B37B4818E2F2B /* TrendsComponents.swift in Sources */, + 7705483CFFCF0B89F8F1A08E /* NowScreen.swift in Sources */, + 3ED693ACBF8F9E416055BD56 /* MainTabBarController.swift in Sources */, 23B98F0A53A093412229364D /* LogbookScreen.swift in Sources */, 6A6C1E1C7A26EC88AFB4FAB1 /* DayDetailView.swift in Sources */, A743DC26D066EE5AC88C2F45 /* LoopHistoryProvider.swift in Sources */, diff --git a/Loop/DerivedAssetsBase.xcassets/status-critical.colorset/Contents.json b/Loop/DerivedAssetsBase.xcassets/status-critical.colorset/Contents.json new file mode 100644 index 0000000000..9c67fd2db6 --- /dev/null +++ b/Loop/DerivedAssetsBase.xcassets/status-critical.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "red": "1.000", + "green": "0.231", + "blue": "0.188", + "alpha": "1.000" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "red": "1.000", + "green": "0.271", + "blue": "0.227", + "alpha": "1.000" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} \ No newline at end of file diff --git a/Loop/Localizable.xcstrings b/Loop/Localizable.xcstrings index 848e10750e..d4dd4b8eea 100644 --- a/Loop/Localizable.xcstrings +++ b/Loop/Localizable.xcstrings @@ -1443,6 +1443,435 @@ } } }, + "%1$@ vs plan" : { + "comment" : "Basal tile on the Now screen: how the current basal rate differs from the scheduled rate. (1: signed difference in U/h, e.g. +0.25)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ مقارنة بالمجدول" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ oproti plánu" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ ift. plan" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ ggü. Plan" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ vs. lo programado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ vs. ohjelmoitu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ vs prévu" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ לעומת התוכנית" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ a tervhez képest" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ vs programmata" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "予定比 %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mot plan" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ t.o.v. gepland" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ wzgl. planu" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ vs. programado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ față de program" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ к плану" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ oproti plánu" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ mot schema" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "plana göre %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ до плану" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ so với lịch" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "较预设 %1$@" + } + } + } + }, + "%1$@ · %2$@ in 5 min" : { + "comment" : "Under the glucose value on the Now screen: the glucose unit and how much glucose changed over 5 minutes. (1: glucose unit, e.g. mmol/L; 2: signed change, e.g. +0.3)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ خلال ٥ دقائق" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ za 5 min" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ på 5 min" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ in 5 Min" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ en 5 min" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ / 5 min" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ en 5 min" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ ב-5 דק׳" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ 5 perc alatt" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ in 5 min" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · 5分で%2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ på 5 min" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ in 5 min" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ w 5 min" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ em 5 min" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ în 5 min" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ за 5 мин" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ za 5 min" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ på 5 min" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · 5 dk'da %2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ за 5 хв" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · %2$@ trong 5 phút" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · 5分钟内%2$@" + } + } + } + }, + "%1$@ · ending soon" : { + "comment" : "Status pill on the Now screen when a pump pod or CGM sensor is near the end of its life. (1: device name from the pump or CGM plugin, e.g. Omnipod 5)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · ينتهي قريبًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · brzy výměna" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · udløber snart" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · läuft bald ab" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · termina pronto" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · päättyy pian" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · fin proche" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · מסתיים בקרוב" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · hamarosan lejár" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · in scadenza" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · まもなく終了" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · utløper snart" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · verloopt binnenkort" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · wkrótce wymiana" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · terminando em breve" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · expiră curând" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · скоро замена" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · čoskoro výmena" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · går snart ut" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · yakında bitiyor" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · незабаром заміна" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · sắp hết hạn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ · 即将到期" + } + } + } + }, "%1$@%%" : { "comment" : "A percentage. (1: number)", "localizations" : { @@ -5148,6 +5577,292 @@ } } }, + "Add carbs" : { + "comment" : "Button on the Now screen that opens carb entry", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إضافة كارب" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přidat sacharidy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilføj kulhydrater" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "KH hinzufügen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agregar carbohidratos" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lisää hiilihydraatteja" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter des glucides" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הוספת פחמימות" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Szénhidrát hozzáadása" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiungi carboidrati" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "カーボを追加" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legg til karbohydrater" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koolhydraten toevoegen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dodaj węglowodany" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicionar carbs" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adăugați carbohidrați" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добавить углеводы" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pridať sacharidy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lägg till kolhydrater" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Karb ekle" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Додати вуглеводи" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thêm Carbs" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "添加碳水" + } + } + } + }, + "Add pump" : { + "comment" : "Status pill on the Now screen when no pump is set up; tapping it starts pump setup", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إضافة مضخة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přidat pumpu" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilføj pumpe" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpe hinzufügen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agregar microinfusadora" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lisää pumppu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter une pompe" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הוספת משאבה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpa hozzáadása" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiungi pompa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ポンプを追加" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legg til pumpe" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pomp toevoegen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dodaj pompę" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicionar bomba" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adăugați pompă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добавить помпу" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pridať pumpu" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lägg till pump" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompa ekle" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Додати помпу" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thêm bơm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "添加胰岛素泵" + } + } + } + }, "Aim: average under %1$@ %2$@, GMI under 7%%, CV 36%% or less." : { "comment" : "Trends Summary footnote: the consensus targets for average glucose, GMI and glucose variability (CV). (1: glucose value, 2: glucose unit)", "localizations" : { @@ -8866,6 +9581,578 @@ } } }, + "Closed loop paused · %1$@" : { + "comment" : "Status pill on the Now screen: closed loop is switched on but paused (e.g. no CGM data), and Loop last ran this long ago. (1: duration, e.g. 3m)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الحلقة المغلقة متوقفة مؤقتًا · %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavřená smyčka pozastavena · %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop sat på pause · %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop pausiert · %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Circuito cerrado en pausa · %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suljettu säätö tauolla · %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boucle fermée en pause · %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop מושהה · %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zárt hurok szüneteltetve · %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop chiuso in pausa · %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クローズドループ一時停止中 · %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop satt på pause · %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve loop gepauzeerd · %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pętla zamknięta wstrzymana · %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ciclo fechado pausado · %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buclă închisă în pauză · %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутый цикл на паузе · %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavretá slučka pozastavená · %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sluten loop pausad · %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kapalı döngü duraklatıldı · %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутий цикл на паузі · %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vòng lặp kín tạm dừng · %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "闭环已暂停 · %1$@" + } + } + } + }, + "Closed loop paused · not run yet" : { + "comment" : "Status pill on the Now screen: closed loop is switched on but paused (e.g. no CGM data), and Loop has never completed", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الحلقة المغلقة متوقفة مؤقتًا · لم يعمل بعد" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavřená smyčka pozastavena · zatím neproběhla" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop sat på pause · ikke kørt endnu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop pausiert · noch nicht gelaufen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Circuito cerrado en pausa · aún no se ha ejecutado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suljettu säätö tauolla · ei vielä ajettu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boucle fermée en pause · pas encore exécuté" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop מושהה · טרם רץ" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zárt hurok szüneteltetve · még nem futott" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop chiuso in pausa · non ancora eseguito" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クローズドループ一時停止中 · 未実行" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop satt på pause · ikke kjørt ennå" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve loop gepauzeerd · nog niet gedraaid" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pętla zamknięta wstrzymana · jeszcze nie działała" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ciclo fechado pausado · ainda não executou" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buclă închisă în pauză · nu a rulat încă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутый цикл на паузе · ещё не запускался" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavretá slučka pozastavená · ešte neprebehla" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sluten loop pausad · inte körd än" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kapalı döngü duraklatıldı · henüz çalışmadı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутий цикл на паузі · ще не запускався" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vòng lặp kín tạm dừng · chưa chạy" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "闭环已暂停 · 尚未运行" + } + } + } + }, + "Closed loop · %1$@" : { + "comment" : "Status pill on the Now screen: closed loop is on and Loop last ran this long ago. (1: duration, e.g. 3m)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حلقة مغلقة · %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavřená smyčka · %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop · %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop · %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Circuito cerrado · %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suljettu säätö · %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boucle fermée · %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop · %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zárt hurok · %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop chiuso · %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クローズドループ · %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop · %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve loop · %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pętla zamknięta · %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ciclo fechado · %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buclă închisă · %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутый цикл · %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavretá slučka · %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sluten loop · %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kapalı döngü · %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутий цикл · %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vòng lặp kín · %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "闭环 · %1$@" + } + } + } + }, + "Closed loop · not run yet" : { + "comment" : "Status pill on the Now screen: closed loop is on but Loop has never completed", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حلقة مغلقة · لم يعمل بعد" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavřená smyčka · zatím neproběhla" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop · ikke kørt endnu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop · noch nicht gelaufen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Circuito cerrado · aún no se ha ejecutado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suljettu säätö · ei vielä ajettu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boucle fermée · pas encore exécuté" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Closed Loop · טרם רץ" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zárt hurok · még nem futott" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop chiuso · non ancora eseguito" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クローズドループ · 未実行" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukket Loop · ikke kjørt ennå" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actieve loop · nog niet gedraaid" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pętla zamknięta · jeszcze nie działała" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ciclo fechado · ainda não executou" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buclă închisă · nu a rulat încă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутый цикл · ещё не запускался" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uzavretá slučka · ešte neprebehla" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sluten loop · inte körd än" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kapalı döngü · henüz çalışmadı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замкнутий цикл · ще не запускався" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vòng lặp kín · chưa chạy" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "闭环 · 尚未运行" + } + } + } + }, "COB" : { "comment" : "Abbreviation for carbs on board (active carbohydrate) Abbreviation for carbs on board; label of a tile on the Now screen", "localizations" : { @@ -9867,6 +11154,149 @@ } } }, + "Couldn't load recent CGM readings" : { + "comment" : "Prediction chart on the Now screen when recent glucose couldn't be read", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعذّر تحميل قراءات CGM الأخيرة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nelze načíst nedávné hodnoty CGM" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunne ikke indlæse de seneste CGM-målinger" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktuelle CGM-Werte konnten nicht geladen werden" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudieron cargar las lecturas recientes del CGM" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Viimeisimpiä CGM-lukemia ei voitu ladata" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de charger les mesures CGM récentes" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא ניתן היה לטעון קריאות CGM אחרונות" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nem sikerült betölteni a legutóbbi CGM-értékeket" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile caricare le letture CGM recenti" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "最近のCGM値を読み込めませんでした" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunne ikke laste inn nylige CGM-målinger" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recente CGM-waarden kunnen niet worden geladen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie udało się wczytać ostatnich odczytów CGM" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível carregar as leituras recentes do CGM" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nu s-au putut încărca citirile CGM recente" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удалось загрузить последние показания CGM" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nedajú sa načítať nedávne hodnoty CGM" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunde inte läsa in senaste CGM-värden" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Son CGM okumaları yüklenemedi" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не вдалося завантажити останні показники CGM" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không thể tải các chỉ số CGM gần đây" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法载入最近的 CGM 读数" + } + } + } + }, "Couldn't load this day: %1$@" : { "comment" : "Day page error when the day's history can't be read. (1: system error description)", "localizations" : { @@ -10010,6 +11440,149 @@ } } }, + "Couldn't load today's history: %1$@" : { + "comment" : "Shown on the Now screen's Today so far card when history couldn't be read. (1: system error description)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعذّر تحميل سجل اليوم: %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nelze načíst dnešní historii: %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunne ikke indlæse dagens historik: %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der heutige Verlauf konnte nicht geladen werden: %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo cargar el historial de hoy: %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tämän päivän historiaa ei voitu ladata: %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de charger l'historique du jour : %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא ניתן היה לטעון את היסטוריית היום: %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nem sikerült betölteni a mai előzményeket: %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile caricare la cronologia di oggi: %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "今日の履歴を読み込めませんでした:%1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunne ikke laste inn dagens historikk: %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De geschiedenis van vandaag kan niet worden geladen: %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie udało się wczytać dzisiejszej historii: %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível carregar o histórico de hoje: %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nu s-a putut încărca istoricul de azi: %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удалось загрузить историю за сегодня: %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dnešná história sa nedá načítať: %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunde inte läsa in dagens historik: %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bugünün geçmişi yüklenemedi: %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не вдалося завантажити історію за сьогодні: %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không thể tải lịch sử hôm nay: %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法载入今天的历史记录:%1$@" + } + } + } + }, "Couldn't load Trends: %1$@" : { "comment" : "Trends error message when the history can't be read. (1: error description)", "localizations" : { @@ -11446,6 +13019,149 @@ } } }, + "Eventually %1$@ %2$@" : { + "comment" : "Prediction card on the Now screen: Loop's eventual predicted glucose. (1: glucose value; 2: glucose unit, e.g. mmol/L)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "في النهاية %1$@ %2$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nakonec %1$@ %2$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Med tiden %1$@ %2$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voraussichtlich %1$@ %2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al final %1$@ %2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ennuste %1$@ %2$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Finalement %1$@ %2$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eventually %1$@ %2$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Várható %1$@ %2$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Infine %1$@ %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "予想 %1$@ %2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Omsider %1$@ %2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uiteindelijk %1$@ %2$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Docelowo %1$@ %2$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "No final %1$@ %2$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "În cele din urmă %1$@ %2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "В конечном итоге %1$@ %2$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nakoniec %1$@ %2$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Förväntat %1$@ %2$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nihai KŞ %1$@ %2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Зрештою %1$@ %2$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kết quả %1$@ %2$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "最终 %1$@ %2$@" + } + } + } + }, "Export" : { "comment" : "Trends menu title: export the period as a PDF report or CSV file", "localizations" : { @@ -12304,6 +14020,149 @@ } } }, + "Glucose prediction unavailable" : { + "comment" : "VoiceOver label for the prediction chart when Loop has no prediction", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "توقع السكر غير متاح" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predikce glukózy není k dispozici" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glukoseforudsigelse ikke tilgængelig" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blutzuckervorhersage nicht verfügbar" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predicción de glucosa no disponible" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glukoosiennuste ei saatavilla" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prédiction de glycémie indisponible" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "תחזית סוכר אינה זמינה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glükózelőrejelzés nem érhető el" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Previsione glicemica non disponibile" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "血糖値の予測はありません" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blodsukkerprognose ikke tilgjengelig" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glucosevoorspelling niet beschikbaar" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prognoza glukozy niedostępna" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Previsão de glicose indisponível" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predicția glicemiei indisponibilă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Прогноз глюкозы недоступен" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predikcia glykémie nie je k dispozícii" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glukosprognos ej tillgänglig" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kan şekeri tahmini yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Прогноз глюкози недоступний" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có dự đoán đường huyết" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "血糖预测不可用" + } + } + } + }, "Glucose too old" : { "comment" : "Reason Loop could not run: the latest glucose reading was too old", "localizations" : { @@ -12876,6 +14735,149 @@ } } }, + "Insulin isn't being adjusted. Tap for details." : { + "comment" : "Notice detail on the Now screen when Loop hasn't completed recently", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا يتم تعديل الأنسولين. اضغط للتفاصيل." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulín se neupravuje. Klepněte pro podrobnosti." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin bliver ikke justeret. Tryk for detaljer." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin wird nicht angepasst. Für Details tippen." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La insulina no se está ajustando. Toca para ver detalles." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuliinia ei säädetä. Napauta nähdäksesi lisätiedot." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'insuline n'est pas ajustée. Touchez pour en savoir plus." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "האינסולין אינו מותאם. הקש לפרטים." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Az inzulin nem kerül módosításra. Koppints a részletekért." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'insulina non viene regolata. Tocca per i dettagli." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "インスリンが調整されていません。タップして詳細を表示。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin blir ikke justert. Trykk for detaljer." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insuline wordt niet aangepast. Tik voor details." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina nie jest dostosowywana. Stuknij, aby zobaczyć szczegóły." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "A insulina não está sendo ajustada. Toque para ver detalhes." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulina nu este ajustată. Atingeți pentru detalii." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Инсулин не регулируется. Нажмите для подробностей." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inzulín sa neupravuje. Klepnite pre podrobnosti." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulinet justeras inte. Tryck för detaljer." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "İnsülin ayarlanmıyor. Ayrıntılar için dokunun." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Інсулін не регулюється. Торкніться, щоб дізнатися більше." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insulin không được điều chỉnh. Chạm để xem chi tiết." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "胰岛素未在调整。轻点查看详情。" + } + } + } + }, "Insulin per day" : { "comment" : "Trends tile and card title: average total insulin per day", "localizations" : { @@ -14020,6 +16022,149 @@ } } }, + "Last reading %1$@ ago" : { + "comment" : "Shown on the Now screen under No reading when the latest CGM reading is too old. (1: duration, e.g. 25m)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "آخر قراءة قبل %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poslední hodnota před %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seneste måling for %1$@ siden" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Letzter Wert vor %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Última lectura hace %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Viimeisin lukema %1$@ sitten" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dernière mesure il y a %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קריאה אחרונה לפני %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utolsó érték %1$@ ezelőtt" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ultima lettura %1$@ fa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "最終測定 %1$@前" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siste måling for %1$@ siden" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Laatste waarde %1$@ geleden" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ostatni odczyt %1$@ temu" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Última leitura há %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ultima citire acum %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Последнее показание %1$@ назад" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posledná hodnota pred %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Senaste värde för %1$@ sedan" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Son okuma %1$@ önce" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Останній показник %1$@ тому" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chỉ số cuối cách đây %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "最后读数在 %1$@前" + } + } + } + }, "Limited" : { "comment" : "Trends Loop status: time closed loop was on but Loop wasn't completing runs", "localizations" : { @@ -14735,6 +16880,149 @@ } } }, + "Loop can't adjust insulin without readings." : { + "comment" : "Notice detail on the Now screen under a CGM problem when there is no recent glucose reading", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا يستطيع Loop تعديل الأنسولين بدون قراءات." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop nemůže upravovat inzulín bez hodnot." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop kan ikke justere insulin uden målinger." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop kann das Insulin ohne Messwerte nicht anpassen." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop no puede ajustar la insulina sin lecturas." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop ei voi säätää insuliinia ilman lukemia." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop ne peut pas ajuster l'insuline sans mesures." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop אינו יכול להתאים אינסולין ללא קריאות." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "A Loop értékek nélkül nem tudja módosítani az inzulint." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop non può regolare l'insulina senza letture." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "測定値がないとLoopはインスリンを調整できません。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop kan ikke justere insulin uten målinger." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop kan de insuline niet aanpassen zonder glucosewaarden." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop nie może dostosowywać insuliny bez odczytów." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "O Loop não consegue ajustar a insulina sem leituras." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop nu poate ajusta insulina fără citiri." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop не может регулировать инсулин без показаний." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop nemôže upravovať inzulín bez hodnôt." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop kan inte justera insulinet utan glukosvärden." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop okuma olmadan insülini ayarlayamaz." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop не може регулювати інсулін без показників." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop không thể điều chỉnh insulin khi không có chỉ số." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "没有读数,Loop 无法调整胰岛素。" + } + } + } + }, "Loop didn't run" : { "comment" : "Reason shown for a stretch of time when Loop did not complete a run and recorded no error", "localizations" : { @@ -14878,4962 +17166,7393 @@ } } }, - "Loop keeps the last 7 days on this phone. Older doses and carbs are in Apple Health and on Trends." : { - "comment" : "Logbook footer explaining why only 7 days are listed", + "Loop failed · %1$@" : { + "comment" : "Status pill on the Now screen: Loop hasn't completed for over 15 minutes. (1: duration since Loop last ran, e.g. 16m)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يحتفظ Loop بآخر 7 أيام على هذا الهاتف. الجرعات والكارب الأقدم موجودة في Apple Health وفي الاتجاهات." + "value" : "فشل Loop · %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Loop uchovává posledních 7 dní v tomto telefonu. Starší dávky a sacharidy najdete v aplikaci Zdraví a v Trendech." + "value" : "Loop selhal · %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop gemmer de seneste 7 dage på denne telefon. Ældre doser og kulhydrater findes i Apple Sundhed og under Tendenser." + "value" : "Loop fejlede · %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Loop speichert die letzten 7 Tage auf diesem iPhone. Ältere Dosen und KH findest Du in Apple Health und unter Trends." + "value" : "Loop fehlgeschlagen · %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Loop guarda los últimos 7 días en este teléfono. Las dosis y carbohidratos anteriores están en Apple Salud y en Tendencias." + "value" : "Loop falló · %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop säilyttää viimeiset 7 päivää tässä puhelimessa. Vanhemmat annokset ja hiilihydraatit ovat Apple Terveydessä ja Trendeissä." + "value" : "Loop epäonnistui · %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop conserve les 7 derniers jours sur ce téléphone. Les doses et glucides plus anciens sont dans Apple Santé et dans Tendances." + "value" : "Échec de Loop · %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Loop שומר את 7 הימים האחרונים בטלפון זה. מנות ופחמימות ישנות יותר נמצאות ב-Apple Health ובמגמות." + "value" : "Loop נכשל · %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "A Loop az utolsó 7 napot őrzi meg ezen a telefonon. A régebbi adagok és szénhidrátok az Apple Egészség appban és a Trendekben találhatók." + "value" : "Loop sikertelen · %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Loop conserva gli ultimi 7 giorni su questo telefono. Dosi e carboidrati precedenti sono in Apple Salute e in Tendenze." + "value" : "Loop non riuscito · %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loopはこの端末に直近7日間を保持します。それより前の投与とカーボはAppleヘルスケアとトレンドにあります。" + "value" : "Loop失敗 · %1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop lagrer de siste 7 dagene på denne telefonen. Eldre doser og karbohydrater finner du i Apple Helse og under Trender." + "value" : "Loop feilet · %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop bewaart de laatste 7 dagen op deze telefoon. Oudere doses en koolhydraten vind je in Apple Health en bij Trends." + "value" : "Loop mislukt · %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop przechowuje ostatnie 7 dni na tym telefonie. Starsze dawki i węglowodany są w aplikacji Zdrowie i w Trendach." + "value" : "Błąd Loop · %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "O Loop guarda os últimos 7 dias neste iPhone. Doses e carbs mais antigos estão no app Saúde e em Tendências." + "value" : "Loop falhou · %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Loop păstrează ultimele 7 zile pe acest telefon. Dozele și carbohidrații mai vechi sunt în Apple Sănătate și în Tendințe." + "value" : "Loop eșuat · %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Loop хранит последние 7 дней на этом телефоне. Более ранние дозы и углеводы — в приложении «Здоровье» и в разделе «Тренды»." + "value" : "Сбой Loop · %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop uchováva posledných 7 dní v tomto telefóne. Staršie dávky a sacharidy nájdete v aplikácii Zdravie a v Trendoch." + "value" : "Loop zlyhal · %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loop sparar de senaste 7 dagarna på den här telefonen. Äldre doser och kolhydrater finns i Apple Hälsa och under Trender." + "value" : "Loop misslyckades · %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop son 7 günü bu telefonda saklar. Daha eski dozlar ve karbonhidratlar Apple Health'te ve Eğilimler'dedir." + "value" : "Loop başarısız · %1$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop зберігає останні 7 днів на цьому телефоні. Старіші дози та вуглеводи є в програмі «Здоровʼя» і в розділі «Тренди»." + "value" : "Збій Loop · %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop lưu 7 ngày gần nhất trên điện thoại này. Các liều và Carbs cũ hơn có trong Apple Health và trong Xu hướng." + "value" : "Loop lỗi · %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 在此手机上保留最近 7 天的数据。更早的剂量和碳水可在 Apple 健康和“趋势”中查看。" + "value" : "Loop 失败 · %1$@" } } } }, - "Loop only works out active insulin and carbs for the days it keeps" : { - "comment" : "Day page placeholder for active insulin and carbs on a day older than Loop's 7-day history", + "Loop hasn't run for %1$@" : { + "comment" : "Notice title on the Now screen when Loop hasn't completed recently. (1: duration since Loop last ran, e.g. 16m)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يحسب Loop الأنسولين والكارب النشطين فقط للأيام التي يحتفظ بها" + "value" : "لم يعمل Loop منذ %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Loop počítá aktivní inzulín a sacharidy jen pro dny, které uchovává" + "value" : "Loop neproběhl %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop beregner kun aktivt insulin og kulhydrater for de dage, den gemmer" + "value" : "Loop har ikke kørt i %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Loop berechnet aktives Insulin und aktive KH nur für die gespeicherten Tage" + "value" : "Loop ist seit %1$@ nicht gelaufen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Loop solo calcula la insulina y los carbohidratos activos de los días que guarda" + "value" : "Loop no se ha ejecutado en %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop laskee aktiivisen insuliinin ja hiilihydraatit vain säilyttämilleen päiville" + "value" : "Loopia ei ole ajettu %1$@ aikana" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop calcule l'insuline et les glucides actifs uniquement pour les jours qu'il conserve" + "value" : "Loop ne s'est pas exécuté depuis %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Loop מחשב אינסולין ופחמימות פעילים רק עבור הימים שהוא שומר" + "value" : "Loop לא רץ כבר %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "A Loop csak a megőrzött napokra számolja ki az aktív inzulint és szénhidrátot" + "value" : "A Loop %1$@ óta nem futott" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Loop calcola insulina e carboidrati attivi solo per i giorni che conserva" + "value" : "Loop non eseguito da %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loopが残存インスリンと残存糖質を計算するのは、保持している日のみです" + "value" : "Loopが%1$@実行されていません" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop beregner bare aktivt insulin og karbohydrater for dagene den lagrer" + "value" : "Loop har ikke kjørt på %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop berekent actieve insuline en koolhydraten alleen voor de dagen die het bewaart" + "value" : "Loop heeft %1$@ niet gedraaid" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop oblicza aktywną insulinę i węglowodany tylko dla przechowywanych dni" + "value" : "Loop nie działa od %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "O Loop só calcula a insulina e os carboidratos ativos dos dias que guarda" + "value" : "O Loop não executa há %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Loop calculează insulina și carbohidrații activi doar pentru zilele pe care le păstrează" + "value" : "Loop nu a rulat de %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Loop рассчитывает активный инсулин и углеводы только за хранимые дни" + "value" : "Loop не запускался уже %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop počíta aktívny inzulín a sacharidy len pre dni, ktoré uchováva" + "value" : "Loop neprebehol %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loop beräknar bara aktivt insulin och kolhydrater för de dagar som sparas" + "value" : "Loop har inte körts på %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aktif insülini ve karbı yalnızca sakladığı günler için hesaplar" + "value" : "Loop %1$@ süredir çalışmadı" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop розраховує активний інсулін і вуглеводи лише за дні, які зберігає" + "value" : "Loop не запускався вже %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop chỉ tính insulin và Carbs còn hoạt động cho những ngày nó lưu" + "value" : "Loop đã không chạy trong %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 只为其保留的日期计算活性胰岛素和碳水" + "value" : "Loop 已 %1$@未运行" } } } }, - "Loop status" : { - "comment" : "Trends card title: how long Loop spent in closed loop, limited or open loop", + "Loop keeps the last 7 days on this phone. Older doses and carbs are in Apple Health and on Trends." : { + "comment" : "Logbook footer explaining why only 7 days are listed", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حالة Loop" + "value" : "يحتفظ Loop بآخر 7 أيام على هذا الهاتف. الجرعات والكارب الأقدم موجودة في Apple Health وفي الاتجاهات." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Stav Loop" + "value" : "Loop uchovává posledních 7 dní v tomto telefonu. Starší dávky a sacharidy najdete v aplikaci Zdraví a v Trendech." } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-status" + "value" : "Loop gemmer de seneste 7 dage på denne telefon. Ældre doser og kulhydrater findes i Apple Sundhed og under Tendenser." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-Status" + "value" : "Loop speichert die letzten 7 Tage auf diesem iPhone. Ältere Dosen und KH findest Du in Apple Health und unter Trends." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Estado de Loop" + "value" : "Loop guarda los últimos 7 días en este teléfono. Las dosis y carbohidratos anteriores están en Apple Salud y en Tendencias." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loopin tila" + "value" : "Loop säilyttää viimeiset 7 päivää tässä puhelimessa. Vanhemmat annokset ja hiilihydraatit ovat Apple Terveydessä ja Trendeissä." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "État de Loop" + "value" : "Loop conserve les 7 derniers jours sur ce téléphone. Les doses et glucides plus anciens sont dans Apple Santé et dans Tendances." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מצב Loop" + "value" : "Loop שומר את 7 הימים האחרונים בטלפון זה. מנות ופחמימות ישנות יותר נמצאות ב-Apple Health ובמגמות." } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Loop állapota" + "value" : "A Loop az utolsó 7 napot őrzi meg ezen a telefonon. A régebbi adagok és szénhidrátok az Apple Egészség appban és a Trendekben találhatók." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Stato di Loop" + "value" : "Loop conserva gli ultimi 7 giorni su questo telefono. Dosi e carboidrati precedenti sono in Apple Salute e in Tendenze." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loopの状態" + "value" : "Loopはこの端末に直近7日間を保持します。それより前の投与とカーボはAppleヘルスケアとトレンドにあります。" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-status" + "value" : "Loop lagrer de siste 7 dagene på denne telefonen. Eldre doser og karbohydrater finner du i Apple Helse og under Trender." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loopstatus" + "value" : "Loop bewaart de laatste 7 dagen op deze telefoon. Oudere doses en koolhydraten vind je in Apple Health en bij Trends." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Stan Loop" + "value" : "Loop przechowuje ostatnie 7 dni na tym telefonie. Starsze dawki i węglowodany są w aplikacji Zdrowie i w Trendach." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Status do Loop" + "value" : "O Loop guarda os últimos 7 dias neste iPhone. Doses e carbs mais antigos estão no app Saúde e em Tendências." } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Starea Loop" + "value" : "Loop păstrează ultimele 7 zile pe acest telefon. Dozele și carbohidrații mai vechi sunt în Apple Sănătate și în Tendințe." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Статус Loop" + "value" : "Loop хранит последние 7 дней на этом телефоне. Более ранние дозы и углеводы — в приложении «Здоровье» и в разделе «Тренды»." } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Stav Loop" + "value" : "Loop uchováva posledných 7 dní v tomto telefóne. Staršie dávky a sacharidy nájdete v aplikácii Zdravie a v Trendoch." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loopstatus" + "value" : "Loop sparar de senaste 7 dagarna på den här telefonen. Äldre doser och kolhydrater finns i Apple Hälsa och under Trender." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop durumu" + "value" : "Loop son 7 günü bu telefonda saklar. Daha eski dozlar ve karbonhidratlar Apple Health'te ve Eğilimler'dedir." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Стан Loop" + "value" : "Loop зберігає останні 7 днів на цьому телефоні. Старіші дози та вуглеводи є в програмі «Здоровʼя» і в розділі «Тренди»." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Trạng thái Loop" + "value" : "Loop lưu 7 ngày gần nhất trên điện thoại này. Các liều và Carbs cũ hơn có trong Apple Health và trong Xu hướng." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 状态" + "value" : "Loop 在此手机上保留最近 7 天的数据。更早的剂量和碳水可在 Apple 健康和“趋势”中查看。" } } } }, - "Loop status is recorded from when this version of Loop first ran." : { - "comment" : "Trends Loop status footnote when there is no Loop status history yet", + "Loop may not be giving insulin." : { + "comment" : "Notice detail on the Now screen under a critical pump problem reported by the pump", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تُسجَّل حالة Loop منذ أول تشغيل لهذا الإصدار من Loop." + "value" : "قد لا يقوم Loop بإعطاء الأنسولين." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Stav Loop se zaznamenává od prvního spuštění této verze Loop." + "value" : "Loop možná nepodává inzulín." } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-status registreres fra første gang denne version af Loop kørte." + "value" : "Loop giver muligvis ikke insulin." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Der Loop-Status wird ab dem ersten Start dieser Loop-Version aufgezeichnet." + "value" : "Loop gibt möglicherweise kein Insulin ab." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "El estado de Loop se registra desde la primera vez que se ejecutó esta versión de Loop." + "value" : "Es posible que Loop no esté administrando insulina." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loopin tila tallennetaan siitä lähtien, kun tämä Loopin versio käynnistettiin ensimmäisen kerran." + "value" : "Loop ei ehkä anna insuliinia." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "L'état de Loop est enregistré depuis le premier lancement de cette version de Loop." + "value" : "Loop n'administre peut-être pas d'insuline." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מצב Loop נרשם מאז ההפעלה הראשונה של גרסה זו של Loop." + "value" : "ייתכן ש-Loop אינו מספק אינסולין." } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "A Loop állapota a Loop ezen verziójának első futtatásától kerül rögzítésre." + "value" : "Lehet, hogy a Loop nem ad inzulint." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Lo stato di Loop è registrato dal primo avvio di questa versione di Loop." + "value" : "Loop potrebbe non erogare insulina." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loopの状態は、このバージョンのLoopを初めて起動した時点から記録されます。" + "value" : "Loopがインスリンを注入していない可能性があります。" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-status registreres fra første gang denne versjonen av Loop kjørte." + "value" : "Loop gir kanskje ikke insulin." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De loopstatus wordt vastgelegd vanaf de eerste keer dat deze versie van Loop draaide." + "value" : "Loop geeft mogelijk geen insuline." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Stan Loop jest zapisywany od pierwszego uruchomienia tej wersji Loop." + "value" : "Loop może nie podawać insuliny." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "O status do Loop é registrado desde a primeira vez que esta versão do Loop foi executada." + "value" : "O Loop pode não estar entregando insulina." } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Starea Loop este înregistrată de la prima rulare a acestei versiuni de Loop." + "value" : "Este posibil ca Loop să nu administreze insulină." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Статус Loop записывается с первого запуска этой версии Loop." + "value" : "Возможно, Loop не подаёт инсулин." } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Stav Loop sa zaznamenáva od prvého spustenia tejto verzie Loop." + "value" : "Loop možno nepodáva inzulín." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loopstatus registreras från när den här versionen av Loop först kördes." + "value" : "Loop kanske inte ger insulin." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop durumu, Loop'un bu sürümünün ilk çalıştığı andan itibaren kaydedilir." + "value" : "Loop insülin vermiyor olabilir." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Стан Loop записується з першого запуску цієї версії Loop." + "value" : "Можливо, Loop не подає інсулін." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Trạng thái Loop được ghi lại từ khi phiên bản Loop này chạy lần đầu." + "value" : "Loop có thể không đang tiêm insulin." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 状态从此版本 Loop 首次运行时开始记录。" + "value" : "Loop 可能未在输注胰岛素。" } } } }, - "Loop trends" : { - "comment" : "Title of the exported Trends PDF report", + "Loop only works out active insulin and carbs for the days it keeps" : { + "comment" : "Day page placeholder for active insulin and carbs on a day older than Loop's 7-day history", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "اتجاهات Loop" + "value" : "يحسب Loop الأنسولين والكارب النشطين فقط للأيام التي يحتفظ بها" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Trendy Loop" + "value" : "Loop počítá aktivní inzulín a sacharidy jen pro dny, které uchovává" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-tendenser" + "value" : "Loop beregner kun aktivt insulin og kulhydrater for de dage, den gemmer" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-Trends" + "value" : "Loop berechnet aktives Insulin und aktive KH nur für die gespeicherten Tage" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Tendencias de Loop" + "value" : "Loop solo calcula la insulina y los carbohidratos activos de los días que guarda" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trendit" + "value" : "Loop laskee aktiivisen insuliinin ja hiilihydraatit vain säilyttämilleen päiville" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Tendances Loop" + "value" : "Loop calcule l'insuline et les glucides actifs uniquement pour les jours qu'il conserve" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מגמות Loop" + "value" : "Loop מחשב אינסולין ופחמימות פעילים רק עבור הימים שהוא שומר" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trendek" + "value" : "A Loop csak a megőrzött napokra számolja ki az aktív inzulint és szénhidrátot" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Tendenze Loop" + "value" : "Loop calcola insulina e carboidrati attivi solo per i giorni che conserva" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loopトレンド" + "value" : "Loopが残存インスリンと残存糖質を計算するのは、保持している日のみです" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trender" + "value" : "Loop beregner bare aktivt insulin og karbohydrater for dagene den lagrer" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trends" + "value" : "Loop berekent actieve insuline en koolhydraten alleen voor de dagen die het bewaart" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Trendy Loop" + "value" : "Loop oblicza aktywną insulinę i węglowodany tylko dla przechowywanych dni" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Tendências do Loop" + "value" : "O Loop só calcula a insulina e os carboidratos ativos dos dias que guarda" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Tendințe Loop" + "value" : "Loop calculează insulina și carbohidrații activi doar pentru zilele pe care le păstrează" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Тренды Loop" + "value" : "Loop рассчитывает активный инсулин и углеводы только за хранимые дни" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Trendy Loop" + "value" : "Loop počíta aktívny inzulín a sacharidy len pre dni, ktoré uchováva" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trender" + "value" : "Loop beräknar bara aktivt insulin och kolhydrater för de dagar som sparas" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop eğilimleri" + "value" : "Loop aktif insülini ve karbı yalnızca sakladığı günler için hesaplar" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Тренди Loop" + "value" : "Loop розраховує активний інсулін і вуглеводи лише за дні, які зберігає" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Xu hướng Loop" + "value" : "Loop chỉ tính insulin và Carbs còn hoạt động cho những ngày nó lưu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 趋势" + "value" : "Loop 只为其保留的日期计算活性胰岛素和碳水" } } } }, - "Loop trends %1$@" : { - "comment" : "File name of an exported Trends report or CSV, without the extension. (1: date range of the period)", + "Loop prediction" : { + "comment" : "Chart legend on the Now screen: dashed line is Loop's predicted glucose", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "اتجاهات Loop %1$@" + "value" : "توقع Loop" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Trendy Loop %1$@" + "value" : "Predikce Loop" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-tendenser %1$@" + "value" : "Loop-forudsigelse" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-Trends %1$@" + "value" : "Loop-Vorhersage" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Tendencias de Loop %1$@" + "value" : "Predicción de Loop" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trendit %1$@" + "value" : "Loopin ennuste" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Tendances Loop %1$@" + "value" : "Prédiction de Loop" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מגמות Loop %1$@" + "value" : "תחזית Loop" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trendek %1$@" + "value" : "Loop-előrejelzés" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Tendenze Loop %1$@" + "value" : "Previsione di Loop" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loopトレンド %1$@" + "value" : "Loopの予測" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trender %1$@" + "value" : "Loop-prognose" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trends %1$@" + "value" : "Loop-voorspelling" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Trendy Loop %1$@" + "value" : "Prognoza Loop" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Tendências do Loop %1$@" + "value" : "Previsão do Loop" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Tendințe Loop %1$@" + "value" : "Predicție Loop" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Тренды Loop %1$@" + "value" : "Прогноз Loop" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Trendy Loop %1$@" + "value" : "Predikcia Loop" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loop-trender %1$@" + "value" : "Loops prognos" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop eğilimleri %1$@" + "value" : "Loop tahmini" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Тренди Loop %1$@" + "value" : "Прогноз Loop" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Xu hướng Loop %1$@" + "value" : "Dự đoán của Loop" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop 趋势 %1$@" + "value" : "Loop 预测" } } } }, - "Loop · %1$@ · tap to list" : { - "comment" : "Logbook folded automatic boluses row subtitle: Loop gave them, the time span they cover, and tapping lists them. (1: time range, e.g. 15:08 – 16:14)", + "Loop predicts %1$@ %2$@ by %3$@" : { + "comment" : "VoiceOver summary of the prediction chart. (1: predicted glucose value; 2: glucose unit, mmol/L or mg/dL; 3: time of the prediction)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · اضغط للعرض" + "value" : "يتوقع Loop %1$@ %2$@ بحلول %3$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · klepnutím zobrazíte" + "value" : "Loop predikuje %1$@ %2$@ do %3$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · tryk for at se listen" + "value" : "Loop forudsiger %1$@ %2$@ kl. %3$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · zum Auflisten tippen" + "value" : "Loop sagt %1$@ %2$@ bis %3$@ voraus" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · toca para ver la lista" + "value" : "Loop predice %1$@ %2$@ a las %3$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · napauta nähdäksesi" + "value" : "Loop ennustaa %1$@ %2$@ klo %3$@ mennessä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · touchez pour la liste" + "value" : "Loop prévoit %1$@ %2$@ à %3$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · הקש להצגה" + "value" : "Loop צופה %1$@ %2$@ עד %3$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · koppintson a listához" + "value" : "A Loop előrejelzése %3$@ időpontra: %1$@ %2$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · tocca per l'elenco" + "value" : "Loop prevede %1$@ %2$@ alle %3$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · タップして一覧" + "value" : "Loopの予測:%3$@までに%1$@ %2$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · trykk for å se listen" + "value" : "Loop forventer %1$@ %2$@ kl. %3$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · tik voor lijst" + "value" : "Loop voorspelt %1$@ %2$@ om %3$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · stuknij, aby wyświetlić" + "value" : "Loop prognozuje %1$@ %2$@ do %3$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · toque para ver a lista" + "value" : "O Loop prevê %1$@ %2$@ às %3$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · atingeți pentru listă" + "value" : "Loop prezice %1$@ %2$@ până la %3$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · нажмите для списка" + "value" : "Loop прогнозирует %1$@ %2$@ к %3$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · ťuknutím zobrazíte" + "value" : "Loop predikuje %1$@ %2$@ do %3$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · tryck för att visa" + "value" : "Loop förutspår %1$@ %2$@ kl. %3$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · listelemek için dokunun" + "value" : "Loop %3$@ itibarıyla %1$@ %2$@ tahmin ediyor" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · торкніться для списку" + "value" : "Loop прогнозує %1$@ %2$@ до %3$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · chạm để xem" + "value" : "Loop dự đoán %1$@ %2$@ vào lúc %3$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %1$@ · 轻点查看列表" + "value" : "Loop 预测到 %3$@ 时为 %1$@ %2$@" } } } }, - "Loop · %lld changes · tap to list" : { - "comment" : "Logbook basal adjustments row subtitle: how many temp basals Loop set in the day; tapping lists them. (1: number of changes)", + "Loop status" : { + "comment" : "Trends card title: how long Loop spent in closed loop, limited or open loop", "localizations" : { - "en" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld change · tap to list" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld changes · tap to list" - } - } - } - } - }, "ar" : { - "variations" : { - "plural" : { - "zero" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld تغيير · اضغط للعرض" - } - }, - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld تغيير · اضغط للعرض" - } - }, - "two" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld تغيير · اضغط للعرض" - } - }, - "few" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld تغييرات · اضغط للعرض" - } - }, - "many" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld تغييرًا · اضغط للعرض" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld تغيير · اضغط للعرض" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "حالة Loop" } }, "cs" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld změna · klepněte pro seznam" - } - }, - "few" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld změny · klepněte pro seznam" - } - }, - "many" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld změny · klepněte pro seznam" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld změn · klepněte pro seznam" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Stav Loop" } }, "da" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld ændring · tryk for liste" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld ændringer · tryk for liste" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Loop-status" } }, "de" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld Änderung · tippen für Liste" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld Änderungen · tippen für Liste" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Loop-Status" } }, "es" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld cambio · toca para ver" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld cambios · toca para ver" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Estado de Loop" } }, "fi" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld muutos · näytä napauttamalla" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld muutosta · näytä napauttamalla" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Loopin tila" } }, "fr" : { - "variations" : { - "plural" : { - "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "État de Loop" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מצב Loop" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop állapota" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stato di Loop" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopの状態" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-status" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopstatus" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stan Loop" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Status do Loop" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Starea Loop" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Статус Loop" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stav Loop" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopstatus" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop durumu" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Стан Loop" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trạng thái Loop" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop 状态" + } + } + } + }, + "Loop status is recorded from when this version of Loop first ran." : { + "comment" : "Trends Loop status footnote when there is no Loop status history yet", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تُسجَّل حالة Loop منذ أول تشغيل لهذا الإصدار من Loop." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stav Loop se zaznamenává od prvního spuštění této verze Loop." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-status registreres fra første gang denne version af Loop kørte." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Loop-Status wird ab dem ersten Start dieser Loop-Version aufgezeichnet." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "El estado de Loop se registra desde la primera vez que se ejecutó esta versión de Loop." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopin tila tallennetaan siitä lähtien, kun tämä Loopin versio käynnistettiin ensimmäisen kerran." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "L'état de Loop est enregistré depuis le premier lancement de cette version de Loop." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מצב Loop נרשם מאז ההפעלה הראשונה של גרסה זו של Loop." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "A Loop állapota a Loop ezen verziójának első futtatásától kerül rögzítésre." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lo stato di Loop è registrato dal primo avvio di questa versione di Loop." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopの状態は、このバージョンのLoopを初めて起動した時点から記録されます。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-status registreres fra første gang denne versjonen av Loop kjørte." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De loopstatus wordt vastgelegd vanaf de eerste keer dat deze versie van Loop draaide." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stan Loop jest zapisywany od pierwszego uruchomienia tej wersji Loop." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "O status do Loop é registrado desde a primeira vez que esta versão do Loop foi executada." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Starea Loop este înregistrată de la prima rulare a acestei versiuni de Loop." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Статус Loop записывается с первого запуска этой версии Loop." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stav Loop sa zaznamenáva od prvého spustenia tejto verzie Loop." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopstatus registreras från när den här versionen av Loop först kördes." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop durumu, Loop'un bu sürümünün ilk çalıştığı andan itibaren kaydedilir." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Стан Loop записується з першого запуску цієї версії Loop." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trạng thái Loop được ghi lại từ khi phiên bản Loop này chạy lần đầu." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop 状态从此版本 Loop 首次运行时开始记录。" + } + } + } + }, + "Loop trends" : { + "comment" : "Title of the exported Trends PDF report", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اتجاهات Loop" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-tendenser" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-Trends" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendencias de Loop" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trendit" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendances Loop" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מגמות Loop" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trendek" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendenze Loop" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopトレンド" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trender" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trends" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendências do Loop" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendințe Loop" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тренды Loop" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trender" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop eğilimleri" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тренди Loop" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xu hướng Loop" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop 趋势" + } + } + } + }, + "Loop trends %1$@" : { + "comment" : "File name of an exported Trends report or CSV, without the extension. (1: date range of the period)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اتجاهات Loop %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-tendenser %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-Trends %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendencias de Loop %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trendit %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendances Loop %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מגמות Loop %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trendek %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendenze Loop %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopトレンド %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trender %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trends %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendências do Loop %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tendințe Loop %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тренды Loop %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trendy Loop %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop-trender %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop eğilimleri %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тренди Loop %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xu hướng Loop %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop 趋势 %1$@" + } + } + } + }, + "Loop · %1$@ · tap to list" : { + "comment" : "Logbook folded automatic boluses row subtitle: Loop gave them, the time span they cover, and tapping lists them. (1: time range, e.g. 15:08 – 16:14)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · اضغط للعرض" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · klepnutím zobrazíte" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · tryk for at se listen" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · zum Auflisten tippen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · toca para ver la lista" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · napauta nähdäksesi" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · touchez pour la liste" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · הקש להצגה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · koppintson a listához" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · tocca per l'elenco" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · タップして一覧" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · trykk for å se listen" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · tik voor lijst" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · stuknij, aby wyświetlić" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · toque para ver a lista" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · atingeți pentru listă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · нажмите для списка" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · ťuknutím zobrazíte" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · tryck för att visa" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · listelemek için dokunun" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · торкніться для списку" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · chạm để xem" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %1$@ · 轻点查看列表" + } + } + } + }, + "Loop · %lld changes · tap to list" : { + "comment" : "Logbook basal adjustments row subtitle: how many temp basals Loop set in the day; tapping lists them. (1: number of changes)", + "localizations" : { + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld change · tap to list" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld changes · tap to list" + } + } + } + } + }, + "ar" : { + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغيير · اضغط للعرض" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغيير · اضغط للعرض" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغيير · اضغط للعرض" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغييرات · اضغط للعرض" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغييرًا · اضغط للعرض" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld تغيير · اضغط للعرض" + } + } + } + } + }, + "cs" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld změna · klepněte pro seznam" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld změny · klepněte pro seznam" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld změny · klepněte pro seznam" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld změn · klepněte pro seznam" + } + } + } + } + }, + "da" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld ændring · tryk for liste" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld ændringer · tryk for liste" + } + } + } + } + }, + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld Änderung · tippen für Liste" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld Änderungen · tippen für Liste" + } + } + } + } + }, + "es" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld cambio · toca para ver" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld cambios · toca para ver" + } + } + } + } + }, + "fi" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld muutos · näytä napauttamalla" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld muutosta · näytä napauttamalla" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modification · touchez pour la liste" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modifications · touchez pour la liste" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modifications · touchez pour la liste" + } + } + } + } + }, + "he" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld שינוי · הקש לרשימה" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld שינויים · הקש לרשימה" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld שינויים · הקש לרשימה" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld שינויים · הקש לרשימה" + } + } + } + } + }, + "hu" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld módosítás · koppints a listához" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld módosítás · koppints a listához" + } + } + } + } + }, + "it" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modifica · tocca per l'elenco" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modifiche · tocca per l'elenco" + } + } + } + } + }, + "ja" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld件の変更 · タップで一覧表示" + } + } + } + } + }, + "nb" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld endring · trykk for liste" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld endringer · trykk for liste" + } + } + } + } + }, + "nl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld wijziging · tik voor lijst" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld wijzigingen · tik voor lijst" + } + } + } + } + }, + "pl" : { + "variations" : { + "plural" : { + "one" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %lld modification · touchez pour la liste" + "value" : "Loop · %lld zmiana · stuknij, aby wyświetlić" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmiany · stuknij, aby wyświetlić" } }, "many" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %lld modifications · touchez pour la liste" + "value" : "Loop · %lld zmian · stuknij, aby wyświetlić" } }, "other" : { "stringUnit" : { "state" : "translated", - "value" : "Loop · %lld modifications · touchez pour la liste" + "value" : "Loop · %lld zmiany · stuknij, aby wyświetlić" + } + } + } + } + }, + "pt-BR" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld alteração · toque para listar" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld alterações · toque para listar" + } + } + } + } + }, + "ro" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modificare · atingeți pentru listă" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld modificări · atingeți pentru listă" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld de modificări · atingeți pentru listă" + } + } + } + } + }, + "ru" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld изменение · нажмите для списка" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld изменения · нажмите для списка" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld изменений · нажмите для списка" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld изменения · нажмите для списка" + } + } + } + } + }, + "sk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmena · klepnite pre zoznam" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmeny · klepnite pre zoznam" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmeny · klepnite pre zoznam" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld zmien · klepnite pre zoznam" + } + } + } + } + }, + "sv" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld ändring · tryck för lista" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld ändringar · tryck för lista" + } + } + } + } + }, + "tr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld değişiklik · listelemek için dokunun" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld değişiklik · listelemek için dokunun" + } + } + } + } + }, + "uk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld зміна · торкніться для списку" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld зміни · торкніться для списку" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld змін · торкніться для списку" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld зміни · торкніться для списку" + } + } + } + } + }, + "vi" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld thay đổi · chạm để xem danh sách" + } + } + } + } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop · %lld次更改 · 轻点查看列表" } } } } + } + } + }, + "Manual %1$@" : { + "comment" : "Basal and bolus: bolus the person gave. (1: units, e.g. 16.2 U)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يدوي %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ručně %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuaalinen %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ידני %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kézi %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuale %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "手動 %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Handmatig %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ręcznie %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вручную %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ručne %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вручну %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thủ công %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "手动 %1$@" + } + } + } + }, + "Manual · %1$@" : { + "comment" : "Logbook temp basal row subtitle: set by hand, then the rate and how long. (1: description, e.g. 0.50 U/h for 30m (scheduled 0.80))", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يدوي · %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ručně · %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel · %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell · %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual · %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuaalinen · %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel · %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ידני · %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kézi · %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuale · %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "手動 · %1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell · %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Handmatig · %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ręcznie · %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual · %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manual · %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вручную · %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ručne · %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuell · %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manuel · %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вручну · %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thủ công · %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "手动 · %1$@" + } + } + } + }, + "Median" : { + "comment" : "Trends Daily pattern legend: the median glucose line", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الوسيط" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medián" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Median" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Median" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediana" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediaani" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Médiane" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חציון" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medián" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediana" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "中央値" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Median" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediaan" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediana" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediana" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediană" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Медиана" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medián" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Median" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medyan" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Медіана" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trung vị" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "中位数" + } + } + } + }, + "Missing data error" : { + "comment" : "Reason Loop could not run: data it needs was missing", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "خطأ بيانات مفقودة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chyba chybějících dat" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fejl: manglende data" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fehler: fehlende Daten" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error de datos faltantes" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tietoja puuttuu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erreur de données manquantes" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שגיאת נתונים חסרים" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hiányzó adat" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Errore dati mancanti" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "データ欠落エラー" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Feil: manglende data" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fout: ontbrekende gegevens" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Błąd brakujących danych" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erro de dados ausentes" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eroare: date lipsă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ошибка: нет данных" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chyba chýbajúcich údajov" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data saknas" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eksik veri hatası" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Помилка: немає даних" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lỗi thiếu dữ liệu" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "数据缺失错误" + } + } + } + }, + "Needs 10 days of CGM" : { + "comment" : "Trends GMI tile detail when fewer than 10 days of CGM data are available", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يحتاج إلى 10 أيام من CGM" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vyžaduje 10 dní CGM" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kræver 10 dages CGM" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Benötigt 10 Tage CGM" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Requiere 10 días de CGM" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaatii 10 päivää CGM-dataa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nécessite 10 jours de CGM" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "נדרשים 10 ימי CGM" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "10 nap CGM-adat szükséges" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servono 10 giorni di CGM" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGMデータが10日分必要" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Krever 10 dager med CGM" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vereist 10 dagen CGM" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wymaga 10 dni CGM" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Requer 10 dias de CGM" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Necesită 10 zile de CGM" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нужно 10 дней CGM" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vyžaduje 10 dní CGM" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kräver 10 dagars CGM" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "10 günlük CGM gerekir" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Потрібно 10 днів CGM" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cần 10 ngày dữ liệu CGM" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "需要 10 天 CGM 数据" + } + } + } + }, + "New Interface" : { + "comment" : "Title of the toggle for the redesigned interface", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الواجهة الجديدة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nové rozhraní" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ny brugerflade" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neue Oberfläche" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nueva interfaz" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uusi käyttöliittymä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nouvelle interface" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ממשק חדש" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Új felület" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nuova interfaccia" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "新しいインターフェイス" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nytt grensesnitt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nieuwe interface" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nowy interfejs" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nova interface" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Interfață nouă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Новый интерфейс" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nové rozhranie" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nytt gränssnitt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yeni Arayüz" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Новий інтерфейс" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giao diện mới" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "新界面" + } + } + } + }, + "Next 3 hours" : { + "comment" : "Title of the prediction chart card on the Now screen", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الساعات الثلاث القادمة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Příští 3 hodiny" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Næste 3 timer" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nächste 3 Stunden" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Próximas 3 horas" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seuraavat 3 tuntia" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "3 prochaines heures" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "3 השעות הבאות" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Következő 3 óra" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prossime 3 ore" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "今後3時間" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neste 3 timer" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Komende 3 uur" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Następne 3 godziny" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Próximas 3 horas" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Următoarele 3 ore" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ближайшие 3 часа" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nasledujúce 3 hodiny" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommande 3 timmar" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sonraki 3 saat" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Наступні 3 години" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "3 giờ tới" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "未来 3 小时" + } + } + } + }, + "Next day" : { + "comment" : "Accessibility label for the day page button that steps forward a day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اليوم التالي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Další den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Næste dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nächster Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Día siguiente" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seuraava päivä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jour suivant" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "היום הבא" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Következő nap" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giorno successivo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "翌日" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neste dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Volgende dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Następny dzień" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Próximo dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ziua următoare" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Следующий день" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ďalší deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nästa dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sonraki gün" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Наступний день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ngày sau" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "后一天" + } + } + } + }, + "Next period" : { + "comment" : "VoiceOver label for the Trends button that moves forward one period", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الفترة التالية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Další období" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Næste periode" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nächster Zeitraum" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Período siguiente" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seuraava jakso" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Période suivante" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "התקופה הבאה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Következő időszak" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Periodo successivo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "次の期間" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neste periode" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Volgende periode" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Następny okres" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Próximo período" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perioada următoare" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Следующий период" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ďalšie obdobie" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nästa period" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sonraki dönem" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Наступний період" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Khoảng thời gian sau" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "下一时段" + } + } + } + }, + "No active insulin or carbs on this day" : { + "comment" : "Day page placeholder when there was no insulin or carbs on board all day", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا يوجد أنسولين أو كارب نشط في هذا اليوم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "V tento den nebyl žádný aktivní inzulín ani sacharidy" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intet aktivt insulin eller kulhydrater denne dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kein aktives Insulin und keine aktiven KH an diesem Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin insulina ni carbohidratos activos este día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei aktiivista insuliinia tai hiilihydraatteja tänä päivänä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune insuline ni glucides actifs ce jour-là" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין אינסולין או פחמימות פעילים ביום זה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ezen a napon nem volt aktív inzulin vagy szénhidrát" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna insulina o carboidrati attivi in questo giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "この日は残存インスリンも残存糖質もありません" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen aktivt insulin eller karbohydrater denne dagen" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen actieve insuline of koolhydraten op deze dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak aktywnej insuliny i węglowodanów tego dnia" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem insulina ou carboidratos ativos neste dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fără insulină sau carbohidrați activi în această zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "В этот день не было активного инсулина или углеводов" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "V tento deň nebol žiadny aktívny inzulín ani sacharidy" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inget aktivt insulin eller kolhydrater denna dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bu gün aktif insülin veya karb yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цього дня не було активного інсуліну чи вуглеводів" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có insulin hoặc Carbs còn hoạt động trong ngày này" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "这一天没有活性胰岛素或碳水" + } + } + } + }, + "No CGM data" : { + "comment" : "Trends Average tile detail when the period has no glucose readings", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد بيانات CGM" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žádná data CGM" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine CGM-Daten" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin datos del CGM" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei CGM-dataa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune donnée CGM" + } }, "he" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld שינוי · הקש לרשימה" - } - }, - "two" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld שינויים · הקש לרשימה" - } - }, - "many" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld שינויים · הקש לרשימה" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld שינויים · הקש לרשימה" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "אין נתוני CGM" } }, "hu" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld módosítás · koppints a listához" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld módosítás · koppints a listához" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Nincs CGM-adat" } }, "it" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld modifica · tocca per l'elenco" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld modifiche · tocca per l'elenco" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Nessun dato CGM" } }, "ja" : { - "variations" : { - "plural" : { - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld件の変更 · タップで一覧表示" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "CGMデータなし" } }, "nb" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld endring · trykk for liste" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld endringer · trykk for liste" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data" } }, "nl" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld wijziging · tik voor lijst" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld wijzigingen · tik voor lijst" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Geen CGM-gegevens" } }, "pl" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld zmiana · stuknij, aby wyświetlić" - } - }, - "few" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld zmiany · stuknij, aby wyświetlić" - } - }, - "many" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld zmian · stuknij, aby wyświetlić" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld zmiany · stuknij, aby wyświetlić" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Brak danych CGM" } }, "pt-BR" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld alteração · toque para listar" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld alterações · toque para listar" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Sem dados do CGM" } }, "ro" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld modificare · atingeți pentru listă" - } - }, - "few" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld modificări · atingeți pentru listă" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld de modificări · atingeți pentru listă" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Fără date CGM" } }, "ru" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld изменение · нажмите для списка" - } - }, - "few" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld изменения · нажмите для списка" - } - }, - "many" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld изменений · нажмите для списка" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld изменения · нажмите для списка" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Нет данных CGM" } }, "sk" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld zmena · klepnite pre zoznam" - } - }, - "few" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld zmeny · klepnite pre zoznam" - } - }, - "many" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld zmeny · klepnite pre zoznam" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld zmien · klepnite pre zoznam" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Žiadne údaje CGM" } }, "sv" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld ändring · tryck för lista" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld ändringar · tryck för lista" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Inga CGM-data" } }, "tr" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld değişiklik · listelemek için dokunun" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld değişiklik · listelemek için dokunun" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "CGM verisi yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає даних CGM" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có dữ liệu CGM" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无 CGM 数据" + } + } + } + }, + "No CGM data %1$@ – %2$@" : { + "comment" : "Chart note: a gap in glucose readings. (1: start time; 2: end time)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد بيانات CGM %1$@ – %2$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žádná data CGM %1$@ – %2$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data %1$@ – %2$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine CGM-Daten %1$@ – %2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin datos del CGM %1$@ – %2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei CGM-dataa %1$@ – %2$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune donnée CGM %1$@ – %2$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין נתוני CGM %1$@ – %2$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincs CGM-adat %1$@ – %2$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessun dato CGM %1$@ – %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGMデータなし %1$@ – %2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen CGM-data %1$@ – %2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen CGM-gegevens %1$@ – %2$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak danych CGM %1$@ – %2$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem dados do CGM %1$@ – %2$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fără date CGM %1$@ – %2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет данных CGM %1$@ – %2$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žiadne údaje CGM %1$@ – %2$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga CGM-data %1$@ – %2$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM verisi yok %1$@ – %2$@" } }, "uk" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld зміна · торкніться для списку" - } - }, - "few" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld зміни · торкніться для списку" - } - }, - "many" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld змін · торкніться для списку" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld зміни · торкніться для списку" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Немає даних CGM %1$@ – %2$@" } }, "vi" : { - "variations" : { - "plural" : { - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld thay đổi · chạm để xem danh sách" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "Không có dữ liệu CGM %1$@ – %2$@" } }, "zh-Hans" : { - "variations" : { - "plural" : { - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loop · %lld次更改 · 轻点查看列表" - } - } - } + "stringUnit" : { + "state" : "translated", + "value" : "无 CGM 数据 %1$@ – %2$@" } } } }, - "Manual %1$@" : { - "comment" : "Basal and bolus: bolus the person gave. (1: units, e.g. 16.2 U)", + "No CGM data %1$@ – %2$@ and %3$@" : { + "comment" : "Chart note: the longest gap in glucose readings, plus the shorter ones. (1: start time; 2: end time; 3: number of shorter gaps, e.g. 2 shorter gaps)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يدوي %1$@" + "value" : "لا توجد بيانات CGM %1$@ – %2$@ و %3$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Ručně %1$@" + "value" : "Žádná data CGM %1$@ – %2$@ a %3$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Manuel %1$@" + "value" : "Ingen CGM-data %1$@ – %2$@ og %3$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Manuell %1$@" + "value" : "Keine CGM-Daten %1$@ – %2$@ und %3$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Manual %1$@" + "value" : "Sin datos del CGM %1$@ – %2$@ y %3$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Manuaalinen %1$@" + "value" : "Ei CGM-dataa %1$@ – %2$@ ja %3$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Manuel %1$@" + "value" : "Aucune donnée CGM %1$@ – %2$@ et %3$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ידני %1$@" + "value" : "אין נתוני CGM %1$@ – %2$@ ו-%3$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Kézi %1$@" + "value" : "Nincs CGM-adat %1$@ – %2$@, és %3$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Manuale %1$@" + "value" : "Nessun dato CGM %1$@ – %2$@ e %3$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "手動 %1$@" + "value" : "CGMデータなし %1$@ – %2$@(%3$@)" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Manuell %1$@" + "value" : "Ingen CGM-data %1$@ – %2$@ og %3$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Handmatig %1$@" + "value" : "Geen CGM-gegevens %1$@ – %2$@ en %3$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ręcznie %1$@" + "value" : "Brak danych CGM %1$@ – %2$@ oraz %3$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Manual %1$@" + "value" : "Sem dados do CGM %1$@ – %2$@ e %3$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Manual %1$@" + "value" : "Fără date CGM %1$@ – %2$@ și %3$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Вручную %1$@" + "value" : "Нет данных CGM %1$@ – %2$@ и ещё %3$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Ručne %1$@" + "value" : "Žiadne údaje CGM %1$@ – %2$@ a %3$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Manuell %1$@" + "value" : "Inga CGM-data %1$@ – %2$@ och %3$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Manuel %1$@" + "value" : "CGM verisi yok %1$@ – %2$@ ve %3$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Вручну %1$@" + "value" : "Немає даних CGM %1$@ – %2$@ і ще %3$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thủ công %1$@" + "value" : "Không có dữ liệu CGM %1$@ – %2$@ và %3$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "手动 %1$@" + "value" : "无 CGM 数据 %1$@ – %2$@,%3$@" } } } }, - "Manual · %1$@" : { - "comment" : "Logbook temp basal row subtitle: set by hand, then the rate and how long. (1: description, e.g. 0.50 U/h for 30m (scheduled 0.80))", + "No CGM data in the last 3 hours" : { + "comment" : "Empty prediction chart on the Now screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يدوي · %1$@" + "value" : "لا توجد بيانات CGM في آخر 3 ساعات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Ručně · %1$@" + "value" : "Žádná data CGM za poslední 3 hodiny" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Manuel · %1$@" + "value" : "Ingen CGM-data de seneste 3 timer" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Manuell · %1$@" + "value" : "Keine CGM-Daten in den letzten 3 Stunden" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Manual · %1$@" + "value" : "Sin datos del CGM en las últimas 3 horas" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Manuaalinen · %1$@" + "value" : "Ei CGM-dataa viimeisen 3 tunnin aikana" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Manuel · %1$@" + "value" : "Aucune donnée CGM ces 3 dernières heures" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ידני · %1$@" + "value" : "אין נתוני CGM ב-3 השעות האחרונות" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Kézi · %1$@" + "value" : "Nincs CGM-adat az elmúlt 3 órában" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Manuale · %1$@" + "value" : "Nessun dato CGM nelle ultime 3 ore" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "手動 · %1$@" + "value" : "過去3時間のCGMデータなし" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Manuell · %1$@" + "value" : "Ingen CGM-data de siste 3 timene" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Handmatig · %1$@" + "value" : "Geen CGM-gegevens in de afgelopen 3 uur" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ręcznie · %1$@" + "value" : "Brak danych CGM z ostatnich 3 godzin" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Manual · %1$@" + "value" : "Sem dados do CGM nas últimas 3 horas" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Manual · %1$@" + "value" : "Fără date CGM în ultimele 3 ore" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Вручную · %1$@" + "value" : "Нет данных CGM за последние 3 часа" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Ručne · %1$@" + "value" : "Žiadne údaje CGM za posledné 3 hodiny" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Manuell · %1$@" + "value" : "Inga CGM-data de senaste 3 timmarna" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Manuel · %1$@" + "value" : "Son 3 saatte CGM verisi yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Вручну · %1$@" + "value" : "Немає даних CGM за останні 3 години" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thủ công · %1$@" + "value" : "Không có dữ liệu CGM trong 3 giờ qua" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "手动 · %1$@" + "value" : "过去 3 小时无 CGM 数据" } } } }, - "Median" : { - "comment" : "Trends Daily pattern legend: the median glucose line", + "No CGM data in this period" : { + "comment" : "Trends chart placeholder when the period has no glucose readings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الوسيط" + "value" : "لا توجد بيانات CGM في هذه الفترة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Medián" + "value" : "Žádná data CGM v tomto období" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Median" + "value" : "Ingen CGM-data i denne periode" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Median" + "value" : "Keine CGM-Daten in diesem Zeitraum" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mediana" + "value" : "Sin datos del CGM en este período" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Mediaani" + "value" : "Ei CGM-dataa tällä jaksolla" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Médiane" + "value" : "Aucune donnée CGM sur cette période" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "חציון" + "value" : "אין נתוני CGM בתקופה זו" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Medián" + "value" : "Nincs CGM-adat ebben az időszakban" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Mediana" + "value" : "Nessun dato CGM in questo periodo" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "中央値" + "value" : "この期間のCGMデータなし" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Median" + "value" : "Ingen CGM-data i denne perioden" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Mediaan" + "value" : "Geen CGM-gegevens in deze periode" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Mediana" + "value" : "Brak danych CGM w tym okresie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Mediana" + "value" : "Sem dados do CGM neste período" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Mediană" + "value" : "Fără date CGM în această perioadă" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Медиана" + "value" : "Нет данных CGM за этот период" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Medián" + "value" : "Žiadne údaje CGM v tomto období" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Median" + "value" : "Inga CGM-data under denna period" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Medyan" + "value" : "Bu dönemde CGM verisi yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Медіана" + "value" : "Немає даних CGM за цей період" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Trung vị" + "value" : "Không có dữ liệu CGM trong khoảng thời gian này" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "中位数" + "value" : "此时段无 CGM 数据" } } } }, - "Missing data error" : { - "comment" : "Reason Loop could not run: data it needs was missing", + "No CGM data on this day" : { + "comment" : "Chart note: there are no glucose readings for the whole day", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "خطأ بيانات مفقودة" + "value" : "لا توجد بيانات CGM في هذا اليوم" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Chyba chybějících dat" + "value" : "Žádná data CGM v tento den" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Fejl: manglende data" + "value" : "Ingen CGM-data denne dag" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fehler: fehlende Daten" + "value" : "Keine CGM-Daten an diesem Tag" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Error de datos faltantes" + "value" : "Sin datos del CGM este día" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Tietoja puuttuu" + "value" : "Ei CGM-dataa tänä päivänä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Erreur de données manquantes" + "value" : "Aucune donnée CGM ce jour-là" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שגיאת נתונים חסרים" + "value" : "אין נתוני CGM ביום זה" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Hiányzó adat" + "value" : "Nincs CGM-adat ezen a napon" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Errore dati mancanti" + "value" : "Nessun dato CGM in questo giorno" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "データ欠落エラー" + "value" : "この日のCGMデータなし" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Feil: manglende data" + "value" : "Ingen CGM-data denne dagen" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Fout: ontbrekende gegevens" + "value" : "Geen CGM-gegevens op deze dag" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Błąd brakujących danych" + "value" : "Brak danych CGM tego dnia" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Erro de dados ausentes" + "value" : "Sem dados do CGM neste dia" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Eroare: date lipsă" + "value" : "Fără date CGM în această zi" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Ошибка: нет данных" + "value" : "Нет данных CGM за этот день" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Chyba chýbajúcich údajov" + "value" : "Žiadne údaje CGM v tento deň" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Data saknas" + "value" : "Inga CGM-data denna dag" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Eksik veri hatası" + "value" : "Bu günde CGM verisi yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Помилка: немає даних" + "value" : "Немає даних CGM за цей день" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Lỗi thiếu dữ liệu" + "value" : "Không có dữ liệu CGM trong ngày này" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "数据缺失错误" + "value" : "这一天无 CGM 数据" } } } }, - "Needs 10 days of CGM" : { - "comment" : "Trends GMI tile detail when fewer than 10 days of CGM data are available", + "No CGM data yet today" : { + "comment" : "Chart note: there are no glucose readings so far today", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يحتاج إلى 10 أيام من CGM" + "value" : "لا توجد بيانات CGM اليوم حتى الآن" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vyžaduje 10 dní CGM" + "value" : "Dnes zatím žádná data CGM" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Kræver 10 dages CGM" + "value" : "Ingen CGM-data endnu i dag" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Benötigt 10 Tage CGM" + "value" : "Heute noch keine CGM-Daten" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Requiere 10 días de CGM" + "value" : "Aún no hay datos del CGM hoy" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Vaatii 10 päivää CGM-dataa" + "value" : "Ei vielä CGM-dataa tänään" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nécessite 10 jours de CGM" + "value" : "Aucune donnée CGM aujourd'hui pour l'instant" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נדרשים 10 ימי CGM" + "value" : "אין עדיין נתוני CGM היום" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "10 nap CGM-adat szükséges" + "value" : "Ma még nincs CGM-adat" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Servono 10 giorni di CGM" + "value" : "Ancora nessun dato CGM oggi" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "CGMデータが10日分必要" + "value" : "今日はまだCGMデータなし" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Krever 10 dager med CGM" + "value" : "Ingen CGM-data ennå i dag" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vereist 10 dagen CGM" + "value" : "Vandaag nog geen CGM-gegevens" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wymaga 10 dni CGM" + "value" : "Dziś jeszcze brak danych CGM" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Requer 10 dias de CGM" + "value" : "Ainda sem dados do CGM hoje" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Necesită 10 zile de CGM" + "value" : "Încă nu există date CGM azi" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нужно 10 дней CGM" + "value" : "Сегодня пока нет данных CGM" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Vyžaduje 10 dní CGM" + "value" : "Dnes zatiaľ žiadne údaje CGM" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kräver 10 dagars CGM" + "value" : "Inga CGM-data än i dag" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "10 günlük CGM gerekir" + "value" : "Bugün henüz CGM verisi yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Потрібно 10 днів CGM" + "value" : "Сьогодні ще немає даних CGM" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Cần 10 ngày dữ liệu CGM" + "value" : "Hôm nay chưa có dữ liệu CGM" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "需要 10 天 CGM 数据" + "value" : "今天尚无 CGM 数据" } } } }, - "Next day" : { - "comment" : "Accessibility label for the day page button that steps forward a day", + "No gaps: Loop ran closed loop all through this period." : { + "comment" : "Trends Gaps list when Loop was in closed loop for the whole period", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "اليوم التالي" + "value" : "لا فجوات: عمل Loop بالحلقة المغلقة طوال هذه الفترة." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Další den" + "value" : "Žádné výpadky: Loop běžel v uzavřené smyčce celé období." } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Næste dag" + "value" : "Ingen huller: Loop kørte lukket Loop i hele perioden." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Nächster Tag" + "value" : "Keine Lücken: Loop lief im gesamten Zeitraum im Closed Loop." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Día siguiente" + "value" : "Sin interrupciones: Loop funcionó en circuito cerrado durante todo el período." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Seuraava päivä" + "value" : "Ei katkoja: Loop toimi suljetulla säädöllä koko jakson ajan." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Jour suivant" + "value" : "Aucune interruption : Loop est resté en boucle fermée toute la période." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "היום הבא" + "value" : "אין פערים: Loop פעל ב-Closed Loop לאורך כל התקופה." } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Következő nap" + "value" : "Nincs kimaradás: a Loop az egész időszakban zárt hurokban futott." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Giorno successivo" + "value" : "Nessuna interruzione: Loop è rimasto in loop chiuso per tutto il periodo." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "翌日" + "value" : "中断なし:この期間中、Loopは常にクローズドループで動作しました。" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Neste dag" + "value" : "Ingen hull: Loop kjørte lukket Loop gjennom hele perioden." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Volgende dag" + "value" : "Geen onderbrekingen: Loop draaide de hele periode in actieve loop." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Następny dzień" + "value" : "Brak przerw: Loop działał w pętli zamkniętej przez cały okres." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Próximo dia" + "value" : "Sem interrupções: o Loop ficou em ciclo fechado durante todo o período." } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Ziua următoare" + "value" : "Fără întreruperi: Loop a rulat în buclă închisă toată perioada." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Следующий день" + "value" : "Пропусков нет: Loop работал в замкнутом цикле весь период." } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Ďalší deň" + "value" : "Žiadne výpadky: Loop bežal v uzavretej slučke celé obdobie." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Nästa dag" + "value" : "Inga luckor: Loop körde sluten loop under hela perioden." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Sonraki gün" + "value" : "Boşluk yok: Loop bu dönem boyunca kapalı döngüde çalıştı." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Наступний день" + "value" : "Пропусків немає: Loop працював у замкнутому циклі весь період." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Ngày sau" + "value" : "Không gián đoạn: Loop chạy vòng lặp kín suốt khoảng thời gian này." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "后一天" + "value" : "无中断:此时段内 Loop 一直以闭环运行。" } } } }, - "Next period" : { - "comment" : "VoiceOver label for the Trends button that moves forward one period", + "No insulin recorded in this period" : { + "comment" : "Trends Insulin per day chart placeholder when no insulin was delivered in the period", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الفترة التالية" + "value" : "لم يُسجَّل أنسولين في هذه الفترة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Další období" + "value" : "V tomto období nebyl zaznamenán žádný inzulín" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Næste periode" + "value" : "Intet insulin registreret i denne periode" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Nächster Zeitraum" + "value" : "Kein Insulin in diesem Zeitraum erfasst" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Período siguiente" + "value" : "No se registró insulina en este período" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Seuraava jakso" + "value" : "Ei kirjattua insuliinia tällä jaksolla" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Période suivante" + "value" : "Aucune insuline enregistrée sur cette période" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התקופה הבאה" + "value" : "לא נרשם אינסולין בתקופה זו" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Következő időszak" + "value" : "Nincs rögzített inzulin ebben az időszakban" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Periodo successivo" + "value" : "Nessuna insulina registrata in questo periodo" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "次の期間" + "value" : "この期間のインスリン記録なし" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Neste periode" + "value" : "Ingen insulin registrert i denne perioden" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Volgende periode" + "value" : "Geen insuline vastgelegd in deze periode" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Następny okres" + "value" : "Brak zapisanej insuliny w tym okresie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Próximo período" + "value" : "Nenhuma insulina registrada neste período" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Perioada următoare" + "value" : "Nicio insulină înregistrată în această perioadă" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Следующий период" + "value" : "За этот период инсулин не записан" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Ďalšie obdobie" + "value" : "V tomto období nebol zaznamenaný žiadny inzulín" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Nästa period" + "value" : "Inget insulin registrerat under denna period" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Sonraki dönem" + "value" : "Bu dönemde kaydedilmiş insülin yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Наступний період" + "value" : "За цей період інсулін не записано" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Khoảng thời gian sau" + "value" : "Không có insulin được ghi lại trong khoảng thời gian này" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "下一时段" + "value" : "此时段内无胰岛素记录" } } } }, - "No active insulin or carbs on this day" : { - "comment" : "Day page placeholder when there was no insulin or carbs on board all day", + "No loop runs kept" : { + "comment" : "Trends Loop active tile detail when no Loop run history is available for the period", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا يوجد أنسولين أو كارب نشط في هذا اليوم" + "value" : "لا توجد تشغيلات Loop محفوظة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "V tento den nebyl žádný aktivní inzulín ani sacharidy" + "value" : "Žádné uložené běhy" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Intet aktivt insulin eller kulhydrater denne dag" + "value" : "Ingen Loop-kørsler gemt" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Kein aktives Insulin und keine aktiven KH an diesem Tag" + "value" : "Keine Loop-Durchläufe gespeichert" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Sin insulina ni carbohidratos activos este día" + "value" : "No se guardaron ejecuciones de Loop" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei aktiivista insuliinia tai hiilihydraatteja tänä päivänä" + "value" : "Ei tallennettuja Loop-ajoja" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune insuline ni glucides actifs ce jour-là" + "value" : "Aucune exécution de Loop conservée" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אין אינסולין או פחמימות פעילים ביום זה" + "value" : "לא נשמרו הרצות Loop" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Ezen a napon nem volt aktív inzulin vagy szénhidrát" + "value" : "Nincs megőrzött Loop-futás" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessuna insulina o carboidrati attivi in questo giorno" + "value" : "Nessuna esecuzione di Loop conservata" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "この日は残存インスリンも残存糖質もありません" + "value" : "Loop実行記録なし" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen aktivt insulin eller karbohydrater denne dagen" + "value" : "Ingen Loop-kjøringer lagret" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen actieve insuline of koolhydraten op deze dag" + "value" : "Geen loop-runs bewaard" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak aktywnej insuliny i węglowodanów tego dnia" + "value" : "Brak zapisanych cykli" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Sem insulina ou carboidratos ativos neste dia" + "value" : "Nenhuma execução do Loop guardada" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Fără insulină sau carbohidrați activi în această zi" + "value" : "Nu sunt păstrate rulări Loop" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "В этот день не было активного инсулина или углеводов" + "value" : "Нет сохранённых запусков" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "V tento deň nebol žiadny aktívny inzulín ani sacharidy" + "value" : "Žiadne uložené behy" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inget aktivt insulin eller kolhydrater denna dag" + "value" : "Inga loopkörningar sparade" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bu gün aktif insülin veya karb yok" + "value" : "Saklanan Loop çalışması yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Цього дня не було активного інсуліну чи вуглеводів" + "value" : "Немає збережених запусків" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có insulin hoặc Carbs còn hoạt động trong ngày này" + "value" : "Không lưu lần chạy Loop nào" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "这一天没有活性胰岛素或碳水" + "value" : "无 Loop 运行记录" } } } }, - "No CGM data" : { - "comment" : "Trends Average tile detail when the period has no glucose readings", + "No loop runs recorded in this period" : { + "comment" : "Trends Loop status placeholder when there is no Loop run history for the period", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا توجد بيانات CGM" + "value" : "لم تُسجَّل تشغيلات Loop في هذه الفترة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Žádná data CGM" + "value" : "V tomto období nebyly zaznamenány žádné běhy Loop" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data" + "value" : "Ingen Loop-kørsler registreret i denne periode" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine CGM-Daten" + "value" : "Keine Loop-Durchläufe in diesem Zeitraum erfasst" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Sin datos del CGM" + "value" : "No se registraron ejecuciones de Loop en este período" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei CGM-dataa" + "value" : "Ei Loop-ajoja tällä jaksolla" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune donnée CGM" + "value" : "Aucune exécution de Loop enregistrée sur cette période" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אין נתוני CGM" + "value" : "לא נרשמו הרצות Loop בתקופה זו" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs CGM-adat" + "value" : "Nincs rögzített Loop-futás ebben az időszakban" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessun dato CGM" + "value" : "Nessuna esecuzione di Loop registrata in questo periodo" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "CGMデータなし" + "value" : "この期間のLoop実行記録なし" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data" + "value" : "Ingen Loop-kjøringer registrert i denne perioden" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen CGM-gegevens" + "value" : "Geen loop-runs vastgelegd in deze periode" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak danych CGM" + "value" : "Brak zapisanych cykli Loop w tym okresie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Sem dados do CGM" + "value" : "Nenhuma execução do Loop registrada neste período" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Fără date CGM" + "value" : "Nicio rulare Loop înregistrată în această perioadă" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет данных CGM" + "value" : "За этот период запуски Loop не записаны" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Žiadne údaje CGM" + "value" : "V tomto období neboli zaznamenané žiadne behy Loop" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inga CGM-data" + "value" : "Inga loopkörningar registrerade under denna period" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "CGM verisi yok" + "value" : "Bu dönemde kaydedilmiş Loop çalışması yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає даних CGM" + "value" : "За цей період запуски Loop не записано" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có dữ liệu CGM" + "value" : "Không có lần chạy Loop nào được ghi lại trong khoảng thời gian này" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "无 CGM 数据" + "value" : "此时段内无 Loop 运行记录" } } } }, - "No CGM data %1$@ – %2$@" : { - "comment" : "Chart note: a gap in glucose readings. (1: start time; 2: end time)", + "No reading" : { + "comment" : "Shown on the Now screen in place of the glucose value when there is no recent CGM reading", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا توجد بيانات CGM %1$@ – %2$@" + "value" : "لا توجد قراءة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Žádná data CGM %1$@ – %2$@" + "value" : "Žádná hodnota" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data %1$@ – %2$@" + "value" : "Ingen måling" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine CGM-Daten %1$@ – %2$@" + "value" : "Kein Wert" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Sin datos del CGM %1$@ – %2$@" + "value" : "Sin lectura" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei CGM-dataa %1$@ – %2$@" + "value" : "Ei lukemaa" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune donnée CGM %1$@ – %2$@" + "value" : "Aucune mesure" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אין נתוני CGM %1$@ – %2$@" + "value" : "אין קריאה" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs CGM-adat %1$@ – %2$@" + "value" : "Nincs érték" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessun dato CGM %1$@ – %2$@" + "value" : "Nessuna lettura" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "CGMデータなし %1$@ – %2$@" + "value" : "測定値なし" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data %1$@ – %2$@" + "value" : "Ingen måling" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen CGM-gegevens %1$@ – %2$@" + "value" : "Geen waarde" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak danych CGM %1$@ – %2$@" + "value" : "Brak odczytu" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Sem dados do CGM %1$@ – %2$@" + "value" : "Sem leitura" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Fără date CGM %1$@ – %2$@" + "value" : "Nicio citire" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет данных CGM %1$@ – %2$@" + "value" : "Нет показаний" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Žiadne údaje CGM %1$@ – %2$@" + "value" : "Žiadna hodnota" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inga CGM-data %1$@ – %2$@" + "value" : "Inget värde" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "CGM verisi yok %1$@ – %2$@" + "value" : "Okuma yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає даних CGM %1$@ – %2$@" + "value" : "Немає показників" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có dữ liệu CGM %1$@ – %2$@" + "value" : "Không có chỉ số" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "无 CGM 数据 %1$@ – %2$@" + "value" : "无读数" } } } }, - "No CGM data %1$@ – %2$@ and %3$@" : { - "comment" : "Chart note: the longest gap in glucose readings, plus the shorter ones. (1: start time; 2: end time; 3: number of shorter gaps, e.g. 2 shorter gaps)", + "Not enough days for a pattern: %1$@ of 5" : { + "comment" : "Trends Daily pattern footnote when fewer than 5 days have readings; the chart shows raw readings instead. (1: days with readings)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا توجد بيانات CGM %1$@ – %2$@ و %3$@" + "value" : "أيام غير كافية لنمط: %1$@ من 5" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Žádná data CGM %1$@ – %2$@ a %3$@" + "value" : "Málo dní pro profil: %1$@ z 5" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data %1$@ – %2$@ og %3$@" + "value" : "Ikke nok dage til et mønster: %1$@ af 5" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine CGM-Daten %1$@ – %2$@ und %3$@" + "value" : "Nicht genug Tage für ein Profil: %1$@ von 5" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Sin datos del CGM %1$@ – %2$@ y %3$@" + "value" : "No hay suficientes días para un patrón: %1$@ de 5" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei CGM-dataa %1$@ – %2$@ ja %3$@" + "value" : "Liian vähän päiviä profiiliin: %1$@ / 5" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune donnée CGM %1$@ – %2$@ et %3$@" + "value" : "Pas assez de jours pour un profil : %1$@ sur 5" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אין נתוני CGM %1$@ – %2$@ ו-%3$@" + "value" : "אין מספיק ימים לדפוס: %1$@ מתוך 5" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs CGM-adat %1$@ – %2$@, és %3$@" + "value" : "Nincs elég nap a mintázathoz: %1$@ / 5" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessun dato CGM %1$@ – %2$@ e %3$@" + "value" : "Giorni insufficienti per un andamento: %1$@ su 5" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "CGMデータなし %1$@ – %2$@(%3$@)" + "value" : "パターン作成には日数不足:%1$@ / 5日" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data %1$@ – %2$@ og %3$@" + "value" : "Ikke nok dager for et mønster: %1$@ av 5" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen CGM-gegevens %1$@ – %2$@ en %3$@" + "value" : "Niet genoeg dagen voor een patroon: %1$@ van 5" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak danych CGM %1$@ – %2$@ oraz %3$@" + "value" : "Za mało dni na profil: %1$@ z 5" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Sem dados do CGM %1$@ – %2$@ e %3$@" + "value" : "Dias insuficientes para um padrão: %1$@ de 5" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Fără date CGM %1$@ – %2$@ și %3$@" + "value" : "Prea puține zile pentru un tipar: %1$@ din 5" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет данных CGM %1$@ – %2$@ и ещё %3$@" + "value" : "Мало дней для профиля: %1$@ из 5" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Žiadne údaje CGM %1$@ – %2$@ a %3$@" + "value" : "Málo dní pre profil: %1$@ z 5" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inga CGM-data %1$@ – %2$@ och %3$@" + "value" : "För få dagar för ett mönster: %1$@ av 5" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "CGM verisi yok %1$@ – %2$@ ve %3$@" + "value" : "Örüntü için yeterli gün yok: %1$@ / 5" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає даних CGM %1$@ – %2$@ і ще %3$@" + "value" : "Замало днів для профілю: %1$@ з 5" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có dữ liệu CGM %1$@ – %2$@ và %3$@" + "value" : "Không đủ ngày để tạo mẫu: %1$@ / 5" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "无 CGM 数据 %1$@ – %2$@,%3$@" + "value" : "天数不足以生成模式:%1$@ / 5" } } } }, - "No CGM data in this period" : { - "comment" : "Trends chart placeholder when the period has no glucose readings", + "Nothing logged" : { + "comment" : "Logbook day with no entries", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا توجد بيانات CGM في هذه الفترة" + "value" : "لا شيء مسجَّل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Žádná data CGM v tomto období" + "value" : "Nic zaznamenáno" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data i denne periode" + "value" : "Intet registreret" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine CGM-Daten in diesem Zeitraum" + "value" : "Nichts eingetragen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Sin datos del CGM en este período" + "value" : "Nada registrado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei CGM-dataa tällä jaksolla" + "value" : "Ei kirjauksia" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune donnée CGM sur cette période" + "value" : "Aucune saisie" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אין נתוני CGM בתקופה זו" + "value" : "לא נרשם דבר" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs CGM-adat ebben az időszakban" + "value" : "Nincs bejegyzés" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessun dato CGM in questo periodo" + "value" : "Nessuna registrazione" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "この期間のCGMデータなし" + "value" : "記録なし" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data i denne perioden" + "value" : "Ingenting registrert" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen CGM-gegevens in deze periode" + "value" : "Niets vastgelegd" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak danych CGM w tym okresie" + "value" : "Brak wpisów" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Sem dados do CGM neste período" + "value" : "Nada registrado" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Fără date CGM în această perioadă" + "value" : "Nimic înregistrat" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет данных CGM за этот период" + "value" : "Нет записей" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Žiadne údaje CGM v tomto období" + "value" : "Nič zaznamenané" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inga CGM-data under denna period" + "value" : "Inget loggat" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bu dönemde CGM verisi yok" + "value" : "Kayıt yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає даних CGM за цей період" + "value" : "Немає записів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có dữ liệu CGM trong khoảng thời gian này" + "value" : "Không có ghi chép" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "此时段无 CGM 数据" + "value" : "无记录" } } } }, - "No CGM data on this day" : { - "comment" : "Chart note: there are no glucose readings for the whole day", + "Nothing logged in the last 7 days." : { + "comment" : "Logbook empty state when there are no entries at all", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا توجد بيانات CGM في هذا اليوم" + "value" : "لا شيء مسجَّل في آخر 7 أيام." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Žádná data CGM v tento den" + "value" : "Za posledních 7 dní nic zaznamenáno." } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data denne dag" + "value" : "Intet registreret de seneste 7 dage." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine CGM-Daten an diesem Tag" + "value" : "In den letzten 7 Tagen nichts eingetragen." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Sin datos del CGM este día" + "value" : "Nada registrado en los últimos 7 días." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei CGM-dataa tänä päivänä" + "value" : "Ei kirjauksia viimeisen 7 päivän aikana." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune donnée CGM ce jour-là" + "value" : "Aucune saisie ces 7 derniers jours." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אין נתוני CGM ביום זה" + "value" : "לא נרשם דבר ב-7 הימים האחרונים." } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs CGM-adat ezen a napon" + "value" : "Nincs bejegyzés az elmúlt 7 napban." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessun dato CGM in questo giorno" + "value" : "Nessuna registrazione negli ultimi 7 giorni." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "この日のCGMデータなし" + "value" : "過去7日間の記録はありません。" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data denne dagen" + "value" : "Ingenting registrert de siste 7 dagene." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen CGM-gegevens op deze dag" + "value" : "Niets vastgelegd in de afgelopen 7 dagen." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak danych CGM tego dnia" + "value" : "Brak wpisów z ostatnich 7 dni." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Sem dados do CGM neste dia" + "value" : "Nada registrado nos últimos 7 dias." } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Fără date CGM în această zi" + "value" : "Nimic înregistrat în ultimele 7 zile." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет данных CGM за этот день" + "value" : "Нет записей за последние 7 дней." } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Žiadne údaje CGM v tento deň" + "value" : "Za posledných 7 dní nič zaznamenané." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inga CGM-data denna dag" + "value" : "Inget loggat de senaste 7 dagarna." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bu günde CGM verisi yok" + "value" : "Son 7 günde kayıt yok." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає даних CGM за цей день" + "value" : "Немає записів за останні 7 днів." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có dữ liệu CGM trong ngày này" + "value" : "Không có ghi chép nào trong 7 ngày qua." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "这一天无 CGM 数据" + "value" : "过去 7 天没有记录。" } } } }, - "No CGM data yet today" : { - "comment" : "Chart note: there are no glucose readings so far today", + "Nothing to show for this filter" : { + "comment" : "Logbook day with no entries matching the selected filter", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا توجد بيانات CGM اليوم حتى الآن" + "value" : "لا شيء لعرضه لهذا الفلتر" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Dnes zatím žádná data CGM" + "value" : "Pro tento filtr nic k zobrazení" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data endnu i dag" + "value" : "Intet at vise for dette filter" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Heute noch keine CGM-Daten" + "value" : "Keine Einträge für diesen Filter" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Aún no hay datos del CGM hoy" + "value" : "Nada que mostrar con este filtro" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei vielä CGM-dataa tänään" + "value" : "Ei näytettävää tällä suodattimella" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune donnée CGM aujourd'hui pour l'instant" + "value" : "Rien à afficher pour ce filtre" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אין עדיין נתוני CGM היום" + "value" : "אין מה להציג עבור מסנן זה" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Ma még nincs CGM-adat" + "value" : "Nincs megjeleníthető elem ehhez a szűrőhöz" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ancora nessun dato CGM oggi" + "value" : "Niente da mostrare per questo filtro" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "今日はまだCGMデータなし" + "value" : "このフィルタに該当する項目はありません" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen CGM-data ennå i dag" + "value" : "Ingenting å vise for dette filteret" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vandaag nog geen CGM-gegevens" + "value" : "Niets te tonen voor dit filter" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dziś jeszcze brak danych CGM" + "value" : "Brak wpisów dla tego filtra" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ainda sem dados do CGM hoje" + "value" : "Nada para mostrar neste filtro" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Încă nu există date CGM azi" + "value" : "Nimic de afișat pentru acest filtru" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Сегодня пока нет данных CGM" + "value" : "Нет записей для этого фильтра" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Dnes zatiaľ žiadne údaje CGM" + "value" : "Pre tento filter nič na zobrazenie" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inga CGM-data än i dag" + "value" : "Inget att visa för detta filter" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bugün henüz CGM verisi yok" + "value" : "Bu filtre için gösterilecek bir şey yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Сьогодні ще немає даних CGM" + "value" : "Немає записів для цього фільтра" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hôm nay chưa có dữ liệu CGM" + "value" : "Không có mục nào cho bộ lọc này" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "今天尚无 CGM 数据" + "value" : "此筛选条件下没有内容" } } } }, - "No gaps: Loop ran closed loop all through this period." : { - "comment" : "Trends Gaps list when Loop was in closed loop for the whole period", + "Now" : { + "comment" : "Title at the top of the Now screen, showing current glucose and Loop status Title of the Now tab in the redesigned interface, showing current glucose and Loop status", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا فجوات: عمل Loop بالحلقة المغلقة طوال هذه الفترة." + "value" : "الآن" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Žádné výpadky: Loop běžel v uzavřené smyčce celé období." + "value" : "Nyní" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen huller: Loop kørte lukket Loop i hele perioden." + "value" : "Nu" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine Lücken: Loop lief im gesamten Zeitraum im Closed Loop." + "value" : "Jetzt" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Sin interrupciones: Loop funcionó en circuito cerrado durante todo el período." + "value" : "Ahora" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei katkoja: Loop toimi suljetulla säädöllä koko jakson ajan." + "value" : "Nyt" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune interruption : Loop est resté en boucle fermée toute la période." + "value" : "Maintenant" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אין פערים: Loop פעל ב-Closed Loop לאורך כל התקופה." + "value" : "עכשיו" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs kimaradás: a Loop az egész időszakban zárt hurokban futott." + "value" : "Most" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessuna interruzione: Loop è rimasto in loop chiuso per tutto il periodo." + "value" : "Ora" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "中断なし:この期間中、Loopは常にクローズドループで動作しました。" + "value" : "現在" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen hull: Loop kjørte lukket Loop gjennom hele perioden." + "value" : "Nå" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen onderbrekingen: Loop draaide de hele periode in actieve loop." + "value" : "Nu" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak przerw: Loop działał w pętli zamkniętej przez cały okres." + "value" : "Teraz" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Sem interrupções: o Loop ficou em ciclo fechado durante todo o período." + "value" : "Agora" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Fără întreruperi: Loop a rulat în buclă închisă toată perioada." + "value" : "Acum" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Пропусков нет: Loop работал в замкнутом цикле весь период." + "value" : "Сейчас" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Žiadne výpadky: Loop bežal v uzavretej slučke celé obdobie." + "value" : "Teraz" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inga luckor: Loop körde sluten loop under hela perioden." + "value" : "Nu" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Boşluk yok: Loop bu dönem boyunca kapalı döngüde çalıştı." + "value" : "Şimdi" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Пропусків немає: Loop працював у замкнутому циклі весь період." + "value" : "Зараз" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không gián đoạn: Loop chạy vòng lặp kín suốt khoảng thời gian này." + "value" : "Hiện tại" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "无中断:此时段内 Loop 一直以闭环运行。" + "value" : "当前" } } } }, - "No insulin recorded in this period" : { - "comment" : "Trends Insulin per day chart placeholder when no insulin was delivered in the period", + "now" : { + "comment" : "Prediction chart x-axis label marking the current time", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لم يُسجَّل أنسولين في هذه الفترة" + "value" : "الآن" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "V tomto období nebyl zaznamenán žádný inzulín" + "value" : "nyní" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Intet insulin registreret i denne periode" + "value" : "nu" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Kein Insulin in diesem Zeitraum erfasst" + "value" : "jetzt" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No se registró insulina en este período" + "value" : "ahora" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei kirjattua insuliinia tällä jaksolla" + "value" : "nyt" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune insuline enregistrée sur cette période" + "value" : "maint." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "לא נרשם אינסולין בתקופה זו" + "value" : "עכשיו" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs rögzített inzulin ebben az időszakban" + "value" : "most" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessuna insulina registrata in questo periodo" + "value" : "ora" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "この期間のインスリン記録なし" + "value" : "現在" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen insulin registrert i denne perioden" + "value" : "nå" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen insuline vastgelegd in deze periode" + "value" : "nu" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak zapisanej insuliny w tym okresie" + "value" : "teraz" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Nenhuma insulina registrada neste período" + "value" : "agora" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Nicio insulină înregistrată în această perioadă" + "value" : "acum" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "За этот период инсулин не записан" + "value" : "сейчас" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "V tomto období nebol zaznamenaný žiadny inzulín" + "value" : "teraz" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inget insulin registrerat under denna period" + "value" : "nu" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bu dönemde kaydedilmiş insülin yok" + "value" : "şimdi" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "За цей період інсулін не записано" + "value" : "зараз" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có insulin được ghi lại trong khoảng thời gian này" + "value" : "bây giờ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "此时段内无胰岛素记录" + "value" : "现在" } } } }, - "No loop runs kept" : { - "comment" : "Trends Loop active tile detail when no Loop run history is available for the period", + "Open loop / manual" : { + "comment" : "Trends Loop status: time closed loop was switched off", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا توجد تشغيلات Loop محفوظة" + "value" : "حلقة مفتوحة / يدوي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Žádné uložené běhy" + "value" : "Otevřená smyčka / ručně" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen Loop-kørsler gemt" + "value" : "Åbent Loop / manuel" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine Loop-Durchläufe gespeichert" + "value" : "Open Loop / manuell" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No se guardaron ejecuciones de Loop" + "value" : "Circuito abierto / manual" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei tallennettuja Loop-ajoja" + "value" : "Avoin säätö / manuaalinen" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune exécution de Loop conservée" + "value" : "Boucle ouverte / manuel" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "לא נשמרו הרצות Loop" + "value" : "Open Loop / ידני" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs megőrzött Loop-futás" + "value" : "Nyitott hurok / kézi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessuna esecuzione di Loop conservata" + "value" : "Loop aperto / manuale" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Loop実行記録なし" + "value" : "オープンループ/手動" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen Loop-kjøringer lagret" + "value" : "Åpen Loop / manuell" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen loop-runs bewaard" + "value" : "Open loop / handmatig" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak zapisanych cykli" + "value" : "Pętla otwarta / ręcznie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Nenhuma execução do Loop guardada" + "value" : "Ciclo aberto / manual" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Nu sunt păstrate rulări Loop" + "value" : "Buclă deschisă / manual" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет сохранённых запусков" + "value" : "Открытый цикл / вручную" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Žiadne uložené behy" + "value" : "Otvorená slučka / ručne" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inga loopkörningar sparade" + "value" : "Öppen loop / manuell" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Saklanan Loop çalışması yok" + "value" : "Açık döngü / manuel" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає збережених запусків" + "value" : "Відкритий цикл / вручну" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không lưu lần chạy Loop nào" + "value" : "Vòng lặp mở / thủ công" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "无 Loop 运行记录" + "value" : "开环 / 手动" } } } }, - "No loop runs recorded in this period" : { - "comment" : "Trends Loop status placeholder when there is no Loop run history for the period", + "Open loop · %1$@" : { + "comment" : "Status pill on the Now screen: closed loop is off (Loop recommends only) and Loop last ran this long ago. (1: duration, e.g. 3m)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لم تُسجَّل تشغيلات Loop في هذه الفترة" + "value" : "حلقة مفتوحة · %1$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "V tomto období nebyly zaznamenány žádné běhy Loop" + "value" : "Otevřená smyčka · %1$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen Loop-kørsler registreret i denne periode" + "value" : "Åbent Loop · %1$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine Loop-Durchläufe in diesem Zeitraum erfasst" + "value" : "Open Loop · %1$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No se registraron ejecuciones de Loop en este período" + "value" : "Circuito abierto · %1$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei Loop-ajoja tällä jaksolla" + "value" : "Avoin säätö · %1$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune exécution de Loop enregistrée sur cette période" + "value" : "Boucle ouverte · %1$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "לא נרשמו הרצות Loop בתקופה זו" + "value" : "Open Loop · %1$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs rögzített Loop-futás ebben az időszakban" + "value" : "Nyitott hurok · %1$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessuna esecuzione di Loop registrata in questo periodo" + "value" : "Loop aperto · %1$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "この期間のLoop実行記録なし" + "value" : "オープンループ · %1$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingen Loop-kjøringer registrert i denne perioden" + "value" : "Åpen Loop · %1$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen loop-runs vastgelegd in deze periode" + "value" : "Open loop · %1$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak zapisanych cykli Loop w tym okresie" + "value" : "Pętla otwarta · %1$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Nenhuma execução do Loop registrada neste período" + "value" : "Ciclo aberto · %1$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Nicio rulare Loop înregistrată în această perioadă" + "value" : "Buclă deschisă · %1$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "За этот период запуски Loop не записаны" + "value" : "Открытый цикл · %1$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "V tomto období neboli zaznamenané žiadne behy Loop" + "value" : "Otvorená slučka · %1$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inga loopkörningar registrerade under denna period" + "value" : "Öppen loop · %1$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bu dönemde kaydedilmiş Loop çalışması yok" + "value" : "Açık döngü · %1$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "За цей період запуски Loop не записано" + "value" : "Відкритий цикл · %1$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có lần chạy Loop nào được ghi lại trong khoảng thời gian này" + "value" : "Vòng lặp mở · %1$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "此时段内无 Loop 运行记录" + "value" : "开环 · %1$@" } } } }, - "Not enough days for a pattern: %1$@ of 5" : { - "comment" : "Trends Daily pattern footnote when fewer than 5 days have readings; the chart shows raw readings instead. (1: days with readings)", + "Open loop · not run yet" : { + "comment" : "Status pill on the Now screen: closed loop is off and Loop has never completed", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أيام غير كافية لنمط: %1$@ من 5" + "value" : "حلقة مفتوحة · لم يعمل بعد" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Málo dní pro profil: %1$@ z 5" + "value" : "Otevřená smyčka · zatím neproběhla" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Ikke nok dage til et mønster: %1$@ af 5" + "value" : "Åbent Loop · ikke kørt endnu" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Nicht genug Tage für ein Profil: %1$@ von 5" + "value" : "Open Loop · noch nicht gelaufen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No hay suficientes días para un patrón: %1$@ de 5" + "value" : "Circuito abierto · aún no se ha ejecutado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Liian vähän päiviä profiiliin: %1$@ / 5" + "value" : "Avoin säätö · ei vielä ajettu" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Pas assez de jours pour un profil : %1$@ sur 5" + "value" : "Boucle ouverte · pas encore exécuté" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אין מספיק ימים לדפוס: %1$@ מתוך 5" + "value" : "Open Loop · טרם רץ" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs elég nap a mintázathoz: %1$@ / 5" + "value" : "Nyitott hurok · még nem futott" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Giorni insufficienti per un andamento: %1$@ su 5" + "value" : "Loop aperto · non ancora eseguito" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "パターン作成には日数不足:%1$@ / 5日" + "value" : "オープンループ · 未実行" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ikke nok dager for et mønster: %1$@ av 5" + "value" : "Åpen Loop · ikke kjørt ennå" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Niet genoeg dagen voor een patroon: %1$@ van 5" + "value" : "Open loop · nog niet gedraaid" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Za mało dni na profil: %1$@ z 5" + "value" : "Pętla otwarta · jeszcze nie działała" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Dias insuficientes para um padrão: %1$@ de 5" + "value" : "Ciclo aberto · ainda não executou" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Prea puține zile pentru un tipar: %1$@ din 5" + "value" : "Buclă deschisă · nu a rulat încă" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Мало дней для профиля: %1$@ из 5" + "value" : "Открытый цикл · ещё не запускался" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Málo dní pre profil: %1$@ z 5" + "value" : "Otvorená slučka · ešte neprebehla" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "För få dagar för ett mönster: %1$@ av 5" + "value" : "Öppen loop · inte körd än" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Örüntü için yeterli gün yok: %1$@ / 5" + "value" : "Açık döngü · henüz çalışmadı" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Замало днів для профілю: %1$@ з 5" + "value" : "Відкритий цикл · ще не запускався" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không đủ ngày để tạo mẫu: %1$@ / 5" + "value" : "Vòng lặp mở · chưa chạy" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "天数不足以生成模式:%1$@ / 5" + "value" : "开环 · 尚未运行" } } } }, - "Nothing logged" : { - "comment" : "Logbook day with no entries", + "Opens carb absorption" : { + "comment" : "VoiceOver hint for the COB tile on the Now screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا شيء مسجَّل" + "value" : "يفتح امتصاص الكارب" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nic zaznamenáno" + "value" : "Otevře vstřebávání sacharidů" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Intet registreret" + "value" : "Åbner kulhydratoptagelse" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Nichts eingetragen" + "value" : "Öffnet die KH-Aufnahme" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Nada registrado" + "value" : "Abre la absorción de carbohidratos" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei kirjauksia" + "value" : "Avaa hiilihydraattien imeytymisen" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune saisie" + "value" : "Ouvre l'absorption des glucides" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "לא נרשם דבר" + "value" : "פותח ספיגת פחמימות" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs bejegyzés" + "value" : "Megnyitja a szénhidrát-felszívódást" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessuna registrazione" + "value" : "Apre l'assorbimento dei carboidrati" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "記録なし" + "value" : "糖質吸収を開きます" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingenting registrert" + "value" : "Åpner karbohydratabsorpsjon" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Niets vastgelegd" + "value" : "Opent koolhydraatopname" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak wpisów" + "value" : "Otwiera wchłanianie węglowodanów" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Nada registrado" + "value" : "Abre a absorção de carboidratos" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Nimic înregistrat" + "value" : "Deschide absorbția carbohidraților" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет записей" + "value" : "Открывает усвоение углеводов" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Nič zaznamenané" + "value" : "Otvorí vstrebávanie sacharidov" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inget loggat" + "value" : "Öppnar kolhydratabsorption" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Kayıt yok" + "value" : "Karb emilimini açar" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає записів" + "value" : "Відкриває засвоєння вуглеводів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có ghi chép" + "value" : "Mở hấp thu Carbs" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "无记录" + "value" : "打开碳水吸收" } } } }, - "Nothing logged in the last 7 days." : { - "comment" : "Logbook empty state when there are no entries at all", + "Opens CGM settings" : { + "comment" : "VoiceOver hint for the CGM status pill and CGM notice on the Now screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا شيء مسجَّل في آخر 7 أيام." + "value" : "يفتح إعدادات CGM" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Za posledních 7 dní nic zaznamenáno." + "value" : "Otevře nastavení CGM" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Intet registreret de seneste 7 dage." + "value" : "Åbner CGM-indstillinger" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "In den letzten 7 Tagen nichts eingetragen." + "value" : "Öffnet die CGM-Einstellungen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Nada registrado en los últimos 7 días." + "value" : "Abre los ajustes del CGM" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei kirjauksia viimeisen 7 päivän aikana." + "value" : "Avaa CGM-asetukset" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucune saisie ces 7 derniers jours." + "value" : "Ouvre les paramètres du CGM" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "לא נרשם דבר ב-7 הימים האחרונים." + "value" : "פותח הגדרות CGM" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs bejegyzés az elmúlt 7 napban." + "value" : "Megnyitja a CGM beállításait" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessuna registrazione negli ultimi 7 giorni." + "value" : "Apre le impostazioni CGM" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "過去7日間の記録はありません。" + "value" : "CGM設定を開きます" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingenting registrert de siste 7 dagene." + "value" : "Åpner CGM-innstillinger" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Niets vastgelegd in de afgelopen 7 dagen." + "value" : "Opent CGM-instellingen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak wpisów z ostatnich 7 dni." + "value" : "Otwiera ustawienia CGM" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Nada registrado nos últimos 7 dias." + "value" : "Abre as configurações do CGM" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Nimic înregistrat în ultimele 7 zile." + "value" : "Deschide setările CGM" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет записей за последние 7 дней." + "value" : "Открывает настройки CGM" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Za posledných 7 dní nič zaznamenané." + "value" : "Otvorí nastavenia CGM" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inget loggat de senaste 7 dagarna." + "value" : "Öppnar CGM-inställningar" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Son 7 günde kayıt yok." + "value" : "CGM ayarlarını açar" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає записів за останні 7 днів." + "value" : "Відкриває налаштування CGM" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có ghi chép nào trong 7 ngày qua." + "value" : "Mở cài đặt CGM" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "过去 7 天没有记录。" + "value" : "打开 CGM 设置" } } } }, - "Nothing to show for this filter" : { - "comment" : "Logbook day with no entries matching the selected filter", + "Opens insulin delivery history" : { + "comment" : "VoiceOver hint for the IOB tile on the Now screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا شيء لعرضه لهذا الفلتر" + "value" : "يفتح سجل توصيل الأنسولين" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Pro tento filtr nic k zobrazení" + "value" : "Otevře historii podávání inzulínu" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Intet at vise for dette filter" + "value" : "Åbner historik for insulintilførsel" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine Einträge für diesen Filter" + "value" : "Öffnet den Verlauf der Insulinabgabe" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Nada que mostrar con este filtro" + "value" : "Abre el historial de administración de insulina" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Ei näytettävää tällä suodattimella" + "value" : "Avaa insuliinin annosteluhistorian" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rien à afficher pour ce filtre" + "value" : "Ouvre l'historique d'administration de l'insuline" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אין מה להציג עבור מסנן זה" + "value" : "פותח היסטוריית מתן אינסולין" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nincs megjeleníthető elem ehhez a szűrőhöz" + "value" : "Megnyitja az inzulinadagolási előzményeket" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Niente da mostrare per questo filtro" + "value" : "Apre la cronologia di erogazione insulina" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "このフィルタに該当する項目はありません" + "value" : "インスリン注入履歴を開きます" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Ingenting å vise for dette filteret" + "value" : "Åpner historikk for insulinlevering" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Niets te tonen voor dit filter" + "value" : "Opent geschiedenis van insulinetoediening" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak wpisów dla tego filtra" + "value" : "Otwiera historię podaży insuliny" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Nada para mostrar neste filtro" + "value" : "Abre o histórico de entrega de insulina" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Nimic de afișat pentru acest filtru" + "value" : "Deschide istoricul administrării insulinei" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет записей для этого фильтра" + "value" : "Открывает историю подачи инсулина" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Pre tento filter nič na zobrazenie" + "value" : "Otvorí históriu podávania inzulínu" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inget att visa för detta filter" + "value" : "Öppnar historik för insulindosering" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Bu filtre için gösterilecek bir şey yok" + "value" : "İnsülin iletim geçmişini açar" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає записів для цього фільтра" + "value" : "Відкриває історію подачі інсуліну" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có mục nào cho bộ lọc này" + "value" : "Mở lịch sử tiêm insulin" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "此筛选条件下没有内容" + "value" : "打开胰岛素输注历史" } } } }, - "Now" : { - "comment" : "Title at the top of the Now screen, showing current glucose and Loop status Title of the Now tab in the redesigned interface, showing current glucose and Loop status", + "Opens Loop's prediction breakdown" : { + "comment" : "VoiceOver hint for the prediction card on the Now screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الآن" + "value" : "يفتح تفاصيل توقع Loop" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nyní" + "value" : "Otevře rozpis predikce Loop" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Nu" + "value" : "Åbner opdelingen af Loops forudsigelse" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Jetzt" + "value" : "Öffnet die Aufschlüsselung der Loop-Vorhersage" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ahora" + "value" : "Abre el desglose de la predicción de Loop" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Nyt" + "value" : "Avaa Loopin ennusteen erittelyn" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Maintenant" + "value" : "Ouvre le détail de la prédiction de Loop" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "עכשיו" + "value" : "פותח פירוט תחזית Loop" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Most" + "value" : "Megnyitja a Loop előrejelzésének részleteit" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ora" + "value" : "Apre il dettaglio della previsione di Loop" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "現在" + "value" : "Loopの予測の内訳を開きます" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Nå" + "value" : "Åpner oppdelingen av Loops prognose" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Nu" + "value" : "Opent de onderbouwing van de Loop-voorspelling" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Teraz" + "value" : "Otwiera szczegóły prognozy Loop" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Agora" + "value" : "Abre o detalhamento da previsão do Loop" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Acum" + "value" : "Deschide detalierea predicției Loop" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Сейчас" + "value" : "Открывает разбор прогноза Loop" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Teraz" + "value" : "Otvorí rozpis predikcie Loop" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Nu" + "value" : "Öppnar detaljer om Loops prognos" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Şimdi" + "value" : "Loop tahmin dökümünü açar" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Зараз" + "value" : "Відкриває розбір прогнозу Loop" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hiện tại" + "value" : "Mở chi tiết dự đoán của Loop" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "当前" + "value" : "打开 Loop 预测明细" } } } }, - "Open loop / manual" : { - "comment" : "Trends Loop status: time closed loop was switched off", + "Opens pump settings" : { + "comment" : "VoiceOver hint for the pump status pill and pump notice on the Now screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حلقة مفتوحة / يدوي" + "value" : "يفتح إعدادات المضخة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Otevřená smyčka / ručně" + "value" : "Otevře nastavení pumpy" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Åbent Loop / manuel" + "value" : "Åbner pumpeindstillinger" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Open Loop / manuell" + "value" : "Öffnet die Pumpeneinstellungen" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Circuito abierto / manual" + "value" : "Abre los ajustes de la microinfusadora" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Avoin säätö / manuaalinen" + "value" : "Avaa pumpun asetukset" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Boucle ouverte / manuel" + "value" : "Ouvre les paramètres de la pompe" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "Open Loop / ידני" + "value" : "פותח הגדרות משאבה" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nyitott hurok / kézi" + "value" : "Megnyitja a pumpa beállításait" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Loop aperto / manuale" + "value" : "Apre le impostazioni della pompa" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "オープンループ/手動" + "value" : "ポンプ設定を開きます" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Åpen Loop / manuell" + "value" : "Åpner pumpeinnstillinger" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Open loop / handmatig" + "value" : "Opent pompinstellingen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Pętla otwarta / ręcznie" + "value" : "Otwiera ustawienia pompy" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ciclo aberto / manual" + "value" : "Abre as configurações da bomba" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Buclă deschisă / manual" + "value" : "Deschide setările pompei" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Открытый цикл / вручную" + "value" : "Открывает настройки помпы" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Otvorená slučka / ručne" + "value" : "Otvorí nastavenia pumpy" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Öppen loop / manuell" + "value" : "Öppnar pumpinställningar" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Açık döngü / manuel" + "value" : "Pompa ayarlarını açar" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Відкритий цикл / вручну" + "value" : "Відкриває налаштування помпи" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Vòng lặp mở / thủ công" + "value" : "Mở cài đặt bơm" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "开环 / 手动" + "value" : "打开胰岛素泵设置" } } } @@ -20410,6 +25129,292 @@ } } }, + "Pre-meal on" : { + "comment" : "Presets button title on the Now screen while the pre-meal target is on", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pre-Meal مفعّل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Před jídlem zapnuto" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Før-måltid til" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vor dem Essen an" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pre-comida activado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ennen ateriaa päällä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pré-repas activé" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pre-Meal פעיל" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pre-Meal bekapcsolva" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pre-pasto attivo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "食前 オン" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pre-måltid på" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voorafgaand aan maaltijd aan" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Przed posiłkiem wł." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pré-refeição ligado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Înainte de masă activ" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "До еды вкл." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pred jedlom zapnuté" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Före måltid på" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yemek öncesi açık" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перед їжою увімк." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trước bữa ăn đang bật" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "餐前已开启" + } + } + } + }, + "Predicted" : { + "comment" : "Prediction chart callout label for Loop's predicted glucose at the touched time", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "متوقع" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predikce" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forudsagt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorhergesagt" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predicción" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ennustettu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prédit" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חזוי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Előrejelzett" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prevista" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "予測" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forventet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voorspeld" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prognoza" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prevista" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prezis" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Прогноз" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predikcia" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Förutsagt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tahmin" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Прогноз" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dự đoán" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "预测" + } + } + } + }, "Previous day" : { "comment" : "Accessibility label for the day page button that steps back a day", "localizations" : { @@ -21554,6 +26559,292 @@ } } }, + "Readings" : { + "comment" : "Chart legend on the Now screen: dots are CGM glucose readings", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القراءات" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hodnoty" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Målinger" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Messwerte" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lecturas" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lukemat" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mesures" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קריאות" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mért értékek" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Letture" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "測定値" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Målinger" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Waarden" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Odczyty" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leituras" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citiri" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показания" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hodnoty" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Värden" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Okumalar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показники" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chỉ số" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "读数" + } + } + } + }, + "Recent readings unavailable: %1$@" : { + "comment" : "Error under the prediction chart on the Now screen. (1: system error description)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "القراءات الأخيرة غير متاحة: %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nedávné hodnoty nejsou k dispozici: %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seneste målinger ikke tilgængelige: %1$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktuelle Messwerte nicht verfügbar: %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lecturas recientes no disponibles: %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Viimeisimmät lukemat eivät saatavilla: %1$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mesures récentes indisponibles : %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קריאות אחרונות אינן זמינות: %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "A legutóbbi értékek nem érhetők el: %1$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Letture recenti non disponibili: %1$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "最近の測定値を取得できません:%1$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nylige målinger ikke tilgjengelige: %1$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recente waarden niet beschikbaar: %1$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ostatnie odczyty niedostępne: %1$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leituras recentes indisponíveis: %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citiri recente indisponibile: %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Последние показания недоступны: %1$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nedávne hodnoty nie sú k dispozícii: %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Senaste värden ej tillgängliga: %1$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Son okumalar kullanılamıyor: %1$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Останні показники недоступні: %1$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có chỉ số gần đây: %1$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "最近读数不可用:%1$@" + } + } + } + }, "Recommendation expired" : { "comment" : "Reason Loop could not run: its dosing recommendation expired before it was used", "localizations" : { @@ -22412,6 +27703,435 @@ } } }, + "Sets up a CGM" : { + "comment" : "VoiceOver hint for the Add CGM status pill on the Now screen", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يُعدّ جهاز CGM" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nastaví CGM" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opsætter en CGM" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Richtet ein CGM ein" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura un CGM" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ottaa CGM:n käyttöön" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configure un CGM" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מגדיר CGM" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM beállítása" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura un CGM" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGMを設定します" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Setter opp en CGM" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stelt een CGM in" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfiguruje CGM" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura um CGM" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurează un CGM" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настраивает CGM" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nastaví CGM" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lägger till en CGM" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM kurar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Налаштовує CGM" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thiết lập CGM" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设置 CGM" + } + } + } + }, + "Sets up a pump" : { + "comment" : "VoiceOver hint for the Add pump status pill on the Now screen", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يُعدّ مضخة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nastaví pumpu" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opsætter en pumpe" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Richtet eine Pumpe ein" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura una microinfusadora" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ottaa pumpun käyttöön" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configure une pompe" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מגדיר משאבה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pumpa beállítása" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura una pompa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ポンプを設定します" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Setter opp en pumpe" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stelt een pomp in" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfiguruje pompę" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configura uma bomba" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurează o pompă" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настраивает помпу" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nastaví pumpu" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lägger till en pump" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pompa kurar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Налаштовує помпу" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thiết lập bơm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设置胰岛素泵" + } + } + } + }, + "Shows when Loop last ran" : { + "comment" : "VoiceOver hint for the Loop status pill and the Loop notice on the Now screen", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يعرض آخر مرة عمل فيها Loop" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ukáže, kdy Loop naposledy proběhl" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Viser, hvornår Loop sidst kørte" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeigt, wann Loop zuletzt gelaufen ist" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muestra cuándo se ejecutó Loop por última vez" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Näyttää, milloin Loop ajettiin viimeksi" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Indique la dernière exécution de Loop" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מציג מתי Loop רץ לאחרונה" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Megmutatja, mikor futott utoljára a Loop" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostra l'ultima esecuzione di Loop" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loopの最終実行時刻を表示します" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Viser når Loop sist kjørte" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toont wanneer Loop voor het laatst draaide" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pokazuje, kiedy Loop ostatnio działał" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostra quando o Loop executou pela última vez" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arată când a rulat Loop ultima dată" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показывает, когда Loop запускался последний раз" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ukáže, kedy Loop naposledy prebehol" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Visar när Loop senast kördes" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loop'un son çalıştığı zamanı gösterir" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показує, коли Loop запускався востаннє" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hiển thị lần chạy gần nhất của Loop" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "显示 Loop 上次运行时间" + } + } + } + }, "Summary" : { "comment" : "Trends card title: the summary statistics tiles", "localizations" : { @@ -22698,6 +28418,149 @@ } } }, + "Target %1$@–%2$@" : { + "comment" : "Glucose target range on the Now screen hero card. (1: lower bound; 2: upper bound)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الهدف %1$@–%2$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cíl %1$@–%2$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål %1$@–%2$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ziel %1$@–%2$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objetivo %1$@–%2$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tavoite %1$@–%2$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cible %1$@–%2$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "יעד %1$@–%2$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cél %1$@–%2$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obiettivo %1$@–%2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "目標 %1$@–%2$@" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål %1$@–%2$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doel %1$@–%2$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cel %1$@–%2$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alvo %1$@–%2$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Țintă %1$@–%2$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цель %1$@–%2$@" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cieľ %1$@–%2$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mål %1$@–%2$@" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hedef %1$@–%2$@" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ціль %1$@–%2$@" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mục tiêu %1$@–%2$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "目标 %1$@–%2$@" + } + } + } + }, "Temp basal" : { "comment" : "Logbook row title: a temporary basal rate", "localizations" : { @@ -22841,6 +28704,149 @@ } } }, + "The new interface adds Trends and Logbook tabs. Turn it off to go back to the classic status screen; Settings closes to switch." : { + "comment" : "Footer for the interface choice in settings", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تضيف الواجهة الجديدة تبويبي الاتجاهات والسجل. أوقفها للعودة إلى شاشة الحالة الكلاسيكية؛ تُغلق الإعدادات للتبديل." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nové rozhraní přidává karty Trendy a Deník. Vypněte ho, chcete-li se vrátit ke klasické obrazovce stavu; Nastavení se pro přepnutí zavře." + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Den nye brugerflade tilføjer fanerne Tendenser og Logbog. Slå den fra for at vende tilbage til den klassiske statusskærm; Indstillinger lukkes for at skifte." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die neue Oberfläche fügt die Tabs Trends und Logbuch hinzu. Schalte sie aus, um zum klassischen Statusbildschirm zurückzukehren; die Einstellungen werden zum Wechseln geschlossen." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "La nueva interfaz añade las pestañas Tendencias y Registro. Desactívala para volver a la pantalla de estado clásica; Ajustes se cierra para cambiar." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uusi käyttöliittymä lisää Trendit- ja Lokikirja-välilehdet. Poista se käytöstä palataksesi perinteiseen tilanäyttöön; Asetukset suljetaan vaihtoa varten." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "La nouvelle interface ajoute les onglets Tendances et Journal. Désactivez-la pour revenir à l'écran d'état classique ; les Paramètres se ferment pour basculer." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הממשק החדש מוסיף לשוניות מגמות ויומן. כבה אותו כדי לחזור למסך הסטטוס הקלאסי; ההגדרות ייסגרו לצורך המעבר." + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Az új felület Trendek és Napló lapokkal bővül. Kapcsold ki, ha vissza szeretnél térni a klasszikus állapotképernyőhöz; a váltáshoz a Beállítások bezárul." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "La nuova interfaccia aggiunge le schede Tendenze e Diario. Disattivala per tornare alla classica schermata di stato; Impostazioni si chiude per cambiare." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "新しいインターフェイスではトレンドとログブックのタブが追加されます。オフにすると従来のステータス画面に戻ります。切り替えると設定が閉じます。" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Det nye grensesnittet legger til fanene Trender og Loggbok. Slå det av for å gå tilbake til den klassiske statusskjermen; Innstillinger lukkes for å bytte." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "De nieuwe interface voegt de tabbladen Trends en Logboek toe. Zet hem uit om terug te gaan naar het klassieke statusscherm; Instellingen sluit om te wisselen." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nowy interfejs dodaje karty Trendy i Dziennik. Wyłącz go, aby wrócić do klasycznego ekranu stanu; Ustawienia zamkną się, aby przełączyć." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "A nova interface adiciona as abas Tendências e Diário. Desligue-a para voltar à tela de status clássica; as Configurações fecham para trocar." + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noua interfață adaugă filele Tendințe și Jurnal. Dezactivați-o pentru a reveni la ecranul de stare clasic; Setările se închid pentru comutare." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Новый интерфейс добавляет вкладки «Тренды» и «Журнал». Выключите его, чтобы вернуться к классическому экрану статуса; для переключения настройки закроются." + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nové rozhranie pridáva karty Trendy a Denník. Vypnite ho, ak sa chcete vrátiť ku klasickej obrazovke stavu; Nastavenia sa pri prepnutí zatvoria." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Det nya gränssnittet lägger till flikarna Trender och Loggbok. Stäng av det för att gå tillbaka till den klassiska statusskärmen; Inställningar stängs vid bytet." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yeni arayüz Eğilimler ve Kayıt Defteri sekmelerini ekler. Klasik durum ekranına dönmek için kapatın; geçiş için Ayarlar kapanır." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Новий інтерфейс додає вкладки «Тренди» і «Журнал». Вимкніть його, щоб повернутися до класичного екрана стану; для перемикання налаштування закриються." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giao diện mới thêm các tab Xu hướng và Nhật ký. Tắt để quay lại màn hình trạng thái cổ điển; Cài đặt sẽ đóng để chuyển đổi." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "新界面增加了“趋势”和“日志”标签页。关闭后将返回经典状态屏幕;切换时设置会关闭。" + } + } + } + }, "this day" : { "comment" : "Trends Basal and bolus card subtitle when the period is one complete day", "localizations" : { @@ -24986,6 +30992,149 @@ } } }, + "U/h" : { + "comment" : "Abbreviation for insulin units per hour", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "وحدة لكل ساعة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/hod" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "E/t" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "IE/Std" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/h" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/h" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/h" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/hr" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/óra" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/ora" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "単位/時" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "E/t" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "E/uur" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "J/h" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/h" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/oră" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ед/ч" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "J/hod" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "IE/h" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ü/Sa" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/год" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/giờ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "U/小时" + } + } + } + }, "unknown" : { "comment" : "VoiceOver value when active insulin or carbs at the current time isn't known", "localizations" : { @@ -25272,6 +31421,149 @@ } } }, + "Urgent low" : { + "comment" : "Pill under the glucose value on the Now screen when glucose is below 54 mg/dL (3.0 mmol/L)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "منخفض عاجل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kriticky nízká" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akut lav" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dringend niedrig" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Baja urgente" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vaarallisen matala" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Basse urgente" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "נמוך דחוף" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sürgősen alacsony" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bassa urgente" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "緊急低値" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akutt lav" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Urgent laag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Krytycznie niska" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Baixa urgente" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Urgent scăzută" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Опасно низкая" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kriticky nízka" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akut lågt" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acil düşük" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Небезпечно низька" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thấp khẩn cấp" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "紧急低血糖" + } + } + } + }, "Usually %1$@ by %2$@" : { "comment" : "Trends: the median insulin delivered by this time of day over the last 14 days. (1: units, e.g. 28.4 U, 2: time of day)", "localizations" : { @@ -31647,6 +37939,24 @@ "state" : "translated", "value" : "添加CGM" } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přidat CGM" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "CGM hozzáadása" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Додати CGM" + } } } }, @@ -59269,7 +65579,147 @@ }, "Presets" : { - "comment" : "Presets screen title\nTitle text for button to Preset Settings" + "comment" : "Presets screen title\nTitle text for button to Preset Settings", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الإعدادات المسبقة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Předvolby" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forudindstillinger" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voreinstellungen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes preestablecidos" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esiasetukset" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Préréglages" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הגדרות מראש" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Előbeállítások" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preimpostazioni" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "プリセット" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forhåndsinnstillinger" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Programma's" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ustawienia wstępne" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predefinições" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Presetări" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пресеты" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Predvoľby" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Förinställningar" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ön Ayarlar" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пресети" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cài đặt sẵn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "预设" + } + } + } }, "Presets can be used for a variety of situations. Explore the uses below to learn tips for these common scenarios." : { @@ -63222,6 +69672,24 @@ "state" : "translated", "value" : "设置" } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nastavení" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beállítások" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Налаштування" + } } } }, diff --git a/Loop/Managers/LoopAppManager.swift b/Loop/Managers/LoopAppManager.swift index aee8e93430..85ae6914e9 100644 --- a/Loop/Managers/LoopAppManager.swift +++ b/Loop/Managers/LoopAppManager.swift @@ -525,6 +525,11 @@ class LoopAppManager: NSObject { .sink { LoopModeHistory.shared.record(closedLoop: $0) } .store(in: &cancellables) + NotificationCenter.default.publisher(for: LoopInterfacePreference.didChange) + .receive(on: RunLoop.main) + .sink { [weak self] _ in self?.relaunchHomeScreenForInterfaceChange() } + .store(in: &cancellables) + state = state.next await loopDataManager.updateDisplayState() @@ -632,10 +637,8 @@ class LoopAppManager: NSObject { return viewModel }() - private func launchHomeScreen() async { - dispatchPrecondition(condition: .onQueue(.main)) - precondition(state == .launchHomeScreen) - + /// Builds the status screen and makes it (or the redesign's tabs around it) the window's root. + private func installHomeScreen() { let viewModel = StatusTableViewModel( alertPermissionsChecker: alertPermissionsChecker, alertMuter: alertManager.alertMuter, @@ -661,26 +664,57 @@ class LoopAppManager: NSObject { self.statusTableViewController = statusTableView.viewController - var rootNavigationController = rootViewController as? RootNavigationController - if rootNavigationController == nil { - rootNavigationController = RootNavigationController() - rootViewController = rootNavigationController - } + let showsActionTabs = !LoopInterfacePreference.useRedesign + let statusScreen = UIHostingController( + rootView: statusTableView + .showsActionTabs(showsActionTabs) + .environmentObject(deviceDataManager.displayGlucosePreference) + .environment(\.appName, Bundle.main.bundleDisplayName) + .environment(\.isInvestigationalDevice, FeatureFlags.isInvestigationalDevice) + .environment(\.guidanceColors, .default) + .environment(\.loopStatusColorPalette, .loopStatus) + .environment(\.colorPalette, .default) + .environment(\.settingsManager, settingsManager) + .environment(\.temporaryPresetsManager, temporaryPresetsManager) + .edgesIgnoringSafeArea(.top) + ) - rootNavigationController?.setViewControllers([ - UIHostingController( - rootView: statusTableView - .environmentObject(deviceDataManager.displayGlucosePreference) - .environment(\.appName, Bundle.main.bundleDisplayName) - .environment(\.isInvestigationalDevice, FeatureFlags.isInvestigationalDevice) - .environment(\.guidanceColors, .default) - .environment(\.loopStatusColorPalette, .loopStatus) - .environment(\.colorPalette, .default) - .environment(\.settingsManager, settingsManager) - .environment(\.temporaryPresetsManager, temporaryPresetsManager) - .edgesIgnoringSafeArea(.top) + if LoopInterfacePreference.useRedesign { + let rootNavigationController = RootNavigationController() + rootNavigationController.setViewControllers([statusScreen], animated: false) + rootViewController = MainTabBarController( + rootNavigationController: rootNavigationController, + statusTableViewController: statusTableView.viewController, + deviceManager: deviceDataManager, + loopDataManager: loopDataManager, + settingsManager: settingsManager ) - ], animated: true) + } else { + var rootNavigationController = rootViewController as? RootNavigationController + if rootNavigationController == nil { + rootNavigationController = RootNavigationController() + rootViewController = rootNavigationController + } + + rootNavigationController?.setViewControllers([statusScreen], animated: true) + } + } + + /// Swaps between the classic and redesigned interface once Loop is running. + private func relaunchHomeScreenForInterfaceChange() { + guard state == .launchComplete else { return } + if let presented = rootViewController?.presentedViewController { + presented.dismiss(animated: true) { [weak self] in self?.installHomeScreen() } + } else { + installHomeScreen() + } + } + + private func launchHomeScreen() async { + dispatchPrecondition(condition: .onQueue(.main)) + precondition(state == .launchHomeScreen) + + installHomeScreen() await deviceDataManager.refreshDeviceData() @@ -764,6 +798,7 @@ class LoopAppManager: NSObject { } analyticsServicesManager.didOpenFromLink() + (rootViewController as? MainTabBarController)?.showNow() switch deeplink { case let .carbEntry(carbEntryLink): diff --git a/Loop/Redesign/DayCharts.swift b/Loop/Redesign/DayCharts.swift index 83e2032d74..0b8350e73f 100644 --- a/Loop/Redesign/DayCharts.swift +++ b/Loop/Redesign/DayCharts.swift @@ -422,6 +422,118 @@ struct ScrubCallout: View { } } +// MARK: - Prediction + +/// The last three hours of readings and Loop's dashed prediction for the next three. +struct PredictionChartView: View { + let readings: [GlucosePoint] + let prediction: [GlucosePoint] + let unit: GlucoseUnitStyle + let targetBands: [TargetBand] + var now: Date = Date() + var height: CGFloat = 150 + + @State private var selection: Date? + + private var window: DateInterval { + DateInterval(start: now.addingTimeInterval(-3 * 3600), end: now.addingTimeInterval(3 * 3600)) + } + + private func clamp(_ mgdl: Double) -> Double { + min(max(unit.value(mgdl), unit.dayDomain.lowerBound), unit.dayDomain.upperBound) + } + + private var futurePrediction: [GlucosePoint] { + prediction.filter { $0.date >= now.addingTimeInterval(-5 * 60) && $0.date <= window.end } + } + + var body: some View { + Chart { + ForEach(Array(targetBands.enumerated()), id: \.offset) { _, band in + RectangleMark(xStart: .value("Start", band.start), xEnd: .value("End", band.end), + yStart: .value("Low", clamp(band.range.lowerBound)), yEnd: .value("High", clamp(band.range.upperBound))) + .foregroundStyle(P.targetBand) + } + ForEach(Array(readings.filter { window.contains($0.date) }.enumerated()), id: \.offset) { _, point in + PointMark(x: .value("Time", point.date), y: .value("Glucose", clamp(point.mgdl))) + .symbolSize(12) + .foregroundStyle(P.glucose) + } + ForEach(Array(futurePrediction.enumerated()), id: \.offset) { index, point in + LineMark(x: .value("Time", point.date), y: .value("Predicted", clamp(point.mgdl)), series: .value("Series", "prediction")) + .foregroundStyle(P.glucose) + .lineStyle(StrokeStyle(lineWidth: 1.5, dash: [4, 3])) + .annotation(position: .topTrailing, spacing: 0) { + if index == futurePrediction.count - 1 { + Text(unit.format(point.mgdl)) + .font(LoopDesign.Typeface.caption.weight(.semibold)) + .foregroundStyle(P.ink) + } + } + } + RuleMark(x: .value("Now", now)) + .foregroundStyle(P.inkMuted) + .lineStyle(StrokeStyle(lineWidth: 1, dash: [2, 3])) + if let selection { + RuleMark(x: .value("Selected", selection)) + .foregroundStyle(P.ink.opacity(0.6)) + } + } + .frame(height: height) + .chartXScale(domain: window.start...window.end) + .chartYScale(domain: unit.dayDomain) + .chartXAxis { + AxisMarks(values: [window.start, now, window.end]) { value in + AxisGridLine().foregroundStyle(P.grid) + AxisValueLabel { + if let date = value.as(Date.self) { + Text(abs(date.timeIntervalSince(now)) < 60 ? NSLocalizedString("now", comment: "Prediction chart x-axis label marking the current time") : LoopFormat.time(date)) + } + } + .font(LoopDesign.Typeface.caption) + .foregroundStyle(P.inkMuted) + } + } + .chartYAxis { + AxisMarks(position: .leading, values: unit.dayTicks) { _ in + AxisGridLine().foregroundStyle(P.grid) + AxisValueLabel().font(LoopDesign.Typeface.caption).foregroundStyle(P.inkMuted) + } + } + .chartOverlay { proxy in + ScrubOverlay(proxy: proxy, selection: $selection, clampTo: window) + } + .overlay(alignment: .top) { + if let selection { + ScrubCallout(rows: scrubRows(at: selection)).allowsHitTesting(false) + } + } + .accessibilityElement(children: .ignore) + .accessibilityLabel(accessibilitySummary) + } + + private func scrubRows(at date: Date) -> [ScrubCallout.Row] { + var rows: [ScrubCallout.Row] = [.init(label: nil, value: LoopFormat.time(date), color: P.ink)] + let source = date <= now ? readings : prediction + if let value = nearest(source.map { ($0.date, $0.mgdl) }, to: date, within: 10 * 60) { + rows.append(.init(label: date <= now + ? NSLocalizedString("Glucose", comment: "Chart callout label for the glucose reading at the touched time") + : NSLocalizedString("Predicted", comment: "Prediction chart callout label for Loop's predicted glucose at the touched time"), + value: "\(unit.format(value)) \(unit.unitName)", color: P.glucoseInk)) + } + return rows + } + + private var accessibilitySummary: String { + guard let last = futurePrediction.last else { + return NSLocalizedString("Glucose prediction unavailable", comment: "VoiceOver label for the prediction chart when Loop has no prediction") + } + return String(format: NSLocalizedString("Loop predicts %1$@ %2$@ by %3$@", comment: "VoiceOver summary of the prediction chart. (1: predicted glucose value; 2: glucose unit, mmol/L or mg/dL; 3: time of the prediction)"), unit.format(last.mgdl), unit.unitName, LoopFormat.time(last.date)) + } +} + +// MARK: - IOB and COB + /// Insulin and carbs on board over the same window, each against its own coloured axis. struct IOBCOBChartView: View { let iob: [TimedValue] diff --git a/Loop/Redesign/DayDetailView.swift b/Loop/Redesign/DayDetailView.swift index 0f8186b71b..916db44136 100644 --- a/Loop/Redesign/DayDetailView.swift +++ b/Loop/Redesign/DayDetailView.swift @@ -130,7 +130,7 @@ struct DayDetailView: View { } } -/// A day on one timeline with its totals, for a one-day period on Trends. +/// A day on one timeline with its totals: Today so far on Now, and a one-day period on Trends. struct DayTimelineCard: View { let title: String let history: DayHistory diff --git a/Loop/Redesign/LoopDesign.swift b/Loop/Redesign/LoopDesign.swift index 3e9b50299e..25ee400867 100644 --- a/Loop/Redesign/LoopDesign.swift +++ b/Loop/Redesign/LoopDesign.swift @@ -63,6 +63,7 @@ enum LoopDesign { // Loop state static let statusAutomated = Color("fresh") static let statusWarning = Color("warning") + static let statusCritical = Color("status-critical") static let criticalInk = Color("critical-ink") } @@ -83,6 +84,7 @@ enum LoopDesign { /// Text styles follow Dynamic Type; the large numbers are fixed sizes in SF Pro Rounded with tabular figures. enum Typeface { + static let glucoseHero = Font.system(size: 64, weight: .bold, design: .rounded).monospacedDigit() static let statValue = Font.system(.title, design: .rounded).weight(.semibold).monospacedDigit() static let statValueSmall = Font.system(.title3, design: .rounded).weight(.semibold).monospacedDigit() static let title = Font.title.bold() @@ -277,9 +279,29 @@ struct AdaptiveRow: View { } } -// MARK: - Tiles +// MARK: - Pills and tiles -/// A small sunken tile with a label over a value, as used under the timeline. +struct StatusPill: View { + let text: String + var dot: Color? = nil + + var body: some View { + HStack(spacing: LoopDesign.Space.s1) { + if let dot { + Circle().fill(dot).frame(width: 8, height: 8) + } + Text(text) + .lineLimit(1) + } + .font(LoopDesign.Typeface.footnote.weight(.medium)) + .foregroundStyle(LoopDesign.Palette.ink) + .padding(.horizontal, 10) + .padding(.vertical, 5) + .background(LoopDesign.Palette.surfaceSunken, in: Capsule()) + } +} + +/// A small sunken tile with a label over a value, as used under the hero and the timeline. struct ValueTile: View { let label: String @ViewBuilder var value: Value diff --git a/Loop/Redesign/LoopHistoryModels.swift b/Loop/Redesign/LoopHistoryModels.swift index 8835c7b839..a3cedc5319 100644 --- a/Loop/Redesign/LoopHistoryModels.swift +++ b/Loop/Redesign/LoopHistoryModels.swift @@ -116,7 +116,7 @@ struct PumpLogEvent: Equatable, Identifiable { let kind: Kind } -/// Everything known about one day or reporting period, for InsulinTimeline. +/// Everything known about one day or reporting period, for InsulinTimeline and the Now screen. struct DayHistory { var window: DateInterval var glucose: [GlucosePoint] = [] @@ -310,6 +310,16 @@ enum LoopFormat { formatter.zeroFormattingBehavior = .dropAll return formatter.string(from: TimeInterval(minutes * 60)) ?? "" } + + /// "2m", "1h 5m": how long ago, for the status pill. + static func ago(_ date: Date, now: Date = Date()) -> String { + let minutes = max(0, Int(now.timeIntervalSince(date) / 60)) + let formatter = DateComponentsFormatter() + formatter.unitsStyle = .abbreviated + formatter.allowedUnits = minutes >= 1440 ? [.day, .hour] : minutes >= 60 ? [.hour, .minute] : [.minute] + formatter.zeroFormattingBehavior = minutes == 0 ? .pad : .dropAll + return formatter.string(from: TimeInterval(minutes * 60)) ?? "" + } } // MARK: - Store conversion boundaries diff --git a/Loop/Redesign/LoopHistoryProvider.swift b/Loop/Redesign/LoopHistoryProvider.swift index 824bd692d0..a2f6fb6f49 100644 --- a/Loop/Redesign/LoopHistoryProvider.swift +++ b/Loop/Redesign/LoopHistoryProvider.swift @@ -132,6 +132,19 @@ final class LoopHistoryProvider { return history } + /// CGM readings for any period, local and Health combined. + func glucose(_ interval: DateInterval) async throws -> [GlucosePoint] { + let boundary = localHistoryStart + var points: [GlucosePoint] = [] + if interval.start < boundary { + points += try await healthGlucose(DateInterval(start: interval.start, end: min(interval.end, boundary))) + } + if interval.end > boundary { + points += try await localGlucose(DateInterval(start: max(interval.start, boundary), end: interval.end)) + } + return points + } + // MARK: - Loop runs /// Loop runs in the local window, oldest first, with when the record of them begins. diff --git a/Loop/Redesign/MainTabBarController.swift b/Loop/Redesign/MainTabBarController.swift new file mode 100644 index 0000000000..ee82791af4 --- /dev/null +++ b/Loop/Redesign/MainTabBarController.swift @@ -0,0 +1,82 @@ +// +// MainTabBarController.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import UIKit +import SwiftUI + +/// Whether Loop shows the redesigned interface (tabs: Now, Trends, Logbook, Settings) or the classic status screen. +enum LoopInterfacePreference { + private static let key = "com.loopkit.Loop.redesign.enabled" + + static let didChange = Notification.Name("LoopInterfacePreferenceDidChange") + + static var useRedesign: Bool { + get { UserDefaults.standard.object(forKey: key) as? Bool ?? true } + set { + guard newValue != useRedesign else { return } + UserDefaults.standard.set(newValue, forKey: key) + NotificationCenter.default.post(name: didChange, object: nil) + } + } +} + +/// The redesign's root. The Now tab is Loop's own RootNavigationController, so every existing flow +/// (bolus, carbs, presets, alerts, device settings, deeplinks) keeps working from StatusTableViewController. +final class MainTabBarController: UITabBarController, UITabBarControllerDelegate { + + let rootNavigationController: RootNavigationController + private weak var statusTableViewController: StatusTableViewController? + private let provider: LoopHistoryProvider + private let settingsPlaceholder = UIViewController() + + init(rootNavigationController: RootNavigationController, statusTableViewController: StatusTableViewController, deviceManager: DeviceDataManager, loopDataManager: LoopDataManager, settingsManager: SettingsManager) { + self.rootNavigationController = rootNavigationController + self.statusTableViewController = statusTableViewController + self.provider = LoopHistoryProvider(deviceManager: deviceManager, loopDataManager: loopDataManager, settingsManager: settingsManager, temporaryPresetsManager: statusTableViewController.temporaryPresetsManager) + super.init(nibName: nil, bundle: nil) + + rootNavigationController.tabBarItem = UITabBarItem(title: NSLocalizedString("Now", comment: "Title of the Now tab in the redesigned interface, showing current glucose and Loop status"), image: UIImage(systemName: "drop.fill"), tag: 0) + + let trends = UIHostingController(rootView: TrendsView(provider: provider)) + trends.tabBarItem = UITabBarItem(title: NSLocalizedString("Trends", comment: "Title of the Trends tab in the redesigned interface"), image: UIImage(systemName: "chart.bar.fill"), tag: 1) + + let logbook = UIHostingController(rootView: LogbookView(provider: provider, deviceManager: deviceManager)) + logbook.tabBarItem = UITabBarItem(title: NSLocalizedString("Logbook", comment: "Title of the Logbook tab in the redesigned interface, listing past events"), image: UIImage(systemName: "list.bullet"), tag: 2) + + settingsPlaceholder.tabBarItem = UITabBarItem(title: NSLocalizedString("Settings", comment: "Title of the Settings tab in the redesigned interface"), image: UIImage(systemName: "gearshape.fill"), tag: 3) + + viewControllers = [rootNavigationController, trends, logbook, settingsPlaceholder] + delegate = self + tabBar.tintColor = UIColor(named: "accent") + if #available(iOS 18.0, *) { + mode = .tabBar + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + /// Settings stays a sheet, as in classic Loop, so the tab only presents it. It is presented from the Now tab: + /// device and service screens open from Settings through StatusTableViewController, which is only in the window + /// while Now is selected. + func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { + guard viewController === settingsPlaceholder else { return true } + showNow() + statusTableViewController?.presentSettingsFromTab() + return false + } + + func showNow() { + selectedViewController = rootNavigationController + } + + override func restoreUserActivityState(_ activity: NSUserActivity) { + showNow() + rootNavigationController.restoreUserActivityState(activity) + } +} diff --git a/Loop/Redesign/NowScreen.swift b/Loop/Redesign/NowScreen.swift new file mode 100644 index 0000000000..59f7da3e2f --- /dev/null +++ b/Loop/Redesign/NowScreen.swift @@ -0,0 +1,770 @@ +// +// NowScreen.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import SwiftUI +import HealthKit +import LoopKit +import LoopKitUI +import LoopCore +import LoopAlgorithm + +private typealias P = LoopDesign.Palette + +/// What the Now screen shows. StatusTableViewController owns it and asks it to refresh whenever Loop's data changes. +@MainActor +final class NowModel: ObservableObject { + + struct Reading: Equatable { + let mgdl: Double + let date: Date + let trend: GlucoseTrend? + /// Change since the previous reading, per 5 minutes. + let delta: Double? + } + + struct Device: Equatable { + let name: String + let message: String? + let state: DeviceStatusHighlightState? + /// Set when the message is the device's own status highlight, which the Now screen spells out in a notice. + var symbol: String? = nil + /// The device's lifecycle (pod, sensor) is near its end; the pill says so. + var endingSoon = false + } + + enum LoopState: Equatable { + case fresh + case stale + case failed + case never + } + + @Published private(set) var reading: Reading? + @Published private(set) var lastLoopCompleted: Date? + @Published private(set) var closedLoop = false + /// Closed loop is switched on but Loop has paused it, for example while the CGM has no session or data. + @Published private(set) var closedLoopPaused = false + @Published private(set) var iob: Double? + @Published private(set) var cob: Double? + @Published private(set) var basalDeliveryState: PumpManagerStatus.BasalDeliveryState? + @Published private(set) var programmedBasalRate: Double? + @Published private(set) var scheduledBasalRate: Double? + @Published private(set) var suspended = false + @Published private(set) var targetRange: ClosedRange? + @Published private(set) var predictionTargets: [TargetBand] = [] + @Published private(set) var todayTargets: [TargetBand] = [] + @Published private(set) var prediction: [GlucosePoint] = [] + @Published private(set) var recentGlucose: [GlucosePoint] = [] + @Published private(set) var historyError: String? + @Published private(set) var glucoseHistoryError: String? + @Published private(set) var today: DayHistory + @Published private(set) var pump: Device? + @Published private(set) var cgm: Device? + @Published private(set) var dosingAvailable = true + @Published private(set) var preMealOn = false + @Published private(set) var overrideName: String? + @Published private(set) var unit: GlucoseUnitStyle + @Published var now = Date() + /// Set by StatusTableViewController, which owns the onboarding state. + var isOnboardingComplete: () -> Bool = { true } + + let provider: LoopHistoryProvider + private let deviceManager: DeviceDataManager + private let loopDataManager: LoopDataManager + private let settingsManager: SettingsManager + private let temporaryPresetsManager: TemporaryPresetsManager + private var refreshTask: Task? + private var pendingRefresh = false + + init(deviceManager: DeviceDataManager, loopDataManager: LoopDataManager, settingsManager: SettingsManager, temporaryPresetsManager: TemporaryPresetsManager) { + self.deviceManager = deviceManager + self.loopDataManager = loopDataManager + self.settingsManager = settingsManager + self.temporaryPresetsManager = temporaryPresetsManager + self.provider = LoopHistoryProvider(deviceManager: deviceManager, loopDataManager: loopDataManager, settingsManager: settingsManager, temporaryPresetsManager: temporaryPresetsManager) + self.unit = provider.unitStyle + let start = Calendar.current.startOfDay(for: Date()) + self.today = DayHistory(window: DateInterval(start: start, duration: 86400)) + } + + var basalRate: Double? { + switch basalDeliveryState { + case .tempBasal(let dose)?: + return dose.startDate <= now && now < dose.endDate ? dose.unitsPerHour : programmedBasalRate + case .active?: return programmedBasalRate + case .suspended?, .suspending?: return 0 + default: return nil + } + } + + var loopState: LoopState { + guard let lastLoopCompleted else { return .never } + let age = now.timeIntervalSince(lastLoopCompleted) + if age <= 5 * 60 + 30 { return .fresh } + if age <= 15 * 60 { return .stale } + return .failed + } + + var readingIsStale: Bool { + guard let reading else { return true } + return now.timeIntervalSince(reading.date) > LoopCoreConstants.inputDataRecencyInterval + } + + /// Refreshes everything; calls made while a refresh runs are coalesced into one more. + func refresh() { + guard refreshTask == nil else { + pendingRefresh = true + return + } + refreshTask = Task { [weak self] in + await self?.load() + guard let self else { return } + self.refreshTask = nil + if self.pendingRefresh { + self.pendingRefresh = false + self.refresh() + } + } + } + + private func load() async { + let now = Date() + self.now = now + unit = provider.unitStyle + + let displayState = await loopDataManager.algorithmDisplayState + let predicted = (displayState.output?.predictedGlucose ?? []).map(\.loopKit).map { + GlucosePoint(date: $0.startDate, mgdl: $0.quantity.doubleValue(for: .milligramsPerDeciliter)) + } + let start = Calendar.current.startOfDay(for: now) + let window = DateInterval(start: start, end: Calendar.current.date(byAdding: .day, value: 1, to: start)!) + async let dayFetch = provider.dayHistory(window) + async let recentFetch = provider.glucose(DateInterval(start: now.addingTimeInterval(-3 * 3600), end: now)) + do { + let targets = try await provider.glucoseTargetBands(in: window) + today = try await dayFetch + todayTargets = targets + historyError = nil + } catch { + historyError = error.localizedDescription + } + do { + let predictionWindow = DateInterval(start: now.addingTimeInterval(-3 * 3600), end: now.addingTimeInterval(3 * 3600)) + predictionTargets = try await provider.glucoseTargetBands(in: predictionWindow) + recentGlucose = try await recentFetch + glucoseHistoryError = nil + } catch { + recentGlucose = [] + glucoseHistoryError = error.localizedDescription + } + + prediction = predicted + iob = loopDataManager.activeInsulin?.value + cob = loopDataManager.activeCarbs?.quantity.doubleValue(for: .gram) + lastLoopCompleted = loopDataManager.lastLoopCompleted + // Closed loop is on but paused while the CGM session or a manual temp basal rules out automation. + closedLoopPaused = settingsManager.dosingEnabled && deviceManager.closedLoopDisallowedLocalizedDescription != nil + closedLoop = settingsManager.dosingEnabled && !closedLoopPaused + targetRange = provider.glucoseTargetRange + + if let latest = loopDataManager.latestGlucose { + let mgdl = latest.quantity.doubleValue(for: .milligramsPerDeciliter) + let previous = recentGlucose.last { $0.date < latest.startDate.addingTimeInterval(-60) } + var delta: Double? = nil + if let previous, latest.startDate.timeIntervalSince(previous.date) <= 15 * 60 { + delta = (mgdl - previous.mgdl) / latest.startDate.timeIntervalSince(previous.date) * 300 + } + // The CGM's own trend, as the classic HUD shows it. + reading = Reading(mgdl: mgdl, date: latest.startDate, trend: deviceManager.glucoseDisplay(for: latest)?.trendType, delta: delta) + } else { + reading = nil + } + + let scheduled = temporaryPresetsManager.basalRateScheduleApplyingOverrideHistory?.value(at: now) + scheduledBasalRate = scheduled + // Presets change Loop's plan; without a temp basal the pump still delivers its programmed profile. + programmedBasalRate = settingsManager.settings.basalRateSchedule?.value(at: now) + basalDeliveryState = deviceManager.pumpManagerStatus?.basalDeliveryState + switch basalDeliveryState { + case .suspended?, .suspending?: suspended = true + default: suspended = false + } + + pump = deviceManager.pumpManager.map { manager in + device(name: manager.localizedTitle, highlight: deviceManager.pumpStatusHighlight, progress: deviceManager.pumpLifecycleProgress) + } + cgm = deviceManager.cgmManager.map { manager in + device(name: manager.localizedTitle, highlight: deviceManager.cgmStatusHighlight, progress: deviceManager.cgmLifecycleProgress) + } + + dosingAvailable = isOnboardingComplete() || deviceManager.pumpManager?.isOnboarded == true + preMealOn = temporaryPresetsManager.isPreMealTargetActive() + overrideName = preMealOn ? nil : temporaryPresetsManager.activePreset?.name + } + + private func device(name: String, highlight: DeviceStatusHighlight?, progress: DeviceLifecycleProgress?) -> Device { + if let highlight { + // Plugins wrap highlights onto two lines for the classic HUD ("Signal\nLoss"). + let message = highlight.localizedMessage.replacingOccurrences(of: "\n", with: " ") + return Device(name: name, message: message, state: highlight.state, symbol: highlight.imageName) + } + if let progress, progress.progressState == .warning || progress.progressState == .critical { + return Device(name: name, message: nil, state: progress.progressState == .critical ? .critical : .warning, endingSoon: true) + } + return Device(name: name, message: nil, state: nil) + } +} + +/// The buttons on the Now screen, wired to StatusTableViewController's existing flows. +struct NowActions { + var addCarbs: () -> Void = {} + var bolus: () -> Void = {} + var presets: () -> Void = {} + var showPrediction: () -> Void = {} + var showToday: () -> Void = {} + var showInsulin: () -> Void = {} + var showCarbs: () -> Void = {} + var pumpTapped: () -> Void = {} + var cgmTapped: () -> Void = {} + var loopStatusTapped: () -> Void = {} +} + +// MARK: - Header: title, status, hero, actions + +struct NowHeaderView: View { + @ObservedObject var model: NowModel + let actions: NowActions + + var body: some View { + VStack(alignment: .leading, spacing: LoopDesign.Space.s3) { + Text(NSLocalizedString("Now", comment: "Title at the top of the Now screen, showing current glucose and Loop status")) + .font(LoopDesign.Typeface.title) + .foregroundStyle(P.ink) + .accessibilityAddTraits(.isHeader) + statusRow + if !noticeItems.isEmpty { + notices + } + hero + actionRow + } + .padding(.horizontal, LoopDesign.Space.s4) + .padding(.top, LoopDesign.Space.s2) + .padding(.bottom, LoopDesign.Space.s2) + .frame(maxWidth: .infinity, alignment: .leading) + .background(P.surface) + .onReceive(Timer.publish(every: 30, on: .main, in: .common).autoconnect()) { model.now = $0 } + } + + // Status pills replace Loop's HUD icons with words. They wrap rather than scroll, so a device is never off-screen. + private var statusRow: some View { + FlowRow(spacing: LoopDesign.Space.s2) { + Button(action: actions.loopStatusTapped) { + StatusPill(text: loopText, dot: loopColor) + } + .accessibilityHint(NSLocalizedString("Shows when Loop last ran", comment: "VoiceOver hint for the Loop status pill and the Loop notice on the Now screen")) + Button(action: actions.pumpTapped) { + if let pump = model.pump { + StatusPill(text: pillText(pump), dot: pillColor(pump)) + } else { + StatusPill(text: NSLocalizedString("Add pump", comment: "Status pill on the Now screen when no pump is set up; tapping it starts pump setup"), dot: P.statusCritical) + } + } + .accessibilityHint(model.pump == nil + ? NSLocalizedString("Sets up a pump", comment: "VoiceOver hint for the Add pump status pill on the Now screen") + : NSLocalizedString("Opens pump settings", comment: "VoiceOver hint for the pump status pill and pump notice on the Now screen")) + Button(action: actions.cgmTapped) { + if let cgm = model.cgm { + StatusPill(text: pillText(cgm), dot: pillColor(cgm)) + } else { + StatusPill(text: NSLocalizedString("Add CGM", comment: "Status pill on the Now screen when no CGM is set up; tapping it starts CGM setup"), dot: P.statusCritical) + } + } + .accessibilityHint(model.cgm == nil + ? NSLocalizedString("Sets up a CGM", comment: "VoiceOver hint for the Add CGM status pill on the Now screen") + : NSLocalizedString("Opens CGM settings", comment: "VoiceOver hint for the CGM status pill and CGM notice on the Now screen")) + } + .buttonStyle(.plain) + } + + // A device problem or a stalled loop gets a notice in words under the pills, most serious first. + private var notices: some View { + let items = noticeItems + return VStack(spacing: LoopDesign.Space.s2) { + ForEach(items) { item in + Button(action: item.action) { + DeviceNotice(item: item) + } + .buttonStyle(.plain) + .accessibilityHint(item.hint) + } + } + } + + private var noticeItems: [DeviceNotice.Item] { + var items: [DeviceNotice.Item] = [] + if model.loopState == .failed, let last = model.lastLoopCompleted { + items.append(DeviceNotice.Item( + id: "loop", + severity: .critical, + symbol: "exclamationmark.arrow.circlepath", + title: String(format: NSLocalizedString("Loop hasn't run for %1$@", comment: "Notice title on the Now screen when Loop hasn't completed recently. (1: duration since Loop last ran, e.g. 16m)"), LoopFormat.ago(last, now: model.now)), + detail: NSLocalizedString("Insulin isn't being adjusted. Tap for details.", comment: "Notice detail on the Now screen when Loop hasn't completed recently"), + hint: NSLocalizedString("Shows when Loop last ran", comment: "VoiceOver hint for the Loop status pill and the Loop notice on the Now screen"), + action: actions.loopStatusTapped)) + } + // While suspended, Loop's own status row says so and offers Resume, so only a critical pump problem adds a notice. + if let pump = model.pump, let symbol = pump.symbol, let message = pump.message, + !model.suspended || pump.state == .critical { + let severity = DeviceNotice.Severity(pump.state) + items.append(DeviceNotice.Item( + id: "pump", + severity: severity, + symbol: symbol, + title: message, + detail: severity == .critical ? NSLocalizedString("Loop may not be giving insulin.", comment: "Notice detail on the Now screen under a critical pump problem reported by the pump") : nil, + hint: NSLocalizedString("Opens pump settings", comment: "VoiceOver hint for the pump status pill and pump notice on the Now screen"), + action: actions.pumpTapped)) + } + if let cgm = model.cgm, let symbol = cgm.symbol, let message = cgm.message { + let severity = DeviceNotice.Severity(cgm.state) + items.append(DeviceNotice.Item( + id: "cgm", + severity: severity, + symbol: symbol, + title: message, + detail: model.readingIsStale ? NSLocalizedString("Loop can't adjust insulin without readings.", comment: "Notice detail on the Now screen under a CGM problem when there is no recent glucose reading") : nil, + hint: NSLocalizedString("Opens CGM settings", comment: "VoiceOver hint for the CGM status pill and CGM notice on the Now screen"), + action: actions.cgmTapped)) + } + return items.sorted { $0.severity > $1.severity } + } + + private var loopText: String { + guard let last = model.lastLoopCompleted else { + if model.closedLoop { + return NSLocalizedString("Closed loop · not run yet", comment: "Status pill on the Now screen: closed loop is on but Loop has never completed") + } else if model.closedLoopPaused { + return NSLocalizedString("Closed loop paused · not run yet", comment: "Status pill on the Now screen: closed loop is switched on but paused (e.g. no CGM data), and Loop has never completed") + } else { + return NSLocalizedString("Open loop · not run yet", comment: "Status pill on the Now screen: closed loop is off and Loop has never completed") + } + } + let ago = LoopFormat.ago(last, now: model.now) + if model.loopState == .failed { + return String(format: NSLocalizedString("Loop failed · %1$@", comment: "Status pill on the Now screen: Loop hasn't completed for over 15 minutes. (1: duration since Loop last ran, e.g. 16m)"), ago) + } else if model.closedLoop { + return String(format: NSLocalizedString("Closed loop · %1$@", comment: "Status pill on the Now screen: closed loop is on and Loop last ran this long ago. (1: duration, e.g. 3m)"), ago) + } else if model.closedLoopPaused { + return String(format: NSLocalizedString("Closed loop paused · %1$@", comment: "Status pill on the Now screen: closed loop is switched on but paused (e.g. no CGM data), and Loop last ran this long ago. (1: duration, e.g. 3m)"), ago) + } else { + return String(format: NSLocalizedString("Open loop · %1$@", comment: "Status pill on the Now screen: closed loop is off (Loop recommends only) and Loop last ran this long ago. (1: duration, e.g. 3m)"), ago) + } + } + + /// Green only while closed loop is running; open loop that ran on time is neutral. + private var loopColor: Color { + switch model.loopState { + case .fresh: return model.closedLoop ? P.statusAutomated : model.closedLoopPaused ? P.statusWarning : P.inkMuted + case .stale: return P.statusWarning + case .failed, .never: return P.statusCritical + } + } + + /// A highlight (always a plugin message with a symbol) is spelled out in its notice, so its pill keeps to the device name. + private func pillText(_ device: NowModel.Device) -> String { + guard device.symbol == nil else { return device.name } + if device.endingSoon { + return String(format: NSLocalizedString("%1$@ · ending soon", comment: "Status pill on the Now screen when a pump pod or CGM sensor is near the end of its life. (1: device name from the pump or CGM plugin, e.g. Omnipod 5)"), device.name) + } + return device.name + } + + private func pillColor(_ device: NowModel.Device) -> Color? { + switch device.state { + case .critical?: return P.statusCritical + case .warning?: return P.statusWarning + default: return device.symbol == nil ? nil : P.inkMuted + } + } + + private var hero: some View { + LoopCard { + HStack(alignment: .top) { + VStack(alignment: .leading, spacing: 0) { + HStack(alignment: .center, spacing: LoopDesign.Space.s2) { + if let reading = model.reading, !model.readingIsStale { + Text(model.unit.format(reading.mgdl)) + .font(LoopDesign.Typeface.glucoseHero) + .foregroundStyle(P.ink) + .lineLimit(1) + .minimumScaleFactor(0.6) + if let trend = reading.trend { + Image(systemName: Self.symbol(for: trend)) + .font(.system(size: 30, weight: .bold)) + .foregroundStyle(P.glucose) + .accessibilityLabel(trend.localizedDescription) + } + } else { + Text(NSLocalizedString("No reading", comment: "Shown on the Now screen in place of the glucose value when there is no recent CGM reading")) + .font(.system(size: 34, weight: .bold, design: .rounded)) + .foregroundStyle(P.inkMuted) + } + } + Text(readingDetail) + .font(LoopDesign.Typeface.subhead) + .foregroundStyle(P.inkMuted) + if let rangePill { + StatusPill(text: rangePill.0, dot: rangePill.1) + .padding(.top, LoopDesign.Space.s1) + } + } + Spacer(minLength: LoopDesign.Space.s2) + VStack(alignment: .trailing, spacing: 2) { + if let reading = model.reading { + Text(LoopFormat.time(reading.date)) + } + if let target = model.targetRange { + Text(String(format: NSLocalizedString("Target %1$@–%2$@", comment: "Glucose target range on the Now screen hero card. (1: lower bound; 2: upper bound)"), model.unit.format(target.lowerBound), model.unit.format(target.upperBound))) + } + } + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + .padding(.top, LoopDesign.Space.s4) + } + .accessibilityElement(children: .combine) + + AdaptiveRow { + // As on the classic charts: insulin opens Loop's delivery history, carbs its absorption screen. + Button(action: actions.showInsulin) { + ValueTile(label: NSLocalizedString("IOB", comment: "Abbreviation for insulin on board; label of a tile on the Now screen")) { + Text(model.iob.map(LoopFormat.doseUnits) ?? "–") + .font(LoopDesign.Typeface.statValueSmall) + .foregroundStyle(P.insulinInk) + } + } + .buttonStyle(.plain) + .accessibilityHint(NSLocalizedString("Opens insulin delivery history", comment: "VoiceOver hint for the IOB tile on the Now screen")) + Button(action: actions.showCarbs) { + ValueTile(label: NSLocalizedString("COB", comment: "Abbreviation for carbs on board; label of a tile on the Now screen")) { + Text(model.cob.map(LoopFormat.grams) ?? "–") + .font(LoopDesign.Typeface.statValueSmall) + .foregroundStyle(P.carbsInk) + } + } + .buttonStyle(.plain) + .accessibilityHint(NSLocalizedString("Opens carb absorption", comment: "VoiceOver hint for the COB tile on the Now screen")) + ValueTile(label: NSLocalizedString("Basal", comment: "Label of a tile showing the current basal rate (Now screen) or the day's basal insulin total (Today so far totals)")) { + VStack(alignment: .leading, spacing: 0) { + HStack(alignment: .firstTextBaseline, spacing: 2) { + Text(model.basalRate.map { LoopFormat.number($0, decimals: 2) } ?? "–") + .font(LoopDesign.Typeface.statValueSmall) + .foregroundStyle(P.ink) + Text(NSLocalizedString("U/h", comment: "Abbreviation for insulin units per hour")) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + } + if model.suspended { + Text(NSLocalizedString("Suspended", comment: "Shown in the Basal tile on the Now screen when insulin delivery is suspended")) + .font(LoopDesign.Typeface.footnote.weight(.semibold)) + .foregroundStyle(P.criticalInk) + } else if let rate = model.basalRate, let scheduled = model.scheduledBasalRate { + Text(String(format: NSLocalizedString("%1$@ vs plan", comment: "Basal tile on the Now screen: how the current basal rate differs from the scheduled rate. (1: signed difference in U/h, e.g. +0.25)"), LoopFormat.signed(rate - scheduled))) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.inkMuted) + .lineLimit(1) + .minimumScaleFactor(0.8) + } + } + } + } + .fixedSize(horizontal: false, vertical: true) + } + .opacity(model.readingIsStale && model.reading != nil ? 0.7 : 1) + } + + private var readingDetail: String { + guard let reading = model.reading else { return model.unit.unitName } + if model.readingIsStale { + return String(format: NSLocalizedString("Last reading %1$@ ago", comment: "Shown on the Now screen under No reading when the latest CGM reading is too old. (1: duration, e.g. 25m)"), LoopFormat.ago(reading.date, now: model.now)) + } + guard let delta = reading.delta else { return model.unit.unitName } + let change = model.unit.isMmol ? LoopFormat.signed(delta / GlucoseUnitStyle.mgdlPerMmol, decimals: 1) : LoopFormat.signed(delta, decimals: 0) + return String(format: NSLocalizedString("%1$@ · %2$@ in 5 min", comment: "Under the glucose value on the Now screen: the glucose unit and how much glucose changed over 5 minutes. (1: glucose unit, e.g. mmol/L; 2: signed change, e.g. +0.3)"), model.unit.unitName, change) + } + + /// Low and high get a pill with words; the number itself is never tinted. + private var rangePill: (String, Color)? { + guard let reading = model.reading, !model.readingIsStale else { return nil } + if reading.mgdl < 54 { return (NSLocalizedString("Urgent low", comment: "Pill under the glucose value on the Now screen when glucose is below 54 mg/dL (3.0 mmol/L)"), P.statusCritical) } + if reading.mgdl < 70 { return (NSLocalizedString("Low", comment: "Pill under the glucose value on the Now screen when glucose is below 70 mg/dL (3.9 mmol/L)"), P.statusCritical) } + if reading.mgdl > 250 { return (NSLocalizedString("High", comment: "Pill under the glucose value on the Now screen when glucose is above 250 mg/dL (13.9 mmol/L)"), P.statusWarning) } + return nil + } + + static func symbol(for trend: GlucoseTrend) -> String { + switch trend { + case .upUpUp: return "arrow.up.to.line" + case .upUp: return "chevron.up.2" + case .up: return "arrow.up" + case .flat: return "arrow.right" + case .down: return "arrow.down" + case .downDown: return "chevron.down.2" + case .downDownDown: return "arrow.down.to.line" + } + } + + private var actionRow: some View { + AdaptiveRow { + actionButton(NSLocalizedString("Add carbs", comment: "Button on the Now screen that opens carb entry"), systemImage: "fork.knife", fill: P.carbs, text: P.onFill, action: actions.addCarbs) + .disabled(!model.dosingAvailable) + .opacity(model.dosingAvailable ? 1 : 0.4) + actionButton(NSLocalizedString("Bolus", comment: "Button on the Now screen that opens the bolus screen"), systemImage: "syringe.fill", fill: P.insulin, text: P.onFill, action: actions.bolus) + .disabled(!model.dosingAvailable) + .opacity(model.dosingAvailable ? 1 : 0.4) + // Pre-meal and the other presets share Loop's Presets screen. + Button(action: actions.presets) { + actionLabel(presetsTitle, systemImage: "slider.horizontal.3", fill: presetsFill, text: P.ink) + } + .buttonStyle(.plain) + .accessibilityLabel(NSLocalizedString("Presets", comment: "Button on the Now screen that opens override presets (and pre-meal); also its VoiceOver label and menu item")) + .accessibilityValue(presetsActive ? presetsTitle : "") + } + .fixedSize(horizontal: false, vertical: true) + } + + private var presetsFill: Color { + model.preMealOn || model.overrideName != nil ? P.accent.opacity(0.18) : P.surfaceSunken + } + + private var presetsActive: Bool { + model.preMealOn || model.overrideName != nil + } + + private var presetsTitle: String { + if model.preMealOn { return NSLocalizedString("Pre-meal on", comment: "Presets button title on the Now screen while the pre-meal target is on") } + if let name = model.overrideName { return name } + return NSLocalizedString("Presets", comment: "Button on the Now screen that opens override presets (and pre-meal); also its VoiceOver label and menu item") + } + + private func actionButton(_ title: String, systemImage: String, fill: Color, text: Color, action: @escaping () -> Void) -> some View { + Button(action: action) { + actionLabel(title, systemImage: systemImage, fill: fill, text: text) + } + .buttonStyle(.plain) + } + + private func actionLabel(_ title: String, systemImage: String, fill: Color, text: Color) -> some View { + // Longer languages ("Agregar carbohidratos") wrap onto a second line rather than truncate. + Text(title) + .font(LoopDesign.Typeface.subhead.weight(.semibold)) + .lineLimit(2) + .multilineTextAlignment(.center) + .minimumScaleFactor(0.8) + .allowsTightening(true) + .foregroundStyle(text) + .padding(.horizontal, LoopDesign.Space.s1) + .padding(.vertical, LoopDesign.Space.s1) + .frame(maxWidth: .infinity, minHeight: 44, maxHeight: .infinity) + .background(fill, in: RoundedRectangle(cornerRadius: LoopDesign.Radius.md, style: .continuous)) + .contentShape(RoundedRectangle(cornerRadius: LoopDesign.Radius.md)) + } +} + +// MARK: - Charts: next 3 hours and today so far + +struct NowChartsView: View { + @ObservedObject var model: NowModel + let actions: NowActions + + var body: some View { + VStack(spacing: LoopDesign.Space.s4) { + predictionCard + todayCard + } + .padding(.horizontal, LoopDesign.Space.s4) + .padding(.top, LoopDesign.Space.s2) + .padding(.bottom, LoopDesign.Space.s6) + .frame(maxWidth: .infinity) + .background(P.surface) + } + + private var readings: [GlucosePoint] { + model.recentGlucose.filter { $0.date >= model.now.addingTimeInterval(-3 * 3600) } + } + + private var predictionCard: some View { + Button(action: actions.showPrediction) { + LoopCard { + CardHeader(title: NSLocalizedString("Next 3 hours", comment: "Title of the prediction chart card on the Now screen")) { + // Loop's eventual glucose is the end of its prediction, which can run past the three hours drawn. + if let last = model.prediction.last { + Text(String(format: NSLocalizedString("Eventually %1$@ %2$@", comment: "Prediction card on the Now screen: Loop's eventual predicted glucose. (1: glucose value; 2: glucose unit, e.g. mmol/L)"), model.unit.format(last.mgdl), model.unit.unitName)) + } + } + if readings.isEmpty && model.prediction.isEmpty { + ChartPlaceholder(message: model.glucoseHistoryError == nil + ? NSLocalizedString("No CGM data in the last 3 hours", comment: "Empty prediction chart on the Now screen") + : NSLocalizedString("Couldn't load recent CGM readings", comment: "Prediction chart on the Now screen when recent glucose couldn't be read")) + } else { + PredictionChartView(readings: readings, prediction: model.prediction, unit: model.unit, + targetBands: model.predictionTargets, now: model.now) + } + ChartLegend(items: [ + LegendItem(swatch: .dot(P.glucose), label: NSLocalizedString("Readings", comment: "Chart legend on the Now screen: dots are CGM glucose readings")), + LegendItem(swatch: .dash(P.glucose), label: NSLocalizedString("Loop prediction", comment: "Chart legend on the Now screen: dashed line is Loop's predicted glucose")), + ]) + if let error = model.glucoseHistoryError { + Text(String(format: NSLocalizedString("Recent readings unavailable: %1$@", comment: "Error under the prediction chart on the Now screen. (1: system error description)"), error)) + .font(LoopDesign.Typeface.footnote) + .foregroundStyle(P.criticalInk) + } + } + } + .buttonStyle(.plain) + .accessibilityHint(NSLocalizedString("Opens Loop's prediction breakdown", comment: "VoiceOver hint for the prediction card on the Now screen")) + } + + private var todayCard: some View { + Button(action: actions.showToday) { + if let error = model.historyError { + LoopCard { + CardHeader(title: DayTimelineCard.todayTitle) + ChartPlaceholder(message: String(format: NSLocalizedString("Couldn't load today's history: %1$@", comment: "Shown on the Now screen's Today so far card when history couldn't be read. (1: system error description)"), error)) + } + } else { + DayTimelineCard(title: DayTimelineCard.todayTitle, history: model.today, unit: model.unit, targetBands: model.todayTargets, + now: model.now, height: 230) + } + } + .buttonStyle(.plain) + .accessibilityHint(NSLocalizedString("Opens the full day", comment: "VoiceOver hint for the Today so far card on the Now screen")) + } +} + +// MARK: - Notices + +/// One problem in words: what the device or Loop reports, what it means, and where to go. +struct DeviceNotice: View { + enum Severity: Int, Comparable { + case info, warning, critical + + init(_ state: DeviceStatusHighlightState?) { + switch state { + case .critical?: self = .critical + case .warning?: self = .warning + default: self = .info + } + } + + static func < (lhs: Severity, rhs: Severity) -> Bool { lhs.rawValue < rhs.rawValue } + } + + struct Item: Identifiable { + let id: String + let severity: Severity + let symbol: String + let title: String + let detail: String? + let hint: String + let action: () -> Void + } + + let item: Item + + private var tint: Color { + switch item.severity { + case .critical: return P.statusCritical + case .warning: return P.statusWarning + case .info: return P.inkMuted + } + } + + var body: some View { + HStack(alignment: .center, spacing: LoopDesign.Space.s3) { + Image(systemName: item.symbol) + .font(.system(size: 20, weight: .semibold)) + .foregroundStyle(item.severity == .critical ? P.criticalInk : tint) + .frame(width: 24) + .accessibilityHidden(true) + VStack(alignment: .leading, spacing: 2) { + Text(item.title) + .font(LoopDesign.Typeface.headline) + .foregroundStyle(item.severity == .critical ? P.criticalInk : P.ink) + if let detail = item.detail { + Text(detail) + .font(LoopDesign.Typeface.subhead) + .foregroundStyle(P.ink) + .fixedSize(horizontal: false, vertical: true) + } + } + Spacer(minLength: 0) + Image(systemName: "chevron.right") + .font(.footnote.weight(.semibold)) + .foregroundStyle(P.inkMuted) + .accessibilityHidden(true) + } + .padding(LoopDesign.Space.s3) + .frame(maxWidth: .infinity, alignment: .leading) + .background(tint.opacity(item.severity == .info ? 0.12 : 0.16), in: RoundedRectangle(cornerRadius: LoopDesign.Radius.lg, style: .continuous)) + .overlay( + RoundedRectangle(cornerRadius: LoopDesign.Radius.lg, style: .continuous) + .strokeBorder(tint.opacity(item.severity == .info ? 0 : 0.45), lineWidth: 1) + ) + .contentShape(Rectangle()) + .accessibilityElement(children: .combine) + } +} + +/// Lays its children out left to right and starts a new line when the next one won't fit. +struct FlowRow: Layout { + var spacing: CGFloat = 8 + + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { + let rows = arrange(width: proposal.width ?? .infinity, subviews: subviews) + let width = rows.map { $0.width }.max() ?? 0 + let height = rows.map { $0.height }.reduce(0, +) + spacing * CGFloat(max(rows.count - 1, 0)) + return CGSize(width: proposal.width ?? width, height: height) + } + + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { + var y = bounds.minY + for row in arrange(width: bounds.width, subviews: subviews) { + var x = bounds.minX + for index in row.indices { + let size = subviews[index].sizeThatFits(ProposedViewSize(width: bounds.width, height: nil)) + subviews[index].place(at: CGPoint(x: x, y: y), proposal: ProposedViewSize(width: min(size.width, bounds.width), height: size.height)) + x += min(size.width, bounds.width) + spacing + } + y += row.height + spacing + } + } + + private struct Row { + var indices: [Int] = [] + var width: CGFloat = 0 + var height: CGFloat = 0 + } + + private func arrange(width: CGFloat, subviews: Subviews) -> [Row] { + var rows: [Row] = [] + var row = Row() + for index in subviews.indices { + let size = subviews[index].sizeThatFits(ProposedViewSize(width: width, height: nil)) + let itemWidth = min(size.width, width) + if !row.indices.isEmpty && row.width + spacing + itemWidth > width { + rows.append(row) + row = Row() + } + row.width += (row.indices.isEmpty ? 0 : spacing) + itemWidth + row.height = max(row.height, size.height) + row.indices.append(index) + } + if !row.indices.isEmpty { rows.append(row) } + return rows + } +} diff --git a/Loop/Redesign/TrendsScreen.swift b/Loop/Redesign/TrendsScreen.swift index 9f8c5960a6..d3f1ba3584 100644 --- a/Loop/Redesign/TrendsScreen.swift +++ b/Loop/Redesign/TrendsScreen.swift @@ -35,7 +35,7 @@ final class TrendsModel: ObservableObject { var automaticBoluses = 0 var loopSpans: [LoopModeSpan] = [] var targetRange: ClosedRange? - /// The whole calendar day, for a one-day period: drawn as one timeline. + /// The whole calendar day, for a one-day period: drawn as the same timeline as Now's Today so far. var day: DayHistory? var dayTargets: [TargetBand] = [] /// What the basal schedule delivers over the same days the split averages. @@ -371,7 +371,7 @@ struct TrendsView: View { private var content: some View { if let s = model.summary { summaryCard(s) - // A single day leads with its timeline. + // A single day leads with its timeline, as Today so far does on Now. if let day = s.day { dayCard(day, targets: s.dayTargets, unit: s.unit) } diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index ece45a5df3..a4ec22e962 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -94,6 +94,7 @@ final class StatusTableViewController: LoopChartsTableViewController { tableView.register(BolusProgressTableViewCell.nib(), forCellReuseIdentifier: BolusProgressTableViewCell.className) tableView.register(InsulinSuspendedTableViewCell.nib(), forCellReuseIdentifier: InsulinSuspendedTableViewCell.className) tableView.register(RecentGlucoseTableViewCell.nib(), forCellReuseIdentifier: RecentGlucoseTableViewCell.className) + tableView.register(UITableViewCell.self, forCellReuseIdentifier: Self.nowHostingCellIdentifier) applyDisplayPreferences() @@ -228,6 +229,11 @@ final class StatusTableViewController: LoopChartsTableViewController { addScenarioStepGestureRecognizers() setupPresetsStatusBar() + + if usesRedesign { + tableView.backgroundColor = .systemGroupedBackground + tableView.separatorStyle = .none + } } override func didReceiveMemoryWarning() { @@ -360,6 +366,32 @@ final class StatusTableViewController: LoopChartsTableViewController { private var bolusProgressReporter: DoseProgressReporter? + // MARK: - Redesign + + /// Read once, so a table never mixes the two layouts; LoopAppManager rebuilds the screen when the preference changes. + let usesRedesign = LoopInterfacePreference.useRedesign + + private static let nowHostingCellIdentifier = "NowHostingCell" + + private lazy var nowModel: NowModel = { + let model = NowModel(deviceManager: deviceManager, loopDataManager: loopManager, settingsManager: settingsManager, temporaryPresetsManager: temporaryPresetsManager) + model.isOnboardingComplete = { [weak self] in self?.onboardingManager.isComplete ?? false } + return model + }() + + private lazy var nowActions = NowActions( + addCarbs: { [weak self] in self?.userTappedAddCarbs() }, + bolus: { [weak self] in self?.presentBolusScreen() }, + presets: { [weak self] in self?.presentPresets() }, + showPrediction: { [weak self] in self?.showPredictionFromNow() }, + showToday: { [weak self] in self?.showDay(Date()) }, + showInsulin: { [weak self] in self?.showInsulinDelivery(sender: self) }, + showCarbs: { [weak self] in self?.performSegue(withIdentifier: CarbAbsorptionViewController.className, sender: self) }, + pumpTapped: { [weak self] in self?.executeHUDTapAction(self?.deviceManager.didTapOnPumpStatus(), from: self?.view) }, + cgmTapped: { [weak self] in self?.executeHUDTapAction(self?.deviceManager.didTapOnCGMStatus(), from: self?.view) }, + loopStatusTapped: { [weak self] in self?.showLoopStatusFromNow() } + ) + private func updateBolusProgress() { if let cell = tableView.cellForRow(at: IndexPath(row: StatusRow.status.rawValue, section: Section.status.rawValue)) as? BolusProgressTableViewCell { if case let .bolusing(_, total) = cell.configuration { @@ -402,7 +434,7 @@ final class StatusTableViewController: LoopChartsTableViewController { } private var shouldShowHUD: Bool { - return !landscapeMode + return !landscapeMode && !usesRedesign } // With a vertical status bar there is no top safe area inset, so keep the first row clear of the display corners. @@ -412,7 +444,7 @@ final class StatusTableViewController: LoopChartsTableViewController { } private var shouldShowStatus: Bool { - return !landscapeMode && statusRowMode.hasRow + return (!landscapeMode || usesRedesign) && statusRowMode.hasRow } override func glucoseUnitDidChange() { @@ -699,10 +731,14 @@ final class StatusTableViewController: LoopChartsTableViewController { let statusRowMode = self.determineStatusRowMode() updateBannerAndHUDandStatusRows(statusRowMode: statusRowMode, newSize: currentContext.newSize, animated: animated) - tableView.contentInset = UIEdgeInsets(top: shouldShowBannerWarning ? 0 : cornerClearanceTopInset, left: 0, bottom: ActionTabBarMetrics.tableContentInset, right: 0) + tableView.contentInset = UIEdgeInsets(top: shouldShowBannerWarning ? 0 : cornerClearanceTopInset, left: 0, bottom: usesRedesign ? 0 : ActionTabBarMetrics.tableContentInset, right: 0) redrawCharts() + if usesRedesign { + nowModel.refresh() + } + reloading = false let reloadNow = !self.refreshContext.isEmpty @@ -717,8 +753,10 @@ final class StatusTableViewController: LoopChartsTableViewController { case presets case alertWarning case hud + case nowHeader case status case charts + case nowCharts } // MARK: - Chart Section Data @@ -979,9 +1017,11 @@ final class StatusTableViewController: LoopChartsTableViewController { case .hud: return shouldShowHUD ? 1 : 0 case .charts: - return ChartRow.allCases.count + return usesRedesign ? 0 : ChartRow.allCases.count case .status: return shouldShowStatus ? StatusRow.allCases.count : 0 + case .nowHeader, .nowCharts: + return usesRedesign ? 1 : 0 } } @@ -1067,6 +1107,10 @@ final class StatusTableViewController: LoopChartsTableViewController { cell.hudView.loopCompletionHUD.loopStatusColors = .loopStatus return cell + case .nowHeader: + return nowHostingCell(for: indexPath) { NowHeaderView(model: self.nowModel, actions: self.nowActions) } + case .nowCharts: + return nowHostingCell(for: indexPath) { NowChartsView(model: self.nowModel, actions: self.nowActions) } case .charts: let cell = tableView.dequeueReusableCell(withIdentifier: ChartTableViewCell.className, for: indexPath) as! ChartTableViewCell @@ -1273,7 +1317,7 @@ final class StatusTableViewController: LoopChartsTableViewController { cell.setSubtitleLabel(label: nil) } } - case .presets, .hud, .status, .alertWarning: + case .presets, .hud, .status, .alertWarning, .nowHeader, .nowCharts: break } } @@ -1298,7 +1342,7 @@ final class StatusTableViewController: LoopChartsTableViewController { } case .alertWarning: return UITableView.automaticDimension - case .presets, .hud, .status: + case .presets, .hud, .status, .nowHeader, .nowCharts: return UITableView.automaticDimension } } @@ -1315,7 +1359,7 @@ final class StatusTableViewController: LoopChartsTableViewController { tableView.deselectRow(at: indexPath, animated: true) presentUnmuteAlertConfirmation() } - case .hud: + case .hud, .nowHeader, .nowCharts: break case .status: switch StatusRow(rawValue: indexPath.row)! { @@ -1389,70 +1433,7 @@ final class StatusTableViewController: LoopChartsTableViewController { performSegue(withIdentifier: PredictionTableViewController.className, sender: indexPath) } case .iob: - let showLegacy = false - - if !showLegacy, let pumpManager = deviceManager.pumpManager { - let hostingController = UIHostingController( - rootView: InsulinDeliveryLog( - viewModel: InsulinDeliveryLogViewModel( - loopDataManager: loopManager, - pumpManager: pumpManager - ), - onTapGesture: { [weak navigationController] doseEntry in - Task { - var dosingDecision: StoredDosingDecision? - if let decisionId = doseEntry.decisionId { - do { - dosingDecision = try await self.loopManager.dosingDecisionStore.findDosingDecisionsById(decisionId) - } catch { - self.log.error("Error finding dosing decision: %{public}@", String(describing: error)) - } - } - - let viewController = CommandResponseViewController(command: { (completionHandler) -> String in - var description = [String]() - - let timeFormatter: DateFormatter = { - let formatter = DateFormatter() - - formatter.dateStyle = .none - formatter.timeStyle = .short - - return formatter - }() - - description.append(timeFormatter.string(from: doseEntry.startDate)) - - description.append(String(describing: doseEntry)) - - if let dosingDecision { - description.append(String(describing: dosingDecision)) - } - - return description.joined(separator: "\n\n") - }) - - navigationController?.pushViewController(viewController, animated: true) - } - }, - onEnterManualDose: { [weak self] in - self?.presentManualDoseEntry() - } - ) - .navigationTitle(Text("Insulin")) - .environment(\.colorPalette, .default) - .environment(\.loopStatusColorPalette, .loopStatus) - ) - - hostingController.hidesBottomBarWhenPushed = true - - navigationController?.pushViewController( - hostingController, - animated: true - ) - } else { - performSegue(withIdentifier: InsulinDeliveryTableViewController.className, sender: indexPath) - } + showInsulinDelivery(sender: indexPath) case .cob: performSegue(withIdentifier: CarbAbsorptionViewController.className, sender: indexPath) } @@ -1807,6 +1788,74 @@ final class StatusTableViewController: LoopChartsTableViewController { } } + /// The insulin screen behind the IOB chart and the Now screen's IOB tile. + private func showInsulinDelivery(sender: Any?) { + let showLegacy = false + + if !showLegacy, let pumpManager = deviceManager.pumpManager { + let hostingController = UIHostingController( + rootView: InsulinDeliveryLog( + viewModel: InsulinDeliveryLogViewModel( + loopDataManager: loopManager, + pumpManager: pumpManager + ), + onTapGesture: { [weak navigationController] doseEntry in + Task { + var dosingDecision: StoredDosingDecision? + if let decisionId = doseEntry.decisionId { + do { + dosingDecision = try await self.loopManager.dosingDecisionStore.findDosingDecisionsById(decisionId) + } catch { + self.log.error("Error finding dosing decision: %{public}@", String(describing: error)) + } + } + + let viewController = CommandResponseViewController(command: { (completionHandler) -> String in + var description = [String]() + + let timeFormatter: DateFormatter = { + let formatter = DateFormatter() + + formatter.dateStyle = .none + formatter.timeStyle = .short + + return formatter + }() + + description.append(timeFormatter.string(from: doseEntry.startDate)) + + description.append(String(describing: doseEntry)) + + if let dosingDecision { + description.append(String(describing: dosingDecision)) + } + + return description.joined(separator: "\n\n") + }) + + navigationController?.pushViewController(viewController, animated: true) + } + }, + onEnterManualDose: { [weak self] in + self?.presentManualDoseEntry() + } + ) + .navigationTitle(Text("Insulin")) + .environment(\.colorPalette, .default) + .environment(\.loopStatusColorPalette, .loopStatus) + ) + + hostingController.hidesBottomBarWhenPushed = true + + navigationController?.pushViewController( + hostingController, + animated: true + ) + } else { + performSegue(withIdentifier: InsulinDeliveryTableViewController.className, sender: sender) + } + } + private func addPumpManagerViewToHUD(_ view: BaseHUDView) { if let hudView = hudView { view.stateColors = .pumpStatus @@ -1843,7 +1892,7 @@ final class StatusTableViewController: LoopChartsTableViewController { let error: Error? // First, check whether we have a device error after the most recent completion date if let deviceError = deviceManager.lastError, - deviceError.date > (hudView?.loopCompletionHUD.lastLoopCompleted ?? .distantPast) + deviceError.date > (loopManager.lastLoopCompleted ?? .distantPast) { error = deviceError.error } else if let lastLoopError = lastLoopError { @@ -2382,3 +2431,47 @@ private struct WarningBannerStyle: ViewModifier { } } } + +// MARK: - Redesigned Now screen + +extension StatusTableViewController { + private func nowHostingCell(for indexPath: IndexPath, @ViewBuilder content: () -> Content) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: Self.nowHostingCellIdentifier, for: indexPath) + cell.selectionStyle = .none + cell.backgroundColor = .clear + cell.contentConfiguration = UIHostingConfiguration(content: content).margins(.all, 0) + return cell + } + + func presentSettingsFromTab() { + if presentedViewController != nil { + return + } + presentSettings() + } + + private func showPredictionFromNow() { + guard settingsManager.dosingEnabled || !FeatureFlags.simpleBolusCalculatorEnabled else { return } + performSegue(withIdentifier: PredictionTableViewController.className, sender: self) + } + + func showDay(_ day: Date) { + let detail = UIHostingController(rootView: DayDetailView(provider: nowModel.provider, day: day)) + // As for the Insulin, Carbohydrates and Predicted Glucose screens pushed from Now. + detail.hidesBottomBarWhenPushed = true + navigationController?.pushViewController(detail, animated: true) + } + + private func showLoopStatusFromNow() { + let lastCompleted = loopManager.lastLoopCompleted + if let deviceError = deviceManager.lastError, deviceError.date > (lastCompleted ?? .distantPast) { + showLastError(self) + } else if lastLoopError != nil { + showLastError(self) + } else { + // The classic HUD keeps this current; the new interface has no HUD, so set it before presenting. + loopCompletionModalViewModel.ago = lastCompleted.map { Date().timeIntervalSince($0) } + showLoopCompletionMessage(self) + } + } +} diff --git a/Loop/Views/SettingsView.swift b/Loop/Views/SettingsView.swift index 71d2b71345..8c20d06d0e 100644 --- a/Loop/Views/SettingsView.swift +++ b/Loop/Views/SettingsView.swift @@ -87,6 +87,7 @@ struct SettingsView: View { } alertManagementSection statisticsSection + interfaceSection if viewModel.pumpManagerSettingsViewModel.isSetUp() { therapySection } @@ -352,24 +353,27 @@ extension SettingsView { private var statisticsSection: some View { if let glucoseStore = viewModel.deviceManager?.glucoseStore { Section { - NavigationLink(destination: StatisticsView( - glucoseStore: glucoseStore, - insulinDeliveryHistory: viewModel.insulinDeliveryHistory, - scheduledBasalTotal: viewModel.therapySettings().basalRateSchedule?.total() - )) { - LargeButton( - action: {}, - includeArrow: false, - imageView: Image(systemName: "chart.xyaxis.line") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 30), - label: NSLocalizedString("Statistics", comment: "Statistics settings button text"), - descriptiveText: NSLocalizedString("Glucose overview, ambulatory glucose profile and daily insulin", comment: "Statistics settings descriptive text") - ) - .accessibilityIdentifier("settingsViewStatistics") + // The new interface's Trends and Logbook tabs cover these. + if !LoopInterfacePreference.useRedesign { + NavigationLink(destination: StatisticsView( + glucoseStore: glucoseStore, + insulinDeliveryHistory: viewModel.insulinDeliveryHistory, + scheduledBasalTotal: viewModel.therapySettings().basalRateSchedule?.total() + )) { + LargeButton( + action: {}, + includeArrow: false, + imageView: Image(systemName: "chart.xyaxis.line") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 30), + label: NSLocalizedString("Statistics", comment: "Statistics settings button text"), + descriptiveText: NSLocalizedString("Glucose overview, ambulatory glucose profile and daily insulin", comment: "Statistics settings descriptive text") + ) + .accessibilityIdentifier("settingsViewStatistics") + } } - if viewModel.historyProvider != nil { + if viewModel.historyProvider != nil && !LoopInterfacePreference.useRedesign { LargeButton( action: { sheet = .trends }, imageView: Image(systemName: "chart.bar.doc.horizontal") @@ -486,6 +490,15 @@ extension SettingsView { .environment(\.insulinTintColor, self.insulinTintColor) } + private var interfaceSection: some View { + Section(footer: Text(NSLocalizedString("The new interface adds Trends and Logbook tabs. Turn it off to go back to the classic status screen; Settings closes to switch.", comment: "Footer for the interface choice in settings"))) { + Toggle(NSLocalizedString("New Interface", comment: "Title of the toggle for the redesigned interface"), isOn: Binding( + get: { LoopInterfacePreference.useRedesign }, + set: { LoopInterfacePreference.useRedesign = $0 } + )) + } + } + private var therapySection: some View { Section { NavigationLink(destination: therapySettingsView) { diff --git a/Loop/Views/StatusTableView.swift b/Loop/Views/StatusTableView.swift index a09e8b9690..f850532f37 100644 --- a/Loop/Views/StatusTableView.swift +++ b/Loop/Views/StatusTableView.swift @@ -97,7 +97,10 @@ struct StatusTableView: View { var wrappedView: some View { wrapped } @Bindable var viewModel: StatusTableViewModel - + + /// The redesign puts these actions on the Now screen and its own tab bar. + private var actionTabsShown = true + init(viewModel: StatusTableViewModel) { self.viewModel = viewModel @@ -123,20 +126,39 @@ struct StatusTableView: View { ) } + func showsActionTabs(_ shown: Bool) -> StatusTableView { + var view = self + view.actionTabsShown = shown + return view + } + + private var statusContent: some View { + wrappedView + .ignoresSafeArea(edges: [.horizontal, .bottom]) + .onChange(of: viewModel.temporaryPresetsManager.activeOverride) { _, _ in + Task { + await viewController.reloadData(animated: true) + } + } + .sheet(item: $viewModel.pendingPreset) { preset in + // This is the active preset; edit disabled + PresetDetentView(preset: preset, roundBasalRate: viewModel.loopDataManager.deliveryDelegate?.roundBasalRate, didTapEdit: { }) + .accessibilityIdentifier("bar_Presets") + } + } + + @ViewBuilder var body: some View { + if actionTabsShown { + actionTabView + } else { + statusContent + } + } + + private var actionTabView: some View { ActionTabView { - wrappedView - .ignoresSafeArea(edges: [.horizontal, .bottom]) - .onChange(of: viewModel.temporaryPresetsManager.activeOverride) { _, _ in - Task { - await viewController.reloadData(animated: true) - } - } - .sheet(item: $viewModel.pendingPreset) { preset in - // This is the active preset; edit disabled - PresetDetentView(preset: preset, roundBasalRate: viewModel.loopDataManager.deliveryDelegate?.roundBasalRate, didTapEdit: { }) - .accessibilityIdentifier("bar_Presets") - } + statusContent } tabs: { ActionTab( title: "Add Carbs", From f8b498e609761254eee48f0ddbeafe96ecb5b395 Mon Sep 17 00:00:00 2001 From: Garry Casey Date: Sun, 1 Nov 2026 14:12:52 +0000 Subject: [PATCH 5/5] Rebuild Add carbs around a live preview of the meal The new interface's Add carbs leads with the amount in large type over a chart of how the meal will absorb, which redraws as the amount, speed and eaten time change. Favourite foods sit under it as chips, then the speed (Fast, Medium, Slow or Other with its own emoji and time) and when it was eaten (Now, 15 or 30 minutes or an hour ago, or an exact time). Warnings and Continue to the bolus screen are as before. Editing a carb entry from the Logbook uses the same screen. Choosing a speed after a favourite lets the favourite go but keeps its amount. The view model applied a favourite selection on the next run loop turn, which could reset an amount or speed set straight after it; it now applies it at once, with a regression test. The amount field follows the model whenever it doesn't already show that amount, so a favourite fills it in even while the field still has focus. An entry older than Loop's usual 12-hour window keeps its own time in the eaten picker rather than showing the clamped limit. Letting a favourite go resets the speed to Medium along with the absorption time, so a speed chosen before the favourite can't stay highlighted over a medium meal. The classic interface keeps the classic carb entry screen. This is part 4 of 6 splitting #2621, and builds on parts 1 to 3. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01QHnVP7ynB62LNuuYMy3yvH --- Loop.xcodeproj/project.pbxproj | 4 + Loop/Localizable.xcstrings | 1367 +++++++++++++++-- Loop/Redesign/CarbEntryScreen.swift | 791 ++++++++++ Loop/Redesign/LogbookScreen.swift | 2 +- .../StatusTableViewController.swift | 11 +- Loop/View Models/CarbEntryViewModel.swift | 3 +- LoopTests/Managers/LoopDataManagerTests.swift | 28 + 7 files changed, 2091 insertions(+), 115 deletions(-) create mode 100644 Loop/Redesign/CarbEntryScreen.swift diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index 3000aa0db2..74f9aa3ca5 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -733,6 +733,7 @@ A743DC26D066EE5AC88C2F45 /* LoopHistoryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */; }; 6A6C1E1C7A26EC88AFB4FAB1 /* DayDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */; }; 23B98F0A53A093412229364D /* LogbookScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEDC42672A7A8C57546EA0C /* LogbookScreen.swift */; }; + C4B5E0A1D2F3A4B5C6D7E8F9 /* CarbEntryScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5E0A1D2F3A4B5C6D7E8FA /* CarbEntryScreen.swift */; }; 3ED693ACBF8F9E416055BD56 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BECBB6357021979B9443F /* MainTabBarController.swift */; }; 7705483CFFCF0B89F8F1A08E /* NowScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0874B7B679DB9CE661CCE152 /* NowScreen.swift */; }; 591F13AF435B37B4818E2F2B /* TrendsComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */; }; @@ -1615,6 +1616,7 @@ 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopHistoryProvider.swift; sourceTree = ""; }; E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayDetailView.swift; sourceTree = ""; }; 7AEDC42672A7A8C57546EA0C /* LogbookScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogbookScreen.swift; sourceTree = ""; }; + C4B5E0A1D2F3A4B5C6D7E8FA /* CarbEntryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbEntryScreen.swift; sourceTree = ""; }; 844BECBB6357021979B9443F /* MainTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = ""; }; 0874B7B679DB9CE661CCE152 /* NowScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowScreen.swift; sourceTree = ""; }; 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendsComponents.swift; sourceTree = ""; }; @@ -3025,6 +3027,7 @@ 4BFDE56977CC73CBDA1170C5 /* TrendsComponents.swift */, 0874B7B679DB9CE661CCE152 /* NowScreen.swift */, 844BECBB6357021979B9443F /* MainTabBarController.swift */, + C4B5E0A1D2F3A4B5C6D7E8FA /* CarbEntryScreen.swift */, 7AEDC42672A7A8C57546EA0C /* LogbookScreen.swift */, E3A83D311A9ECAE563F74A51 /* DayDetailView.swift */, 09448CF97996C40BEDDE6588 /* LoopHistoryProvider.swift */, @@ -3699,6 +3702,7 @@ 7705483CFFCF0B89F8F1A08E /* NowScreen.swift in Sources */, 3ED693ACBF8F9E416055BD56 /* MainTabBarController.swift in Sources */, 23B98F0A53A093412229364D /* LogbookScreen.swift in Sources */, + C4B5E0A1D2F3A4B5C6D7E8F9 /* CarbEntryScreen.swift in Sources */, 6A6C1E1C7A26EC88AFB4FAB1 /* DayDetailView.swift in Sources */, A743DC26D066EE5AC88C2F45 /* LoopHistoryProvider.swift in Sources */, 2ADBB92A8ABB74360D18EEFA /* LoopHistoryModels.swift in Sources */, diff --git a/Loop/Localizable.xcstrings b/Loop/Localizable.xcstrings index d4dd4b8eea..6808025436 100644 --- a/Loop/Localizable.xcstrings +++ b/Loop/Localizable.xcstrings @@ -12,6 +12,149 @@ }, " -" : { + }, + "%1$@ ago" : { + "comment" : "Carb entry time choice: eaten this long before now. (1: duration, e.g. 15m or 1h)", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "قبل %1$@" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "před %1$@" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ siden" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "vor %1$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "hace %1$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ sitten" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "il y a %1$@" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לפני %1$@" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ ezelőtt" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ fa" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@前" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ siden" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ geleden" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ temu" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "há %1$@" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "acum %1$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ назад" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "pred %1$@" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "för %1$@ sedan" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ önce" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ тому" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ trước" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@前" + } + } + } }, "%1$@ d" : { "comment" : "Trends period picker, short label for a period of several days, e.g. 7 d. (1: number of days)", @@ -5148,6 +5291,9 @@ } } }, + "Absorbed by %1$@" : { + "comment" : "Under the end of the carb absorption curve: when the carbs will have absorbed (1: time)" + }, "absorbs over %1$@ h" : { "comment" : "Logbook carb row detail: carb absorption time in hours. (1: hours, e.g. 3 or 2.5)", "localizations" : { @@ -12012,591 +12158,734 @@ } } }, - "Daily pattern" : { - "comment" : "Trends card title: the ambulatory glucose profile, glucose by time of day across the period", + "Custom" : { + "comment" : "Carb entry absorption choice Other, before it is chosen: you set the food and absorption time yourself", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "النمط اليومي" + "value" : "مخصص" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Denní profil" + "value" : "Vlastní" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster" + "value" : "Brugerdefineret" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tagesprofil" + "value" : "Eigene" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Patrón diario" + "value" : "Personalizado" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Vuorokausiprofiili" + "value" : "Oma" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Profil journalier" + "value" : "Personnalisé" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "דפוס יומי" + "value" : "מותאם אישית" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Napi mintázat" + "value" : "Egyéni" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Andamento giornaliero" + "value" : "Personalizzato" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "日内パターン" + "value" : "カスタム" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster" + "value" : "Egendefinert" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dagpatroon" + "value" : "Aangepast" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Profil dobowy" + "value" : "Własne" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Padrão diário" + "value" : "Personalizado" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Tipar zilnic" + "value" : "Personalizat" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Суточный профиль" + "value" : "Своё" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Denný profil" + "value" : "Vlastné" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Dygnsmönster" + "value" : "Anpassad" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Günlük örüntü" + "value" : "Özel" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Добовий профіль" + "value" : "Власне" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mẫu hằng ngày" + "value" : "Tùy chỉnh" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日模式" + "value" : "自定义" } } } }, - "Daily pattern over %1$@. Median highest %2$@ at %3$@, lowest %4$@ at %5$@" : { - "comment" : "VoiceOver label for the Trends Daily pattern chart. (1: days with readings, e.g. 14 days; 2: highest median glucose; 3: time of day; 4: lowest median glucose; 5: time of day)", + "Daily pattern" : { + "comment" : "Trends card title: the ambulatory glucose profile, glucose by time of day across the period", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "النمط اليومي خلال %1$@. أعلى وسيط %2$@ عند %3$@، وأدنى وسيط %4$@ عند %5$@" + "value" : "النمط اليومي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Denní profil za %1$@. Nejvyšší medián %2$@ v %3$@, nejnižší %4$@ v %5$@" + "value" : "Denní profil" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster over %1$@. Median højest %2$@ kl. %3$@, lavest %4$@ kl. %5$@" + "value" : "Døgnmønster" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tagesprofil über %1$@. Median am höchsten %2$@ um %3$@, am niedrigsten %4$@ um %5$@" + "value" : "Tagesprofil" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Patrón diario de %1$@. Mediana más alta %2$@ a las %3$@, más baja %4$@ a las %5$@" + "value" : "Patrón diario" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Vuorokausiprofiili %1$@ ajalta. Korkein mediaani %2$@ klo %3$@, matalin %4$@ klo %5$@" + "value" : "Vuorokausiprofiili" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Profil journalier sur %1$@. Médiane la plus haute %2$@ à %3$@, la plus basse %4$@ à %5$@" + "value" : "Profil journalier" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "דפוס יומי במשך %1$@. חציון גבוה ביותר %2$@ ב-%3$@, נמוך ביותר %4$@ ב-%5$@" + "value" : "דפוס יומי" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Napi mintázat, %1$@. Legmagasabb medián %2$@ ekkor: %3$@, legalacsonyabb %4$@ ekkor: %5$@" + "value" : "Napi mintázat" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Andamento giornaliero su %1$@. Mediana più alta %2$@ alle %3$@, più bassa %4$@ alle %5$@" + "value" : "Andamento giornaliero" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@の日内パターン。中央値の最高は%3$@の%2$@、最低は%5$@の%4$@" + "value" : "日内パターン" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster over %1$@. Median høyest %2$@ kl. %3$@, lavest %4$@ kl. %5$@" + "value" : "Døgnmønster" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dagpatroon over %1$@. Mediaan hoogst %2$@ om %3$@, laagst %4$@ om %5$@" + "value" : "Dagpatroon" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Profil dobowy z %1$@. Najwyższa mediana %2$@ o %3$@, najniższa %4$@ o %5$@" + "value" : "Profil dobowy" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Padrão diário em %1$@. Mediana mais alta %2$@ às %3$@, mais baixa %4$@ às %5$@" + "value" : "Padrão diário" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Tipar zilnic pe %1$@. Mediana cea mai mare %2$@ la %3$@, cea mai mică %4$@ la %5$@" + "value" : "Tipar zilnic" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Суточный профиль за %1$@. Наибольшая медиана %2$@ в %3$@, наименьшая %4$@ в %5$@" + "value" : "Суточный профиль" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Denný profil za %1$@. Najvyšší medián %2$@ o %3$@, najnižší %4$@ o %5$@" + "value" : "Denný profil" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Dygnsmönster över %1$@. Högsta median %2$@ kl. %3$@, lägsta %4$@ kl. %5$@" + "value" : "Dygnsmönster" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@ boyunca günlük örüntü. En yüksek medyan %3$@ saatinde %2$@, en düşük %5$@ saatinde %4$@" + "value" : "Günlük örüntü" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Добовий профіль за %1$@. Найвища медіана %2$@ о %3$@, найнижча %4$@ о %5$@" + "value" : "Добовий профіль" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mẫu hằng ngày trong %1$@. Trung vị cao nhất %2$@ lúc %3$@, thấp nhất %4$@ lúc %5$@" + "value" : "Mẫu hằng ngày" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "%1$@的每日模式。中位数最高 %2$@(%3$@),最低 %4$@(%5$@)" + "value" : "每日模式" } } } }, - "Daily pattern: not enough days" : { - "comment" : "VoiceOver label for the Trends Daily pattern chart when fewer than 5 days have readings", + "Daily pattern over %1$@. Median highest %2$@ at %3$@, lowest %4$@ at %5$@" : { + "comment" : "VoiceOver label for the Trends Daily pattern chart. (1: days with readings, e.g. 14 days; 2: highest median glucose; 3: time of day; 4: lowest median glucose; 5: time of day)", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "النمط اليومي: لا توجد أيام كافية" + "value" : "النمط اليومي خلال %1$@. أعلى وسيط %2$@ عند %3$@، وأدنى وسيط %4$@ عند %5$@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Denní profil: málo dní" + "value" : "Denní profil za %1$@. Nejvyšší medián %2$@ v %3$@, nejnižší %4$@ v %5$@" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster: ikke nok dage" + "value" : "Døgnmønster over %1$@. Median højest %2$@ kl. %3$@, lavest %4$@ kl. %5$@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tagesprofil: nicht genug Tage" + "value" : "Tagesprofil über %1$@. Median am höchsten %2$@ um %3$@, am niedrigsten %4$@ um %5$@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Patrón diario: no hay suficientes días" + "value" : "Patrón diario de %1$@. Mediana más alta %2$@ a las %3$@, más baja %4$@ a las %5$@" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Vuorokausiprofiili: liian vähän päiviä" + "value" : "Vuorokausiprofiili %1$@ ajalta. Korkein mediaani %2$@ klo %3$@, matalin %4$@ klo %5$@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Profil journalier : pas assez de jours" + "value" : "Profil journalier sur %1$@. Médiane la plus haute %2$@ à %3$@, la plus basse %4$@ à %5$@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "דפוס יומי: אין מספיק ימים" + "value" : "דפוס יומי במשך %1$@. חציון גבוה ביותר %2$@ ב-%3$@, נמוך ביותר %4$@ ב-%5$@" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Napi mintázat: nincs elég nap" + "value" : "Napi mintázat, %1$@. Legmagasabb medián %2$@ ekkor: %3$@, legalacsonyabb %4$@ ekkor: %5$@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Andamento giornaliero: giorni insufficienti" + "value" : "Andamento giornaliero su %1$@. Mediana più alta %2$@ alle %3$@, più bassa %4$@ alle %5$@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "日内パターン:日数が不足しています" + "value" : "%1$@の日内パターン。中央値の最高は%3$@の%2$@、最低は%5$@の%4$@" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Døgnmønster: ikke nok dager" + "value" : "Døgnmønster over %1$@. Median høyest %2$@ kl. %3$@, lavest %4$@ kl. %5$@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dagpatroon: niet genoeg dagen" + "value" : "Dagpatroon over %1$@. Mediaan hoogst %2$@ om %3$@, laagst %4$@ om %5$@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Profil dobowy: za mało dni" + "value" : "Profil dobowy z %1$@. Najwyższa mediana %2$@ o %3$@, najniższa %4$@ o %5$@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Padrão diário: dias insuficientes" + "value" : "Padrão diário em %1$@. Mediana mais alta %2$@ às %3$@, mais baixa %4$@ às %5$@" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Tipar zilnic: prea puține zile" + "value" : "Tipar zilnic pe %1$@. Mediana cea mai mare %2$@ la %3$@, cea mai mică %4$@ la %5$@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Суточный профиль: недостаточно дней" + "value" : "Суточный профиль за %1$@. Наибольшая медиана %2$@ в %3$@, наименьшая %4$@ в %5$@" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Denný profil: málo dní" + "value" : "Denný profil za %1$@. Najvyšší medián %2$@ o %3$@, najnižší %4$@ o %5$@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Dygnsmönster: för få dagar" + "value" : "Dygnsmönster över %1$@. Högsta median %2$@ kl. %3$@, lägsta %4$@ kl. %5$@" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Günlük örüntü: yeterli gün yok" + "value" : "%1$@ boyunca günlük örüntü. En yüksek medyan %3$@ saatinde %2$@, en düşük %5$@ saatinde %4$@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Добовий профіль: замало днів" + "value" : "Добовий профіль за %1$@. Найвища медіана %2$@ о %3$@, найнижча %4$@ о %5$@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mẫu hằng ngày: không đủ số ngày" + "value" : "Mẫu hằng ngày trong %1$@. Trung vị cao nhất %2$@ lúc %3$@, thấp nhất %4$@ lúc %5$@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日模式:天数不足" + "value" : "%1$@的每日模式。中位数最高 %2$@(%3$@),最低 %4$@(%5$@)" } } } }, - "Day" : { - "comment" : "Appearance option and app icon name: the light (daytime) version", + "Daily pattern: not enough days" : { + "comment" : "VoiceOver label for the Trends Daily pattern chart when fewer than 5 days have readings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "نهار" + "value" : "النمط اليومي: لا توجد أيام كافية" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Den" + "value" : "Denní profil: málo dní" } }, "da" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Døgnmønster: ikke nok dage" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tag" + "value" : "Tagesprofil: nicht genug Tage" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Día" + "value" : "Patrón diario: no hay suficientes días" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Päivä" + "value" : "Vuorokausiprofiili: liian vähän päiviä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Jour" + "value" : "Profil journalier : pas assez de jours" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "יום" + "value" : "דפוס יומי: אין מספיק ימים" } }, "hu" : { "stringUnit" : { "state" : "translated", - "value" : "Nap" + "value" : "Napi mintázat: nincs elég nap" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Giorno" + "value" : "Andamento giornaliero: giorni insufficienti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "日" + "value" : "日内パターン:日数が不足しています" } }, "nb" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Døgnmønster: ikke nok dager" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Dagpatroon: niet genoeg dagen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dzień" + "value" : "Profil dobowy: za mało dni" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Dia" + "value" : "Padrão diário: dias insuficientes" } }, "ro" : { "stringUnit" : { "state" : "translated", - "value" : "Zi" + "value" : "Tipar zilnic: prea puține zile" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "День" + "value" : "Суточный профиль: недостаточно дней" } }, "sk" : { "stringUnit" : { "state" : "translated", - "value" : "Deň" + "value" : "Denný profil: málo dní" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Dag" + "value" : "Dygnsmönster: för få dagar" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Gündüz" + "value" : "Günlük örüntü: yeterli gün yok" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "День" + "value" : "Добовий профіль: замало днів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Ngày" + "value" : "Mẫu hằng ngày: không đủ số ngày" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "日" + "value" : "每日模式:天数不足" } } } }, - "Day (Trends period)" : { - "comment" : "Trends period picker: a period of one calendar day (not daytime)", + "Day" : { + "comment" : "Appearance option and app icon name: the light (daytime) version", "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Day" - } - }, "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يوم" + "value" : "نهار" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Den" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tag" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Día" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Päivä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "יום" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nap" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "日" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dzień" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dia" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zi" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "День" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deň" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dag" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gündüz" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "День" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "日" + } + } + } + }, + "Day (Trends period)" : { + "comment" : "Trends period picker: a period of one calendar day (not daytime)", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Day" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يوم" } }, "cs" : { @@ -12733,6 +13022,152 @@ } } }, + "Eaten" : { + "comment" : "Carb entry section title: when the food was eaten", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "وقت الأكل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Snězeno" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spist" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gegessen" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comido" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Syöty" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mangé" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "נאכל" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elfogyasztva" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mangiato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "食べた時刻" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Spist" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gegeten" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zjedzono" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consumido" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consumat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Съедено" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zjedené" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ätit" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yenme zamanı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "З'їдено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã ăn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "进食时间" + } + } + } + }, + "Eaten at %1$@, absorbed by %2$@" : { + "comment" : "Accessibility summary of the carb entry preview chart (1: time eaten, 2: time absorbed)" + }, "Edit" : { "comment" : "Swipe action to edit a Logbook carb entry", "localizations" : { @@ -13305,6 +13740,149 @@ } } }, + "Fast" : { + "comment" : "Carb entry absorption choice: food that absorbs quickly, such as sweets", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "سريع" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rychle" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hurtig" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schnell" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rápido" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nopea" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rapide" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מהיר" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gyors" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Veloce" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "速い" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rask" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Snel" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Szybko" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rápido" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rapid" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Быстро" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rýchlo" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Snabb" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hızlı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Швидко" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nhanh" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "快" + } + } + } + }, "From Apple Health: Loop keeps 7 days, so this day has no temp basal or schedule detail." : { "comment" : "Day page note when the day is older than Loop's 7-day history and was read from Apple Health", "localizations" : { @@ -14449,6 +15027,149 @@ } } }, + "How fast it absorbs" : { + "comment" : "Carb entry section title: choose how quickly the food's carbs absorb", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "سرعة الامتصاص" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jak rychle se vstřebá" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hvor hurtigt det optages" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wie schnell es aufgenommen wird" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Qué tan rápido se absorbe" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kuinka nopeasti imeytyy" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vitesse d'absorption" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "באיזו מהירות נספג" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Milyen gyorsan szívódik fel" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velocità di assorbimento" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "吸収の速さ" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hvor raskt det tas opp" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoe snel het wordt opgenomen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jak szybko się wchłania" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velocidade de absorção" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cât de repede se absoarbe" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скорость усвоения" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ako rýchlo sa vstrebe" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hur snabbt det tas upp" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ne kadar hızlı emilir" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Швидкість засвоєння" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tốc độ hấp thu" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "吸收速度" + } + } + } + }, "In range" : { "comment" : "Time in range band: glucose 70–180 mg/dL (3.9–10.0 mmol/L) Time-in-range band name: glucose 70–180 mg/dL (3.9–10.0 mmol/L)", "localizations" : { @@ -19838,6 +20559,149 @@ } } }, + "Medium" : { + "comment" : "Carb entry absorption choice: a typical mixed meal", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "متوسط" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Středně" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Middel" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mittel" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medio" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keskinopea" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Moyen" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בינוני" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Közepes" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medio" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "普通" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Middels" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gemiddeld" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Średnio" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Médio" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediu" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Средне" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stredne" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medel" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Orta" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Середньо" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trung bình" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "中" + } + } + } + }, "Missing data error" : { "comment" : "Reason Loop could not run: data it needs was missing", "localizations" : { @@ -28132,6 +28996,149 @@ } } }, + "Slow" : { + "comment" : "Carb entry absorption choice: food that absorbs slowly, such as pizza", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "بطيء" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pomalu" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Langsom" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Langsam" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lento" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hidas" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lent" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "איטי" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lassú" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lento" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "遅い" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Langsom" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Langzaam" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wolno" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lento" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lent" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Медленно" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pomaly" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Långsam" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yavaş" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Повільно" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chậm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "慢" + } + } + } + }, "Summary" : { "comment" : "Trends card title: the summary statistics tiles", "localizations" : { @@ -64354,7 +65361,147 @@ "isCommentAutoGenerated" : true }, "Other" : { - "comment" : "The group name for plugins that do not declare a manufacturer or category" + "comment" : "The group name for plugins that do not declare a manufacturer or category", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أخرى" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jiné" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Andet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Andere" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otro" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Autre" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אחר" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Egyéb" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Altro" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "その他" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annet" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anders" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inne" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Outro" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Altul" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Другое" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iné" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annat" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diğer" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Інше" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Khác" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "其他" + } + } + } }, "Override Presets" : { "comment" : "The title text for the override presets", diff --git a/Loop/Redesign/CarbEntryScreen.swift b/Loop/Redesign/CarbEntryScreen.swift new file mode 100644 index 0000000000..742fe73558 --- /dev/null +++ b/Loop/Redesign/CarbEntryScreen.swift @@ -0,0 +1,791 @@ +// +// CarbEntryScreen.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import SwiftUI +import LoopKit +import LoopKitUI +import Charts + +private typealias P = LoopDesign.Palette + +/// Adding or editing carbs in the redesign. It drives the same CarbEntryViewModel as CarbEntryView, so validation, +/// favourite foods and the hand-off to the bolus screen are unchanged; only the layout is new. +/// +/// The top of the screen is a preview of the meal as Loop will model it: the amount, over Loop's absorption curve +/// drawn on a real time axis. The amount sets the curve's height, the speed its length, and when it was eaten where +/// it sits against now, so every choice below shows its effect there. +struct CarbEntryScreen: View { + @EnvironmentObject private var displayGlucosePreference: DisplayGlucosePreference + @Environment(\.dismissAction) private var dismiss + @Environment(\.guidanceColors) private var guidanceColors + @Environment(\.dynamicTypeSize) private var typeSize + @Environment(\.accessibilityReduceMotion) private var reduceMotion + + @ObservedObject var viewModel: CarbEntryViewModel + + @State private var amountText: String + @State private var eatenChoice: Int? + @State private var showHowAbsorptionTimeWorks = false + @State private var showAddFavoriteFood = false + /// Other was tapped, so it stays chosen even while its food and time still match a preset. + @State private var otherChosen = false + @FocusState private var amountFocused: Bool + @FocusState private var foodTypeFocused: Bool + @Namespace private var thumbs + + private let isNewEntry: Bool + + /// Minutes before now offered as one-tap times. + private static let eatenChoices = [0, 15, 30, 60] + + init(viewModel: CarbEntryViewModel) { + self.viewModel = viewModel + isNewEntry = viewModel.originalCarbEntry == nil + _amountText = State(initialValue: viewModel.carbsQuantity.flatMap { $0 > 0 ? Self.amountFormatter.string(from: NSNumber(value: $0)) : nil } ?? "") + _eatenChoice = State(initialValue: viewModel.originalCarbEntry == nil && abs(viewModel.time.timeIntervalSinceNow) < 60 ? 0 : nil) + } + + var body: some View { + if isNewEntry { + NavigationStack { + content + .navigationTitle(NSLocalizedString("Add carbs", comment: "Button on the Now screen that opens carb entry")) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button(NSLocalizedString("Cancel", comment: "Button label for cancel"), action: dismiss) + } + } + } + } else { + content + } + } + + private var content: some View { + ScrollView { + VStack(alignment: .leading, spacing: 22) { + if let override = viewModel.currentOverride { + ActivePresetBanner(override: override) + } + ForEach(Array(viewModel.warnings).sorted { $0.priority < $1.priority }) { warning in + warningView(warning) + } + mealPreview + if isNewEntry, FeatureFlags.allowExperimentalFeatures { + favourites + } + absorption + eaten + } + .padding(.horizontal, LoopDesign.Space.s4) + .padding(.top, LoopDesign.Space.s2) + .padding(.bottom, LoopDesign.Space.s6) + } + // Any scroll puts the keyboard away: at large text sizes it otherwise covers the rest of the form for good. + .scrollDismissesKeyboard(.immediately) + .toolbar { + if amountFocused || foodTypeFocused { + ToolbarItem(placement: .navigationBarTrailing) { + Button("Done") { + amountFocused = false + foodTypeFocused = false + } + } + } + } + .background(P.surface) + .safeAreaInset(edge: .bottom) { continueBar } + .onAppear { + // As FoodTypeRow does: a new entry starts as the medium food, so the entry carries its emoji. + if isNewEntry && !viewModel.usesCustomFoodType && viewModel.selectedDefaultAbsorptionTimeEmoji.isEmpty { + viewModel.selectedDefaultAbsorptionTimeEmoji = Speed.medium.emoji + } + } + .task { + // Focus set while the sheet is still presenting is dropped, so wait for it to settle. + guard viewModel.shouldBeginEditingQuantity else { return } + try? await Task.sleep(for: .milliseconds(450)) + amountFocused = true + } + .onChange(of: viewModel.carbsQuantity) { _, quantity in + // A favourite fills the amount in. Typing already shows its own amount ("3." is 3), so only a change from + // elsewhere rewrites the field. Focus can't decide this: a tap's unfocus hasn't applied when this runs. + guard Self.amountFormatter.number(from: amountText)?.doubleValue != quantity else { return } + amountText = quantity.flatMap { $0 > 0 ? Self.amountFormatter.string(from: NSNumber(value: $0)) : nil } ?? "" + } + .navigationDestination(isPresented: Binding(get: { viewModel.bolusViewModel != nil }, set: { if !$0 { viewModel.bolusViewModel = nil } })) { + if let bolusViewModel = viewModel.bolusViewModel { + BolusEntryView(viewModel: bolusViewModel) + .environmentObject(displayGlucosePreference) + .environment(\.dismissAction, dismiss) + .environment(\.guidanceColors, guidanceColors) + } + } + .alert(item: $viewModel.alert, content: alert(for:)) + .sheet(isPresented: $showHowAbsorptionTimeWorks) { + HowAbsorptionTimeWorksView() + } + .sheet(isPresented: $showAddFavoriteFood) { + FavoriteFoodAddEditView(carbsQuantity: viewModel.carbsQuantity, + foodType: viewModel.usesCustomFoodType ? viewModel.foodType : viewModel.selectedDefaultAbsorptionTimeEmoji, + absorptionTime: viewModel.absorptionTime) { food in + showAddFavoriteFood = false + viewModel.onFavoriteFoodSave(food) + } + } + } + + private func animated(_ change: () -> Void) { + if reduceMotion { + change() + } else { + withAnimation(.snappy(duration: 0.3), change) + } + } + + // MARK: Meal preview + + private static let amountFormatter: NumberFormatter = { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.maximumFractionDigits = 1 + formatter.usesGroupingSeparator = false + return formatter + }() + + private var mealPreview: some View { + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + HStack(alignment: .firstTextBaseline, spacing: LoopDesign.Space.s1) { + TextField(text: $amountText, prompt: Text(verbatim: "0").foregroundStyle(P.inkMuted.opacity(0.45))) { + Text(NSLocalizedString("Amount Consumed", comment: "Label for carb quantity entry row on carb entry screen")) + } + .font(.system(size: 72, weight: .bold, design: .rounded).monospacedDigit()) + .foregroundStyle(P.carbsInk) + .tint(P.carbsInk) + .keyboardType(.decimalPad) + .accessibilityLabel(NSLocalizedString("Amount Consumed", comment: "Label for carb quantity entry row on carb entry screen")) + .accessibilityIdentifier("textField_CarbsAmount") + .fixedSize() + .focused($amountFocused) + .onChange(of: amountText) { _, text in + viewModel.carbsQuantity = Self.amountFormatter.number(from: text)?.doubleValue + } + Text(NSLocalizedString("g", comment: "Abbreviation for grams")) + .font(.system(size: 30, weight: .semibold, design: .rounded)) + .foregroundStyle(P.inkMuted) + .accessibilityHidden(true) + Spacer(minLength: 0) + } + .contentShape(Rectangle()) + .onTapGesture { amountFocused = true } + + TimelineView(.everyMinute) { context in + MealAbsorptionChart(grams: viewModel.carbsQuantity ?? 0, + absorptionTime: viewModel.absorptionTime, + eaten: viewModel.time, + now: context.date, + axisLength: max(viewModel.defaultAbsorptionTimes.slow, viewModel.absorptionTime)) + } + } + .padding(.horizontal, LoopDesign.Space.s4) + .padding(.top, LoopDesign.Space.s1) + .padding(.bottom, LoopDesign.Space.s4) + .background(P.surfaceRaised, in: RoundedRectangle(cornerRadius: 22, style: .continuous)) + } + + // MARK: Favourites + + @ViewBuilder + private var favourites: some View { + if !viewModel.favoriteFoods.isEmpty || !viewModel.saveFavoriteFoodButtonDisabled { + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + sectionTitle(NSLocalizedString("Favorite Foods", comment: "The section title for Carb entry screen where Favorite Foods can be selected")) + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: LoopDesign.Space.s2) { + ForEach(Array(viewModel.favoriteFoods.enumerated()), id: \.element.id) { index, food in + let selected = viewModel.selectedFavoriteFoodIndex == index + Button { + amountFocused = false + // A favourite brings its own food and time, so the speed shown follows it rather than an earlier Other. + otherChosen = false + animated { + viewModel.selectedFavoriteFoodIndex = selected ? -1 : index + // Letting a new meal's favourite go resets it to medium absorption, so the speed shown and + // the food type saved go back to medium too, rather than a speed chosen before the favourite. + if selected && viewModel.originalCarbEntry == nil { + viewModel.selectedDefaultAbsorptionTimeEmoji = Speed.medium.emoji + } + } + } label: { + HStack(spacing: 6) { + Text(food.foodType) + Text(food.name).foregroundStyle(P.ink) + Text(LoopFormat.grams(food.carbsQuantity.doubleValue(for: .gram))) + .monospacedDigit() + .foregroundStyle(selected ? P.carbsInk : P.inkMuted) + } + .font(LoopDesign.Typeface.subhead.weight(.medium)) + .padding(.horizontal, 14) + .frame(minHeight: 40) + .background(selected ? P.carbs.opacity(0.18) : P.surfaceRaised, in: Capsule()) + .overlay(Capsule().strokeBorder(selected ? P.carbs : .clear, lineWidth: 1.5)) + } + .buttonStyle(.plain) + .accessibilityAddTraits(selected ? .isSelected : []) + } + if viewModel.selectedFavoriteFood == nil { + Button { + showAddFavoriteFood = true + } label: { + Label(NSLocalizedString("Save as favorite food", comment: "Button on the carb entry screen to save the entry as a favourite food"), systemImage: "star") + .font(LoopDesign.Typeface.subhead.weight(.medium)) + .padding(.horizontal, 14) + .frame(minHeight: 40) + .overlay(Capsule().strokeBorder(P.separator.opacity(0.7), style: StrokeStyle(lineWidth: 1, dash: [3, 3]))) + } + .buttonStyle(.plain) + .foregroundStyle(P.accentInk) + .disabled(viewModel.saveFavoriteFoodButtonDisabled) + .opacity(viewModel.saveFavoriteFoodButtonDisabled ? 0.4 : 1) + } + } + .padding(.vertical, 1) + } + .scrollClipDisabled() + } + } + } + + // MARK: Eaten + + /// Loop's usual 12 hours back, reaching further for an older entry being edited: the picker shows a clamped time + /// otherwise, one the entry never had. + private var eatenRange: ClosedRange { + let earliest = viewModel.originalCarbEntry.map { min($0.startDate, viewModel.minimumDate) } ?? viewModel.minimumDate + return earliest...viewModel.maximumDate + } + + private func eatenTitle(_ minutes: Int) -> String { + minutes == 0 + ? NSLocalizedString("Now", comment: "Carb entry time choice: eaten now") + : String(format: NSLocalizedString("%1$@ ago", comment: "Carb entry time choice: eaten this long before now. (1: duration, e.g. 15m or 1h)"), LoopFormat.duration(Double(minutes) * 60)) + } + + private var eaten: some View { + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + let header = typeSize.isAccessibilitySize + ? AnyLayout(VStackLayout(alignment: .leading, spacing: LoopDesign.Space.s2)) + : AnyLayout(HStackLayout(alignment: .center)) + header { + sectionTitle(NSLocalizedString("Eaten", comment: "Carb entry section title: when the food was eaten")) + if !typeSize.isAccessibilitySize { Spacer() } + DatePicker(selection: Binding(get: { viewModel.time }, set: { viewModel.time = $0; eatenChoice = nil }), + in: eatenRange, displayedComponents: [.date, .hourAndMinute]) { + Text(NSLocalizedString("Time", comment: "Carb entry row label: the exact time the food was eaten")) + } + .labelsHidden() + .tint(P.carbsInk) + } + ChoiceTrack(choices: Self.eatenChoices, selection: eatenChoice, thumb: "eaten", namespace: thumbs) { minutes in + amountFocused = false + let date = Date().addingTimeInterval(-Double(minutes) * 60) + animated { + eatenChoice = minutes + viewModel.time = min(max(date, viewModel.minimumDate), viewModel.maximumDate) + } + } label: { minutes, selected in + Text(eatenTitle(minutes)) + .font(LoopDesign.Typeface.subhead.weight(selected ? .semibold : .medium).monospacedDigit()) + .foregroundStyle(selected ? P.carbsInk : P.ink) + .lineLimit(1) + .minimumScaleFactor(0.8) + .padding(.vertical, LoopDesign.Space.s2) + } + } + } + + // MARK: Absorption + + enum Speed: CaseIterable { + case fast, medium, slow, other + + /// The emoji FoodTypeRow uses for each default absorption time; the entry stores it as its food type. + var emoji: String { + switch self { + case .fast: return "🍭" + case .medium: return "🌮" + case .slow: return "🍕" + case .other: return "🍽️" + } + } + + var title: String { + switch self { + case .fast: return NSLocalizedString("Fast", comment: "Carb entry absorption choice: food that absorbs quickly, such as sweets") + case .medium: return NSLocalizedString("Medium", comment: "Carb entry absorption choice: a typical mixed meal") + case .slow: return NSLocalizedString("Slow", comment: "Carb entry absorption choice: food that absorbs slowly, such as pizza") + case .other: return NSLocalizedString("Other", comment: "Carb entry absorption choice: set your own food and absorption time") + } + } + } + + private func hours(_ speed: Speed) -> TimeInterval { + switch speed { + case .fast: return viewModel.defaultAbsorptionTimes.fast + case .medium: return viewModel.defaultAbsorptionTimes.medium + case .slow: return viewModel.defaultAbsorptionTimes.slow + case .other: return viewModel.absorptionTime + } + } + + private var selectedSpeed: Speed { + let presets = Speed.allCases.filter { $0 != .other } + if viewModel.usesCustomFoodType { + if otherChosen { return .other } + // A saved entry comes back as a custom food; one that is exactly a preset's emoji and time shows as that preset. + return presets.first { $0.emoji == viewModel.foodType && hours($0) == viewModel.absorptionTime } ?? .other + } + return presets.first { $0.emoji == viewModel.selectedDefaultAbsorptionTimeEmoji } ?? .medium + } + + private var absorption: some View { + VStack(alignment: .leading, spacing: LoopDesign.Space.s2) { + HStack { + sectionTitle(NSLocalizedString("How fast it absorbs", comment: "Carb entry section title: choose how quickly the food's carbs absorb")) + Spacer() + Button { + showHowAbsorptionTimeWorks = true + } label: { + Image(systemName: "info.circle") + .font(LoopDesign.Typeface.subhead.weight(.semibold)) + .frame(minWidth: 44, minHeight: 28, alignment: .trailing) + } + .foregroundStyle(P.accentInk) + .accessibilityLabel(NSLocalizedString("Absorption Time", comment: "Accessibility label for the button that explains carb absorption time")) + } + ChoiceTrack(choices: Speed.allCases, selection: selectedSpeed, thumb: "speed", namespace: thumbs) { speed in + animated { choose(speed) } + } label: { speed, selected in + VStack(spacing: 2) { + Text(speed.title) + .font(LoopDesign.Typeface.subhead.weight(selected ? .semibold : .medium)) + .foregroundStyle(selected ? P.carbsInk : P.ink) + HStack(spacing: 3) { + // The food the entry is saved with; Other shows the one typed in. + Text(speed == .other && selected && !viewModel.foodType.isEmpty ? viewModel.foodType : speed.emoji) + .dynamicTypeSize(...DynamicTypeSize.xxxLarge) + Text(durationLabel(speed, selected: selected)) + .foregroundStyle(selected ? P.carbsInk : P.inkMuted) + } + .font(LoopDesign.Typeface.footnote.monospacedDigit()) + } + .lineLimit(1) + .minimumScaleFactor(0.75) + .padding(.vertical, 10) + .accessibilityElement(children: .ignore) + .accessibilityLabel("\(speed.title), \(durationLabel(speed, selected: selected))") + } + if selectedSpeed == .other { + otherFood + .transition(.opacity.combined(with: .move(edge: .top))) + } + } + } + + private func durationLabel(_ speed: Speed, selected: Bool) -> String { + speed == .other && !selected + ? NSLocalizedString("Custom", comment: "Carb entry absorption choice Other, before it is chosen: you set the food and absorption time yourself") + : LoopFormat.duration(hours(speed)) + } + + private var otherFood: some View { + VStack(spacing: 0) { + HStack { + Text(NSLocalizedString("Food Type", comment: "Label for the food type row on the carb entry screen")) + .font(LoopDesign.Typeface.body) + Spacer() + TextField(Speed.other.emoji, text: $viewModel.foodType) + .multilineTextAlignment(.trailing) + .font(.title3) + .focused($foodTypeFocused) + .frame(maxWidth: 120) + } + .padding(.vertical, 10) + Divider() + Stepper(value: Binding(get: { viewModel.absorptionTime }, set: { newValue in animated { viewModel.absorptionTime = newValue } }), + in: viewModel.absorptionRimesRange, step: 30 * 60) { + HStack { + Text(NSLocalizedString("Absorption Time", comment: "Accessibility label for the button that explains carb absorption time")) + .font(LoopDesign.Typeface.body) + Spacer() + Text(LoopFormat.duration(viewModel.absorptionTime)) + .font(LoopDesign.Typeface.body.monospacedDigit()) + .foregroundStyle(P.carbsInk) + } + } + .padding(.vertical, 10) + } + .padding(.horizontal, LoopDesign.Space.s4) + .background(P.surfaceRaised, in: RoundedRectangle(cornerRadius: 15, style: .continuous)) + } + + private func choose(_ speed: Speed) { + amountFocused = false + // Re-choosing the shown speed changes nothing, so an unedited entry stays unedited. + guard speed != selectedSpeed else { return } + // A favourite sets its own food and absorption; choosing a speed instead lets it go but keeps the amount. + if viewModel.selectedFavoriteFood != nil { + let quantity = viewModel.carbsQuantity + viewModel.selectedFavoriteFoodIndex = -1 + viewModel.carbsQuantity = quantity + } + otherChosen = speed == .other + if speed == .other { + viewModel.usesCustomFoodType = true + foodTypeFocused = true + } else { + viewModel.usesCustomFoodType = false + viewModel.foodType = "" + viewModel.selectedDefaultAbsorptionTimeEmoji = speed.emoji + viewModel.absorptionTime = hours(speed) + } + } + + // MARK: Continue + + private var continueBar: some View { + Button(action: viewModel.continueToBolus) { + Text(NSLocalizedString("Continue", comment: "Button label for continue")) + .font(LoopDesign.Typeface.headline) + .foregroundStyle(viewModel.continueButtonDisabled ? P.inkMuted : P.onFill) + .frame(maxWidth: .infinity, minHeight: 54) + .background(viewModel.continueButtonDisabled ? P.surfaceRaised : P.carbs, + in: RoundedRectangle(cornerRadius: 18, style: .continuous)) + } + .buttonStyle(.plain) + .disabled(viewModel.continueButtonDisabled) + .accessibilityIdentifier("button_Continue") + .padding(.horizontal, LoopDesign.Space.s4) + .padding(.top, LoopDesign.Space.s2) + .padding(.bottom, LoopDesign.Space.s1) + .background(P.surface) + } + + // MARK: Pieces + + private func sectionTitle(_ title: String) -> some View { + Text(title) + .font(LoopDesign.Typeface.subhead.weight(.semibold)) + .foregroundStyle(P.inkMuted) + .accessibilityAddTraits(.isHeader) + } + + private func warningView(_ warning: CarbEntryViewModel.Warning) -> some View { + HStack(alignment: .top, spacing: LoopDesign.Space.s3) { + Image(systemName: "exclamationmark.triangle.fill") + .foregroundStyle(P.criticalInk) + .accessibilityHidden(true) + Text(warningText(warning)) + .font(LoopDesign.Typeface.subhead) + .foregroundStyle(P.ink) + .fixedSize(horizontal: false, vertical: true) + } + .padding(LoopDesign.Space.s3) + .frame(maxWidth: .infinity, alignment: .leading) + .background(P.statusCritical.opacity(0.16), in: RoundedRectangle(cornerRadius: LoopDesign.Radius.lg, style: .continuous)) + .overlay(RoundedRectangle(cornerRadius: LoopDesign.Radius.lg, style: .continuous).strokeBorder(P.statusCritical.opacity(0.45), lineWidth: 1)) + } + + private func warningText(_ warning: CarbEntryViewModel.Warning) -> String { + switch warning { + case .entryIsMissedMeal: + return NSLocalizedString("Loop has detected an missed meal and estimated its size. Edit the carb amount to match the amount of any carbs you may have eaten.", comment: "Warning displayed when user is adding a meal from an missed meal notification") + case .glucoseRisingRapidly: + return NSLocalizedString("Your glucose is rapidly rising. Check that any carbs you've eaten were logged. If you logged carbs, check that the time you entered lines up with when you started eating.", comment: "Warning to ensure the carb entry is accurate") + } + } + + private func alert(for alert: CarbEntryViewModel.Alert) -> SwiftUI.Alert { + switch alert { + case .maxQuantityExceded: + let message = String( + format: NSLocalizedString("The maximum allowed amount is %@ grams.", comment: "Alert body displayed for quantity greater than max (1: maximum quantity in grams)"), + NumberFormatter.localizedString(from: NSNumber(value: viewModel.maxCarbEntryQuantity.doubleValue(for: viewModel.preferredCarbUnit)), number: .none) + ) + let okMessage = NSLocalizedString("com.loudnate.LoopKit.errorAlertActionTitle", value: "OK", comment: "The title of the action used to dismiss an error alert") + return SwiftUI.Alert( + title: Text("Large Meal Entered", comment: "Title of the warning shown when a large meal was entered"), + message: Text(message), + dismissButton: .cancel(Text(okMessage), action: viewModel.clearAlert) + ) + case .warningQuantityValidation: + let message = String( + format: NSLocalizedString("Did you intend to enter %1$@ grams as the amount of carbohydrates for this meal?", comment: "Alert body when entered carbohydrates is greater than threshold (1: entered quantity in grams)"), + NumberFormatter.localizedString(from: NSNumber(value: viewModel.carbsQuantity ?? 0), number: .none) + ) + return SwiftUI.Alert( + title: Text("Large Meal Entered", comment: "Title of the warning shown when a large meal was entered"), + message: Text(message), + primaryButton: .default(Text("No, edit amount", comment: "The title of the action used when rejecting the the amount of carbohydrates entered."), action: viewModel.clearAlert), + secondaryButton: .cancel(Text("Yes", comment: "The title of the action used when confirming entered amount of carbohydrates."), action: viewModel.clearAlertAndContinueToBolus) + ) + } + } +} + +/// A row of choices sharing one track, with a thumb that slides to the chosen one. Two by two at accessibility text sizes. +private struct ChoiceTrack: View { + let choices: [Choice] + let selection: Choice? + let thumb: String + let namespace: Namespace.ID + let action: (Choice) -> Void + @ViewBuilder let label: (Choice, Bool) -> Label + + @Environment(\.dynamicTypeSize) private var typeSize + + var body: some View { + let columns = Array(repeating: GridItem(.flexible(), spacing: 4), count: typeSize.isAccessibilitySize ? 2 : choices.count) + LazyVGrid(columns: columns, spacing: 4) { + ForEach(choices, id: \.self) { choice in + let selected = choice == selection + Button { + action(choice) + } label: { + label(choice, selected) + .padding(.horizontal, 4) + .frame(maxWidth: .infinity, minHeight: 44) + .background { + if selected { + RoundedRectangle(cornerRadius: 11, style: .continuous) + .fill(P.carbs.opacity(0.18)) + .overlay(RoundedRectangle(cornerRadius: 11, style: .continuous).strokeBorder(P.carbs, lineWidth: 1.5)) + .matchedGeometryEffect(id: thumb, in: namespace) + } + } + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .accessibilityAddTraits(selected ? .isSelected : []) + } + } + .padding(4) + .background(P.surfaceRaised, in: RoundedRectangle(cornerRadius: 15, style: .continuous)) + } +} + +/// The meal's carbs absorbing over time, drawn the way the day page draws carbs on board: a soft fill under a line, +/// solid up to now and dashed after, over hour gridlines. Touch and hold to read how much is left at any time. +private struct MealAbsorptionChart: View { + let grams: Double + let absorptionTime: TimeInterval + let eaten: Date + let now: Date + /// The longest absorption on offer, so a faster food draws a visibly shorter curve. + let axisLength: TimeInterval + + @State private var selection: Date? + @Environment(\.accessibilityReduceMotion) private var reduceMotion + + private static let height: CGFloat = 120 + private static let nowLabelHeight: CGFloat = 20 + + private var absorbed: Date { eaten.addingTimeInterval(absorptionTime) } + + private var window: DateInterval { + let start = min(eaten, now) + return DateInterval(start: start, end: max(eaten.addingTimeInterval(axisLength), now.addingTimeInterval(15 * 60))) + } + + /// Loop's absorption rate as a share of its peak (PiecewiseLinearAbsorption in LoopAlgorithm): rising to the + /// peak at 15% of the absorption time, steady until half way, then falling to nothing. + private static func shape(_ t: Double) -> Double { + switch t { + case ..<0: return 0 + case ..<0.15: return t / 0.15 + case ..<0.5: return 1 + case ..<1: return (1 - t) / 0.5 + default: return 0 + } + } + + /// The share of the carbs absorbed by `t`: the area under `shape` so far, over its whole area of 0.675. + private static func absorbedShare(_ t: Double) -> Double { + let t = min(max(t, 0), 1) + let area: Double + if t < 0.15 { + area = t * t / 0.3 + } else if t < 0.5 { + area = 0.075 + (t - 0.15) + } else { + area = 0.425 + ((t - 0.5) - (t * t - 0.25) / 2) / 0.5 + } + return min(1, area / 0.675) + } + + /// The plotted height of the peak. Its area stands for the grams, so the peak is the rate, grams over + /// 0.675 × the absorption time; a square root keeps a small snack visible beside a large fast one. + private var peak: Double { + guard grams > 0, absorptionTime > 0 else { return 0.42 } + let gramsPerHour = grams / (0.675 * absorptionTime / 3600) + return min(1, max(0.14, (gramsPerHour / 150).squareRoot())) + } + + private struct Sample: Identifiable { + let date: Date + let value: Double + var id: Date { date } + } + + /// The curve's corners, plus the point where it crosses now, so the solid and dashed parts meet exactly. + private var samples: [Sample] { + var shares = [0, 0.15, 0.5, 1.0] + let nowShare = now.timeIntervalSince(eaten) / max(absorptionTime, 1) + if nowShare > 0 && nowShare < 1 { shares.append(nowShare) } + return shares.sorted().map { Sample(date: eaten.addingTimeInterval($0 * absorptionTime), value: Self.shape($0) * peak) } + } + + private var hourMarks: [Date] { + stride(from: 3600.0, to: window.duration, by: 3600).map { eaten.addingTimeInterval($0) }.filter { window.contains($0) } + } + + var body: some View { + let samples = samples + let past = samples.filter { $0.date <= now } + let future = samples.filter { $0.date >= now } + VStack(spacing: 6) { + Chart { + ForEach(samples) { sample in + AreaMark(x: .value("Time", sample.date), yStart: .value("Base", 0), yEnd: .value("Rate", sample.value), series: .value("Series", "fill")) + .foregroundStyle(P.carbs.opacity(grams > 0 ? 0.16 : 0)) + } + // Already absorbed: a firmer fill under the solid part. + ForEach(past) { sample in + AreaMark(x: .value("Time", sample.date), yStart: .value("Base", 0), yEnd: .value("Rate", sample.value), series: .value("Series", "past-fill")) + .foregroundStyle(P.carbs.opacity(grams > 0 ? 0.22 : 0)) + } + ForEach(past) { sample in + LineMark(x: .value("Time", sample.date), y: .value("Rate", sample.value), series: .value("Series", "past")) + .foregroundStyle(grams > 0 ? P.carbs : P.inkMuted.opacity(0.5)) + .lineStyle(StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round)) + } + ForEach(future) { sample in + LineMark(x: .value("Time", sample.date), y: .value("Rate", sample.value), series: .value("Series", "future")) + .foregroundStyle(grams > 0 ? P.carbs : P.inkMuted.opacity(0.5)) + .lineStyle(StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round, dash: [4, 3])) + } + RuleMark(y: .value("Zero", 0)) + .foregroundStyle(P.separator) + .lineStyle(StrokeStyle(lineWidth: 1)) + RuleMark(x: .value("Now", now)) + .foregroundStyle(P.inkMuted) + .lineStyle(StrokeStyle(lineWidth: 1, dash: [1, 3])) + if let selection { + RuleMark(x: .value("Selected", selection)).foregroundStyle(P.ink.opacity(0.6)) + } + } + .chartXScale(domain: window.start...window.end) + .chartYScale(domain: 0...1.0) + .chartXAxis { + AxisMarks(values: hourMarks) { _ in + AxisGridLine().foregroundStyle(P.grid) + } + } + .chartYAxis(.hidden) + .chartOverlay { proxy in + ZStack(alignment: .topLeading) { + ScrubOverlay(proxy: proxy, selection: $selection, clampTo: window) + if selection == nil, let nowX = proxy.position(forX: now) { + GeometryReader { geometry in + // Centred over the line, kept inside the chart. + Text(NSLocalizedString("Now", comment: "Carb entry time choice: eaten now")) + .font(LoopDesign.Typeface.caption) + .foregroundStyle(P.inkMuted) + .dynamicTypeSize(...DynamicTypeSize.xxxLarge) + .fixedSize() + .frame(width: 48) + .position(x: min(max(nowX, 24), geometry.size.width - 24), y: -Self.nowLabelHeight / 2) + .allowsHitTesting(false) + } + } + } + } + .overlay(alignment: .top) { + if let selection { + ScrubCallout(rows: scrubRows(at: selection)) + .offset(y: -Self.nowLabelHeight) + .allowsHitTesting(false) + } + } + .frame(height: Self.height) + .padding(.top, Self.nowLabelHeight) + .animation(reduceMotion ? nil : .snappy(duration: 0.35), value: grams) + .animation(reduceMotion ? nil : .snappy(duration: 0.35), value: absorptionTime) + .animation(reduceMotion ? nil : .snappy(duration: 0.35), value: eaten) + + AxisLabels(start: eaten.timeIntervalSince(window.start) / window.duration, end: absorbed.timeIntervalSince(window.start) / window.duration) { + Text(LoopFormat.time(eaten)) + .foregroundStyle(P.ink) + Text(String(format: NSLocalizedString("Absorbed by %1$@", comment: "Under the end of the carb absorption curve: when the carbs will have absorbed (1: time)"), LoopFormat.time(absorbed))) + .foregroundStyle(P.inkMuted) + } + .font(LoopDesign.Typeface.footnote.monospacedDigit()) + } + .accessibilityElement(children: .ignore) + .accessibilityLabel(String(format: NSLocalizedString("Eaten at %1$@, absorbed by %2$@", comment: "Accessibility summary of the carb entry preview chart (1: time eaten, 2: time absorbed)"), LoopFormat.time(eaten), LoopFormat.time(absorbed))) + } + + private func scrubRows(at date: Date) -> [ScrubCallout.Row] { + var rows: [ScrubCallout.Row] = [.init(label: nil, value: LoopFormat.time(date), color: P.ink)] + if grams > 0 { + let left = grams * (1 - Self.absorbedShare(date.timeIntervalSince(eaten) / max(absorptionTime, 1))) + rows.append(.init(label: NSLocalizedString("COB", comment: "Abbreviation for carbs on board (active carbohydrate)"), value: LoopFormat.grams(left), color: P.carbsInk)) + } + return rows + } +} + +/// Two labels on a time axis: the first starts at `start`, the second ends at `end`, pushed apart when they'd meet. +/// When they can't share a line, as at large text sizes, the second goes under the first. +private struct AxisLabels: Layout { + var start: Double + var end: Double + + private static let gap: CGFloat = 12 + + private func stacked(_ width: CGFloat, _ subviews: Subviews) -> Bool { + subviews.map { $0.sizeThatFits(.unspecified).width }.reduce(Self.gap, +) > width + } + + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { + let width = proposal.width ?? 0 + guard stacked(width, subviews) else { + return CGSize(width: width, height: subviews.map { $0.sizeThatFits(.unspecified).height }.max() ?? 0) + } + // Stacked labels wrap to the width rather than run off it. + let height = subviews.map { $0.sizeThatFits(ProposedViewSize(width: width, height: nil)).height }.reduce(0, +) + return CGSize(width: width, height: height) + } + + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { + guard subviews.count == 2 else { return } + if stacked(bounds.width, subviews) { + let wrapped = ProposedViewSize(width: bounds.width, height: nil) + let firstHeight = subviews[0].sizeThatFits(wrapped).height + subviews[0].place(at: CGPoint(x: bounds.minX, y: bounds.minY), proposal: wrapped) + subviews[1].place(at: CGPoint(x: bounds.minX, y: bounds.minY + firstHeight), proposal: wrapped) + return + } + let first = subviews[0].sizeThatFits(.unspecified) + let second = subviews[1].sizeThatFits(.unspecified) + let firstX = min(max(bounds.minX, bounds.minX + bounds.width * start), bounds.maxX - first.width) + let secondX = min(max(bounds.minX + bounds.width * end - second.width, firstX + first.width + Self.gap), bounds.maxX - second.width) + subviews[0].place(at: CGPoint(x: firstX, y: bounds.minY), proposal: ProposedViewSize(first)) + subviews[1].place(at: CGPoint(x: secondX, y: bounds.minY), proposal: ProposedViewSize(second)) + } +} diff --git a/Loop/Redesign/LogbookScreen.swift b/Loop/Redesign/LogbookScreen.swift index 852337c7cd..c812df3102 100644 --- a/Loop/Redesign/LogbookScreen.swift +++ b/Loop/Redesign/LogbookScreen.swift @@ -602,7 +602,7 @@ private struct LogbookCarbEditor: View { } var body: some View { - CarbEntryView(viewModel: viewModel) + CarbEntryScreen(viewModel: viewModel) .environmentObject(deviceManager.displayGlucosePreference) .environment(\.dismissAction, dismiss) .navigationTitle(NSLocalizedString("Edit Carb Entry", comment: "Title of the screen for editing a carb entry from the Logbook")) diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index a4ec22e962..f2e811b515 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -1586,9 +1586,14 @@ final class StatusTableViewController: LoopChartsTableViewController { if let activity { viewModel.restoreUserActivityState(activity) } - let carbEntryView = CarbEntryView(viewModel: viewModel) - .environmentObject(deviceManager.displayGlucosePreference) - let hostingController = DismissibleHostingController(rootView: carbEntryView, isModalInPresentation: false) + let hostingController: UIViewController + if LoopInterfacePreference.useRedesign { + hostingController = DismissibleHostingController(rootView: CarbEntryScreen(viewModel: viewModel) + .environmentObject(deviceManager.displayGlucosePreference), isModalInPresentation: false) + } else { + hostingController = DismissibleHostingController(rootView: CarbEntryView(viewModel: viewModel) + .environmentObject(deviceManager.displayGlucosePreference), isModalInPresentation: false) + } present(hostingController, animated: true) } analyticsServicesManager?.didDisplayCarbEntryScreen() diff --git a/Loop/View Models/CarbEntryViewModel.swift b/Loop/View Models/CarbEntryViewModel.swift index 7b84705527..f2d6e34353 100644 --- a/Loop/View Models/CarbEntryViewModel.swift +++ b/Loop/View Models/CarbEntryViewModel.swift @@ -262,8 +262,9 @@ final class CarbEntryViewModel: ObservableObject { } private func observeFavoriteFoodIndexChange() { + // Selection is already on the main actor. Apply it before a caller sets a replacement + // amount or absorption time; a queued reset would otherwise overwrite that choice. $selectedFavoriteFoodIndex - .receive(on: RunLoop.main) .dropFirst() .sink { [weak self] index in self?.favoriteFoodSelected(at: index) diff --git a/LoopTests/Managers/LoopDataManagerTests.swift b/LoopTests/Managers/LoopDataManagerTests.swift index e82fdd17ea..60d7356ea3 100644 --- a/LoopTests/Managers/LoopDataManagerTests.swift +++ b/LoopTests/Managers/LoopDataManagerTests.swift @@ -648,6 +648,34 @@ class LoopDataManagerTests: XCTestCase { try await assertMissingCarbScheduleThrows(.insulinSensitivitySchedule, preview: true) } + func testFavoriteSelectionCannotOverwriteAReplacementMeal() async throws { + let viewModel = CarbEntryViewModel(delegate: loopDataManager) + let savedFoods = viewModel.favoriteFoods + defer { viewModel.favoriteFoods = savedFoods } + viewModel.favoriteFoods = [StoredFavoriteFood(name: "Meal", carbsQuantity: LoopQuantity(unit: .gram, doubleValue: 35), + foodType: "🌮", absorptionTime: .hours(3))] + viewModel.selectedFavoriteFoodIndex = 0 + let selected = expectation(description: "Favourite selection settled") + RunLoop.main.perform { selected.fulfill() } + await fulfillment(of: [selected], timeout: 2) + XCTAssertEqual(viewModel.carbsQuantity, 35) + + // The person replaces the favourite with a fast meal, retaining its amount. + let quantity = viewModel.carbsQuantity + viewModel.selectedFavoriteFoodIndex = -1 + viewModel.carbsQuantity = quantity + viewModel.usesCustomFoodType = false + viewModel.selectedDefaultAbsorptionTimeEmoji = "🍭" + viewModel.absorptionTime = .minutes(30) + let settled = expectation(description: "Replacement meal settled") + RunLoop.main.perform { settled.fulfill() } + await fulfillment(of: [settled], timeout: 2) + + XCTAssertEqual(viewModel.carbsQuantity, 35) + XCTAssertEqual(viewModel.absorptionTime, .minutes(30)) + XCTAssertFalse(viewModel.continueButtonDisabled) + } + func testHistoricalTargetsUseStoredProfilesAndEndedPresets() async throws { func target(_ low: Double, _ high: Double) -> ClosedRange { LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: low)...LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: high)