diff --git a/DynamicIsland/Localizable.xcstrings b/DynamicIsland/Localizable.xcstrings index e8f1a17..c6c4bc2 100644 --- a/DynamicIsland/Localizable.xcstrings +++ b/DynamicIsland/Localizable.xcstrings @@ -3335,6 +3335,9 @@ } } } + }, + "Adjusts the lock screen timer widget width without affecting button sizing." : { + }, "AI Assistant" : { "localizations" : { @@ -6699,6 +6702,9 @@ } } } + }, + "Change Media Output" : { + }, "Charging" : { "comment" : "Charging fallback label when no estimate is available", @@ -9318,6 +9324,9 @@ } } } + }, + "Custom Focus metadata" : { + }, "Custom height" : { "localizations" : { @@ -10295,6 +10304,9 @@ } } } + }, + "Denied" : { + }, "Denied/Revoked" : { "comment" : "A label describing an extension with denied or revoked permissions.", @@ -11872,6 +11884,9 @@ } } } + }, + "Empty Slot" : { + }, "Enable \"Idle Animation\" to customize animations" : { "localizations" : { @@ -12006,6 +12021,9 @@ }, "Enable Color Picker" : { + }, + "Enable colored spectograms" : { + }, "Enable colored spectrograms" : { "localizations" : { @@ -13888,6 +13906,9 @@ } } } + }, + "Forward 10s" : { + }, "Free" : { @@ -13936,6 +13957,9 @@ } } } + }, + "Full Access" : { + }, "Full charge" : { "comment" : "Text displayed in the status text when the battery is fully charged.", @@ -13971,6 +13995,9 @@ } } } + }, + "Full Disk Access unlocks custom Focus icons, colors, and labels. Standard Focus detection still works without it—grant access only if you need personalized indicators." : { + }, "Fully charged" : { "comment" : "Charging fallback label when already charged", @@ -14942,6 +14969,9 @@ } } } + }, + "Hide active event and show next upcoming event" : { + }, "Hide all-day events" : { "comment" : "A toggle to hide all-day events in the calendar.", @@ -15385,6 +15415,9 @@ } }, "shouldTranslate" : false + }, + "Hours" : { + }, "https://example.com/animation.json" : { "comment" : "A placeholder text for a Lottie JSON URL input field.", @@ -16473,6 +16506,9 @@ } } } + }, + "Last Update" : { + }, "Last Updated" : { "localizations" : { @@ -17468,6 +17504,9 @@ } } } + }, + "Lyrics" : { + }, "Mac battery at %d percent" : { "comment" : "Mac battery gauge accessibility label", @@ -18402,6 +18441,9 @@ } } } + }, + "Media Panel Width" : { + }, "Media playback live activity" : { "localizations" : { @@ -19736,6 +19778,9 @@ } } } + }, + "Music" : { + }, "Music panel variant" : { "localizations" : { @@ -20366,6 +20411,9 @@ } } } + }, + "Next Track" : { + }, "No active interfaces detected yet." : { "comment" : "A message displayed when there are no active network interfaces to display.", @@ -21273,6 +21321,9 @@ } } } + }, + "Not Determined" : { + }, "Not Now" : { "localizations" : { @@ -22140,6 +22191,9 @@ }, "Open notch on hover" : { + }, + "Open Privacy & Security" : { + }, "Open shelf tab by default if items added" : { @@ -22621,6 +22675,9 @@ } } } + }, + "Play / Pause" : { + }, "Play feedback when volume is changed" : { @@ -23001,6 +23058,9 @@ } } } + }, + "Previous Track" : { + }, "Privacy Indicators" : { "comment" : "A heading for the section that lists privacy indicators.", @@ -23632,6 +23692,9 @@ } } } + }, + "Recommended minimum width adjusts automatically based on the number of enabled tabs." : { + }, "Recording" : { "localizations" : { @@ -24059,6 +24122,9 @@ }, "Renderer" : { + }, + "Repeat" : { + }, "Request Access" : { "localizations" : { @@ -24087,6 +24153,9 @@ } } } + }, + "Request Full Disk Access" : { + }, "Requires a third-party client with API plugin enabled." : { "comment" : "Description of a music controller type when it requires a third-party client with an API plugin enabled.", @@ -24213,6 +24282,12 @@ } } } + }, + "Reset Media Width" : { + + }, + "Reset Music" : { + }, "Reset Notch Experiences" : { "comment" : "A menu item that resets all notch experiences for a particular app.", @@ -24249,6 +24324,12 @@ } } } + }, + "Reset Timer" : { + + }, + "Reset Timer Width" : { + }, "Reset to Default" : { "localizations" : { @@ -24289,6 +24370,9 @@ } } } + }, + "Reset Weather" : { + }, "Reset Width" : { "comment" : "A button to reset the expanded notch width to its default value.", @@ -24526,6 +24610,9 @@ } } } + }, + "Restricted" : { + }, "Resume" : { "localizations" : { @@ -24572,6 +24659,9 @@ } } } + }, + "Rewind 10s" : { + }, "Rich Text" : { "comment" : "Name for a clipboard item type that represents a rich text format.", @@ -25213,7 +25303,6 @@ } }, "Seconds" : { - "extractionState" : "stale", "localizations" : { "fr" : { "stringUnit" : { @@ -26104,6 +26193,9 @@ } } } + }, + "Show \"Change Media Output\" control" : { + }, "Show All Color Formats" : { @@ -26510,6 +26602,9 @@ } } } + }, + "Show floating media controls" : { + }, "Show floating pause/stop controls" : { "localizations" : { @@ -26586,6 +26681,9 @@ }, "Show media controls in Dynamic Island" : { + }, + "Show merged AirPlay and output devices" : { + }, "Show next calendar event" : { @@ -27084,6 +27182,12 @@ } } } + }, + "Shrinks the lock screen media panel while keeping the expanded view full width." : { + + }, + "Shuffle" : { + }, "Simple and minimal interface with just the essentials for everyday tasks." : { "localizations" : { @@ -29482,6 +29586,9 @@ } } } + }, + "Timer Widget Width" : { + }, "Title" : { "comment" : "A label for the title input field in the note editor.", @@ -31106,6 +31213,9 @@ } } } + }, + "Vertical Bar" : { + }, "Vertical offset" : { "localizations" : { @@ -31782,6 +31892,9 @@ } } } + }, + "Width adjustments apply only to the standard notch layout. Disable Minimalistic UI to edit this value." : { + }, "Width:" : { "extractionState" : "stale", @@ -31882,6 +31995,9 @@ } } } + }, + "Write Only" : { + }, "Written" : { diff --git a/DynamicIsland/Providers/CalendarServiceProviding.swift b/DynamicIsland/Providers/CalendarServiceProviding.swift index 055de25..280b90d 100644 --- a/DynamicIsland/Providers/CalendarServiceProviding.swift +++ b/DynamicIsland/Providers/CalendarServiceProviding.swift @@ -272,7 +272,7 @@ extension EKCalendar { var accountTitle: String { switch source.sourceType { case .local, .subscribed, .birthdays: - return "Other" + return String(localized: "Other") default: return source.title } diff --git a/DynamicIsland/components/Settings/SettingsView.swift b/DynamicIsland/components/Settings/SettingsView.swift index cfdfc26..8e5aa18 100644 --- a/DynamicIsland/components/Settings/SettingsView.swift +++ b/DynamicIsland/components/Settings/SettingsView.swift @@ -1610,7 +1610,7 @@ private struct HUDAndOSDSettingsView: View { VStack(spacing: 20) { HStack(spacing: 16) { HUDSelectionCard( - title: "Dynamic Island", + title: String(localized: "Dynamic Island"), isSelected: selectedTab == .hud, action: { selectedTab = .hud @@ -1650,7 +1650,7 @@ private struct HUDAndOSDSettingsView: View { } HUDSelectionCard( - title: "Custom OSD", + title: String(localized: "Custom OSD"), isSelected: selectedTab == .osd, action: { selectedTab = .osd @@ -1692,7 +1692,7 @@ private struct HUDAndOSDSettingsView: View { } HUDSelectionCard( - title: "Vertical Bar", + title: String(localized: "Vertical Bar"), isSelected: selectedTab == .vertical, action: { selectedTab = .vertical @@ -1735,7 +1735,7 @@ private struct HUDAndOSDSettingsView: View { } HUDSelectionCard( - title: "Circular", + title: String(localized: "Circular"), isSelected: selectedTab == .circular, action: { selectedTab = .circular @@ -2528,9 +2528,11 @@ struct Media: View { } } if showShuffleAndRepeat { - Defaults.Toggle("Show \"Change Media Output\" control", key: .showMediaOutputControl) - .settingsHighlight(id: highlightID("Show Change Media Output control")) - .help("Adds the AirPlay/route picker button back to the customizable controls palette.") + Defaults.Toggle(key: .showMediaOutputControl) { + Text("Show \"Change Media Output\" control") + } + .settingsHighlight(id: highlightID("Show Change Media Output control")) + .help("Adds the AirPlay/route picker button back to the customizable controls palette.") MusicSlotConfigurationView() } else { Text("Turn on customizable controls to rearrange media buttons.") @@ -2575,10 +2577,9 @@ struct Media: View { ) .disabled(standardControlsSuppressed) .help(standardControlsSuppressed ? "Standard notch media controls are hidden while this toggle is off." : "") - Defaults.Toggle( - "Show floating media controls", - key: .musicControlWindowEnabled - ) + Defaults.Toggle(key: .musicControlWindowEnabled) { + Text("Show floating media controls") + } .disabled(!coordinator.musicLiveActivityEnabled || standardControlsSuppressed) .help("Displays play/pause and skip buttons beside the notch while music is active. Disabled by default.") Toggle("Enable sneak peek", isOn: $enableSneakPeek) @@ -2623,9 +2624,11 @@ struct Media: View { } .disabled(!enableLockScreenMediaWidget) if isAppleMusicActive { - Defaults.Toggle("Show merged AirPlay and output devices", key: .lockScreenMusicMergedAirPlayOutput) - .disabled(!enableLockScreenMediaWidget) - .settingsHighlight(id: highlightID("Show merged AirPlay and output devices")) + Defaults.Toggle(key: .lockScreenMusicMergedAirPlayOutput) { + Text("Show merged AirPlay and output devices") + } + .disabled(!enableLockScreenMediaWidget) + .settingsHighlight(id: highlightID("Show merged AirPlay and output devices")) } Defaults.Toggle(key: .lockScreenPanelShowsBorder) { Text("Show panel border") @@ -2975,10 +2978,9 @@ struct CalendarSettings: View { } } - Defaults.Toggle( - "Hide active event and show next upcoming event", - key: .lockScreenShowCalendarEventAfterStartEnabled - ) + Defaults.Toggle(key: .lockScreenShowCalendarEventAfterStartEnabled) { + Text("Hide active event and show next upcoming event") + } .disabled(!lockScreenShowCalendarEvent || lockScreenShowCalendarEventEntireDuration) .settingsHighlight(id: highlightID("Hide active event and show next upcoming event")) @@ -3077,12 +3079,12 @@ struct CalendarSettings: View { private func statusText(for status: EKAuthorizationStatus) -> String { switch status { - case .fullAccess, .authorized: return "Full Access" - case .writeOnly: return "Write Only" - case .denied: return "Denied" - case .restricted: return "Restricted" - case .notDetermined: return "Not Determined" - @unknown default: return "Unknown" + case .fullAccess, .authorized: return String(localized: "Full Access") + case .writeOnly: return String(localized: "Write Only") + case .denied: return String(localized: "Denied") + case .restricted: return String(localized: "Restricted") + case .notDetermined: return String(localized: "Not Determined") + @unknown default: return String(localized: "Unknown") } } @@ -3597,12 +3599,12 @@ struct LiveActivitiesSettings: View { Section { if !fullDiskAccessPermission.isAuthorized { SettingsPermissionCallout( - title: "Custom Focus metadata", - message: "Full Disk Access unlocks custom Focus icons, colors, and labels. Standard Focus detection still works without it—grant access only if you need personalized indicators.", + title: String(localized: "Custom Focus metadata"), + message: String(localized: "Full Disk Access unlocks custom Focus icons, colors, and labels. Standard Focus detection still works without it—grant access only if you need personalized indicators."), icon: "externaldrive.fill", iconColor: .purple, - requestButtonTitle: "Request Full Disk Access", - openSettingsButtonTitle: "Open Privacy & Security", + requestButtonTitle: String(localized: "Request Full Disk Access"), + openSettingsButtonTitle: String(localized: "Open Privacy & Security"), requestAction: { fullDiskAccessPermission.requestAccessPrompt() }, openSettingsAction: { fullDiskAccessPermission.openSystemSettings() } ) @@ -3982,8 +3984,9 @@ struct Appearance: View { Text("Enable colored spectrograms") } .settingsHighlight(id: highlightID("Enable colored spectrograms")) - Defaults - .Toggle("Player tinting", key: .playerColorTinting) + Defaults.Toggle(key: .playerColorTinting) { + Text("Enable colored spectograms") + } Defaults.Toggle(key: .lightingEffect) { Text("Enable blur effect behind album art") } @@ -4447,8 +4450,8 @@ struct Appearance: View { } let description = enableMinimalisticUI - ? "Width adjustments apply only to the standard notch layout. Disable Minimalistic UI to edit this value." - : "Recommended minimum width adjusts automatically based on the number of enabled tabs." + ? String(localized: "Width adjustments apply only to the standard notch layout. Disable Minimalistic UI to edit this value.") + : String(localized: "Recommended minimum width adjusts automatically based on the number of enabled tabs.") Text(description) .font(.caption) @@ -4682,9 +4685,11 @@ struct LockScreenSettings: View { .disabled(!enableLockScreenMediaWidget) .settingsHighlight(id: highlightID("Show media app icon")) if isAppleMusicActive { - Defaults.Toggle("Show merged AirPlay and output devices", key: .lockScreenMusicMergedAirPlayOutput) - .disabled(!enableLockScreenMediaWidget) - .settingsHighlight(id: highlightID("Show merged AirPlay and output devices")) + Defaults.Toggle(key: .lockScreenMusicMergedAirPlayOutput) { + Text("Show merged AirPlay and output devices") + } + .disabled(!enableLockScreenMediaWidget) + .settingsHighlight(id: highlightID("Show merged AirPlay and output devices")) } Defaults.Toggle(key: .lockScreenPanelShowsBorder) { Text("Show panel border") @@ -5292,9 +5297,9 @@ private struct LockScreenPositioningControls: View { HStack(alignment: .top, spacing: 24) { offsetColumn( - title: "Weather", + title: String(localized: "Weather"), value: weatherOffset, - resetTitle: "Reset Weather", + resetTitle: String(localized: "Reset Weather"), resetAction: resetWeatherOffset ) @@ -5302,9 +5307,9 @@ private struct LockScreenPositioningControls: View { .frame(height: 64) offsetColumn( - title: "Timer", + title: String(localized: "Timer"), value: timerOffset, - resetTitle: "Reset Timer", + resetTitle: String(localized: "Reset Timer"), resetAction: resetTimerOffset ) @@ -5312,9 +5317,9 @@ private struct LockScreenPositioningControls: View { .frame(height: 64) offsetColumn( - title: "Music", + title: String(localized: "Music"), value: musicOffset, - resetTitle: "Reset Music", + resetTitle: String(localized: "Reset Music"), resetAction: resetMusicOffset ) @@ -5326,21 +5331,21 @@ private struct LockScreenPositioningControls: View { VStack(alignment: .leading, spacing: 16) { widthSlider( - title: "Media Panel Width", + title: String(localized: "Media Panel Width"), value: musicWidthBinding, range: musicWidthRange, - resetTitle: "Reset Media Width", + resetTitle: String(localized: "Reset Media Width"), resetAction: resetMusicWidth, - helpText: "Shrinks the lock screen media panel while keeping the expanded view full width." + helpText: String(localized: "Shrinks the lock screen media panel while keeping the expanded view full width.") ) widthSlider( - title: "Timer Widget Width", + title: String(localized: "Timer Widget Width"), value: timerWidthBinding, range: timerWidthRange, - resetTitle: "Reset Timer Width", + resetTitle: String(localized: "Reset Timer Width"), resetAction: resetTimerWidth, - helpText: "Adjusts the lock screen timer widget width without affecting button sizing." + helpText: String(localized: "Adjusts the lock screen timer widget width without affecting button sizing.") ) } } header: { @@ -6180,9 +6185,9 @@ struct TimerSettings: View { Text("Default Custom Timer") .font(.headline) - TimerDurationStepperRow(title: "Hours", value: $customHours, range: 0...23) - TimerDurationStepperRow(title: "Minutes", value: $customMinutes, range: 0...59) - TimerDurationStepperRow(title: "Seconds", value: $customSeconds, range: 0...59) + TimerDurationStepperRow(title: String(localized: "Hours"), value: $customHours, range: 0...23) + TimerDurationStepperRow(title: String(localized: "Minutes"), value: $customMinutes, range: 0...59) + TimerDurationStepperRow(title: String(localized: "Seconds"), value: $customSeconds, range: 0...59) HStack { Text("Current default:") @@ -6522,9 +6527,9 @@ private struct TimerPresetEditorRow: View { } HStack(spacing: 16) { - TimerPresetComponentControl(title: "Hours", value: hoursBinding, range: 0...23) - TimerPresetComponentControl(title: "Minutes", value: minutesBinding, range: 0...59) - TimerPresetComponentControl(title: "Seconds", value: secondsBinding, range: 0...59) + TimerPresetComponentControl(title: String(localized: "Hours"), value: hoursBinding, range: 0...23) + TimerPresetComponentControl(title: String(localized: "Minutes"), value: minutesBinding, range: 0...59) + TimerPresetComponentControl(title: String(localized: "Seconds"), value: secondsBinding, range: 0...59) } ColorPicker("Accent colour", selection: colorBinding, supportsOpacity: false) diff --git a/DynamicIsland/components/Stats/CPUStatsSharedViews.swift b/DynamicIsland/components/Stats/CPUStatsSharedViews.swift index 29b96be..773a788 100644 --- a/DynamicIsland/components/Stats/CPUStatsSharedViews.swift +++ b/DynamicIsland/components/Stats/CPUStatsSharedViews.swift @@ -77,10 +77,10 @@ struct CPUUsageDashboard: View { Text("Usage") .font(.caption) .foregroundColor(.secondary) - DetailRow(color: systemColor.opacity(0.95), label: "System", value: StatsFormatting.percentage(breakdown.system)) - DetailRow(color: userColor.opacity(0.95), label: "User", value: StatsFormatting.percentage(breakdown.user)) - DetailRow(color: idleColor.opacity(0.7), label: "Idle", value: StatsFormatting.percentage(breakdown.idle)) - DetailRow(color: nil, label: "Active", value: StatsFormatting.percentage(breakdown.activeUsage)) + DetailRow(color: systemColor.opacity(0.95), label: String(localized: "System"), value: StatsFormatting.percentage(breakdown.system)) + DetailRow(color: userColor.opacity(0.95), label: String(localized: "User"), value: StatsFormatting.percentage(breakdown.user)) + DetailRow(color: idleColor.opacity(0.7), label: String(localized: "Idle"), value: StatsFormatting.percentage(breakdown.idle)) + DetailRow(color: nil, label: String(localized: "stats_resource_active", defaultValue: "Active"), value: StatsFormatting.percentage(breakdown.activeUsage)) } .frame(maxWidth: .infinity, alignment: .leading) @@ -171,7 +171,7 @@ private struct CPUTemperatureGauge: View { var body: some View { CircularGaugeView( - title: "Temperature", + title: String(localized: "Temperature"), value: normalizedValue, tint: tint, centerPrimaryText: centerPrimary, diff --git a/DynamicIsland/components/Stats/GPUStatsDetailView.swift b/DynamicIsland/components/Stats/GPUStatsDetailView.swift index 064d2b5..1cac62a 100644 --- a/DynamicIsland/components/Stats/GPUStatsDetailView.swift +++ b/DynamicIsland/components/Stats/GPUStatsDetailView.swift @@ -133,7 +133,7 @@ private struct GPUUsageDashboard: View { .font(.caption) .foregroundColor(.secondary) DetailRow(color: nil, label: String(localized: "Session Avg"), value: StatsFormatting.percentage(averageUsage)) - DetailRow(color: nil, label: String("localized: Last Update"), value: formattedTimestamp(lastUpdated)) + DetailRow(color: nil, label: String(localized: "Last Update"), value: formattedTimestamp(lastUpdated)) if let device = primaryDevice { Divider().padding(.vertical, 4) DetailRow(color: nil, label: String(localized: "Active GPU"), value: device.formattedVendorModel) diff --git a/DynamicIsland/models/MusicControlButton.swift b/DynamicIsland/models/MusicControlButton.swift index 3d2bd23..fb5555b 100644 --- a/DynamicIsland/models/MusicControlButton.swift +++ b/DynamicIsland/models/MusicControlButton.swift @@ -76,27 +76,27 @@ enum MusicControlButton: String, CaseIterable, Identifiable, Codable, Defaults.S var label: String { switch self { case .shuffle: - return "Shuffle" + return String(localized: "Shuffle") case .trackBackward: - return "Previous Track" + return String(localized: "Previous Track") case .playPause: - return "Play / Pause" + return String(localized: "Play / Pause") case .trackForward: - return "Next Track" + return String(localized: "Next Track") case .repeatMode: - return "Repeat" + return String(localized: "Repeat") case .mediaOutput: - return "Change Media Output" + return String(localized: "Change Media Output") case .airPlay: - return "AirPlay" + return String(localized: "AirPlay") case .lyrics: - return "Lyrics" + return String(localized: "Lyrics") case .seekBackward: - return "Rewind 10s" + return String(localized: "Rewind 10s") case .seekForward: - return "Forward 10s" + return String(localized: "Forward 10s") case .none: - return "Empty Slot" + return String(localized: "Empty Slot") } }