Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions PureMac.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@
F661A0F64CF93E482CB1728F /* DashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = "<group>"; };
F77B9D7D5353CD42BD03ADBB /* ApplicationUpdaterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationUpdaterTests.swift; sourceTree = "<group>"; };
F9DCBD97CC88D77AE8DB01A4 /* PermissionCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionCoordinator.swift; sourceTree = "<group>"; };
A10FC6CEEEB75992ADCA4C01 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
A10FC6CEEEB75992ADCA4C02 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/ServicesMenu.strings"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -683,6 +685,7 @@
25D1CF5248C1D471BCBD212F /* fr */,
6ED5C9C10C69C3143B1F5CA6 /* ru */,
374A3BE46E71E54B6927E9EF /* uk */,
A10FC6CEEEB75992ADCA4C02 /* zh-Hans */,
);
name = ServicesMenu.strings;
sourceTree = "<group>";
Expand All @@ -694,6 +697,7 @@
0A544093C897FBCBA8E256F9 /* fr */,
ED0C4D8912DA94F81B2AEF8F /* ru */,
DFC4A1FD7DB92C82725B4314 /* uk */,
A10FC6CEEEB75992ADCA4C01 /* zh-Hans */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
Expand Down
24 changes: 12 additions & 12 deletions PureMac/Services/ApplicationUpdater.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,19 @@ enum ApplicationUpdaterError: LocalizedError, Equatable {
var errorDescription: String? {
switch self {
case .homebrewNotInstalled:
return "Homebrew was not found in a standard installation location."
return String(localized: "Homebrew was not found in a standard installation location.")
case .invalidResponse:
return "Homebrew returned update information PureMac could not read."
return String(localized: "Homebrew returned update information PureMac could not read.")
case .invalidCaskIdentifier(let token):
return "Homebrew returned an invalid cask identifier: \(token)"
return String(localized: "Homebrew returned an invalid cask identifier: \(token)")
case .noSelection:
return "Select at least one app to update."
return String(localized: "Select at least one app to update.")
case .commandFailed(let message):
return message
case .timedOut:
return "Homebrew did not finish before the operation timed out."
return String(localized: "Homebrew did not finish before the operation timed out.")
case .outputLimitExceeded:
return "Homebrew produced more output than PureMac could safely retain."
return String(localized: "Homebrew produced more output than PureMac could safely retain.")
}
}
}
Expand Down Expand Up @@ -152,17 +152,17 @@ final class ApplicationUpdater: ObservableObject {
selectedTokens.formIntersection(Set(updates.filter { !$0.isPinned }.map(\.token)))
if remaining.isEmpty {
statusMessage = tokens.count == 1
? "Homebrew updated the selected app."
: "Homebrew updated the \(tokens.count) selected apps."
? String(localized: "Homebrew updated the selected app.")
: String(localized: "Homebrew updated the \(tokens.count) selected apps.")
} else {
statusMessage = remaining.count == 1
? "Homebrew finished, but 1 selected app still reports an update."
: "Homebrew finished, but \(remaining.count) selected apps still report updates."
? String(localized: "Homebrew finished, but 1 selected app still reports an update.")
: String(localized: "Homebrew finished, but \(remaining.count) selected apps still report updates.")
}
} catch is CancellationError {
statusMessage = "Homebrew finished. Check again to verify installed versions."
statusMessage = String(localized: "Homebrew finished. Check again to verify installed versions.")
} catch {
statusMessage = "Homebrew finished. Check again to verify installed versions."
statusMessage = String(localized: "Homebrew finished. Check again to verify installed versions.")
errorMessage = Self.displayMessage(for: error)
}
} catch is CancellationError {
Expand Down
26 changes: 13 additions & 13 deletions PureMac/Services/DuplicateFinder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ enum DuplicateSkipReason: String, Hashable, Sendable {

var label: String {
switch self {
case .symbolicLink: return "Symbolic link"
case .package: return "Package contents"
case .cloudPlaceholder: return "Cloud-only file"
case .hardLink: return "Hard-linked copy"
case .inaccessible: return "Access denied"
case .changedDuringScan: return "Changed during scan"
case .unreadable: return "Could not read"
case .symbolicLink: return String(localized: "Symbolic link")
case .package: return String(localized: "Package contents")
case .cloudPlaceholder: return String(localized: "Cloud-only file")
case .hardLink: return String(localized: "Hard-linked copy")
case .inaccessible: return String(localized: "Access denied")
case .changedDuringScan: return String(localized: "Changed during scan")
case .unreadable: return String(localized: "Could not read")
}
}
}
Expand Down Expand Up @@ -128,15 +128,15 @@ enum DuplicateFinderError: LocalizedError, Equatable {
var errorDescription: String? {
switch self {
case .invalidFolder(let path):
return "The selected folder cannot be scanned: \(path)"
return String(localized: "The selected folder cannot be scanned: \(path)")
case .invalidSelection(let path):
return "The protected copy cannot be moved to Trash: \(path)"
return String(localized: "The protected copy cannot be moved to Trash: \(path)")
case .keeperChanged(let path):
return "The copy marked Keep is missing or changed: \(path)"
return String(localized: "The copy marked Keep is missing or changed: \(path)")
case .fileChanged(let path):
return "A selected file is missing or changed: \(path)"
return String(localized: "A selected file is missing or changed: \(path)")
case .contentChanged(let path):
return "A selected file no longer matches its kept copy: \(path)"
return String(localized: "A selected file no longer matches its kept copy: \(path)")
}
}
}
Expand Down Expand Up @@ -241,7 +241,7 @@ actor DuplicateFinder {
skipped.append(DuplicateSkippedFile(
url: normalizedURL,
reason: .hardLink,
detail: "Shares storage with \(firstURL.lastPathComponent)"
detail: String(localized: "Shares storage with \(firstURL.lastPathComponent)")
))
} else {
seenPhysicalFiles[identity.physicalID] = normalizedURL
Expand Down
46 changes: 23 additions & 23 deletions PureMac/Services/PerformanceInspector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ enum PerformanceStartupDomain: String, CaseIterable, Hashable, Sendable {

var title: String {
switch self {
case .userAgent: return "User agent"
case .systemAgent: return "System-wide agent"
case .systemDaemon: return "System daemon"
case .userAgent: return String(localized: "User agent")
case .systemAgent: return String(localized: "System-wide agent")
case .systemDaemon: return String(localized: "System daemon")
}
}
}
Expand All @@ -35,9 +35,9 @@ struct PerformanceSnapshot: Identifiable, Hashable, Sendable {

var title: String {
switch self {
case .timeMachine: return "Time Machine"
case .systemUpdate: return "System update"
case .local: return "Local snapshot"
case .timeMachine: return String(localized: "Time Machine")
case .systemUpdate: return String(localized: "System update")
case .local: return String(localized: "Local snapshot")
}
}
}
Expand Down Expand Up @@ -71,14 +71,14 @@ enum PerformanceInspectorError: LocalizedError, Equatable {
var errorDescription: String? {
switch self {
case .invalidPropertyList:
return "The launch item is not a property-list dictionary."
return String(localized: "The launch item is not a property-list dictionary.")
case .invalidSnapshotIdentifier:
return "This snapshot does not have a verified Time Machine timestamp."
return String(localized: "This snapshot does not have a verified Time Machine timestamp.")
case .timedOut(let command):
return "\(command) did not finish within the allowed time."
return String(localized: "\(command) did not finish within the allowed time.")
case .commandFailed(let command, let status, let detail):
let suffix = detail.isEmpty ? "" : ": \(detail)"
return "\(command) exited with status \(status)\(suffix)"
return String(localized: "\(command) exited with status \(status)\(suffix)")
}
}
}
Expand Down Expand Up @@ -162,29 +162,29 @@ final class PerformanceInspector {

var triggers: [String] = []
if bool(dictionary["RunAtLoad"]) == true {
triggers.append("At login")
triggers.append(String(localized: "At login"))
}
if let keepAlive = dictionary["KeepAlive"] {
if bool(keepAlive) == true {
triggers.append("Keep alive")
triggers.append(String(localized: "Keep alive"))
} else if let conditions = keepAlive as? [String: Any], !conditions.isEmpty {
triggers.append("Conditional keep alive")
triggers.append(String(localized: "Conditional keep alive"))
}
}
if let interval = number(dictionary["StartInterval"]), interval.intValue > 0 {
triggers.append("Every \(interval.intValue) seconds")
triggers.append(String(localized: "Every \(interval.intValue) seconds"))
}
if dictionary["StartCalendarInterval"] != nil {
triggers.append("Scheduled")
triggers.append(String(localized: "Scheduled"))
}
if !stringArray(dictionary["WatchPaths"]).isEmpty {
triggers.append("Watches paths")
triggers.append(String(localized: "Watches paths"))
}
if !stringArray(dictionary["QueueDirectories"]).isEmpty {
triggers.append("Watches folders")
triggers.append(String(localized: "Watches folders"))
}
if bool(dictionary["NetworkState"]) == true {
triggers.append("Network state")
triggers.append(String(localized: "Network state"))
}

return PerformanceStartupItem(
Expand Down Expand Up @@ -265,7 +265,7 @@ final class PerformanceInspector {
} catch {
issues.append(
PerformanceInspectionIssue(
title: "Could not read \(directory.path)",
title: String(localized: "Could not read \(directory.path)"),
detail: error.localizedDescription
)
)
Expand All @@ -282,7 +282,7 @@ final class PerformanceInspector {
} catch {
issues.append(
PerformanceInspectionIssue(
title: "Could not inspect \(file.lastPathComponent)",
title: String(localized: "Could not inspect \(file.lastPathComponent)"),
detail: error.localizedDescription
)
)
Expand Down Expand Up @@ -311,8 +311,8 @@ final class PerformanceInspector {
return (
[],
[PerformanceInspectionIssue(
title: "Time Machine snapshots are unavailable",
detail: "tmutil is not available on this Mac."
title: String(localized: "Time Machine snapshots are unavailable"),
detail: String(localized: "tmutil is not available on this Mac.")
)]
)
}
Expand All @@ -337,7 +337,7 @@ final class PerformanceInspector {
return (
[],
[PerformanceInspectionIssue(
title: "Could not list Time Machine snapshots",
title: String(localized: "Could not list Time Machine snapshots"),
detail: error.localizedDescription
)]
)
Expand Down
30 changes: 15 additions & 15 deletions PureMac/Services/ProtectionAudit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ struct ProtectionAudit: Sendable {
return ProtectionFinding(
check: check,
status: .unknown,
detail: result.timedOut ? "The check timed out." : "PureMac could not read this setting.",
detail: result.timedOut ? String(localized: "The check timed out.") : String(localized: "PureMac could not read this setting."),
version: nil
)
}
Expand All @@ -334,31 +334,31 @@ struct ProtectionAudit: Sendable {
switch check {
case .gatekeeper:
if output.contains("assessments enabled") {
return finding(check, .enabled, "App downloads are checked before opening.")
return finding(check, .enabled, String(localized: "App downloads are checked before opening."))
}
if output.contains("assessments disabled") {
return finding(check, .disabled, "Downloaded apps are not being assessed by Gatekeeper.")
return finding(check, .disabled, String(localized: "Downloaded apps are not being assessed by Gatekeeper."))
}
case .fileVault:
if output.contains("filevault is on") {
return finding(check, .enabled, "The startup disk is encrypted with FileVault.")
return finding(check, .enabled, String(localized: "The startup disk is encrypted with FileVault."))
}
if output.contains("filevault is off") {
return finding(check, .disabled, "The startup disk is not encrypted with FileVault.")
return finding(check, .disabled, String(localized: "The startup disk is not encrypted with FileVault."))
}
case .firewall:
if output.contains("firewall is enabled") || output.contains("state = 1") {
return finding(check, .enabled, "Incoming network connections are filtered.")
return finding(check, .enabled, String(localized: "Incoming network connections are filtered."))
}
if output.contains("firewall is disabled") || output.contains("state = 0") {
return finding(check, .disabled, "The macOS application firewall is turned off.")
return finding(check, .disabled, String(localized: "The macOS application firewall is turned off."))
}
case .systemIntegrityProtection:
if output.contains("system integrity protection status: enabled") {
return finding(check, .enabled, "Protected system locations and processes are restricted.")
return finding(check, .enabled, String(localized: "Protected system locations and processes are restricted."))
}
if output.contains("system integrity protection status: disabled") {
return finding(check, .disabled, "System Integrity Protection is turned off.")
return finding(check, .disabled, String(localized: "System Integrity Protection is turned off."))
}
case .xProtect:
break
Expand All @@ -367,7 +367,7 @@ struct ProtectionAudit: Sendable {
return ProtectionFinding(
check: check,
status: .unknown,
detail: "The system returned an unrecognized status.",
detail: String(localized: "The system returned an unrecognized status."),
version: nil
)
}
Expand All @@ -380,20 +380,20 @@ struct ProtectionAudit: Sendable {
return ProtectionFinding(
check: .xProtect,
status: .unknown,
detail: "PureMac could not confirm the local XProtect installation.",
detail: String(localized: "PureMac could not confirm the local XProtect installation."),
version: nil
)
}

let parts = [
engine.map { "engine \($0)" },
definitions.map { "definitions \($0)" }
engine.map { String(localized: "engine \($0)") },
definitions.map { String(localized: "definitions \($0)") }
].compactMap { $0 }
let version = parts.joined(separator: ", ")
let version = parts.joined(separator: String(localized: ", "))
return ProtectionFinding(
check: .xProtect,
status: .enabled,
detail: "Apple's built-in malware protection is installed. Update recency is managed by macOS.",
detail: String(localized: "Apple's built-in malware protection is installed. Update recency is managed by macOS."),
version: version
)
}
Expand Down
2 changes: 1 addition & 1 deletion PureMac/Services/SimilarPhotoFinder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ enum SimilarPhotoFinderError: LocalizedError, Equatable {
var errorDescription: String? {
switch self {
case .invalidFolder(let path):
return "The selected folder cannot be scanned: \(path)"
return String(localized: "The selected folder cannot be scanned: \(path)")
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions PureMac/Services/StorageExplorer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ enum StorageExplorerError: LocalizedError, Equatable {
var errorDescription: String? {
switch self {
case .notDirectory(let path):
return "The selected item is not a folder: \(path)"
return String(localized: "The selected item is not a folder: \(path)")
case .symbolicLink(let path):
return "Symbolic links cannot be scanned: \(path)"
return String(localized: "Symbolic links cannot be scanned: \(path)")
case .package(let path):
return "App and document packages are excluded: \(path)"
return String(localized: "App and document packages are excluded: \(path)")
case .cloudOnly(let path):
return "The selected folder is stored in the cloud and has not been downloaded: \(path)"
return String(localized: "The selected folder is stored in the cloud and has not been downloaded: \(path)")
case .unavailable(let path):
return "The selected folder could not be read: \(path)"
return String(localized: "The selected folder could not be read: \(path)")
}
}
}
Expand Down
Loading