Fix string detection issues and complete pt-BR localization (100% done, as far as I can tell)

This commit is contained in:
Felipe Giacomini Cocco 2026-02-05 22:55:25 -03:00
commit f6f6321def
14 changed files with 402 additions and 77 deletions

View file

@ -888,11 +888,59 @@
} }
} }
}, },
"%lld day%@ ago" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%1$lld day%2$@ ago"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "%1$lld dias %2$@ atrás"
}
}
}
},
"%lld hour%@ ago" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%1$lld hour%2$@ ago"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "%1$lld horas %2$@ atrás"
}
}
}
},
"%lld min" : { "%lld min" : {
"comment" : "A text label displaying the current value of the \"Notify before\" slider. The argument is the current value of the slider, expressed in minutes.", "comment" : "A text label displaying the current value of the \"Notify before\" slider. The argument is the current value of the slider, expressed in minutes.",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
"shouldTranslate" : false "shouldTranslate" : false
}, },
"%lld minute%@ ago" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%1$lld minute%2$@ ago"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "%1$lld minutos %2$@ atrás"
}
}
}
},
"%lld px" : { "%lld px" : {
"comment" : "A label displaying the current expanded notch width value. The value is shown in pixels.", "comment" : "A label displaying the current expanded notch width value. The value is shown in pixels.",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
@ -1014,6 +1062,9 @@
} }
} }
}, },
"%lldh" : {
"shouldTranslate" : false
},
"%lldh ago" : { "%lldh ago" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -1024,6 +1075,9 @@
} }
} }
}, },
"%lldm" : {
"shouldTranslate" : false
},
"%lldm ago" : { "%lldm ago" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -3546,6 +3600,9 @@
} }
} }
}, },
"Apple Music" : {
"shouldTranslate" : false
},
"Applies the notch-style parallax effect to the lock screen media widget album art." : { "Applies the notch-style parallax effect to the lock screen media widget album art." : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -5349,6 +5406,16 @@
} }
} }
}, },
"Charging battery" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Carregando Bateria"
}
}
}
},
"charging icon" : { "charging icon" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -7227,6 +7294,9 @@
"cpu" : { "cpu" : {
"shouldTranslate" : false "shouldTranslate" : false
}, },
"CPU" : {
"shouldTranslate" : false
},
"CPU Usage" : { "CPU Usage" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -7749,7 +7819,7 @@
"pt-BR" : { "pt-BR" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "translated",
"value" : "Temporizador Personalizado" "value" : "Temporizador Customizado"
} }
} }
} }
@ -8127,7 +8197,7 @@
"pt-BR" : { "pt-BR" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "translated",
"value" : "Temporizador Personalizado Padrão" "value" : "Temporizador Customizado Padrão"
} }
} }
} }
@ -8449,6 +8519,16 @@
} }
} }
}, },
"Disk" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Disco"
}
}
}
},
"Disk I/O" : { "Disk I/O" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -9017,7 +9097,7 @@
"pt-BR" : { "pt-BR" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "translated",
"value" : "Visibilidade da Dynamic Islan" "value" : "Visibilidade da Dynamic Island"
} }
} }
} }
@ -10699,6 +10779,16 @@
"fahrenheit" : { "fahrenheit" : {
"shouldTranslate" : false "shouldTranslate" : false
}, },
"Favorites" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Favoritos"
}
}
}
},
"feedback" : { "feedback" : {
"shouldTranslate" : false "shouldTranslate" : false
}, },
@ -10932,6 +11022,16 @@
} }
} }
}, },
"Full charge" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bateria Completa"
}
}
}
},
"Full Disk Access required" : { "Full Disk Access required" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -11703,6 +11803,9 @@
"gpu" : { "gpu" : {
"shouldTranslate" : false "shouldTranslate" : false
}, },
"GPU" : {
"shouldTranslate" : false
},
"GPU Usage" : { "GPU Usage" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -12315,6 +12418,16 @@
} }
} }
}, },
"History" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Histórico"
}
}
}
},
"History Size" : { "History Size" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -13123,6 +13236,16 @@
} }
} }
}, },
"just now" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "agora mesmo"
}
}
}
},
"Just now" : { "Just now" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -13945,6 +14068,16 @@
} }
} }
}, },
"Low battery" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bateria baixa"
}
}
}
},
"Low Power Mode" : { "Low Power Mode" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -14045,6 +14178,16 @@
} }
} }
}, },
"Low Power: %@" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Pouca Energia: %@"
}
}
}
},
"lyrics" : { "lyrics" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -15285,6 +15428,16 @@
} }
} }
}, },
"Memory" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Memória"
}
}
}
},
"Memory Usage" : { "Memory Usage" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -15421,6 +15574,16 @@
} }
} }
}, },
"Mic" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Microfone"
}
}
}
},
"Mic muted" : { "Mic muted" : {
"comment" : "A description of the state when the microphone is muted.", "comment" : "A description of the state when the microphone is muted.",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
@ -16545,6 +16708,16 @@
} }
} }
}, },
"Network" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Rede"
}
}
}
},
"Network Activity" : { "Network Activity" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -17335,6 +17508,16 @@
} }
} }
}, },
"Not charging" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Carregamento Parado"
}
}
}
},
"Not Determined" : { "Not Determined" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -17809,6 +17992,26 @@
} }
} }
}, },
"Now Playing" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Tocando Agora"
}
}
}
},
"Off" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Desligado"
}
}
}
},
"OK" : { "OK" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -17910,6 +18113,16 @@
}, },
"shouldTranslate" : false "shouldTranslate" : false
}, },
"On" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ligado"
}
}
}
},
"on-screen display" : { "on-screen display" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -18216,6 +18429,16 @@
} }
} }
}, },
"Panel" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Painel"
}
}
}
},
"panel border" : { "panel border" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -19177,6 +19400,17 @@
} }
} }
}, },
"R " : {
"comment" : "Abbreviation for “Disk Read”",
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "L"
}
}
}
},
"ram" : { "ram" : {
"shouldTranslate" : false "shouldTranslate" : false
}, },
@ -20431,7 +20665,7 @@
"pt-BR" : { "pt-BR" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "translated",
"value" : "Selecione um som personalizado para reproduzir quando um cronômetro terminar. Os formatos suportados incluem MP3, M4A, WAV e AIFF." "value" : "Selecione um som Customizado para reproduzir quando um cronômetro terminar. Os formatos suportados incluem MP3, M4A, WAV e AIFF."
} }
} }
} }
@ -21226,6 +21460,16 @@
} }
} }
}, },
"Show \"Change Media Output\" control" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mostrar controle \"Alterar Saída de Mídia\""
}
}
}
},
"Show All Color Formats" : { "Show All Color Formats" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -21442,6 +21686,16 @@
} }
} }
}, },
"Show floating media controls" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mostrar controles de mídia flutuantes"
}
}
}
},
"Show floating pause/stop controls" : { "Show floating pause/stop controls" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -21574,6 +21828,16 @@
} }
} }
}, },
"Show media controls in Dynamic Island" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mostrar controles de mídia na Ilha Dinâmica"
}
}
}
},
"Show on a specific display" : { "Show on a specific display" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -21910,6 +22174,26 @@
} }
} }
}, },
"Shows screen assistant as a dropdown attached to the AI button" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mostra o assistente de tela como um menu suspenso anexado ao botão de IA"
}
}
}
},
"Shows screen assistant in a floating panel near the notch" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mostra o assistente de tela em um painel flutuante próximo ao notch"
}
}
}
},
"Shows the model's reasoning process before the final answer" : { "Shows the model's reasoning process before the final answer" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -22624,6 +22908,9 @@
} }
} }
}, },
"Spotify" : {
"shouldTranslate" : false
},
"Square" : { "Square" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -22899,7 +23186,7 @@
"pt-BR" : { "pt-BR" : {
"stringUnit" : { "stringUnit" : {
"state" : "translated", "state" : "translated",
"value" : "Comece a digital…" "value" : "Comece a digitar…"
} }
} }
} }
@ -24529,6 +24816,16 @@
} }
} }
}, },
"Unplugged" : {
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Desconectado"
}
}
}
},
"Update interval" : { "Update interval" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -25337,6 +25634,17 @@
} }
} }
}, },
"W " : {
"comment" : "Abbreviation for “Disk Write”",
"localizations" : {
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "G"
}
}
}
},
"weather" : { "weather" : {
"localizations" : { "localizations" : {
"pt-BR" : { "pt-BR" : {
@ -26011,6 +26319,9 @@
} }
} }
}, },
"Youtube Music" : {
"shouldTranslate" : false
},
"YouTube Music requires this third-party app to be installed: " : { "YouTube Music requires this third-party app to be installed: " : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {

View file

@ -456,16 +456,16 @@ struct ClipboardPanelItemRow: View {
let interval = Date().timeIntervalSince(date) let interval = Date().timeIntervalSince(date)
if interval < 60 { if interval < 60 {
return "Just now" return String(localized: "Just now")
} else if interval < 3600 { } else if interval < 3600 {
let minutes = Int(interval / 60) let minutes = Int(interval / 60)
return "\(minutes)m ago" return String(localized: "\(minutes)m ago")
} else if interval < 86400 { } else if interval < 86400 {
let hours = Int(interval / 3600) let hours = Int(interval / 3600)
return "\(hours)h ago" return String(localized: "\(hours)h ago")
} else { } else {
let days = Int(interval / 86400) let days = Int(interval / 86400)
return "\(days)d ago" return String(localized: "\(days)d ago")
} }
} }
} }

