diff --git a/PureMac/Logic/Utilities/SimulatorRuntimeSupport.swift b/PureMac/Logic/Utilities/SimulatorRuntimeSupport.swift index 07017e4..64104ff 100644 --- a/PureMac/Logic/Utilities/SimulatorRuntimeSupport.swift +++ b/PureMac/Logic/Utilities/SimulatorRuntimeSupport.swift @@ -7,7 +7,7 @@ enum SimulatorRuntimeSupport { static let xcrunPath = "/usr/bin/xcrun" static let xcodeSelectPath = "/usr/bin/xcode-select" static let missingXcrunMessage = - "Xcode command-line tools not found — install Xcode or run xcode-select --install" + String(localized: "Xcode command-line tools not found — install Xcode or run xcode-select --install") struct RuntimeInfo: Equatable { let identifier: String @@ -57,7 +57,7 @@ enum SimulatorRuntimeSupport { var parts: [String] = [] if let platform { parts.append(platform) } if let version { parts.append(version) } - let base = parts.isEmpty ? "Simulator Runtime" : parts.joined(separator: " ") + let base = parts.isEmpty ? String(localized: "Simulator runtime") : parts.joined(separator: " ") if let build { return "\(base) (\(build))" } return base }() diff --git a/PureMac/Logic/Utilities/XcodeBuildMCPDerivedDataSupport.swift b/PureMac/Logic/Utilities/XcodeBuildMCPDerivedDataSupport.swift index 47712ae..f4cb150 100644 --- a/PureMac/Logic/Utilities/XcodeBuildMCPDerivedDataSupport.swift +++ b/PureMac/Logic/Utilities/XcodeBuildMCPDerivedDataSupport.swift @@ -229,7 +229,7 @@ enum XcodeBuildMCPDerivedDataSupport { let tool = root.lastPathComponent let legacyDerivedData = root.appendingPathComponent("DerivedData", isDirectory: true) if isRealDirectory(legacyDerivedData, fileManager: fileManager) { - results.append((name: "\(tool): Legacy DerivedData", url: legacyDerivedData)) + results.append((name: String(localized: "\(tool): Legacy DerivedData"), url: legacyDerivedData)) } let workspacesRoot = root.appendingPathComponent("workspaces", isDirectory: true) diff --git a/PureMac/Models/ScanError.swift b/PureMac/Models/ScanError.swift index 2dc567f..ae1a8db 100644 --- a/PureMac/Models/ScanError.swift +++ b/PureMac/Models/ScanError.swift @@ -11,17 +11,17 @@ enum ScanError: LocalizedError { var errorDescription: String? { switch self { case .permissionDenied(let path): - return "Permission denied when accessing '\(path)'. Grant Full Disk Access in System Settings." + return String(localized: "Permission denied when accessing '\(path)'. Grant Full Disk Access in System Settings.") case .directoryEnumerationFailed(let path, let underlying): - return "Failed to enumerate directory '\(path)': \(underlying.localizedDescription)" + return String(localized: "Failed to enumerate directory '\(path)': \(underlying.localizedDescription)") case .processExecutionFailed(let tool, let underlying): - return "Failed to execute '\(tool)': \(underlying.localizedDescription)" + return String(localized: "Failed to execute '\(tool)': \(underlying.localizedDescription)") case .invalidData(let context): - return "Invalid data encountered: \(context)" + return String(localized: "Invalid data encountered: \(context)") case .helperToolUnavailable: - return "The privileged helper tool is not installed or unavailable." + return String(localized: "The privileged helper tool is not installed or unavailable.") case .operationCancelled: - return "The operation was cancelled." + return String(localized: "The operation was cancelled.") } } } diff --git a/PureMac/Services/ApplicationUpdater.swift b/PureMac/Services/ApplicationUpdater.swift index 0fe5e1a..124125f 100644 --- a/PureMac/Services/ApplicationUpdater.swift +++ b/PureMac/Services/ApplicationUpdater.swift @@ -295,7 +295,7 @@ final class ApplicationUpdater: ObservableObject { let stdout = String(decoding: output.stdout, as: UTF8.self) .trimmingCharacters(in: .whitespacesAndNewlines) let detail = stderr.isEmpty ? stdout : stderr - return detail.isEmpty ? "Homebrew exited with status \(output.status)." : detail + return detail.isEmpty ? String(localized: "Homebrew exited with status \(output.status).") : detail } nonisolated private static func fetchUpdates(using brewURL: URL) async throws -> [ManagedAppUpdate] { diff --git a/PureMac/Services/BinaryThinner.swift b/PureMac/Services/BinaryThinner.swift index 351efa1..52868b8 100644 --- a/PureMac/Services/BinaryThinner.swift +++ b/PureMac/Services/BinaryThinner.swift @@ -68,21 +68,21 @@ actor BinaryThinner { var errorDescription: String? { switch self { case .needsAdmin(let path): - return "Not writable by current user: \(path)" + return String(localized: "Not writable by current user: \(path)") case .restrictedEntitlements(let app): - return "Cannot modify \(app): its entitlements require the original developer signature" + return String(localized: "Cannot modify \(app): its entitlements require the original developer signature") case .signatureProtected(let app): - return "Cannot thin \(app): it is signed and notarized, and stripping a slice would break its signature" + return String(localized: "Cannot thin \(app): it is signed and notarized, and stripping a slice would break its signature") case .lipoFailed(let path, let detail): - return "lipo failed for \(path): \(detail)" + return String(localized: "lipo failed for \(path): \(detail)") case .swapFailed(let path, let detail): - return "Could not swap modified bundle into place at \(path): \(detail)" + return String(localized: "Could not swap modified bundle into place at \(path): \(detail)") case .codesignFailed(let app, let detail): - return "Re-signing failed for \(app): \(detail)" + return String(localized: "Re-signing failed for \(app): \(detail)") case .verificationFailed(let app, let detail): - return "Signature verification failed for \(app): \(detail)" + return String(localized: "Signature verification failed for \(app): \(detail)") case .nothingToThin(let app): - return "No removable slices in \(app)" + return String(localized: "No removable slices in \(app)") } } } @@ -125,7 +125,7 @@ actor BinaryThinner { let app = (appPath as NSString).standardizingPath let lproj = (lprojPath as NSString).standardizingPath guard lproj.hasPrefix(app + "/") else { - return .failure(ThinningError.swapFailed(lprojPath, "localization is outside the app bundle")) + return .failure(ThinningError.swapFailed(lprojPath, String(localized: "localization is outside the app bundle"))) } let appName = ((app as NSString).lastPathComponent as NSString).deletingPathExtension @@ -199,7 +199,7 @@ actor BinaryThinner { try fileManager.copyItem(atPath: appPath, toPath: stagingPath) } catch { try? fileManager.removeItem(atPath: stagingPath) - return .failure(ThinningError.swapFailed(appPath, "could not stage a working copy: \(error.localizedDescription)")) + return .failure(ThinningError.swapFailed(appPath, String(localized: "could not stage a working copy: \(error.localizedDescription)"))) } let freedBytes: Int64 @@ -275,7 +275,7 @@ actor BinaryThinner { try fileManager.moveItem(atPath: oldPath, toPath: appPath) } catch { Logger.shared.log("Could not undo bundle swap for \(appPath); original remains at \(oldPath): \(error.localizedDescription)", level: .error) - return .failure(ThinningError.swapFailed(appPath, "original bundle left at \(oldPath)")) + return .failure(ThinningError.swapFailed(appPath, String(localized: "original bundle left at \(oldPath)"))) } return .failure(ThinningError.needsAdmin(appPath)) } @@ -288,7 +288,7 @@ actor BinaryThinner { private func thinBinary(_ binary: FatBinary, appPath: String, stagedPath: String) throws -> Int64 { let binaryPath = (binary.path as NSString).standardizingPath guard binaryPath.hasPrefix(appPath + "/") else { - throw ThinningError.lipoFailed(binary.path, "binary is outside the app bundle") + throw ThinningError.lipoFailed(binary.path, String(localized: "binary is outside the app bundle")) } let stagedBinary = stagedPath + String(binaryPath.dropFirst(appPath.count)) diff --git a/PureMac/Services/CleaningEngine.swift b/PureMac/Services/CleaningEngine.swift index 5e338b3..6f08079 100644 --- a/PureMac/Services/CleaningEngine.swift +++ b/PureMac/Services/CleaningEngine.swift @@ -43,7 +43,7 @@ actor CleaningEngine { let progress = Double(index + 1) / Double(total) defer { progressHandler(progress) } if CleanupExclusions.excludes(item.path, paths: exclusions) { - result.errors.append("Excluded from cleanup: \(item.name)") + result.errors.append(String(localized: "Excluded from cleanup: \(item.name)")) continue } @@ -126,7 +126,7 @@ actor CleaningEngine { do { let itemURL = URL(fileURLWithPath: item.path) guard !hasUnexpectedSymlink(in: item.path) else { - let msg = "Skipped symlink or unsafe path: \(item.path)" + let msg = String(localized: "Skipped symlink or unsafe path: \(item.path)") Logger.shared.log(msg, level: .warning) result.errors.append(msg) continue @@ -145,7 +145,7 @@ actor CleaningEngine { // the staged re-sign flow, not the delete path. let pathAccepted = isSafeToDelete(item: item, resolvedPath: resolved) guard pathAccepted else { - let msg = "Skipped symlink or unsafe path: \(item.path) -> \(resolved)" + let msg = String(localized: "Skipped symlink or unsafe path: \(item.path) -> \(resolved)") Logger.shared.log(msg, level: .warning) result.errors.append(msg) continue @@ -156,7 +156,7 @@ actor CleaningEngine { // swap between check and delete aborts the operation. let reResolved = itemURL.resolvingSymlinksInPath().path guard reResolved == resolved, !hasUnexpectedSymlink(in: item.path) else { - let msg = "Aborting delete: path resolution changed between check and unlink for \(item.path)" + let msg = String(localized: "Aborting delete: path resolution changed between check and unlink for \(item.path)") Logger.shared.log(msg, level: .warning) result.errors.append(msg) continue @@ -183,7 +183,7 @@ actor CleaningEngine { homeDirectory: homeDirectory, fileManager: fileManager ) else { - let detail = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: \(item.path)" + let detail = String(localized: "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: \(item.path)") Logger.shared.log(detail, level: .warning) result.errors.append(detail) continue @@ -224,7 +224,7 @@ actor CleaningEngine { item.path, homeDirectory: homeDirectory ) { - let detail = "Refusing administrator escalation for XcodeBuildMCP DerivedData: \(item.path)" + let detail = String(localized: "Refusing administrator escalation for XcodeBuildMCP DerivedData: \(item.path)") result.errors.append(detail) Logger.shared.log(detail, level: .warning) continue @@ -242,7 +242,7 @@ actor CleaningEngine { result.requiresAdmin.append(item) Logger.shared.log("Deferring to admin pass: \(item.path)", level: .info) } else { - let detail = "\(item.name) at \(item.path): \(error.localizedDescription)" + let detail = String(localized: "\(item.name) at \(item.path): \(error.localizedDescription)") result.errors.append(detail) Logger.shared.log("Clean failed: \(detail)", level: .error) } @@ -389,7 +389,7 @@ actor CleaningEngine { result.freedSpace += item.size Logger.shared.log("Deleted and recreated by its daemon (live log): \(item.path)", level: .info) } else { - let detail = "\(item.name) at \(item.path) survived admin removal" + let detail = String(localized: "\(item.name) at \(item.path) survived admin removal") result.errors.append(detail) Logger.shared.log("Admin pass survivor: \(detail)", level: .error) } @@ -410,7 +410,7 @@ actor CleaningEngine { func pruneDockerSystem() async -> (freed: Int64, error: String?) { let dockerBinPaths = ["/usr/local/bin/docker", "/opt/homebrew/bin/docker"] guard let dockerBin = dockerBinPaths.first(where: { fileManager.isExecutableFile(atPath: $0) }) else { - return (0, "Docker CLI not found — install Docker Desktop or OrbStack") + return (0, String(localized: "Docker CLI not found — install Docker Desktop or OrbStack")) } let task = Process() @@ -424,7 +424,7 @@ actor CleaningEngine { try task.run() } catch { Logger.shared.log("docker system prune failed to launch: \(error.localizedDescription)", level: .error) - return (0, "Couldn't run docker system prune: \(error.localizedDescription)") + return (0, String(localized: "Couldn't run docker system prune: \(error.localizedDescription)")) } // Drain both pipes BEFORE waiting: prune lists every deleted object // and can overflow the 64 KB pipe buffer, deadlocking waitUntilExit. @@ -436,10 +436,10 @@ actor CleaningEngine { let stderrText = String(data: errData, encoding: .utf8) ?? "" if stderrText.contains("Cannot connect") || stderrText.contains("dial unix") || stderrText.contains("daemon") { - return (0, "Docker isn't running — start Docker Desktop or OrbStack, then clean again") + return (0, String(localized: "Docker isn't running — start Docker Desktop or OrbStack, then clean again")) } Logger.shared.log("docker system prune exited \(task.terminationStatus): \(stderrText)", level: .error) - return (0, "docker system prune failed (exit \(task.terminationStatus))") + return (0, String(localized: "docker system prune failed (exit \(task.terminationStatus))")) } // Final line reads "Total reclaimed space: 1.234GB" ("0B" when idle). @@ -473,10 +473,10 @@ actor CleaningEngine { } if detail.localizedCaseInsensitiveContains("CoreSimulator") || detail.localizedCaseInsensitiveContains("simdiskimaged") { - return (0, false, "Simulator services unavailable — open Xcode once, then try again") + return (0, false, String(localized: "Simulator services unavailable — open Xcode once, then try again")) } Logger.shared.log("simctl runtime delete \(identifier) exited \(result.status): \(detail)", level: .error) - return (0, false, "Couldn't delete simulator runtime \(identifier)") + return (0, false, String(localized: "Couldn't delete simulator runtime \(identifier)")) } Logger.shared.log("Deleted simulator runtime \(identifier) (~\(reportedSize) bytes)", level: .info) @@ -722,7 +722,7 @@ actor CleaningEngine { let resolved = URL(fileURLWithPath: item.path).resolvingSymlinksInPath().path guard isAppBundlePath((resolved as NSString).standardizingPath, rootedAt: "/Applications") || isAppBundlePath((resolved as NSString).standardizingPath, rootedAt: "\(fileManager.homeDirectoryForCurrentUser.path)/Applications") else { - let msg = "Skipped unsafe path for thinning: \(item.path) -> \(resolved)" + let msg = String(localized: "Skipped unsafe path for thinning: \(item.path) -> \(resolved)") Logger.shared.log(msg, level: .warning) return (0, false, msg) } @@ -739,11 +739,11 @@ actor CleaningEngine { return (freed, true, nil) case .failure(let error): if case BinaryThinner.ThinningError.needsAdmin = error { - let msg = "\(item.name): needs administrator access to thin; skipped" + let msg = String(localized: "\(item.name): needs administrator access to thin; skipped") Logger.shared.log(msg, level: .warning) return (0, false, msg) } - let msg = "Couldn't thin \(item.name): \(error.localizedDescription)" + let msg = String(localized: "Couldn't thin \(item.name): \(error.localizedDescription)") Logger.shared.log(msg, level: .error) return (0, false, msg) } @@ -758,7 +758,7 @@ actor CleaningEngine { let resolved = URL(fileURLWithPath: item.path).resolvingSymlinksInPath().path let normalized = (resolved as NSString).standardizingPath guard isRemovableLprojPath(normalized) else { - let msg = "Skipped symlink or unsafe path: \(item.path) -> \(resolved)" + let msg = String(localized: "Skipped symlink or unsafe path: \(item.path) -> \(resolved)") Logger.shared.log(msg, level: .warning) return (0, false, msg) } @@ -779,11 +779,11 @@ actor CleaningEngine { return (item.size, true, nil) case .failure(let error): if case BinaryThinner.ThinningError.needsAdmin = error { - let msg = "\(item.name): needs administrator access to modify the app; skipped" + let msg = String(localized: "\(item.name): needs administrator access to modify the app; skipped") Logger.shared.log(msg, level: .warning) return (0, false, msg) } - let msg = "Couldn't remove localization \(item.name): \(error.localizedDescription)" + let msg = String(localized: "Couldn't remove localization \(item.name): \(error.localizedDescription)") Logger.shared.log(msg, level: .error) return (0, false, msg) } diff --git a/PureMac/Services/DuplicateFinder.swift b/PureMac/Services/DuplicateFinder.swift index 2a8334c..226bd0e 100644 --- a/PureMac/Services/DuplicateFinder.swift +++ b/PureMac/Services/DuplicateFinder.swift @@ -357,7 +357,7 @@ actor DuplicateFinder { } let selectedFiles = group.files.filter { selectedIDs.contains($0.url) } guard selectedFiles.count == selectedIDs.count else { - let unknownPath = selectedIDs.first { !group.contains($0) }?.path ?? "Unknown file" + let unknownPath = selectedIDs.first { !group.contains($0) }?.path ?? String(localized: "Unknown file") throw DuplicateFinderError.invalidSelection(unknownPath) } guard !selectedFiles.isEmpty else { diff --git a/PureMac/Services/PerformanceInspector.swift b/PureMac/Services/PerformanceInspector.swift index 91ecfab..d7f732f 100644 --- a/PureMac/Services/PerformanceInspector.swift +++ b/PureMac/Services/PerformanceInspector.swift @@ -122,7 +122,7 @@ final class PerformanceInspector { } let executable = URL(fileURLWithPath: "/usr/bin/tmutil") guard FileManager.default.isExecutableFile(atPath: executable.path) else { - throw PerformanceInspectorError.commandFailed("tmutil deletelocalsnapshots", -1, "tmutil is unavailable") + throw PerformanceInspectorError.commandFailed("tmutil deletelocalsnapshots", -1, String(localized: "tmutil is not available on this Mac.")) } let result = try await Self.runCommand( executable: executable, diff --git a/PureMac/Services/ScanEngine.swift b/PureMac/Services/ScanEngine.swift index 67e47b9..321e919 100644 --- a/PureMac/Services/ScanEngine.swift +++ b/PureMac/Services/ScanEngine.swift @@ -206,7 +206,7 @@ actor ScanEngine { let resolvedCachePath = URL(fileURLWithPath: cachePath).resolvingSymlinksInPath().path guard normalizePath(resolvedCachePath) == normalizePath(cachePath) else { continue } if let item = makeCleanupItem( - name: "\(container) (sandbox cache)", + name: String(localized: "\(container) (sandbox cache)"), path: cachePath, category: .userCache, minimumSize: 1024 * 1024 @@ -416,7 +416,7 @@ actor ScanEngine { // the row shows no misleading filesystem location and the reveal- // in-Finder action stays disabled for this entry. (See issue #112.) items.append(CleanableItem( - name: "APFS Purgeable Space", + name: String(localized: "APFS purgeable disk space"), path: "", size: diskInfo.purgeableSpace, category: .purgeableSpace, @@ -432,7 +432,7 @@ actor ScanEngine { let snapshotSize = snapshot.size > 0 ? snapshot.size : 0 if snapshotSize > 0 { items.append(CleanableItem( - name: "TM Snapshot: \(snapshot.name)", + name: String(localized: "Time Machine snapshot: \(snapshot.name)"), path: snapshot.name, size: snapshotSize, category: .purgeableSpace, diff --git a/PureMac/ar.lproj/Localizable.strings b/PureMac/ar.lproj/Localizable.strings index 7e00d96..050fa2d 100644 --- a/PureMac/ar.lproj/Localizable.strings +++ b/PureMac/ar.lproj/Localizable.strings @@ -927,3 +927,48 @@ "View releases" = "View releases"; "You're using the latest available version of PureMac." = "You're using the latest available version of PureMac."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMac/de.lproj/Localizable.strings b/PureMac/de.lproj/Localizable.strings index dae6e57..5764927 100644 --- a/PureMac/de.lproj/Localizable.strings +++ b/PureMac/de.lproj/Localizable.strings @@ -930,3 +930,48 @@ "Some processes started or stopped since the last sample. Their values appear in the next one." = "Some processes started or stopped since the last sample. Their values appear in the next one."; "Sort by" = "Sort by"; "System processes are not listed. Use Activity Monitor for those." = "System processes are not listed. Use Activity Monitor for those."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMac/en.lproj/Localizable.strings b/PureMac/en.lproj/Localizable.strings index 1d04ffb..68f4989 100644 --- a/PureMac/en.lproj/Localizable.strings +++ b/PureMac/en.lproj/Localizable.strings @@ -927,3 +927,48 @@ "View releases" = "View releases"; "You're using the latest available version of PureMac." = "You're using the latest available version of PureMac."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMac/es.lproj/Localizable.strings b/PureMac/es.lproj/Localizable.strings index dde65fa..41a4b6b 100644 --- a/PureMac/es.lproj/Localizable.strings +++ b/PureMac/es.lproj/Localizable.strings @@ -927,3 +927,48 @@ "View releases" = "View releases"; "You're using the latest available version of PureMac." = "You're using the latest available version of PureMac."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMac/fr.lproj/Localizable.strings b/PureMac/fr.lproj/Localizable.strings index 1eb3f03..5e45052 100644 --- a/PureMac/fr.lproj/Localizable.strings +++ b/PureMac/fr.lproj/Localizable.strings @@ -923,3 +923,48 @@ "View releases" = "View releases"; "You're using the latest available version of PureMac." = "You're using the latest available version of PureMac."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMac/it.lproj/Localizable.strings b/PureMac/it.lproj/Localizable.strings index 574d5f9..b71afea 100644 --- a/PureMac/it.lproj/Localizable.strings +++ b/PureMac/it.lproj/Localizable.strings @@ -927,3 +927,48 @@ "View releases" = "Visualizza versioni"; "You're using the latest available version of PureMac." = "Stai usando l'ultima versione disponibile di PureMac."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMac/ja.lproj/Localizable.strings b/PureMac/ja.lproj/Localizable.strings index 9fd6daf..30f9963 100644 --- a/PureMac/ja.lproj/Localizable.strings +++ b/PureMac/ja.lproj/Localizable.strings @@ -927,3 +927,48 @@ "View releases" = "View releases"; "You're using the latest available version of PureMac." = "You're using the latest available version of PureMac."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMac/pl.lproj/Localizable.strings b/PureMac/pl.lproj/Localizable.strings index 34bdb31..b24c2e5 100644 --- a/PureMac/pl.lproj/Localizable.strings +++ b/PureMac/pl.lproj/Localizable.strings @@ -927,3 +927,48 @@ "View releases" = "View releases"; "You're using the latest available version of PureMac." = "You're using the latest available version of PureMac."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMac/pt-BR.lproj/Localizable.strings b/PureMac/pt-BR.lproj/Localizable.strings index abd841e..4cfc8e8 100644 --- a/PureMac/pt-BR.lproj/Localizable.strings +++ b/PureMac/pt-BR.lproj/Localizable.strings @@ -927,3 +927,48 @@ "View releases" = "View releases"; "You're using the latest available version of PureMac." = "You're using the latest available version of PureMac."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMac/ru.lproj/Localizable.strings b/PureMac/ru.lproj/Localizable.strings index a14025e..a350c24 100644 --- a/PureMac/ru.lproj/Localizable.strings +++ b/PureMac/ru.lproj/Localizable.strings @@ -927,3 +927,48 @@ "View releases" = "View releases"; "You're using the latest available version of PureMac." = "You're using the latest available version of PureMac."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMac/uk.lproj/Localizable.strings b/PureMac/uk.lproj/Localizable.strings index c1b5e02..4c89f38 100644 --- a/PureMac/uk.lproj/Localizable.strings +++ b/PureMac/uk.lproj/Localizable.strings @@ -927,3 +927,48 @@ "View releases" = "View releases"; "You're using the latest available version of PureMac." = "You're using the latest available version of PureMac."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMac/zh-Hans.lproj/Localizable.strings b/PureMac/zh-Hans.lproj/Localizable.strings index bbc1775..928dbf2 100644 --- a/PureMac/zh-Hans.lproj/Localizable.strings +++ b/PureMac/zh-Hans.lproj/Localizable.strings @@ -9,7 +9,7 @@ "Ready to clean" = "可以清理"; "Limited access" = "访问受限"; "Full Disk Access granted" = "已授予完全磁盘访问权限"; -"Grant FDA in Settings" = "在系统设置中授予权限"; +"Grant FDA in Settings" = "在系统设置中授予完全磁盘访问权限"; "Select a category from the sidebar to get started." = "从侧边栏选择一个类别开始使用。"; /* FDA toast + clean error alert (MainWindow) */ @@ -21,21 +21,21 @@ "Grant Access" = "授予权限"; /* Dashboard */ -"Storage" = "存储"; +"Storage" = "存储空间"; "Smart Scan" = "智能扫描"; "free of %@" = "%@ 中的可用空间"; "%lld%% used" = "已使用 %lld%%"; "Used" = "已用"; -"Junk" = "垃圾"; +"Junk" = "垃圾文件"; "Purgeable" = "可清除"; "USED" = "已用"; "SCANNING" = "扫描中"; "Free Space" = "可用空间"; -"Junk Found" = "发现的垃圾"; +"Junk Found" = "发现的垃圾文件"; "Apps" = "应用"; "of %@ · %lld%% used" = "总计 %@ · 已使用 %lld%%"; "Run a scan" = "运行扫描"; -"across %lld categories" = "跨 %lld 个类别"; +"across %lld categories" = "涉及 %lld 个类别"; "installed" = "已安装"; "APFS reclaimable" = "APFS 可回收空间"; "Suggested for you" = "推荐"; @@ -47,14 +47,14 @@ "Without it, most caches and uninstall flows fail." = "缺少权限时,部分缓存和应用文件可能无法处理。"; "Action" = "操作"; "Scanning your Mac" = "正在扫描你的 Mac"; -"Currently in: %@" = "当前:%@"; +"Currently in: %@" = "当前位置:%@"; "found" = "已发现"; "Your Mac is clean" = "你的 Mac 很干净"; "Scan Again" = "重新扫描"; "Clean %@" = "清理 %@"; -"Clean %@?" = "清理 %@?"; +"Clean %@?" = "清理 %@?"; "Cleaning…" = "正在清理…"; -"%lld%% complete" = "%lld%% 完成"; +"%lld%% complete" = "已完成 %lld%%"; "freed" = "已释放"; "Done" = "完成"; "%lld items" = "%lld 项"; @@ -70,19 +70,19 @@ "Not Scanned" = "未扫描"; "Run a scan to analyze this category." = "运行扫描以分析此类别。"; "Scan Now" = "立即扫描"; -"Filter files" = "过滤文件"; +"Filter files" = "筛选文件"; "Scan" = "扫描"; "Select All" = "全选"; "Deselect All" = "取消全选"; -"Largest First" = "最大优先"; -"Smallest First" = "最小优先"; -"Sorted: Largest First" = "排序:最大优先"; -"Sorted: Smallest First" = "排序:最小优先"; +"Largest First" = "从大到小"; +"Smallest First" = "从小到大"; +"Sorted: Largest First" = "排序:从大到小"; +"Sorted: Smallest First" = "排序:从小到大"; "Clean %lld items" = "清理 %lld 项"; "%lld items · %@" = "%lld 项 · %@"; "Scanning…" = "扫描中…"; "Rescan" = "重新扫描"; -"%lld of %lld selected" = "已选 %lld / %lld"; +"%lld of %lld selected" = "已选 %lld / %lld 项"; "Reveal in Finder" = "在访达中显示"; "Copy Path" = "复制路径"; "Move to Trash" = "移到废纸篓"; @@ -90,9 +90,9 @@ /* Apps (AppListView) */ "Search apps" = "搜索应用"; "Refresh" = "刷新"; -"Installed Apps (%lld)" = "已安装应用 (%lld)"; +"Installed Apps (%lld)" = "已安装的应用(%lld)"; "Uninstall (%lld files)" = "卸载(%lld 个文件)"; -"Loading installed apps..." = "正在加载已安装应用..."; +"Loading installed apps..." = "正在加载已安装的应用…"; "No Apps Found" = "未找到应用"; "Could not find any installed applications." = "未能找到任何已安装的应用程序。"; "Retry" = "重试"; @@ -105,14 +105,14 @@ "%lld files" = "%lld 个文件"; "1 folder" = "1 个文件夹"; "1 file" = "1 个文件"; -"Scanning for related files..." = "正在扫描相关文件..."; -"Checking %lld locations..." = "正在检查 %lld 个位置..."; +"Scanning for related files..." = "正在扫描相关文件…"; +"Checking %lld locations..." = "正在检查 %lld 个位置…"; "No Related Files" = "没有相关文件"; "No additional files found for %@." = "未找到 %@ 的额外文件。"; -"Remove %lld files (%@)" = "删除 %lld 个文件 (%@)"; +"Remove %lld files (%@)" = "删除 %lld 个文件(%@)"; "Removal Failed" = "删除失败"; "Remove this file" = "删除此文件"; -"Remove %@?" = "删除 %@?"; +"Remove %@?" = "删除 %@?"; "Remove" = "删除"; "This will permanently delete this file. This action cannot be undone." = "这将永久删除此文件。此操作无法撤销。"; @@ -122,7 +122,7 @@ "No leftover files from uninstalled apps were found." = "未发现已卸载应用的残留文件。"; "Scan for Orphans" = "查找应用残留"; "Orphaned Files (%lld)" = "应用残留(%lld 项)"; -"Remove Selected (%lld)" = "删除所选 (%lld)"; +"Remove Selected (%lld)" = "删除所选项目(%lld)"; "Some files could not be removed" = "部分文件无法删除"; "blocked by safety policy" = "已被安全策略阻止"; "%lld item(s) failed to delete.\n\n%@" = "有 %lld 个项目删除失败。\n\n%@"; @@ -145,7 +145,7 @@ "Privacy & Security → Full Disk Access" = "隐私与安全性 → 完全磁盘访问权限"; "Find **PureMac** and turn the toggle on" = "找到 **PureMac** 并打开开关"; "Authenticate with Touch ID or your password" = "使用触控 ID 或密码进行验证"; -"Reopen Settings" = "重新打开“设置”"; +"Reopen Settings" = "重新打开“系统设置”"; "PureMac isn't in the list — reveal it" = "列表中没有 PureMac?在访达中显示"; "Reset permissions and re-prompt" = "重置权限并再次提示"; "Hide diagnostics" = "隐藏诊断"; @@ -155,8 +155,8 @@ "Permission granted." = "已授予权限。"; "PureMac can now manage protected files." = "PureMac 现在可以管理受保护的文件。"; "You're Ready" = "已准备就绪"; -"%lld/%lld protected locations accessible" = "可访问的受保护位置 %lld/%lld"; -"Some features will be limited without Full Disk Access. You can grant it later in Settings." = "没有完全磁盘访问权限时,部分功能将受限。你可以稍后在设置中授予该权限。"; +"%lld/%lld protected locations accessible" = "可访问 %lld/%lld 个受保护的位置"; +"Some features will be limited without Full Disk Access. You can grant it later in Settings." = "没有完全磁盘访问权限时,部分功能将受限。你可以稍后在系统设置中授予该权限。"; "Trash" = "废纸篓"; "Mail Data" = "邮件数据"; "Safari Data" = "Safari 数据"; @@ -171,7 +171,7 @@ /* Settings */ "General" = "通用"; -"Cleaning" = "正在清理"; +"Cleaning" = "清理"; "Schedule" = "计划"; "About" = "关于"; "Startup" = "启动"; @@ -181,9 +181,9 @@ "Strict" = "严格"; "Enhanced" = "增强"; "Deep" = "深度"; -"Exact bundle ID and name matches only. Safest option." = "仅精确匹配套件 ID 和名称。最安全。"; -"Includes partial name matching and bundle ID components." = "包含部分名称匹配和套件 ID 组件。"; -"Includes company name, entitlements, and team identifier matching." = "包含公司名称、授权和团队标识符匹配。"; +"Exact bundle ID and name matches only. Safest option." = "仅精确匹配应用标识符(Bundle ID)和名称。这是最安全的选项。"; +"Includes partial name matching and bundle ID components." = "还会匹配部分名称和应用标识符(Bundle ID)的组成部分。"; +"Includes company name, entitlements, and team identifier matching." = "还会匹配公司名称、应用权限声明和开发团队标识符。"; "Language" = "语言"; "Restart PureMac to apply the selected language." = "请重新启动 PureMac 以应用所选语言。"; "Relaunch Now" = "立即重新启动"; @@ -203,23 +203,23 @@ "Chinese (Traditional)" = "繁体中文"; "Safety" = "安全"; "Confirm before deleting files" = "删除文件前确认"; -"File Discovery" = "文件发现"; +"File Discovery" = "文件查找"; "Skip hidden files during scan" = "扫描时跳过隐藏文件"; "Large Files" = "大文件"; -"Minimum size: %lld MB" = "最小大小:%lld MB"; -"Files older than: %lld months" = "早于:%lld 个月的文件"; +"Minimum size: %lld MB" = "文件大小下限:%lld MB"; +"Files older than: %lld months" = "距上次修改超过 %lld 个月的文件"; "Automatic Scanning" = "自动扫描"; "Enable scheduled scanning" = "启用计划扫描"; "Scan interval" = "扫描间隔"; "Auto-clean after scan" = "扫描后自动清理"; -"Auto-purge purgeable space" = "自动清除可清理空间"; +"Auto-purge purgeable space" = "自动释放可清除空间"; "Notify on completion" = "完成时通知"; "Last run" = "上次运行"; "Version %@" = "版本 %@"; "Free, open-source macOS app manager." = "免费、开源的 macOS 应用管理工具。"; "GitHub Repository" = "GitHub 仓库"; "Report an Issue" = "报告问题"; -"MIT License" = "MIT 许可"; +"MIT License" = "MIT 许可证"; "Updates" = "更新"; "Check for Updates" = "检查更新"; @@ -242,11 +242,11 @@ "Scan everything at once" = "一次扫描所有内容"; "System caches, logs, and temporary files" = "系统缓存、日志和临时文件"; "Application caches and browser data" = "应用程序缓存和浏览器数据"; -"Local AI app logs, caches, and optional history" = "本地 AI 应用的日志、缓存和可选历史"; +"Local AI app logs, caches, and optional history" = "本地 AI 应用的日志、缓存和历史记录(可选)"; "Downloaded mail attachments" = "已下载的邮件附件"; "Files in your Trash" = "废纸篓中的文件"; "Files over 100 MB or older than 1 year" = "大于 100 MB 或超过 1 年的文件"; -"APFS purgeable disk space" = "APFS 可清理磁盘空间"; +"APFS purgeable disk space" = "APFS 可清除磁盘空间"; "Derived data, archives, and simulator runtimes" = "构建数据、归档和模拟器运行时"; "Homebrew download cache" = "Homebrew 下载缓存"; "npm, yarn, and pnpm download caches" = "npm、yarn 和 pnpm 下载缓存"; @@ -266,7 +266,7 @@ /* Node Cache subcategories */ "npm cache" = "npm 缓存"; -"yarn classic cache" = "yarn 经典缓存"; +"yarn classic cache" = "Yarn Classic 缓存"; "pnpm content-addressable store" = "pnpm 内容寻址存储"; /* Docker Cache helper */ @@ -283,8 +283,8 @@ "Monthly" = "每月"; /* Schedule status */ -"Never" = "从不"; -"Not scheduled" = "未计划"; +"Never" = "尚未运行"; +"Not scheduled" = "未设置计划"; /* Appearance modes (AppearanceMode.label) */ "System" = "系统"; @@ -298,29 +298,29 @@ "Grant Full Disk Access" = "授予完全磁盘访问权限"; "%lld item(s) need Full Disk Access" = "%lld 个项目需要完全磁盘访问权限"; "Uninstalling %@: %lld file(s) need Full Disk Access" = "卸载 %@:%lld 个文件需要完全磁盘访问权限"; -"%lld item(s) need Full Disk Access to remove. Tap Grant Access to fix in one step." = "%lld 个项目需要完全磁盘访问权限才能删除。轻点「授予访问权限」一步解决。"; +"%lld item(s) need Full Disk Access to remove. Tap Grant Access to fix in one step." = "%lld 个项目需要完全磁盘访问权限才能删除。点击“授予权限”前往设置。"; "Couldn't remove %@%@. They may be in use or protected by macOS." = "无法删除 %@%@。它们可能正在使用或受 macOS 保护。"; -" and %lld more" = " 还有 %lld 个"; -"Access granted. Retrying…" = "已授予访问权限。正在重试…"; +" and %lld more" = ",另有 %lld 项"; +"Access granted. Retrying…" = "已授予权限,正在重试…"; "1-tap setup. We'll detect the change automatically." = "前往系统设置授权,我们会自动检测权限变化。"; -"Open Settings & reveal PureMac" = "打开“设置”并显示 PureMac"; +"Open Settings & reveal PureMac" = "打开系统设置并在访达中显示 PureMac"; "We'll open both windows side-by-side." = "将同时打开系统设置和访达。"; -"Turn on PureMac" = "开启 PureMac"; +"Turn on PureMac" = "开启 PureMac 的访问权限"; "Toggle the row that appears." = "打开 PureMac 对应的开关。"; "Authenticate" = "验证身份"; -"Touch ID or password." = "Touch ID 或密码。"; +"Touch ID or password." = "使用触控 ID 或密码。"; "We auto-retry — no need to come back." = "授权后会自动重试,无需手动返回。"; -"Watching for permission change…" = "正在监视权限变更…"; +"Watching for permission change…" = "正在等待权限变更…"; "PureMac not in the list?" = "PureMac 不在列表中?"; "Hide help" = "隐藏帮助"; "Try this if PureMac doesn't appear:" = "如果 PureMac 没有出现,请尝试:"; -"Reveal app" = "显示 App"; +"Reveal app" = "在访达中显示应用"; "Drag PureMac.app into the list" = "将 PureMac.app 拖入列表"; "Reset + reprompt" = "重置并重新提示"; "Clear stale TCC entry" = "清除过时的 TCC 条目"; -"+ %lld more" = "+ 还有 %lld 个"; -"%lld blocked path(s)" = "%lld 个被阻止的路径"; -"Access granted" = "已授予访问权限"; +"+ %lld more" = "+ %lld 项"; +"%lld blocked path(s)" = "%lld 个路径无法访问"; +"Access granted" = "已授予权限"; "Retrying the operation now." = "正在重试此操作。"; "Skip for now" = "暂时跳过"; "I granted it — retry" = "已授权,重试"; @@ -341,7 +341,7 @@ "Free. Open source. MIT licensed." = "免费开源,采用 MIT 许可证。"; "What's inside" = "主要功能"; "Three things, done well." = "专注做好三件事。"; -"Find caches, logs, broken installs, and the AI-app history hiding in your library." = "找出缓存、日志、损坏的安装文件,以及隐藏在资源库中的 AI 应用历史。"; +"Find caches, logs, broken installs, and the AI-app history hiding in your library." = "找出缓存、日志、损坏的安装文件,以及隐藏在资源库中的 AI 应用历史记录。"; "Drag an app, see every file it dropped, remove all of it. No leftovers." = "拖入应用,查看关联文件,确认后一起清理。"; "Surfaces files that outlived the apps that created them." = "查找应用卸载后留下的文件。"; "One permission, then we're done" = "再授予一项权限即可"; @@ -359,10 +359,10 @@ "Continue" = "继续"; /* Drag handle */ -"Drag to the Settings list" = "拖到设置列表"; +"Drag to the Settings list" = "拖入系统设置列表"; "Drag this icon into the Full Disk Access list in System Settings." = "将此图标拖入系统设置中的完全磁盘访问权限列表。"; "Tip: drag the icon on the left straight into the list." = "提示:把左侧图标直接拖入列表。"; -"Or drag the icon on the left straight into Settings." = "或者把左侧图标直接拖入设置。"; +"Or drag the icon on the left straight into Settings." = "或者把左侧图标直接拖入系统设置的权限列表。"; /* Dashboard storage composition (v2.7.0) */ "Storage composition" = "存储构成"; @@ -392,9 +392,9 @@ /* v2.8.2: orphan ignore list (#114) */ "Always Ignore" = "始终忽略"; -"Ignore Selected (%lld)" = "忽略所选 (%lld)"; +"Ignore Selected (%lld)" = "忽略所选项目(%lld)"; "Ignored orphans: %lld" = "已忽略的应用残留:%lld"; -"Forget Ignored" = "清除已忽略"; +"Forget Ignored" = "清空忽略列表"; "Ignored files won't appear in future orphan scans." = "已忽略的文件不会出现在以后的应用残留扫描中。"; /* v2.8.3: menu-bar system monitor */ @@ -469,14 +469,14 @@ "\nAnd %lld more." = "\n另有 %lld 项。"; "%@ %@ to %@" = "%@:%@ → %@"; "%@ available" = "可用 %@"; -"%@ could not be removed because PureMac does not have Full Disk Access. Grant Full Disk Access in System Settings, then try again." = "由于 PureMac 没有完全磁盘访问权限,无法删除%@。请在系统设置中授予权限后重试。"; +"%@ could not be removed because PureMac does not have Full Disk Access. Grant Full Disk Access in System Settings, then try again." = "由于 PureMac 没有完全磁盘访问权限,无法删除 %@。请在系统设置中授予权限后重试。"; "%@ did not finish within the allowed time." = "%@ 未能在规定时间内完成。"; "%@ exited with status %d%@" = "%@ 已退出,状态码为 %d%@"; "%@ files inspected" = "已检查 %@ 个文件"; "%@ found across %lld categories." = "在 %2$lld 个类别中发现 %1$@ 可清理内容。"; "%@ found before the scan stopped. Results may be incomplete." = "扫描停止前发现 %@ 可清理内容,结果可能不完整。"; "%@ of %@" = "已用 %@,共 %@"; -"%@ of %@ candidates · %@ verified" = "已校验 %@ / %@ 个候选文件,共 %@"; +"%@ of %@ candidates · %@ verified" = "已校验 %@ / %@ 个候选文件 · 已校验数据量 %@"; "%@ reviewable" = "可供清理 %@"; "%@ selected. This permanently removes the selected files or runs the listed cleanup actions. It cannot be undone." = "已选择 %@。将永久删除所选文件或执行列出的清理操作,此操作无法撤销。"; "%@ will move to Trash" = "将把 %@ 移到废纸篓"; @@ -767,7 +767,7 @@ "Scan stopped" = "扫描已停止"; "Scan this Mac" = "扫描此 Mac"; "Scan this category" = "扫描此类别"; -"Scanning %@…" = "正在扫描%@…"; +"Scanning %@…" = "正在扫描 %@…"; "Scheduled" = "定时运行"; "Security Settings" = "安全设置"; "See current resource use and review what starts in the background." = "查看当前资源使用情况和后台启动项目。"; @@ -874,55 +874,100 @@ /* Additional interface strings. */ "%lld processes" = "%lld 个进程"; "1 process" = "1 个进程"; -"A PureMac update is available" = "A PureMac update is available"; -"About these readings" = "About these readings"; -"App memory shows resident memory, including shared pages. App totals can differ from the summary." = "App memory shows resident memory, including shared pages. App totals can differ from the summary."; -"Apps and processes" = "Apps and processes"; -"Build %@" = "Build %@"; -"CPU - Memory" = "CPU - Memory"; -"CPU uses the Mac’s total capacity. App helpers are grouped with their app." = "CPU uses the Mac’s total capacity. App helpers are grouped with their app."; -"CPU values appear after the next sample." = "CPU values appear after the next sample."; -"Check access" = "Check access"; -"Checking for updates…" = "Checking for updates…"; -"Checks the official app releases on GitHub." = "Checks the official app releases on GitHub."; -"Collapsed" = "Collapsed"; -"Copy command" = "Copy command"; -"Download (DMG / ZIP)" = "Download (DMG / ZIP)"; -"Download the new app from the release page, quit PureMac, then replace it in Applications." = "Download the new app from the release page, quit PureMac, then replace it in Applications."; -"Expanded" = "Expanded"; -"GitHub is receiving too many requests. Please try again later." = "GitHub is receiving too many requests. Please try again later."; +"A PureMac update is available" = "PureMac 有可用更新"; +"About these readings" = "数据说明"; +"App memory shows resident memory, including shared pages. App totals can differ from the summary." = "应用内存显示的是常驻内存,包含共享内存页。各应用的内存用量之和可能与总览中的数值不同。"; +"Apps and processes" = "应用与进程"; +"Build %@" = "构建版本 %@"; +"CPU - Memory" = "CPU 与内存"; +"CPU uses the Mac’s total capacity. App helpers are grouped with their app." = "CPU 使用率以此 Mac 的总处理能力为基准。辅助进程会归入所属应用。"; +"CPU values appear after the next sample." = "CPU 使用率将在下一次采样后显示。"; +"Check access" = "检查权限"; +"Checking for updates…" = "正在检查更新…"; +"Checks the official app releases on GitHub." = "检查 GitHub 上的应用官方发布版本。"; +"Collapsed" = "已收起"; +"Copy command" = "复制命令"; +"Download (DMG / ZIP)" = "下载(DMG / ZIP)"; +"Download the new app from the release page, quit PureMac, then replace it in Applications." = "从发布页面下载新版应用,退出 PureMac,然后替换“应用程序”文件夹中的旧版应用。"; +"Expanded" = "已展开"; +"GitHub is receiving too many requests. Please try again later." = "GitHub 请求过于频繁,请稍后重试。"; "Homebrew" = "Homebrew"; -"Keep PureMac up to date" = "Keep PureMac up to date"; -"Last checked: %@" = "Last checked: %@"; -"Live updates paused" = "Live updates paused"; - -"Make PureMac work your way." = "Make PureMac work your way."; -"Measuring apps and processes…" = "Measuring apps and processes…"; -"Memory estimate includes active, wired and compressed memory." = "Memory estimate includes active, wired and compressed memory."; -"Memory in use" = "Memory in use"; -"No matching apps or processes" = "No matching apps or processes"; -"No stable PureMac app release was found. Try again later." = "No stable PureMac app release was found. Try again later."; -"Open release page" = "Open release page"; -"Paused" = "Paused"; -"Process information is unavailable. Try Activity Monitor for more detail." = "Process information is unavailable. Try Activity Monitor for more detail."; -"PureMac %@ is available" = "PureMac %@ is available"; -"Restart to apply your language" = "Restart to apply your language"; -"Review Full Disk Access" = "Review Full Disk Access"; -"Review launch at login" = "Review launch at login"; -"Search apps and processes" = "Search apps and processes"; -"Settings" = "Settings"; -"Settings need attention" = "Settings need attention"; -"Settings to review" = "Settings to review"; -"Show all apps and processes" = "Show all apps and processes"; -"Show top 8" = "Show top 8"; -"Some of this app's processes have no CPU reading yet, so this total is partial." = "Some of this app's processes have no CPU reading yet, so this total is partial."; -"Some processes started or stopped since the last sample. Their values appear in the next one." = "Some processes started or stopped since the last sample. Their values appear in the next one."; -"Sort by" = "Sort by"; -"System processes are not listed. Use Activity Monitor for those." = "System processes are not listed. Use Activity Monitor for those."; -"The release service is unavailable. Please try again later." = "The release service is unavailable. Please try again later."; -"This build has no readable version. Open the releases page to check manually." = "This build has no readable version. Open the releases page to check manually."; -"To update with Homebrew, run this command in Terminal. The cask may follow the GitHub release a little later." = "To update with Homebrew, run this command in Terminal. The cask may follow the GitHub release a little later."; -"Update method" = "Update method"; - -"View releases" = "View releases"; -"You're using the latest available version of PureMac." = "You're using the latest available version of PureMac."; +"Keep PureMac up to date" = "让 PureMac 保持最新版本"; +"Last checked: %@" = "上次检查:%@"; +"Live updates paused" = "实时刷新已暂停"; + +"Make PureMac work your way." = "按你的习惯设置 PureMac。"; +"Measuring apps and processes…" = "正在统计应用与进程的资源用量…"; +"Memory estimate includes active, wired and compressed memory." = "内存估算值包含活跃内存、联动内存和压缩内存。"; +"Memory in use" = "已用内存"; +"No matching apps or processes" = "未找到匹配的应用或进程"; +"No stable PureMac app release was found. Try again later." = "未找到 PureMac 应用的稳定发布版本,请稍后重试。"; +"Open release page" = "打开发布页面"; +"Paused" = "已暂停"; +"Process information is unavailable. Try Activity Monitor for more detail." = "无法获取进程信息,请打开活动监视器查看更多详情。"; +"PureMac %@ is available" = "PureMac %@ 已发布"; +"Restart to apply your language" = "重新启动 PureMac 以应用所选语言"; +"Review Full Disk Access" = "检查完全磁盘访问权限"; +"Review launch at login" = "检查登录时启动设置"; +"Search apps and processes" = "搜索应用与进程"; +"Settings" = "设置"; +"Settings need attention" = "部分设置需要处理"; +"Settings to review" = "待检查的设置"; +"Show all apps and processes" = "显示所有应用与进程"; +"Show top 8" = "仅显示前 8 项"; +"Some of this app's processes have no CPU reading yet, so this total is partial." = "此应用的部分进程尚无 CPU 使用率数据,因此当前总计仅包含部分进程。"; +"Some processes started or stopped since the last sample. Their values appear in the next one." = "部分进程在上次采样后启动或退出,相关数据将在下一次采样后更新。"; +"Sort by" = "排序方式"; +"System processes are not listed. Use Activity Monitor for those." = "此处不列出系统进程,请在活动监视器中查看。"; +"The release service is unavailable. Please try again later." = "版本发布服务暂时不可用,请稍后重试。"; +"This build has no readable version. Open the releases page to check manually." = "无法读取当前构建的版本号,请打开发布页面手动检查。"; +"To update with Homebrew, run this command in Terminal. The cask may follow the GitHub release a little later." = "要通过 Homebrew 更新,请在终端中运行此命令。Homebrew 中的应用版本可能稍晚于 GitHub 发布版本更新。"; +"Update method" = "更新方式"; + +"View releases" = "查看发布版本"; +"You're using the latest available version of PureMac." = "你已在使用 PureMac 的最新版本。"; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@(沙盒缓存)"; +"%@: Legacy DerivedData" = "%@:旧版构建数据"; +"Time Machine snapshot: %@" = "时间机器快照:%@"; +"Unknown file" = "未知文件"; +"Excluded from cleanup: %@" = "已排除此项,不会清理:%@"; +"Skipped symlink or unsafe path: %@" = "已跳过符号链接或不安全的路径:%@"; +"Skipped symlink or unsafe path: %@ -> %@" = "已跳过符号链接或不安全的路径:%@ → %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "已取消删除:%@ 的实际路径在检查后发生了变化。"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "已跳过 XcodeBuildMCP 构建数据:无法获取生命周期锁:%@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "为保护构建数据,不会使用管理员权限删除 XcodeBuildMCP 构建数据:%@"; +"%@ at %@: %@" = "%@(%@):%@"; +"%@ at %@ survived admin removal" = "使用管理员权限后仍未能删除 %@(%@)"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "未找到 Docker 命令行工具,请安装 Docker Desktop 或 OrbStack。"; +"Couldn't run docker system prune: %@" = "无法运行 docker system prune:%@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker 未运行,请启动 Docker Desktop 或 OrbStack 后重新清理。"; +"docker system prune failed (exit %d)" = "docker system prune 执行失败(退出状态码:%d)"; +"Simulator services unavailable — open Xcode once, then try again" = "模拟器服务不可用,请先打开一次 Xcode 后重试。"; +"Couldn't delete simulator runtime %@" = "无法删除模拟器运行时 %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "未找到 Xcode 命令行工具,请安装 Xcode 或运行 xcode-select --install。"; +"Homebrew exited with status %d." = "Homebrew 已退出,状态码为 %d。"; +"Skipped unsafe path for thinning: %@ -> %@" = "已跳过不安全的应用瘦身路径:%@ → %@"; +"%@: needs administrator access to thin; skipped" = "%@:应用瘦身需要管理员权限,已跳过。"; +"Couldn't thin %@: %@" = "无法为 %@ 瘦身:%@"; +"%@: needs administrator access to modify the app; skipped" = "%@:修改应用需要管理员权限,已跳过。"; +"Couldn't remove localization %@: %@" = "无法删除本地化文件 %@:%@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "无权访问“%@”。请在系统设置中授予完全磁盘访问权限。"; +"Failed to enumerate directory '%@': %@" = "无法读取文件夹“%@”中的内容:%@"; +"Failed to execute '%@': %@" = "无法执行“%@”:%@"; +"Invalid data encountered: %@" = "遇到无效数据:%@"; +"The privileged helper tool is not installed or unavailable." = "特权辅助工具未安装或不可用。"; +"The operation was cancelled." = "操作已取消。"; +"Not writable by current user: %@" = "当前用户没有写入权限:%@"; +"Cannot modify %@: its entitlements require the original developer signature" = "无法修改 %@:其权限声明要求保留原开发者签名。"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "无法为 %@ 瘦身:该应用已签名并通过公证,移除架构代码会破坏其签名。"; +"lipo failed for %@: %@" = "lipo 处理 %@ 时失败:%@"; +"Could not swap modified bundle into place at %@: %@" = "无法将修改后的应用程序包替换到 %@:%@"; +"Re-signing failed for %@: %@" = "为 %@ 重新签名失败:%@"; +"Signature verification failed for %@: %@" = "%@ 的签名验证失败:%@"; +"No removable slices in %@" = "%@ 中没有可移除的架构代码"; +"localization is outside the app bundle" = "本地化文件不在应用程序包内"; +"could not stage a working copy: %@" = "无法创建用于修改的临时副本:%@"; +"original bundle left at %@" = "原应用程序包保留在 %@"; +"binary is outside the app bundle" = "二进制文件不在应用程序包内"; diff --git a/PureMac/zh-Hant.lproj/Localizable.strings b/PureMac/zh-Hant.lproj/Localizable.strings index 7c79fed..c6e7266 100644 --- a/PureMac/zh-Hant.lproj/Localizable.strings +++ b/PureMac/zh-Hant.lproj/Localizable.strings @@ -927,3 +927,48 @@ "View releases" = "View releases"; "You're using the latest available version of PureMac." = "You're using the latest available version of PureMac."; + +/* Runtime scan labels and cleanup errors. */ +"%@ (sandbox cache)" = "%@ (sandbox cache)"; +"%@: Legacy DerivedData" = "%@: Legacy DerivedData"; +"Time Machine snapshot: %@" = "Time Machine snapshot: %@"; +"Unknown file" = "Unknown file"; +"Excluded from cleanup: %@" = "Excluded from cleanup: %@"; +"Skipped symlink or unsafe path: %@" = "Skipped symlink or unsafe path: %@"; +"Skipped symlink or unsafe path: %@ -> %@" = "Skipped symlink or unsafe path: %@ -> %@"; +"Aborting delete: path resolution changed between check and unlink for %@" = "Aborting delete: path resolution changed between check and unlink for %@"; +"Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@" = "Skipped XcodeBuildMCP DerivedData: lifecycle lock is unavailable: %@"; +"Refusing administrator escalation for XcodeBuildMCP DerivedData: %@" = "Refusing administrator escalation for XcodeBuildMCP DerivedData: %@"; +"%@ at %@: %@" = "%@ at %@: %@"; +"%@ at %@ survived admin removal" = "%@ at %@ survived admin removal"; +"Docker CLI not found — install Docker Desktop or OrbStack" = "Docker CLI not found — install Docker Desktop or OrbStack"; +"Couldn't run docker system prune: %@" = "Couldn't run docker system prune: %@"; +"Docker isn't running — start Docker Desktop or OrbStack, then clean again" = "Docker isn't running — start Docker Desktop or OrbStack, then clean again"; +"docker system prune failed (exit %d)" = "docker system prune failed (exit %d)"; +"Simulator services unavailable — open Xcode once, then try again" = "Simulator services unavailable — open Xcode once, then try again"; +"Couldn't delete simulator runtime %@" = "Couldn't delete simulator runtime %@"; +"Xcode command-line tools not found — install Xcode or run xcode-select --install" = "Xcode command-line tools not found — install Xcode or run xcode-select --install"; +"Homebrew exited with status %d." = "Homebrew exited with status %d."; +"Skipped unsafe path for thinning: %@ -> %@" = "Skipped unsafe path for thinning: %@ -> %@"; +"%@: needs administrator access to thin; skipped" = "%@: needs administrator access to thin; skipped"; +"Couldn't thin %@: %@" = "Couldn't thin %@: %@"; +"%@: needs administrator access to modify the app; skipped" = "%@: needs administrator access to modify the app; skipped"; +"Couldn't remove localization %@: %@" = "Couldn't remove localization %@: %@"; +"Permission denied when accessing '%@'. Grant Full Disk Access in System Settings." = "Permission denied when accessing '%@'. Grant Full Disk Access in System Settings."; +"Failed to enumerate directory '%@': %@" = "Failed to enumerate directory '%@': %@"; +"Failed to execute '%@': %@" = "Failed to execute '%@': %@"; +"Invalid data encountered: %@" = "Invalid data encountered: %@"; +"The privileged helper tool is not installed or unavailable." = "The privileged helper tool is not installed or unavailable."; +"The operation was cancelled." = "The operation was cancelled."; +"Not writable by current user: %@" = "Not writable by current user: %@"; +"Cannot modify %@: its entitlements require the original developer signature" = "Cannot modify %@: its entitlements require the original developer signature"; +"Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature" = "Cannot thin %@: it is signed and notarized, and stripping a slice would break its signature"; +"lipo failed for %@: %@" = "lipo failed for %@: %@"; +"Could not swap modified bundle into place at %@: %@" = "Could not swap modified bundle into place at %@: %@"; +"Re-signing failed for %@: %@" = "Re-signing failed for %@: %@"; +"Signature verification failed for %@: %@" = "Signature verification failed for %@: %@"; +"No removable slices in %@" = "No removable slices in %@"; +"localization is outside the app bundle" = "localization is outside the app bundle"; +"could not stage a working copy: %@" = "could not stage a working copy: %@"; +"original bundle left at %@" = "original bundle left at %@"; +"binary is outside the app bundle" = "binary is outside the app bundle"; diff --git a/PureMacTests/CleaningEngineTests.swift b/PureMacTests/CleaningEngineTests.swift index 5529d89..ee9c437 100644 --- a/PureMacTests/CleaningEngineTests.swift +++ b/PureMacTests/CleaningEngineTests.swift @@ -69,7 +69,7 @@ final class CleaningEngineTests: XCTestCase { .typeSymbolicLink ) XCTAssertFalse(result.cleanedPaths.contains(link.path)) - XCTAssertTrue(result.errors.contains { $0.localizedCaseInsensitiveContains("symlink") }) + XCTAssertEqual(result.errors, [String(localized: "Skipped symlink or unsafe path: \(link.path)")]) } func testSymlinkedParentIsRefusedWithoutDeletingItsTarget() async throws { @@ -85,7 +85,7 @@ final class CleaningEngineTests: XCTestCase { XCTAssertTrue(fileManager.fileExists(atPath: target.path)) XCTAssertFalse(result.cleanedPaths.contains(linkedTarget.path)) - XCTAssertTrue(result.errors.contains { $0.localizedCaseInsensitiveContains("symlink") }) + XCTAssertEqual(result.errors, [String(localized: "Skipped symlink or unsafe path: \(linkedTarget.path)")]) } func testDanglingSymlinkIsRefusedInsteadOfReportedAsCleaned() async throws { @@ -100,7 +100,7 @@ final class CleaningEngineTests: XCTestCase { .typeSymbolicLink ) XCTAssertFalse(result.cleanedPaths.contains(link.path)) - XCTAssertTrue(result.errors.contains { $0.localizedCaseInsensitiveContains("symlink") }) + XCTAssertEqual(result.errors, [String(localized: "Skipped symlink or unsafe path: \(link.path)")]) } func testExcludedFileIsNeverDeleted() async throws { @@ -112,7 +112,7 @@ final class CleaningEngineTests: XCTestCase { XCTAssertTrue(fileManager.fileExists(atPath: file.path)) XCTAssertFalse(result.cleanedPaths.contains(file.path)) - XCTAssertEqual(result.errors, ["Excluded from cleanup: excluded.bin"]) + XCTAssertEqual(result.errors, [String(localized: "Excluded from cleanup: \(file.lastPathComponent)")]) } func testAppModifyingCategoriesNeverFallThroughToFileDeletion() async throws { diff --git a/PureMacTests/LocalizationFilesTests.swift b/PureMacTests/LocalizationFilesTests.swift index 1db8573..17076c5 100644 --- a/PureMacTests/LocalizationFilesTests.swift +++ b/PureMacTests/LocalizationFilesTests.swift @@ -91,6 +91,29 @@ final class LocalizationFilesTests: XCTestCase { } } + func testSimplifiedChineseValuesAreTranslated() throws { + let localizationFiles = try localizableStringsFiles() + let englishURL = try XCTUnwrap(localizationFiles["en"]) + let chineseURL = try XCTUnwrap(localizationFiles["zh-Hans"]) + let englishStrings = try localizedStrings(in: englishURL) + let chineseStrings = try localizedStrings(in: chineseURL) + // Product names and standard technical abbreviations stay unchanged. + let untranslatedNames: Set = ["PureMac", "PureMac.app", "CPU", "Homebrew", "XProtect"] + + for (key, englishValue) in englishStrings where !untranslatedNames.contains(key) { + let chineseValue = try XCTUnwrap(chineseStrings[key], "Missing Simplified Chinese key: \(key)") + XCTAssertFalse( + chineseValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, + "Empty Simplified Chinese translation for key: \(key)" + ) + XCTAssertNotEqual( + chineseValue, + englishValue, + "Untranslated Simplified Chinese value for key: \(key)" + ) + } + } + func testLocalizableStringsFilesDoNotContainDuplicateKeys() throws { for (language, fileURL) in try localizableStringsFiles() { let keys = try declaredKeys(in: fileURL) @@ -106,6 +129,44 @@ final class LocalizationFilesTests: XCTestCase { } } + func testSimplifiedChineseRuntimeMessagesResolveInterpolatedKeys() throws { + let localizationFiles = try localizableStringsFiles() + let chineseURL = try XCTUnwrap(localizationFiles["zh-Hans"]) + let chineseStrings = try localizedStrings(in: chineseURL) + let bundle = try XCTUnwrap(Bundle(path: chineseURL.deletingLastPathComponent().path)) + let path = "/tmp/PureMac Test/example.bin" + let tool = "XcodeBuildMCP" + let status: Int32 = 7 + let cases: [(key: String, value: String, arguments: [CVarArg])] = [ + ( + "Skipped symlink or unsafe path: %@", + String(localized: "Skipped symlink or unsafe path: \(path)", bundle: bundle), + [path] + ), + ( + "%@: Legacy DerivedData", + String(localized: "\(tool): Legacy DerivedData", bundle: bundle), + [tool] + ), + ( + "docker system prune failed (exit %d)", + String(localized: "docker system prune failed (exit \(status))", bundle: bundle), + [status] + ), + ( + "Homebrew exited with status %d.", + String(localized: "Homebrew exited with status \(status).", bundle: bundle), + [status] + ) + ] + + for sample in cases { + let format = try XCTUnwrap(chineseStrings[sample.key]) + XCTAssertNotEqual(format, sample.key) + XCTAssertEqual(sample.value, String(format: format, arguments: sample.arguments)) + } + } + private func localizableStringsFiles() throws -> [String: URL] { let sourceRoot = URL(fileURLWithPath: #filePath) .deletingLastPathComponent() diff --git a/PureMacTests/XcodeBuildMCPDerivedDataSupportTests.swift b/PureMacTests/XcodeBuildMCPDerivedDataSupportTests.swift index 00ae888..214b494 100644 --- a/PureMacTests/XcodeBuildMCPDerivedDataSupportTests.swift +++ b/PureMacTests/XcodeBuildMCPDerivedDataSupportTests.swift @@ -132,7 +132,7 @@ final class XcodeBuildMCPDerivedDataSupportTests: XCTestCase { URL(fileURLWithPath: $0.path).resolvingSymlinksInPath().path == expectedPath }) - XCTAssertEqual(item.name, "XcodeBuildMCP: Legacy DerivedData") + XCTAssertEqual(item.name, String(format: String(localized: "%@: Legacy DerivedData"), "XcodeBuildMCP")) XCTAssertFalse(item.isSelected) } @@ -150,7 +150,8 @@ final class XcodeBuildMCPDerivedDataSupportTests: XCTestCase { .write(to: legacyDerivedData.appendingPathComponent("legacy-build-product")) let scan = await ScanEngine(homeDirectory: temporaryHome).scanCategory(.xcodeJunk) - let item = try XCTUnwrap(scan.items.first { $0.name == "XcodeBuildMCP: Legacy DerivedData" }) + let expectedPath = legacyDerivedData.resolvingSymlinksInPath().path + let item = try XCTUnwrap(scan.items.first { $0.path == expectedPath }) let result = await CleaningEngine(homeDirectory: temporaryHome).cleanItems([item]) { _ in } XCTAssertEqual(result.itemsCleaned, 1)