View file

@ -28,6 +28,13 @@ enum ClipboardTab: String, CaseIterable {
case .favorites: return "heart.fill" case .favorites: return "heart.fill"
} }
} }
var localizedName: String {
switch self {
case .history: return String(localized: "History")
case .favorites: return String(localized: "Favorites")
}
}
} }
struct ClipboardTabButton: View { struct ClipboardTabButton: View {
@ -51,7 +58,7 @@ struct ClipboardTabButton: View {
Image(systemName: tab.icon) Image(systemName: tab.icon)
.font(.system(size: 11)) .font(.system(size: 11))
Text(tab.rawValue) Text(tab.localizedName)
.font(.system(size: 11, weight: .medium)) .font(.system(size: 11, weight: .medium))
if itemCount > 0 { if itemCount > 0 {

View file

@ -530,16 +530,16 @@ struct ColorListRow: View {
let interval = Date().timeIntervalSince(date) let interval = Date().timeIntervalSince(date)
if interval < 60 { if interval < 60 {
return "Just now" return String(localized: "Just now")
} else if interval < 3600 { } else if interval < 3600 {
let minutes = Int(interval / 60) let minutes = Int(interval / 60)
return "\(minutes)m ago" return String(localized: "\(minutes)m ago")
} else if interval < 86400 { } else if interval < 86400 {
let hours = Int(interval / 3600) let hours = Int(interval / 3600)
return "\(hours)h ago" return String(localized: "\(hours)h ago")
} else { } else {
let days = Int(interval / 86400) let days = Int(interval / 86400)
return "\(days)d ago" return String(localized: "\(days)d ago")
} }
} }
} }
@ -589,16 +589,16 @@ struct ColorDetailsView: View {
let interval = Date().timeIntervalSince(date) let interval = Date().timeIntervalSince(date)
if interval < 60 { if interval < 60 {
return "just now" return String(localized: "just now")
} else if interval < 3600 { } else if interval < 3600 {
let minutes = Int(interval / 60) let minutes = Int(interval / 60)
return "\(minutes) minute\(minutes == 1 ? "" : "s") ago" return String(localized: "\(minutes) minute\(minutes == 1 ? "" : "s") ago")
} else if interval < 86400 { } else if interval < 86400 {
let hours = Int(interval / 3600) let hours = Int(interval / 3600)
return "\(hours) hour\(hours == 1 ? "" : "s") ago" return String(localized: "\(hours) hour\(hours == 1 ? "" : "s") ago")
} else { } else {
let days = Int(interval / 86400) let days = Int(interval / 86400)
return "\(days) day\(days == 1 ? "" : "s") ago" return String(localized: "\(days) day\(days == 1 ? "" : "s") ago")
} }
} }
} }

View file

@ -432,17 +432,17 @@ struct InlineHUD: View {
func Type2Name(_ type: SneakContentType) -> String { func Type2Name(_ type: SneakContentType) -> String {
switch(type) { switch(type) {
case .volume: case .volume:
return "Volume" return String(localized: "Volume")
case .brightness: case .brightness:
return "Brightness" return String(localized: "Brightness")
case .backlight: case .backlight:
return "Backlight" return String(localized: "Backlight")
case .mic: case .mic:
return "Mic" return String(localized: "Mic")
case .bluetoothAudio: case .bluetoothAudio:
return BluetoothAudioManager.shared.lastConnectedDevice?.name ?? "Bluetooth" return BluetoothAudioManager.shared.lastConnectedDevice?.name ?? "Bluetooth"
case .capsLock: case .capsLock:
return "Caps Lock" return String(localized: "Caps Lock")
default: default:
return "" return ""
} }

View file

@ -193,16 +193,16 @@ struct ClipboardItemRow: View {
let interval = Date().timeIntervalSince(date) let interval = Date().timeIntervalSince(date)
if interval < 60 { if interval < 60 {
return "Just now" return String(localized: "Just now")
} else if interval < 3600 { } else if interval < 3600 {
let minutes = Int(interval / 60) let minutes = Int(interval / 60)
return "\(minutes)m ago" return String(localized: "\(minutes)m ago")
} else if interval < 86400 { } else if interval < 86400 {
let hours = Int(interval / 3600) let hours = Int(interval / 3600)
return "\(hours)h ago" return String(localized: "\(hours)h ago")
} else { } else {
let days = Int(interval / 86400) let days = Int(interval / 86400)
return "\(days)d ago" return String(localized: "\(days)d ago")
} }
} }
} }

View file

@ -288,16 +288,16 @@ struct ColorInfoPopup: View {
let interval = Date().timeIntervalSince(date) let interval = Date().timeIntervalSince(date)
if interval < 60 { if interval < 60 {
return "Just now" return String(localized: "Just now")
} else if interval < 3600 { } else if interval < 3600 {
let minutes = Int(interval / 60) let minutes = Int(interval / 60)
return "\(minutes)m ago" return String(localized: "\(minutes)m ago")
} else if interval < 86400 { } else if interval < 86400 {
let hours = Int(interval / 3600) let hours = Int(interval / 3600)
return "\(hours)h ago" return String(localized: "\(hours)h ago")
} else { } else {
let days = Int(interval / 86400) let days = Int(interval / 86400)
return "\(days)d ago" return String(localized: "\(days)d ago")
} }
} }
} }

View file

@ -527,9 +527,9 @@ struct NotchClipboardItemRow: View {
private func timeAgoString(from date: Date) -> String { private func timeAgoString(from date: Date) -> String {
let interval = Date().timeIntervalSince(date) let interval = Date().timeIntervalSince(date)
if interval < 60 { return "Just now" } if interval < 60 { return String(localized: "Just now") }
if interval < 3600 { return "\(Int(interval/60))m" } if interval < 3600 { return String(localized: "\(Int(interval/60))m") }
return "\(Int(interval/3600))h" return String(localized: "\(Int(interval/3600))h")
} }
} }
@ -936,9 +936,9 @@ struct NoteRow: View {
private func timeAgoString(from date: Date) -> String { private func timeAgoString(from date: Date) -> String {
let interval = Date().timeIntervalSince(date) let interval = Date().timeIntervalSince(date)
if interval < 60 { return "Just now" } if interval < 60 { return String(localized: "Just now") }
if interval < 3600 { return "\(Int(interval/60))m" } if interval < 3600 { return String(localized: "\(Int(interval/60))m") }
if interval < 86400 { return "\(Int(interval/3600))h" } if interval < 86400 { return String(localized: "\(Int(interval/3600))h") }
return date.formatted(.dateTime.day().month()) return date.formatted(.dateTime.day().month())
} }
} }

View file

@ -82,7 +82,7 @@ struct NotchStatsView: View {
if showCpuGraph { if showCpuGraph {
graphs.append(SingleGraphData( graphs.append(SingleGraphData(
title: "CPU", title: String(localized: "CPU"),
value: statsManager.cpuUsageString, value: statsManager.cpuUsageString,
data: statsManager.cpuHistory, data: statsManager.cpuHistory,
color: .blue, color: .blue,
@ -92,7 +92,7 @@ struct NotchStatsView: View {
if showMemoryGraph { if showMemoryGraph {
graphs.append(SingleGraphData( graphs.append(SingleGraphData(
title: "Memory", title: String(localized: "Memory"),
value: statsManager.memoryUsageString, value: statsManager.memoryUsageString,
data: statsManager.memoryHistory, data: statsManager.memoryHistory,
color: .green, color: .green,
@ -102,7 +102,7 @@ struct NotchStatsView: View {
if showGpuGraph { if showGpuGraph {
graphs.append(SingleGraphData( graphs.append(SingleGraphData(
title: "GPU", title: String(localized: "GPU"),
value: statsManager.gpuUsageString, value: statsManager.gpuUsageString,
data: statsManager.gpuHistory, data: statsManager.gpuHistory,
color: .purple, color: .purple,
@ -112,7 +112,7 @@ struct NotchStatsView: View {
if showNetworkGraph { if showNetworkGraph {
graphs.append(DualGraphData( graphs.append(DualGraphData(
title: "Network", title: String(localized: "Network"),
positiveValue: "↓" + statsManager.networkDownloadString, positiveValue: "↓" + statsManager.networkDownloadString,
negativeValue: "↑" + statsManager.networkUploadString, negativeValue: "↑" + statsManager.networkUploadString,
positiveData: statsManager.networkDownloadHistory, positiveData: statsManager.networkDownloadHistory,
@ -126,9 +126,9 @@ struct NotchStatsView: View {
if showDiskGraph { if showDiskGraph {
graphs.append(DualGraphData( graphs.append(DualGraphData(
title: "Disk", title: String(localized: "Disk"),
positiveValue: "R " + statsManager.diskReadString, positiveValue: String(localized: "R ") + statsManager.diskReadString,
negativeValue: "W " + statsManager.diskWriteString, negativeValue: String(localized: "W ") + statsManager.diskWriteString,
positiveData: statsManager.diskReadHistory, positiveData: statsManager.diskReadHistory,
negativeData: statsManager.diskWriteHistory, negativeData: statsManager.diskWriteHistory,
positiveColor: .cyan, positiveColor: .cyan,

View file

@ -461,7 +461,7 @@ struct NotchTimerView: View {
private var startButton: some View { private var startButton: some View {
Button { Button {
withAnimation(.smooth) { withAnimation(.smooth) {
timerManager.startTimer(duration: customDurationInSeconds, name: "Custom Timer") timerManager.startTimer(duration: customDurationInSeconds, name: String(localized: "Custom Timer"))
if !enableMinimalisticUI { if !enableMinimalisticUI {
coordinator.currentView = .timer coordinator.currentView = .timer
} }

View file

@ -2427,13 +2427,13 @@ private struct DevicesSettingsView: View {
var body: some View { var body: some View {
Form { Form {
Section { Section {
Defaults.Toggle("Show Bluetooth device connections", key: .showBluetoothDeviceConnections) Defaults.Toggle(String(localized: "Show Bluetooth device connections"), key: .showBluetoothDeviceConnections)
.settingsHighlight(id: highlightID("Show Bluetooth device connections")) .settingsHighlight(id: highlightID("Show Bluetooth device connections"))
Defaults.Toggle("Use circular battery indicator", key: .useCircularBluetoothBatteryIndicator) Defaults.Toggle(String(localized: "Use circular battery indicator"), key: .useCircularBluetoothBatteryIndicator)
.settingsHighlight(id: highlightID("Use circular battery indicator")) .settingsHighlight(id: highlightID("Use circular battery indicator"))
Defaults.Toggle("Show battery percentage text in HUD", key: .showBluetoothBatteryPercentageText) Defaults.Toggle(String(localized: "Show battery percentage text in HUD"), key: .showBluetoothBatteryPercentageText)
.settingsHighlight(id: highlightID("Show battery percentage text in HUD")) .settingsHighlight(id: highlightID("Show battery percentage text in HUD"))
Defaults.Toggle("Scroll device name in HUD", key: .showBluetoothDeviceNameMarquee) Defaults.Toggle(String(localized: "Scroll device name in HUD"), key: .showBluetoothDeviceNameMarquee)
.settingsHighlight(id: highlightID("Scroll device name in HUD")) .settingsHighlight(id: highlightID("Scroll device name in HUD"))
} header: { } header: {
Text("Bluetooth Audio Devices") Text("Bluetooth Audio Devices")
@ -2444,7 +2444,7 @@ private struct DevicesSettingsView: View {
} }
Section { Section {
Defaults.Toggle("Color-coded battery display", key: .useColorCodedBatteryDisplay) Defaults.Toggle(String(localized: "Color-coded battery display"), key: .useColorCodedBatteryDisplay)
.disabled(colorCodingDisabled) .disabled(colorCodingDisabled)
.settingsHighlight(id: highlightID("Color-coded battery display")) .settingsHighlight(id: highlightID("Color-coded battery display"))
} header: { } header: {
@ -2643,7 +2643,7 @@ struct Media: View {
Section { Section {
Picker("Music Source", selection: $mediaController) { Picker("Music Source", selection: $mediaController) {
ForEach(availableMediaControllers) { controller in ForEach(availableMediaControllers) { controller in
Text(controller.rawValue).tag(controller) Text(controller.localizedName).tag(controller)
} }
} }
.onChange(of: mediaController) { _, _ in .onChange(of: mediaController) { _, _ in
@ -2672,7 +2672,7 @@ struct Media: View {
} }
} }
Section { Section {
Defaults.Toggle("Show media controls in Dynamic Island", key: .showStandardMediaControls) Defaults.Toggle(String(localized: "Show media controls in Dynamic Island"), key: .showStandardMediaControls)
.disabled(enableMinimalisticUI) .disabled(enableMinimalisticUI)
.settingsHighlight(id: highlightID("Show media controls in Dynamic Island")) .settingsHighlight(id: highlightID("Show media controls in Dynamic Island"))
@ -2696,7 +2696,7 @@ struct Media: View {
} }
} }
if showShuffleAndRepeat { if showShuffleAndRepeat {
Defaults.Toggle("Show \"Change Media Output\" control", key: .showMediaOutputControl) Defaults.Toggle(String(localized: "Show \"Change Media Output\" control"), key: .showMediaOutputControl)
.settingsHighlight(id: highlightID("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.") .help("Adds the AirPlay/route picker button back to the customizable controls palette.")
MusicSlotConfigurationView() MusicSlotConfigurationView()
@ -2711,7 +2711,7 @@ struct Media: View {
} }
Section(header: Text("Lock Screen Media")) { Section(header: Text("Lock Screen Media")) {
Defaults.Toggle("Enable album art parallax", key: .lockScreenMusicAlbumParallaxEnabled) Defaults.Toggle(String(localized: "Enable album art parallax"), key: .lockScreenMusicAlbumParallaxEnabled)
.settingsHighlight(id: highlightID("Enable album art parallax")) .settingsHighlight(id: highlightID("Enable album art parallax"))
Text("Applies the notch-style parallax effect to the lock screen media widget album art.") Text("Applies the notch-style parallax effect to the lock screen media widget album art.")
.font(.caption) .font(.caption)
@ -2741,18 +2741,16 @@ struct Media: View {
) )
.disabled(standardControlsSuppressed) .disabled(standardControlsSuppressed)
.help(standardControlsSuppressed ? "Standard notch media controls are hidden while this toggle is off." : "") .help(standardControlsSuppressed ? "Standard notch media controls are hidden while this toggle is off." : "")
Defaults.Toggle( Defaults.Toggle(String(localized: "Show floating media controls"),
"Show floating media controls", key: .musicControlWindowEnabled)
key: .musicControlWindowEnabled
)
.disabled(!coordinator.musicLiveActivityEnabled || standardControlsSuppressed) .disabled(!coordinator.musicLiveActivityEnabled || standardControlsSuppressed)
.help("Displays play/pause and skip buttons beside the notch while music is active. Disabled by default.") .help("Displays play/pause and skip buttons beside the notch while music is active. Disabled by default.")
Toggle("Enable sneak peek", isOn: $enableSneakPeek) Toggle("Enable sneak peek", isOn: $enableSneakPeek)
Toggle("Show sneak peek on playback changes", isOn: $showSneakPeekOnTrackChange) Toggle("Show sneak peek on playback changes", isOn: $showSneakPeekOnTrackChange)
.disabled(!enableSneakPeek) .disabled(!enableSneakPeek)
Defaults.Toggle("Enable lyrics", key: .enableLyrics) Defaults.Toggle(String(localized: "Enable lyrics"), key: .enableLyrics)
.settingsHighlight(id: highlightID("Enable lyrics")) .settingsHighlight(id: highlightID("Enable lyrics"))
Defaults.Toggle("Enable album art parallax effect", key: .enableParallaxEffect) Defaults.Toggle(String(localized: "Enable album art parallax effect"), key: .enableParallaxEffect)
.settingsHighlight(id: highlightID("Enable album art parallax effect")) .settingsHighlight(id: highlightID("Enable album art parallax effect"))
Picker("Sneak Peek Style", selection: $sneakPeekStyles){ Picker("Sneak Peek Style", selection: $sneakPeekStyles){
ForEach(SneakPeekStyle.allCases) { style in ForEach(SneakPeekStyle.allCases) { style in
@ -2789,10 +2787,10 @@ struct Media: View {
} }
Section { Section {
Defaults.Toggle("Show lock screen media panel", key: .enableLockScreenMediaWidget) Defaults.Toggle(String(localized: "Show lock screen media panel"), key: .enableLockScreenMediaWidget)
Defaults.Toggle("Show media app icon", key: .lockScreenShowAppIcon) Defaults.Toggle(String(localized: "Show media app icon"), key: .lockScreenShowAppIcon)
.disabled(!enableLockScreenMediaWidget) .disabled(!enableLockScreenMediaWidget)
Defaults.Toggle("Show panel border", key: .lockScreenPanelShowsBorder) Defaults.Toggle(String(localized: "Show panel border"), key: .lockScreenPanelShowsBorder)
.disabled(!enableLockScreenMediaWidget) .disabled(!enableLockScreenMediaWidget)
if lockScreenGlassCustomizationMode == .customLiquid { if lockScreenGlassCustomizationMode == .customLiquid {
customLiquidBlurRow customLiquidBlurRow
@ -6346,7 +6344,7 @@ struct ScreenAssistantSettings: View {
} }
} }
.pickerStyle(.menu) .pickerStyle(.menu)
.frame(width: 100) .frame(width: 150)
} }
.settingsHighlight(id: highlightID("Display Mode")) .settingsHighlight(id: highlightID("Display Mode"))
@ -6473,7 +6471,7 @@ struct ColorPickerSettings: View {
} }
} }
.pickerStyle(.menu) .pickerStyle(.menu)
.frame(width: 100) .frame(width: 150)
} }
.settingsHighlight(id: highlightID("Display Mode")) .settingsHighlight(id: highlightID("Display Mode"))
@ -6487,7 +6485,7 @@ struct ColorPickerSettings: View {
Text("20 colors").tag(20) Text("20 colors").tag(20)
} }
.pickerStyle(.menu) .pickerStyle(.menu)
.frame(width: 100) .frame(width: 150)
} }
.settingsHighlight(id: highlightID("History Size")) .settingsHighlight(id: highlightID("History Size"))

View file

@ -100,7 +100,7 @@ struct TimerPopover: View {
let duration = customDurationInSeconds let duration = customDurationInSeconds
guard duration > 0 else { return } guard duration > 0 else { return }
withAnimation(.smooth) { withAnimation(.smooth) {
timerManager.startTimer(duration: duration, name: "Custom Timer") timerManager.startTimer(duration: duration, name: String(localized: "Custom Timer"))
} }
dismiss() dismiss()
} }

View file

@ -81,7 +81,7 @@ class BatteryStatusViewModel: ObservableObject {
print("🔌 Power source: \(isPluggedIn ? "Connected" : "Disconnected")") print("🔌 Power source: \(isPluggedIn ? "Connected" : "Disconnected")")
withAnimation { withAnimation {
self.isPluggedIn = isPluggedIn self.isPluggedIn = isPluggedIn
self.statusText = isPluggedIn ? "Plugged In" : "Unplugged" self.statusText = isPluggedIn ? String(localized: "Plugged In") : String(localized: "Unplugged")
self.notifyImportanChangeStatus() self.notifyImportanChangeStatus()
} }
@ -98,7 +98,7 @@ class BatteryStatusViewModel: ObservableObject {
self.notifyImportanChangeStatus() self.notifyImportanChangeStatus()
withAnimation { withAnimation {
self.isInLowPowerMode = isEnabled self.isInLowPowerMode = isEnabled
self.statusText = "Low Power: \(self.isInLowPowerMode ? "On" : "Off")" self.statusText = String(localized: "Low Power: \(self.isInLowPowerMode ? String(localized: "On") : String(localized: "Off"))")
} }
case .isChargingChanged(let isCharging): case .isChargingChanged(let isCharging):
@ -110,8 +110,8 @@ class BatteryStatusViewModel: ObservableObject {
self.isCharging = isCharging self.isCharging = isCharging
self.statusText = self.statusText =
isCharging isCharging
? "Charging battery" ? String(localized: "Charging battery")
: (self.levelBattery < self.maxCapacity ? "Not charging" : "Full charge") : (self.levelBattery < self.maxCapacity ? String(localized: "Not charging") : String(localized: "Full charge"))
} }
case .timeToFullChargeChanged(let time): case .timeToFullChargeChanged(let time):
@ -141,7 +141,7 @@ class BatteryStatusViewModel: ObservableObject {
self.isInLowPowerMode = batteryInfo.isInLowPowerMode self.isInLowPowerMode = batteryInfo.isInLowPowerMode
self.timeToFullCharge = batteryInfo.timeToFullCharge self.timeToFullCharge = batteryInfo.timeToFullCharge
self.maxCapacity = batteryInfo.maxCapacity self.maxCapacity = batteryInfo.maxCapacity
self.statusText = batteryInfo.isPluggedIn ? "Plugged In" : "Unplugged" self.statusText = batteryInfo.isPluggedIn ? String(localized: "Plugged In") : String(localized: "Unplugged")
} }
} }
@ -161,7 +161,7 @@ class BatteryStatusViewModel: ObservableObject {
for threshold in lowBatteryAlertThresholds { for threshold in lowBatteryAlertThresholds {
if previousLevel >= threshold && newLevel < threshold { if previousLevel >= threshold && newLevel < threshold {
self.statusText = "Low battery" self.statusText = String(localized: "Low battery")
notifyImportanChangeStatus() notifyImportanChangeStatus()
playLowBatteryAlertSound() playLowBatteryAlertSound()
break break

View file

@ -284,15 +284,15 @@ enum ScreenAssistantDisplayMode: String, CaseIterable, Codable, Defaults.Seriali
var displayName: String { var displayName: String {
switch self { switch self {
case .popover: return "Popover" case .popover: return String(localized: "Popover")
case .panel: return "Panel" case .panel: return String(localized: "Panel")
} }
} }
var description: String { var description: String {
switch self { switch self {
case .popover: return "Shows screen assistant as a dropdown attached to the AI button" case .popover: return String(localized: "Shows screen assistant as a dropdown attached to the AI button")
case .panel: return "Shows screen assistant in a floating panel near the notch" case .panel: return String(localized: "Shows screen assistant in a floating panel near the notch")
} }
} }
} }
@ -335,6 +335,15 @@ enum MediaControllerType: String, CaseIterable, Identifiable, Defaults.Serializa
case youtubeMusic = "Youtube Music" case youtubeMusic = "Youtube Music"
var id: String { self.rawValue } var id: String { self.rawValue }
var localizedName: String {
switch self {
case .nowPlaying: return String(localized: "Now Playing")
case .appleMusic: return String(localized: "Apple Music")
case .spotify: return String(localized: "Spotify")
case .youtubeMusic: return String(localized: "Youtube Music")
}
}
} }
// Sneak peek styles for selection in settings // Sneak peek styles for selection in settings