feat: add Atlas native app UX overhaul
This commit is contained in:
15
Packages/AtlasDomain/README.md
Normal file
15
Packages/AtlasDomain/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# AtlasDomain
|
||||
|
||||
## Responsibility
|
||||
|
||||
- Core models and domain rules
|
||||
|
||||
## Planned Types
|
||||
|
||||
- `Finding`
|
||||
- `ActionPlan`
|
||||
- `ActionItem`
|
||||
- `TaskRun`
|
||||
- `RecoveryItem`
|
||||
- `AppFootprint`
|
||||
- `PermissionState`
|
||||
772
Packages/AtlasDomain/Sources/AtlasDomain/AtlasDomain.swift
Normal file
772
Packages/AtlasDomain/Sources/AtlasDomain/AtlasDomain.swift
Normal file
@@ -0,0 +1,772 @@
|
||||
import Foundation
|
||||
|
||||
public enum AtlasRoute: String, CaseIterable, Codable, Hashable, Identifiable, Sendable {
|
||||
case overview
|
||||
case smartClean
|
||||
case apps
|
||||
case history
|
||||
case permissions
|
||||
case settings
|
||||
|
||||
public var id: String { rawValue }
|
||||
|
||||
public var title: String {
|
||||
switch self {
|
||||
case .overview:
|
||||
return AtlasL10n.string("route.overview.title")
|
||||
case .smartClean:
|
||||
return AtlasL10n.string("route.smartclean.title")
|
||||
case .apps:
|
||||
return AtlasL10n.string("route.apps.title")
|
||||
case .history:
|
||||
return AtlasL10n.string("route.history.title")
|
||||
case .permissions:
|
||||
return AtlasL10n.string("route.permissions.title")
|
||||
case .settings:
|
||||
return AtlasL10n.string("route.settings.title")
|
||||
}
|
||||
}
|
||||
|
||||
public var subtitle: String {
|
||||
switch self {
|
||||
case .overview:
|
||||
return AtlasL10n.string("route.overview.subtitle")
|
||||
case .smartClean:
|
||||
return AtlasL10n.string("route.smartclean.subtitle")
|
||||
case .apps:
|
||||
return AtlasL10n.string("route.apps.subtitle")
|
||||
case .history:
|
||||
return AtlasL10n.string("route.history.subtitle")
|
||||
case .permissions:
|
||||
return AtlasL10n.string("route.permissions.subtitle")
|
||||
case .settings:
|
||||
return AtlasL10n.string("route.settings.subtitle")
|
||||
}
|
||||
}
|
||||
|
||||
public var systemImage: String {
|
||||
switch self {
|
||||
case .overview:
|
||||
return "rectangle.grid.2x2"
|
||||
case .smartClean:
|
||||
return "sparkles"
|
||||
case .apps:
|
||||
return "square.stack.3d.up"
|
||||
case .history:
|
||||
return "clock.arrow.circlepath"
|
||||
case .permissions:
|
||||
return "lock.shield"
|
||||
case .settings:
|
||||
return "gearshape"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum RiskLevel: String, CaseIterable, Codable, Hashable, Sendable {
|
||||
case safe
|
||||
case review
|
||||
case advanced
|
||||
|
||||
public var title: String {
|
||||
switch self {
|
||||
case .safe:
|
||||
return AtlasL10n.string("risk.safe")
|
||||
case .review:
|
||||
return AtlasL10n.string("risk.review")
|
||||
case .advanced:
|
||||
return AtlasL10n.string("risk.advanced")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct Finding: Identifiable, Codable, Hashable, Sendable {
|
||||
public var id: UUID
|
||||
public var title: String
|
||||
public var detail: String
|
||||
public var bytes: Int64
|
||||
public var risk: RiskLevel
|
||||
public var category: String
|
||||
public var targetPaths: [String]?
|
||||
|
||||
public init(
|
||||
id: UUID = UUID(),
|
||||
title: String,
|
||||
detail: String,
|
||||
bytes: Int64,
|
||||
risk: RiskLevel,
|
||||
category: String,
|
||||
targetPaths: [String]? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.bytes = bytes
|
||||
self.risk = risk
|
||||
self.category = category
|
||||
self.targetPaths = targetPaths
|
||||
}
|
||||
}
|
||||
|
||||
public struct ActionItem: Identifiable, Codable, Hashable, Sendable {
|
||||
public enum Kind: String, Codable, Hashable, Sendable {
|
||||
case removeCache
|
||||
case removeApp
|
||||
case archiveFile
|
||||
case inspectPermission
|
||||
}
|
||||
|
||||
public var id: UUID
|
||||
public var title: String
|
||||
public var detail: String
|
||||
public var kind: Kind
|
||||
public var recoverable: Bool
|
||||
|
||||
public init(
|
||||
id: UUID = UUID(),
|
||||
title: String,
|
||||
detail: String,
|
||||
kind: Kind,
|
||||
recoverable: Bool
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.kind = kind
|
||||
self.recoverable = recoverable
|
||||
}
|
||||
}
|
||||
|
||||
public struct ActionPlan: Identifiable, Codable, Hashable, Sendable {
|
||||
public var id: UUID
|
||||
public var title: String
|
||||
public var items: [ActionItem]
|
||||
public var estimatedBytes: Int64
|
||||
|
||||
public init(
|
||||
id: UUID = UUID(),
|
||||
title: String,
|
||||
items: [ActionItem],
|
||||
estimatedBytes: Int64
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.items = items
|
||||
self.estimatedBytes = estimatedBytes
|
||||
}
|
||||
}
|
||||
|
||||
public enum TaskKind: String, Codable, Hashable, Sendable {
|
||||
case scan
|
||||
case executePlan
|
||||
case uninstallApp
|
||||
case restore
|
||||
case inspectPermissions
|
||||
|
||||
public var title: String {
|
||||
switch self {
|
||||
case .scan:
|
||||
return AtlasL10n.string("taskkind.scan")
|
||||
case .executePlan:
|
||||
return AtlasL10n.string("taskkind.executePlan")
|
||||
case .uninstallApp:
|
||||
return AtlasL10n.string("taskkind.uninstallApp")
|
||||
case .restore:
|
||||
return AtlasL10n.string("taskkind.restore")
|
||||
case .inspectPermissions:
|
||||
return AtlasL10n.string("taskkind.inspectPermissions")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum TaskStatus: String, Codable, Hashable, Sendable {
|
||||
case queued
|
||||
case running
|
||||
case completed
|
||||
case failed
|
||||
case cancelled
|
||||
|
||||
public var title: String {
|
||||
switch self {
|
||||
case .queued:
|
||||
return AtlasL10n.string("taskstatus.queued")
|
||||
case .running:
|
||||
return AtlasL10n.string("taskstatus.running")
|
||||
case .completed:
|
||||
return AtlasL10n.string("taskstatus.completed")
|
||||
case .failed:
|
||||
return AtlasL10n.string("taskstatus.failed")
|
||||
case .cancelled:
|
||||
return AtlasL10n.string("taskstatus.cancelled")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct TaskRun: Identifiable, Codable, Hashable, Sendable {
|
||||
public var id: UUID
|
||||
public var kind: TaskKind
|
||||
public var status: TaskStatus
|
||||
public var summary: String
|
||||
public var startedAt: Date
|
||||
public var finishedAt: Date?
|
||||
|
||||
public init(
|
||||
id: UUID = UUID(),
|
||||
kind: TaskKind,
|
||||
status: TaskStatus,
|
||||
summary: String,
|
||||
startedAt: Date,
|
||||
finishedAt: Date? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.kind = kind
|
||||
self.status = status
|
||||
self.summary = summary
|
||||
self.startedAt = startedAt
|
||||
self.finishedAt = finishedAt
|
||||
}
|
||||
}
|
||||
|
||||
public enum RecoveryPayload: Codable, Hashable, Sendable {
|
||||
case finding(Finding)
|
||||
case app(AppFootprint)
|
||||
}
|
||||
|
||||
public struct RecoveryPathMapping: Codable, Hashable, Sendable {
|
||||
public var originalPath: String
|
||||
public var trashedPath: String
|
||||
|
||||
public init(originalPath: String, trashedPath: String) {
|
||||
self.originalPath = originalPath
|
||||
self.trashedPath = trashedPath
|
||||
}
|
||||
}
|
||||
|
||||
public struct RecoveryItem: Identifiable, Codable, Hashable, Sendable {
|
||||
public var id: UUID
|
||||
public var title: String
|
||||
public var detail: String
|
||||
public var originalPath: String
|
||||
public var bytes: Int64
|
||||
public var deletedAt: Date
|
||||
public var expiresAt: Date?
|
||||
public var payload: RecoveryPayload?
|
||||
public var restoreMappings: [RecoveryPathMapping]?
|
||||
|
||||
public init(
|
||||
id: UUID = UUID(),
|
||||
title: String,
|
||||
detail: String,
|
||||
originalPath: String,
|
||||
bytes: Int64,
|
||||
deletedAt: Date,
|
||||
expiresAt: Date? = nil,
|
||||
payload: RecoveryPayload? = nil,
|
||||
restoreMappings: [RecoveryPathMapping]? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.originalPath = originalPath
|
||||
self.bytes = bytes
|
||||
self.deletedAt = deletedAt
|
||||
self.expiresAt = expiresAt
|
||||
self.payload = payload
|
||||
self.restoreMappings = restoreMappings
|
||||
}
|
||||
}
|
||||
|
||||
public struct AppFootprint: Identifiable, Codable, Hashable, Sendable {
|
||||
public var id: UUID
|
||||
public var name: String
|
||||
public var bundleIdentifier: String
|
||||
public var bundlePath: String
|
||||
public var bytes: Int64
|
||||
public var leftoverItems: Int
|
||||
|
||||
public init(
|
||||
id: UUID = UUID(),
|
||||
name: String,
|
||||
bundleIdentifier: String,
|
||||
bundlePath: String,
|
||||
bytes: Int64,
|
||||
leftoverItems: Int
|
||||
) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.bundleIdentifier = bundleIdentifier
|
||||
self.bundlePath = bundlePath
|
||||
self.bytes = bytes
|
||||
self.leftoverItems = leftoverItems
|
||||
}
|
||||
}
|
||||
|
||||
public enum PermissionKind: String, Codable, CaseIterable, Hashable, Sendable, Identifiable {
|
||||
case fullDiskAccess
|
||||
case accessibility
|
||||
case notifications
|
||||
|
||||
public var id: String { rawValue }
|
||||
|
||||
public var isRequiredForCurrentWorkflows: Bool {
|
||||
switch self {
|
||||
case .fullDiskAccess:
|
||||
return true
|
||||
case .accessibility, .notifications:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public var title: String {
|
||||
switch self {
|
||||
case .fullDiskAccess:
|
||||
return AtlasL10n.string("permission.fullDiskAccess")
|
||||
case .accessibility:
|
||||
return AtlasL10n.string("permission.accessibility")
|
||||
case .notifications:
|
||||
return AtlasL10n.string("permission.notifications")
|
||||
}
|
||||
}
|
||||
|
||||
public var systemImage: String {
|
||||
switch self {
|
||||
case .fullDiskAccess:
|
||||
return "externaldrive.badge.checkmark"
|
||||
case .accessibility:
|
||||
return "figure.wave"
|
||||
case .notifications:
|
||||
return "bell.badge"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct PermissionState: Identifiable, Codable, Hashable, Sendable {
|
||||
public var kind: PermissionKind
|
||||
public var isGranted: Bool
|
||||
public var rationale: String
|
||||
|
||||
public init(kind: PermissionKind, isGranted: Bool, rationale: String) {
|
||||
self.kind = kind
|
||||
self.isGranted = isGranted
|
||||
self.rationale = rationale
|
||||
}
|
||||
|
||||
public var id: PermissionKind { kind }
|
||||
}
|
||||
|
||||
public struct AtlasOptimizationRecommendation: Identifiable, Codable, Hashable, Sendable {
|
||||
public var id: String { action }
|
||||
public var category: String
|
||||
public var name: String
|
||||
public var detail: String
|
||||
public var action: String
|
||||
public var isSafe: Bool
|
||||
|
||||
public init(category: String, name: String, detail: String, action: String, isSafe: Bool) {
|
||||
self.category = category
|
||||
self.name = name
|
||||
self.detail = detail
|
||||
self.action = action
|
||||
self.isSafe = isSafe
|
||||
}
|
||||
}
|
||||
|
||||
public struct AtlasHealthSnapshot: Codable, Hashable, Sendable {
|
||||
public var memoryUsedGB: Double
|
||||
public var memoryTotalGB: Double
|
||||
public var diskUsedGB: Double
|
||||
public var diskTotalGB: Double
|
||||
public var diskUsedPercent: Double
|
||||
public var uptimeDays: Double
|
||||
public var optimizations: [AtlasOptimizationRecommendation]
|
||||
|
||||
public init(
|
||||
memoryUsedGB: Double,
|
||||
memoryTotalGB: Double,
|
||||
diskUsedGB: Double,
|
||||
diskTotalGB: Double,
|
||||
diskUsedPercent: Double,
|
||||
uptimeDays: Double,
|
||||
optimizations: [AtlasOptimizationRecommendation]
|
||||
) {
|
||||
self.memoryUsedGB = memoryUsedGB
|
||||
self.memoryTotalGB = memoryTotalGB
|
||||
self.diskUsedGB = diskUsedGB
|
||||
self.diskTotalGB = diskTotalGB
|
||||
self.diskUsedPercent = diskUsedPercent
|
||||
self.uptimeDays = uptimeDays
|
||||
self.optimizations = optimizations
|
||||
}
|
||||
}
|
||||
|
||||
public struct StorageInsight: Identifiable, Codable, Hashable, Sendable {
|
||||
public var id: UUID
|
||||
public var title: String
|
||||
public var path: String
|
||||
public var bytes: Int64
|
||||
public var ageDescription: String
|
||||
|
||||
public init(
|
||||
id: UUID = UUID(),
|
||||
title: String,
|
||||
path: String,
|
||||
bytes: Int64,
|
||||
ageDescription: String
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.path = path
|
||||
self.bytes = bytes
|
||||
self.ageDescription = ageDescription
|
||||
}
|
||||
}
|
||||
|
||||
public struct AtlasSettings: Codable, Hashable, Sendable {
|
||||
public var recoveryRetentionDays: Int
|
||||
public var notificationsEnabled: Bool
|
||||
public var excludedPaths: [String]
|
||||
public var language: AtlasLanguage
|
||||
public var acknowledgementText: String
|
||||
public var thirdPartyNoticesText: String
|
||||
|
||||
public init(
|
||||
recoveryRetentionDays: Int,
|
||||
notificationsEnabled: Bool,
|
||||
excludedPaths: [String],
|
||||
language: AtlasLanguage = .default,
|
||||
acknowledgementText: String? = nil,
|
||||
thirdPartyNoticesText: String? = nil
|
||||
) {
|
||||
self.recoveryRetentionDays = recoveryRetentionDays
|
||||
self.notificationsEnabled = notificationsEnabled
|
||||
self.excludedPaths = excludedPaths
|
||||
self.language = language
|
||||
self.acknowledgementText = acknowledgementText ?? AtlasL10n.acknowledgement(language: language)
|
||||
self.thirdPartyNoticesText = thirdPartyNoticesText ?? AtlasL10n.thirdPartyNotices(language: language)
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case recoveryRetentionDays
|
||||
case notificationsEnabled
|
||||
case excludedPaths
|
||||
case language
|
||||
case acknowledgementText
|
||||
case thirdPartyNoticesText
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let language = try container.decodeIfPresent(AtlasLanguage.self, forKey: .language) ?? .default
|
||||
self.recoveryRetentionDays = try container.decodeIfPresent(Int.self, forKey: .recoveryRetentionDays) ?? 7
|
||||
self.notificationsEnabled = try container.decodeIfPresent(Bool.self, forKey: .notificationsEnabled) ?? true
|
||||
self.excludedPaths = try container.decodeIfPresent([String].self, forKey: .excludedPaths) ?? []
|
||||
self.language = language
|
||||
self.acknowledgementText = try container.decodeIfPresent(String.self, forKey: .acknowledgementText)
|
||||
?? AtlasL10n.acknowledgement(language: language)
|
||||
self.thirdPartyNoticesText = try container.decodeIfPresent(String.self, forKey: .thirdPartyNoticesText)
|
||||
?? AtlasL10n.thirdPartyNotices(language: language)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(recoveryRetentionDays, forKey: .recoveryRetentionDays)
|
||||
try container.encode(notificationsEnabled, forKey: .notificationsEnabled)
|
||||
try container.encode(excludedPaths, forKey: .excludedPaths)
|
||||
try container.encode(language, forKey: .language)
|
||||
try container.encode(acknowledgementText, forKey: .acknowledgementText)
|
||||
try container.encode(thirdPartyNoticesText, forKey: .thirdPartyNoticesText)
|
||||
}
|
||||
}
|
||||
|
||||
public enum AtlasScaffoldFixtures {
|
||||
private static func uuid(_ value: String) -> UUID {
|
||||
UUID(uuidString: value) ?? UUID()
|
||||
}
|
||||
|
||||
private static let now = Date()
|
||||
|
||||
public static var findings: [Finding] {
|
||||
findings(language: AtlasL10n.currentLanguage)
|
||||
}
|
||||
|
||||
public static func findings(language: AtlasLanguage) -> [Finding] {
|
||||
[
|
||||
Finding(
|
||||
id: uuid("00000000-0000-0000-0000-000000000001"),
|
||||
title: AtlasL10n.string("fixture.finding.derivedData.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.finding.derivedData.detail", language: language),
|
||||
bytes: 18_400_000_000,
|
||||
risk: .safe,
|
||||
category: "Developer"
|
||||
),
|
||||
Finding(
|
||||
id: uuid("00000000-0000-0000-0000-000000000002"),
|
||||
title: AtlasL10n.string("fixture.finding.browserCaches.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.finding.browserCaches.detail", language: language),
|
||||
bytes: 4_800_000_000,
|
||||
risk: .safe,
|
||||
category: "System"
|
||||
),
|
||||
Finding(
|
||||
id: uuid("00000000-0000-0000-0000-000000000003"),
|
||||
title: AtlasL10n.string("fixture.finding.oldRuntimes.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.finding.oldRuntimes.detail", language: language),
|
||||
bytes: 12_100_000_000,
|
||||
risk: .review,
|
||||
category: "Developer"
|
||||
),
|
||||
Finding(
|
||||
id: uuid("00000000-0000-0000-0000-000000000004"),
|
||||
title: AtlasL10n.string("fixture.finding.launchAgents.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.finding.launchAgents.detail", language: language),
|
||||
bytes: 820_000_000,
|
||||
risk: .advanced,
|
||||
category: "Apps"
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
public static var actionPlan: ActionPlan {
|
||||
actionPlan(language: AtlasL10n.currentLanguage)
|
||||
}
|
||||
|
||||
public static func actionPlan(language: AtlasLanguage) -> ActionPlan {
|
||||
ActionPlan(
|
||||
id: uuid("00000000-0000-0000-0000-000000000010"),
|
||||
title: AtlasL10n.string("fixture.plan.reclaimCommonClutter.title", language: language),
|
||||
items: [
|
||||
ActionItem(
|
||||
id: uuid("00000000-0000-0000-0000-000000000011"),
|
||||
title: AtlasL10n.string("fixture.plan.item.moveDerivedData.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.plan.item.moveDerivedData.detail", language: language),
|
||||
kind: .removeCache,
|
||||
recoverable: true
|
||||
),
|
||||
ActionItem(
|
||||
id: uuid("00000000-0000-0000-0000-000000000012"),
|
||||
title: AtlasL10n.string("fixture.plan.item.reviewRuntimes.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.plan.item.reviewRuntimes.detail", language: language),
|
||||
kind: .archiveFile,
|
||||
recoverable: true
|
||||
),
|
||||
ActionItem(
|
||||
id: uuid("00000000-0000-0000-0000-000000000013"),
|
||||
title: AtlasL10n.string("fixture.plan.item.inspectAgents.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.plan.item.inspectAgents.detail", language: language),
|
||||
kind: .inspectPermission,
|
||||
recoverable: false
|
||||
),
|
||||
],
|
||||
estimatedBytes: 23_200_000_000
|
||||
)
|
||||
}
|
||||
|
||||
public static let apps: [AppFootprint] = [
|
||||
AppFootprint(
|
||||
id: uuid("00000000-0000-0000-0000-000000000020"),
|
||||
name: "Final Cut Pro",
|
||||
bundleIdentifier: "com.apple.FinalCut",
|
||||
bundlePath: "/Applications/Final Cut Pro.app",
|
||||
bytes: 9_600_000_000,
|
||||
leftoverItems: 6
|
||||
),
|
||||
AppFootprint(
|
||||
id: uuid("00000000-0000-0000-0000-000000000021"),
|
||||
name: "Xcode",
|
||||
bundleIdentifier: "com.apple.dt.Xcode",
|
||||
bundlePath: "/Applications/Xcode.app",
|
||||
bytes: 34_800_000_000,
|
||||
leftoverItems: 12
|
||||
),
|
||||
AppFootprint(
|
||||
id: uuid("00000000-0000-0000-0000-000000000022"),
|
||||
name: "Docker",
|
||||
bundleIdentifier: "com.docker.docker",
|
||||
bundlePath: "/Applications/Docker.app",
|
||||
bytes: 7_400_000_000,
|
||||
leftoverItems: 8
|
||||
),
|
||||
]
|
||||
|
||||
public static var taskRuns: [TaskRun] {
|
||||
taskRuns(language: AtlasL10n.currentLanguage)
|
||||
}
|
||||
|
||||
public static func taskRuns(language: AtlasLanguage) -> [TaskRun] {
|
||||
[
|
||||
TaskRun(
|
||||
id: uuid("00000000-0000-0000-0000-000000000030"),
|
||||
kind: .scan,
|
||||
status: .completed,
|
||||
summary: AtlasL10n.string("fixture.task.scan.summary", language: language),
|
||||
startedAt: now.addingTimeInterval(-9_000),
|
||||
finishedAt: now.addingTimeInterval(-8_940)
|
||||
),
|
||||
TaskRun(
|
||||
id: uuid("00000000-0000-0000-0000-000000000031"),
|
||||
kind: .executePlan,
|
||||
status: .running,
|
||||
summary: AtlasL10n.string("fixture.task.execute.summary", language: language),
|
||||
startedAt: now.addingTimeInterval(-800)
|
||||
),
|
||||
TaskRun(
|
||||
id: uuid("00000000-0000-0000-0000-000000000032"),
|
||||
kind: .inspectPermissions,
|
||||
status: .completed,
|
||||
summary: AtlasL10n.string("fixture.task.permissions.summary", language: language),
|
||||
startedAt: now.addingTimeInterval(-300),
|
||||
finishedAt: now.addingTimeInterval(-285)
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
public static var recoveryItems: [RecoveryItem] {
|
||||
recoveryItems(language: AtlasL10n.currentLanguage)
|
||||
}
|
||||
|
||||
public static func recoveryItems(language: AtlasLanguage) -> [RecoveryItem] {
|
||||
[
|
||||
RecoveryItem(
|
||||
id: uuid("00000000-0000-0000-0000-000000000040"),
|
||||
title: AtlasL10n.string("fixture.recovery.chromeCache.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.recovery.chromeCache.detail", language: language),
|
||||
originalPath: "~/Library/Caches/Google/Chrome",
|
||||
bytes: 1_200_000_000,
|
||||
deletedAt: now.addingTimeInterval(-86_400),
|
||||
expiresAt: now.addingTimeInterval(518_400),
|
||||
payload: .finding(
|
||||
Finding(
|
||||
title: AtlasL10n.string("fixture.recovery.chromeCache.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.recovery.chromeCache.payload", language: language),
|
||||
bytes: 1_200_000_000,
|
||||
risk: .safe,
|
||||
category: "Browsers"
|
||||
)
|
||||
)
|
||||
),
|
||||
RecoveryItem(
|
||||
id: uuid("00000000-0000-0000-0000-000000000041"),
|
||||
title: AtlasL10n.string("fixture.recovery.simulatorSupport.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.recovery.simulatorSupport.detail", language: language),
|
||||
originalPath: "~/Library/Developer/Xcode/iOS DeviceSupport",
|
||||
bytes: 3_400_000_000,
|
||||
deletedAt: now.addingTimeInterval(-172_800),
|
||||
expiresAt: now.addingTimeInterval(432_000),
|
||||
payload: .finding(
|
||||
Finding(
|
||||
title: AtlasL10n.string("fixture.recovery.simulatorSupport.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.recovery.simulatorSupport.payload", language: language),
|
||||
bytes: 3_400_000_000,
|
||||
risk: .review,
|
||||
category: "Developer"
|
||||
)
|
||||
)
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
public static var permissions: [PermissionState] {
|
||||
permissions(language: AtlasL10n.currentLanguage)
|
||||
}
|
||||
|
||||
public static func permissions(language: AtlasLanguage) -> [PermissionState] {
|
||||
[
|
||||
PermissionState(
|
||||
kind: .fullDiskAccess,
|
||||
isGranted: false,
|
||||
rationale: AtlasL10n.string("fixture.permission.fullDiskAccess.rationale", language: language)
|
||||
),
|
||||
PermissionState(
|
||||
kind: .accessibility,
|
||||
isGranted: false,
|
||||
rationale: AtlasL10n.string("fixture.permission.accessibility.rationale", language: language)
|
||||
),
|
||||
PermissionState(
|
||||
kind: .notifications,
|
||||
isGranted: true,
|
||||
rationale: AtlasL10n.string("fixture.permission.notifications.rationale", language: language)
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
public static var healthSnapshot: AtlasHealthSnapshot {
|
||||
healthSnapshot(language: AtlasL10n.currentLanguage)
|
||||
}
|
||||
|
||||
public static func healthSnapshot(language: AtlasLanguage) -> AtlasHealthSnapshot {
|
||||
AtlasHealthSnapshot(
|
||||
memoryUsedGB: 14.2,
|
||||
memoryTotalGB: 24.0,
|
||||
diskUsedGB: 303.0,
|
||||
diskTotalGB: 460.0,
|
||||
diskUsedPercent: 65.9,
|
||||
uptimeDays: 6.4,
|
||||
optimizations: [
|
||||
AtlasOptimizationRecommendation(
|
||||
category: "system",
|
||||
name: AtlasL10n.string("fixture.health.optimization.dns.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.health.optimization.dns.detail", language: language),
|
||||
action: "system_maintenance",
|
||||
isSafe: true
|
||||
),
|
||||
AtlasOptimizationRecommendation(
|
||||
category: "system",
|
||||
name: AtlasL10n.string("fixture.health.optimization.finder.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.health.optimization.finder.detail", language: language),
|
||||
action: "cache_refresh",
|
||||
isSafe: true
|
||||
),
|
||||
AtlasOptimizationRecommendation(
|
||||
category: "system",
|
||||
name: AtlasL10n.string("fixture.health.optimization.memory.title", language: language),
|
||||
detail: AtlasL10n.string("fixture.health.optimization.memory.detail", language: language),
|
||||
action: "memory_pressure_relief",
|
||||
isSafe: true
|
||||
),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
public static var storageInsights: [StorageInsight] {
|
||||
storageInsights(language: AtlasL10n.currentLanguage)
|
||||
}
|
||||
|
||||
public static func storageInsights(language: AtlasLanguage) -> [StorageInsight] {
|
||||
[
|
||||
StorageInsight(
|
||||
id: uuid("00000000-0000-0000-0000-000000000050"),
|
||||
title: AtlasL10n.string("fixture.storage.downloads.title", language: language),
|
||||
path: "~/Downloads",
|
||||
bytes: 13_100_000_000,
|
||||
ageDescription: AtlasL10n.string("fixture.storage.downloads.age", language: language)
|
||||
),
|
||||
StorageInsight(
|
||||
id: uuid("00000000-0000-0000-0000-000000000051"),
|
||||
title: AtlasL10n.string("fixture.storage.movies.title", language: language),
|
||||
path: "~/Movies/Exports",
|
||||
bytes: 21_400_000_000,
|
||||
ageDescription: AtlasL10n.string("fixture.storage.movies.age", language: language)
|
||||
),
|
||||
StorageInsight(
|
||||
id: uuid("00000000-0000-0000-0000-000000000052"),
|
||||
title: AtlasL10n.string("fixture.storage.installers.title", language: language),
|
||||
path: "~/Desktop/Installers",
|
||||
bytes: 6_200_000_000,
|
||||
ageDescription: AtlasL10n.string("fixture.storage.installers.age", language: language)
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
public static let settings: AtlasSettings = settings(language: .default)
|
||||
|
||||
public static func settings(language: AtlasLanguage) -> AtlasSettings {
|
||||
AtlasSettings(
|
||||
recoveryRetentionDays: 7,
|
||||
notificationsEnabled: true,
|
||||
excludedPaths: [
|
||||
"~/Projects/ActiveClientWork",
|
||||
"~/Movies/Exports",
|
||||
],
|
||||
language: language
|
||||
)
|
||||
}
|
||||
}
|
||||
101
Packages/AtlasDomain/Sources/AtlasDomain/AtlasLocalization.swift
Normal file
101
Packages/AtlasDomain/Sources/AtlasDomain/AtlasLocalization.swift
Normal file
@@ -0,0 +1,101 @@
|
||||
import Foundation
|
||||
|
||||
public enum AtlasLanguage: String, CaseIterable, Codable, Hashable, Sendable, Identifiable {
|
||||
case zhHans = "zh-Hans"
|
||||
case en = "en"
|
||||
|
||||
public static let `default`: AtlasLanguage = .zhHans
|
||||
|
||||
public var id: String { rawValue }
|
||||
|
||||
public init(localeIdentifier: String) {
|
||||
let normalized = localeIdentifier.lowercased()
|
||||
if normalized.hasPrefix("en") {
|
||||
self = .en
|
||||
} else {
|
||||
self = .zhHans
|
||||
}
|
||||
}
|
||||
|
||||
public var locale: Locale {
|
||||
Locale(identifier: rawValue)
|
||||
}
|
||||
|
||||
public var displayName: String {
|
||||
switch self {
|
||||
case .zhHans:
|
||||
return "简体中文"
|
||||
case .en:
|
||||
return "English"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum AtlasL10n {
|
||||
private static let stateLock = NSLock()
|
||||
private static var storedLanguage: AtlasLanguage = .default
|
||||
|
||||
public static var currentLanguage: AtlasLanguage {
|
||||
stateLock.withLock {
|
||||
storedLanguage
|
||||
}
|
||||
}
|
||||
|
||||
public static func setCurrentLanguage(_ language: AtlasLanguage) {
|
||||
stateLock.withLock {
|
||||
storedLanguage = language
|
||||
}
|
||||
}
|
||||
|
||||
public static func string(_ key: String, language: AtlasLanguage? = nil, _ arguments: CVarArg...) -> String {
|
||||
string(key, language: language, arguments: arguments)
|
||||
}
|
||||
|
||||
public static func string(_ key: String, language: AtlasLanguage? = nil, arguments: [CVarArg]) -> String {
|
||||
let resolvedLanguage = language ?? currentLanguage
|
||||
let format = bundle(for: resolvedLanguage).localizedString(forKey: key, value: nil, table: nil)
|
||||
guard !arguments.isEmpty else {
|
||||
return format
|
||||
}
|
||||
return String(format: format, locale: resolvedLanguage.locale, arguments: arguments)
|
||||
}
|
||||
|
||||
public static func localizedCategory(_ rawCategory: String, language: AtlasLanguage? = nil) -> String {
|
||||
switch rawCategory.lowercased() {
|
||||
case "developer":
|
||||
return string("category.developer", language: language)
|
||||
case "system":
|
||||
return string("category.system", language: language)
|
||||
case "apps":
|
||||
return string("category.apps", language: language)
|
||||
case "browsers":
|
||||
return string("category.browsers", language: language)
|
||||
default:
|
||||
return rawCategory
|
||||
}
|
||||
}
|
||||
|
||||
public static func acknowledgement(language: AtlasLanguage? = nil) -> String {
|
||||
string("settings.acknowledgement.body", language: language)
|
||||
}
|
||||
|
||||
public static func thirdPartyNotices(language: AtlasLanguage? = nil) -> String {
|
||||
string("settings.notices.body", language: language)
|
||||
}
|
||||
|
||||
private static func bundle(for language: AtlasLanguage) -> Bundle {
|
||||
guard let path = Bundle.module.path(forResource: language.rawValue, ofType: "lproj"),
|
||||
let bundle = Bundle(path: path) else {
|
||||
return Bundle.module
|
||||
}
|
||||
return bundle
|
||||
}
|
||||
}
|
||||
|
||||
private extension NSLock {
|
||||
func withLock<T>(_ body: () -> T) -> T {
|
||||
lock()
|
||||
defer { unlock() }
|
||||
return body()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,561 @@
|
||||
"app.name" = "Atlas for Mac";
|
||||
"language.zhHans" = "Simplified Chinese";
|
||||
"language.en" = "English";
|
||||
|
||||
"route.overview.title" = "Overview";
|
||||
"route.overview.subtitle" = "See health, reclaimable space, and the next safe step first.";
|
||||
"route.smartclean.title" = "Smart Clean";
|
||||
"route.smartclean.subtitle" = "Build and review a cleanup plan before you run anything.";
|
||||
"route.apps.title" = "Apps";
|
||||
"route.apps.subtitle" = "Review app footprints, leftovers, and uninstall plans before you remove anything.";
|
||||
"route.history.title" = "History";
|
||||
"route.history.subtitle" = "Track runs, outcomes, and recovery entry points.";
|
||||
"route.permissions.title" = "Permissions";
|
||||
"route.permissions.subtitle" = "Explain why access is needed before asking for it.";
|
||||
"route.settings.title" = "Settings";
|
||||
"route.settings.subtitle" = "Preferences, exclusions, acknowledgement, and notices.";
|
||||
|
||||
"risk.safe" = "Safe";
|
||||
"risk.review" = "Review";
|
||||
"risk.advanced" = "Advanced";
|
||||
|
||||
"taskkind.scan" = "Smart Clean Scan";
|
||||
"taskkind.executePlan" = "Run Cleanup Plan";
|
||||
"taskkind.uninstallApp" = "Uninstall App";
|
||||
"taskkind.restore" = "Restore Item";
|
||||
"taskkind.inspectPermissions" = "Check Permission Status";
|
||||
|
||||
"taskstatus.queued" = "Queued";
|
||||
"taskstatus.running" = "Running";
|
||||
"taskstatus.completed" = "Completed";
|
||||
"taskstatus.failed" = "Failed";
|
||||
"taskstatus.cancelled" = "Cancelled";
|
||||
|
||||
"permission.fullDiskAccess" = "Full Disk Access";
|
||||
"permission.accessibility" = "Accessibility";
|
||||
"permission.notifications" = "Notifications";
|
||||
|
||||
"category.developer" = "Developer";
|
||||
"category.system" = "System";
|
||||
"category.apps" = "Apps";
|
||||
"category.browsers" = "Browsers";
|
||||
|
||||
"settings.acknowledgement.body" = "Atlas for Mac includes software derived from the open-source project Mole by tw93 and contributors, used under the MIT License. Atlas for Mac is an independent product and is not affiliated with or endorsed by the original authors.";
|
||||
"settings.notices.body" = "This product includes software derived from the open-source project Mole by tw93 and contributors, used under the MIT License.";
|
||||
"fixture.finding.derivedData.title" = "Xcode Derived Data";
|
||||
"fixture.finding.derivedData.detail" = "Build artifacts and indexes from older projects.";
|
||||
"fixture.finding.browserCaches.title" = "Browser caches";
|
||||
"fixture.finding.browserCaches.detail" = "WebKit and Chromium cache folders with low recovery risk.";
|
||||
"fixture.finding.oldRuntimes.title" = "Old simulator runtimes";
|
||||
"fixture.finding.oldRuntimes.detail" = "Unused iOS simulator assets that need review before deletion.";
|
||||
"fixture.finding.launchAgents.title" = "Launch-agent leftovers";
|
||||
"fixture.finding.launchAgents.detail" = "Background helpers tied to removed apps and older experiments.";
|
||||
|
||||
"fixture.plan.reclaimCommonClutter.title" = "Reclaim common clutter";
|
||||
"fixture.plan.item.moveDerivedData.title" = "Move Xcode Derived Data to Trash";
|
||||
"fixture.plan.item.moveDerivedData.detail" = "Keep recovery available from History and Recovery.";
|
||||
"fixture.plan.item.reviewRuntimes.title" = "Review simulator runtimes";
|
||||
"fixture.plan.item.reviewRuntimes.detail" = "Ask the worker to confirm the plan details before execution.";
|
||||
"fixture.plan.item.inspectAgents.title" = "Inspect launch agents";
|
||||
"fixture.plan.item.inspectAgents.detail" = "Require helper validation before removing privileged items.";
|
||||
|
||||
"fixture.task.scan.summary" = "Scanned 214 locations and generated a cleanup plan that can free 35.3 GB.";
|
||||
"fixture.task.execute.summary" = "Moving safe cache items into recovery storage.";
|
||||
"fixture.task.permissions.summary" = "Permission status refreshed without prompting the user.";
|
||||
|
||||
"fixture.recovery.chromeCache.title" = "Chrome cache bundle";
|
||||
"fixture.recovery.chromeCache.detail" = "Recoverable cache package from the previous Smart Clean run.";
|
||||
"fixture.recovery.chromeCache.payload" = "Recovered cache data from the previous Smart Clean run.";
|
||||
"fixture.recovery.simulatorSupport.title" = "Legacy simulator device support";
|
||||
"fixture.recovery.simulatorSupport.detail" = "This item still needs review and will clear after one week.";
|
||||
"fixture.recovery.simulatorSupport.payload" = "Simulator support files waiting for review.";
|
||||
|
||||
"fixture.permission.fullDiskAccess.rationale" = "Needed only when scanning system-wide caches, app leftovers, and protected Library paths.";
|
||||
"fixture.permission.accessibility.rationale" = "Needed later for app uninstall flows that need to close running apps safely.";
|
||||
"fixture.permission.notifications.rationale" = "Used for long-running task completion and recovery reminders.";
|
||||
|
||||
"fixture.health.optimization.dns.title" = "DNS & Spotlight Check";
|
||||
"fixture.health.optimization.dns.detail" = "Refresh DNS cache and verify Spotlight status.";
|
||||
"fixture.health.optimization.finder.title" = "Finder Cache Refresh";
|
||||
"fixture.health.optimization.finder.detail" = "Refresh QuickLook thumbnails and icon services cache.";
|
||||
"fixture.health.optimization.memory.title" = "Memory Optimization";
|
||||
"fixture.health.optimization.memory.detail" = "Release inactive memory to improve system responsiveness.";
|
||||
|
||||
"fixture.storage.downloads.title" = "Downloads archive";
|
||||
"fixture.storage.downloads.age" = "Mostly older than 90 days";
|
||||
"fixture.storage.movies.title" = "Movies export folder";
|
||||
"fixture.storage.movies.age" = "Large media files from the last 6 months";
|
||||
"fixture.storage.installers.title" = "Unused installers";
|
||||
"fixture.storage.installers.age" = "Mostly disk images older than 30 days";
|
||||
"application.error.workerRejected" = "Worker rejected request (%@): %@";
|
||||
"xpc.error.encodingFailed" = "Could not encode the background worker request: %@";
|
||||
"xpc.error.decodingFailed" = "Could not decode the background worker response: %@";
|
||||
"xpc.error.invalidResponse" = "The background worker returned an invalid response. Fully quit and reopen Atlas; if it still fails, reinstall the current build.";
|
||||
"xpc.error.connectionUnavailable" = "The background worker is unavailable. Fully quit Atlas and reopen the installed copy from Applications; if it still fails, remove older copies and reinstall. System detail: %@";
|
||||
"xpc.error.timedOut" = "The background worker timed out after %.1f seconds. Try again; if it keeps happening, reopen Atlas.";
|
||||
"application.plan.inspectPrivileged" = "Inspect privileged cleanup for %@";
|
||||
"application.plan.reviewFinding" = "Review %@";
|
||||
"application.plan.reviewSelected.one" = "Review 1 selected finding";
|
||||
"application.plan.reviewSelected.other" = "Review %d selected findings";
|
||||
"application.scan.completed" = "Smart Clean scan completed and a cleanup plan is ready.";
|
||||
"application.preview.updated.one" = "Updated the cleanup plan for 1 action.";
|
||||
"application.preview.updated.other" = "Updated the cleanup plan for %d actions.";
|
||||
"application.plan.executed" = "Cleanup plan completed.";
|
||||
"application.recovery.completed" = "Recovery restore completed.";
|
||||
"application.apps.loaded.one" = "Loaded 1 app footprint.";
|
||||
"application.apps.loaded.other" = "Loaded %d app footprints.";
|
||||
"application.apps.previewUpdated" = "Updated the uninstall plan for %@.";
|
||||
"application.apps.uninstallCompleted" = "App uninstall completed.";
|
||||
|
||||
"infrastructure.permission.fullDiskAccess.granted" = "Atlas can inspect protected user data locations required for broader cleanup coverage.";
|
||||
"infrastructure.permission.fullDiskAccess.needed" = "Needed only when scanning protected Library areas such as Mail, Safari, or Messages data. If you just enabled it in System Settings, fully quit and reopen Atlas before you check again.";
|
||||
"infrastructure.permission.accessibility.granted" = "Atlas can coordinate app shutdown for safer uninstall flows.";
|
||||
"infrastructure.permission.accessibility.needed" = "Needed later when uninstall flows must close running apps safely before cleanup.";
|
||||
"infrastructure.permission.notifications.granted" = "Atlas can send completion and recovery reminders for long-running tasks.";
|
||||
"infrastructure.permission.notifications.needed" = "Used for long-running task completion and recovery reminders.";
|
||||
"infrastructure.scan.completed.one" = "Scanned 1 finding group and generated a cleanup plan.";
|
||||
"infrastructure.scan.completed.other" = "Scanned %d finding groups and generated a cleanup plan.";
|
||||
"infrastructure.execute.summary.clean.one" = "Moved 1 Smart Clean item into recovery.";
|
||||
"infrastructure.execute.summary.clean.other" = "Moved %d Smart Clean items into recovery.";
|
||||
"infrastructure.execute.summary.clean.mixed" = "Moved %d Smart Clean items into recovery; %d advanced items still need review.";
|
||||
"infrastructure.restore.summary.one" = "Restored 1 item back into the workspace.";
|
||||
"infrastructure.restore.summary.other" = "Restored %d items back into the workspace.";
|
||||
"infrastructure.apps.loaded.one" = "Loaded 1 app footprint.";
|
||||
"infrastructure.apps.loaded.other" = "Loaded %d app footprints.";
|
||||
"infrastructure.apps.preview.summary" = "Generated an uninstall plan for %@.";
|
||||
"infrastructure.apps.uninstall.summary" = "Moved %@ and its leftovers into recovery.";
|
||||
"infrastructure.recovery.app.detail.one" = "Recoverable uninstall bundle with 1 leftover item.";
|
||||
"infrastructure.recovery.app.detail.other" = "Recoverable uninstall bundle with %d leftover items.";
|
||||
"infrastructure.plan.review.one" = "Review 1 selected finding";
|
||||
"infrastructure.plan.review.other" = "Review %d selected findings";
|
||||
"infrastructure.plan.uninstall.title" = "Uninstall %@";
|
||||
"infrastructure.plan.uninstall.moveBundle.title" = "Move %@ bundle into recovery";
|
||||
"infrastructure.plan.uninstall.moveBundle.detail" = "Trash the bundle at %@ while keeping the uninstall visible in History.";
|
||||
"infrastructure.plan.uninstall.archive.one" = "Archive 1 leftover item";
|
||||
"infrastructure.plan.uninstall.archive.other" = "Archive %d leftover items";
|
||||
"infrastructure.plan.uninstall.archive.detail" = "Support files, caches, and launch items remain reviewable through recovery history.";
|
||||
"infrastructure.action.reviewUninstall" = "Review uninstall plan for %@";
|
||||
"infrastructure.action.inspectPrivileged" = "Inspect privileged cleanup for %@";
|
||||
"infrastructure.action.archiveRecovery" = "Archive %@ into recovery";
|
||||
"infrastructure.action.moveToTrash" = "Move %@ to Trash";
|
||||
"app.search.prompt" = "Search findings, apps, history";
|
||||
"app.search.hint" = "Searches the visible content in the current route.";
|
||||
"app.search.prompt.route" = "Search %@";
|
||||
"app.search.hint.route" = "Searches the visible content in %@.";
|
||||
|
||||
"commands.navigate.menu" = "Navigate";
|
||||
"commands.actions.menu" = "Actions";
|
||||
"commands.taskcenter.open" = "Open Task Center";
|
||||
"commands.taskcenter.close" = "Close Task Center";
|
||||
"commands.actions.refreshCurrent" = "Refresh Current Screen";
|
||||
"commands.actions.runScan" = "Run Smart Clean Scan";
|
||||
"commands.actions.refreshApps" = "Refresh App Footprints";
|
||||
"commands.actions.refreshPermissions" = "Check Permission Status";
|
||||
"commands.actions.refreshHealth" = "Refresh System Snapshot";
|
||||
|
||||
"toolbar.taskcenter" = "Task Center";
|
||||
"toolbar.taskcenter.help" = "Open recent task activity (⌘7)";
|
||||
"toolbar.taskcenter.accessibilityLabel" = "Open Task Center";
|
||||
"toolbar.taskcenter.accessibilityHint" = "Shows recent task activity and a shortcut into History.";
|
||||
"toolbar.permissions" = "Permissions";
|
||||
"toolbar.permissions.help" = "Check permission status (⌥⌘P)";
|
||||
"toolbar.permissions.accessibilityLabel" = "Open Permissions and check status";
|
||||
"toolbar.permissions.accessibilityHint" = "Takes you to the Permissions screen and checks the current permission snapshot.";
|
||||
"toolbar.settings" = "Settings";
|
||||
"toolbar.settings.help" = "Open settings (⌘6)";
|
||||
"toolbar.settings.accessibilityLabel" = "Open Settings";
|
||||
"toolbar.settings.accessibilityHint" = "Takes you to preferences, retention, notifications, and notices.";
|
||||
"sidebar.route.hint" = "Keyboard shortcut Command-%@.";
|
||||
|
||||
"common.days" = "days";
|
||||
"common.enabled" = "Enabled";
|
||||
"common.disabled" = "Disabled";
|
||||
"common.granted" = "Granted";
|
||||
"common.neededLater" = "Not Needed Yet";
|
||||
"common.recoverable" = "Recoverable";
|
||||
"common.manualReview" = "Needs Review";
|
||||
"common.visible" = "Visible";
|
||||
|
||||
"model.scan.ready" = "Ready to build a cleanup plan.";
|
||||
"model.scan.submitting" = "Starting a Smart Clean scan and building a cleanup plan…";
|
||||
"model.apps.ready" = "Ready to inspect app footprints and build uninstall plans.";
|
||||
"model.apps.refreshing" = "Refreshing app footprints and uninstall plans…";
|
||||
"model.permissions.ready" = "Check permission status whenever you want an updated access snapshot.";
|
||||
"model.permissions.refreshing" = "Checking permission status…";
|
||||
"model.permissions.summary.one" = "%d of %d tracked permissions is currently available.";
|
||||
"model.permissions.summary.other" = "%d of %d tracked permissions are currently available.";
|
||||
"model.taskcenter.none" = "No active tasks";
|
||||
"model.taskcenter.matching.one" = "1 matching task";
|
||||
"model.taskcenter.matching.other" = "%d matching tasks";
|
||||
"model.taskcenter.active.one" = "1 active task";
|
||||
"model.taskcenter.active.other" = "%d active tasks";
|
||||
|
||||
"taskcenter.title" = "Task Center";
|
||||
"taskcenter.callout.empty.title" = "No matching task activity";
|
||||
"taskcenter.callout.empty.detail" = "Run a scan, run a cleanup plan, uninstall an app, or restore an item to start the task timeline.";
|
||||
"taskcenter.callout.active.title" = "Recent task activity stays visible here";
|
||||
"taskcenter.callout.active.detail" = "Open History for the full audit trail and available recovery items.";
|
||||
"taskcenter.empty.title" = "No tasks yet";
|
||||
"taskcenter.empty.detail" = "Try another search, or run a new Smart Clean scan to populate the task timeline.";
|
||||
"taskcenter.openHistory" = "Open History";
|
||||
"taskcenter.openHistory.hint" = "Moves to the History screen with the full audit trail and recovery items.";
|
||||
"taskcenter.timeline.finished" = "Started %@ • Finished %@";
|
||||
"taskcenter.timeline.running" = "Started %@ • Still in progress";
|
||||
|
||||
"overview.screen.title" = "Overview";
|
||||
"overview.screen.subtitle" = "See what matters first, understand the trade-offs, and move into the next safe maintenance step with confidence.";
|
||||
"overview.callout.ready.title" = "Atlas is ready for the current core workflows";
|
||||
"overview.callout.ready.detail" = "The core permissions required right now are available. Optional access such as Accessibility or notifications no longer marks the app as limited.";
|
||||
"overview.callout.limited.title" = "You can keep going in limited mode";
|
||||
"overview.callout.limited.detail" = "At least one permission required for the current workflows is still unavailable, so Atlas stays in limited mode until that access is ready.";
|
||||
"overview.metric.reclaimable.title" = "Reclaimable Space";
|
||||
"overview.metric.reclaimable.detail" = "Estimated from the current cleanup plan and recent workspace state. After a plan runs, this value recalculates from the remaining items.";
|
||||
"overview.metric.findings.title" = "Findings Ready";
|
||||
"overview.metric.findings.detail" = "Grouped into Safe, Review, and Advanced lanes before you execute anything.";
|
||||
"overview.metric.permissions.title" = "Permissions Ready";
|
||||
"overview.metric.permissions.ready" = "Atlas has the access it needs for the current workflows.";
|
||||
"overview.metric.permissions.limited" = "A permission required for the current workflows is still missing, so Atlas remains in limited mode.";
|
||||
"overview.snapshot.title" = "System Snapshot";
|
||||
"overview.snapshot.subtitle" = "High-signal health data and the safest next optimizations.";
|
||||
"overview.snapshot.loading.title" = "Refreshing system snapshot";
|
||||
"overview.snapshot.loading.detail" = "Atlas is collecting the latest health information before updating the recommendations.";
|
||||
"overview.snapshot.memory.title" = "Memory Used";
|
||||
"overview.snapshot.memory.detail" = "A quick signal for memory pressure and long-lived background load.";
|
||||
"overview.snapshot.disk.title" = "Disk Used";
|
||||
"overview.snapshot.disk.detail" = "%@/%@ GB on the current system volume.";
|
||||
"overview.snapshot.uptime.title" = "Uptime";
|
||||
"overview.snapshot.uptime.detail" = "Useful for spotting stale caches, long sessions, and restart candidates.";
|
||||
"overview.snapshot.callout.warning.title" = "Storage pressure is worth addressing soon";
|
||||
"overview.snapshot.callout.warning.detail" = "Start with the Safe lane in Smart Clean to reclaim space without widening risk.";
|
||||
"overview.snapshot.callout.ok.title" = "Your system looks stable enough for a safe cleanup pass";
|
||||
"overview.snapshot.callout.ok.detail" = "Review the recommended maintenance items below, then open Smart Clean when you want a deeper plan.";
|
||||
"overview.snapshot.empty.title" = "System snapshot unavailable";
|
||||
"overview.snapshot.empty.detail" = "Atlas can still show persisted findings and recovery data while it waits for the next health refresh.";
|
||||
"overview.actions.title" = "Recommended Actions";
|
||||
"overview.actions.subtitle" = "The highest-value findings from the current workspace snapshot.";
|
||||
"overview.actions.empty.title" = "No matching findings";
|
||||
"overview.actions.empty.detail" = "Run Smart Clean again or clear the current search to repopulate the recommended actions list.";
|
||||
"overview.activity.title" = "Recent Activity";
|
||||
"overview.activity.subtitle" = "See what Atlas changed recently and which actions are still recoverable.";
|
||||
"overview.activity.empty.title" = "No recent activity";
|
||||
"overview.activity.empty.detail" = "Your task timeline will appear here after the next scan, execution, or restore action.";
|
||||
"overview.activity.timeline.finished" = "Started %@ • Finished %@";
|
||||
"overview.activity.timeline.running" = "Started %@ • Still in progress";
|
||||
"overview.risk.safe" = "Low-risk cleanup";
|
||||
"overview.risk.review" = "Review before removing";
|
||||
"overview.risk.advanced" = "Advanced inspection recommended";
|
||||
|
||||
"smartclean.screen.title" = "Smart Clean";
|
||||
"smartclean.screen.subtitle" = "Turn scan results into a clear cleanup plan before you decide to run anything destructive.";
|
||||
"smartclean.controls.title" = "Scan & Plan";
|
||||
"smartclean.controls.subtitle" = "Run a scan, update the plan, then choose the safest next step.";
|
||||
"smartclean.loading.scan" = "Smart Clean is scanning";
|
||||
"smartclean.loading.execute" = "Running the reviewed cleanup plan";
|
||||
"smartclean.action.runScan" = "Run Scan";
|
||||
"smartclean.action.runScan.hint" = "Starts a new Smart Clean scan and rebuilds the current cleanup plan.";
|
||||
"smartclean.action.refreshPreview" = "Update Plan";
|
||||
"smartclean.action.refreshPreview.hint" = "Regenerates the cleanup plan from current findings without running anything.";
|
||||
"smartclean.action.execute" = "Run Plan";
|
||||
"smartclean.action.execute.hint" = "Runs the reviewed cleanup plan and keeps recoverable work visible in History.";
|
||||
"smartclean.primary.scan.title" = "Start with a fresh scan";
|
||||
"smartclean.primary.scan.detail" = "Scan first when you do not yet have a current plan to review.";
|
||||
"smartclean.primary.refresh.title" = "Refresh the current plan";
|
||||
"smartclean.primary.refresh.detail" = "Update the plan before you trust it, especially after the findings or validation state changed.";
|
||||
"smartclean.primary.execute.title" = "Run the reviewed plan";
|
||||
"smartclean.primary.execute.detail" = "The current plan is ready, so execution becomes the single primary action.";
|
||||
"smartclean.metric.previewSize.title" = "Estimated Space";
|
||||
"smartclean.metric.previewSize.detail" = "Estimated space the current cleanup plan can free. After a plan runs, this value recalculates from the remaining items.";
|
||||
"smartclean.metric.actions.title" = "Selected Actions";
|
||||
"smartclean.metric.actions.detail" = "Every step is listed before Atlas changes anything.";
|
||||
"smartclean.metric.review.title" = "Needs Review";
|
||||
"smartclean.metric.review.none" = "Everything in this plan is recoverable.";
|
||||
"smartclean.metric.review.some" = "These items deserve a closer look before execution.";
|
||||
"smartclean.preview.title" = "Current Cleanup Plan";
|
||||
"smartclean.execution.real" = "Can Run";
|
||||
"smartclean.execution.reviewOnly" = "Review Only";
|
||||
"smartclean.execution.coverage.full" = "All %d steps in this plan can run directly";
|
||||
"smartclean.execution.coverage.partial" = "Only %d of %d steps in this plan can run directly";
|
||||
"smartclean.execution.coverage.full.detail" = "These steps will really move items to Trash and support restore when a recovery path is available.";
|
||||
"smartclean.execution.coverage.partial.detail" = "%d remaining step(s) still need review or are not supported for direct execution yet.";
|
||||
"smartclean.preview.metric.space.title" = "This Plan Can Free";
|
||||
"smartclean.preview.metric.space.detail.one" = "Estimated across 1 planned step.";
|
||||
"smartclean.preview.metric.space.detail.other" = "Estimated across %d planned steps.";
|
||||
"smartclean.preview.callout.safe.title" = "This plan stays mostly in the Safe lane";
|
||||
"smartclean.preview.callout.safe.detail" = "Most selected steps should remain recoverable through History and Recovery.";
|
||||
"smartclean.preview.empty.detail" = "Run a scan or update the plan to turn current findings into concrete cleanup steps. If you just ran a plan, this section shows only the remaining items.";
|
||||
"smartclean.preview.callout.review.detail" = "Check the highlighted steps before you run the plan so you understand what stays recoverable and what needs extra judgment.";
|
||||
"smartclean.preview.empty.title" = "No cleanup plan yet";
|
||||
"smartclean.preview.empty.detail" = "Run a scan or update the plan to turn current findings into concrete cleanup steps.";
|
||||
"smartclean.empty.title" = "No matching findings";
|
||||
"smartclean.empty.detail" = "Clear the current search or run another scan to repopulate the Smart Clean lanes.";
|
||||
"smartclean.status.scanning" = "Scanning for reclaimable clutter";
|
||||
"smartclean.status.executing" = "Applying the reviewed cleanup plan";
|
||||
"smartclean.status.empty" = "Run a fresh scan to generate a cleanup plan";
|
||||
"smartclean.status.cached" = "This plan has not been revalidated yet";
|
||||
"smartclean.status.revalidationFailed" = "Could not update the current plan";
|
||||
"smartclean.cached.title" = "This plan is from a previous result";
|
||||
"smartclean.revalidationFailed.title" = "Revalidation failed, so this still shows the previous plan";
|
||||
"smartclean.cached.detail" = "Run a fresh scan or update the plan before you execute it. What you see here is the last saved plan, not a verified current result.";
|
||||
"smartclean.status.empty.detail" = "Atlas always turns scan results into a cleanup plan before anything runs, so you can review risk and recoverability first.";
|
||||
"smartclean.status.ready" = "Review the plan, then run it";
|
||||
"smartclean.status.ready.detail" = "%d findings are ready. Start with the Safe lane, then review anything marked Review or Advanced before you run the plan.";
|
||||
"smartclean.support.removeCache" = "Good candidate for a reversible cleanup step.";
|
||||
"smartclean.support.removeApp" = "Review the footprint before you uninstall so leftovers are expected.";
|
||||
"smartclean.support.archiveFile" = "Atlas will keep this in a reversible path when possible.";
|
||||
"smartclean.support.inspectPermission" = "Requires extra attention before Atlas can safely proceed.";
|
||||
"smartclean.section.safe" = "High-confidence cleanup candidates that should stay easy to recover.";
|
||||
"smartclean.section.review" = "Worth a quick review before you remove them.";
|
||||
"smartclean.section.advanced" = "Potentially sensitive items that deserve an informed decision.";
|
||||
"smartclean.expectation.safe" = "Usually recoverable";
|
||||
"smartclean.expectation.review" = "Review before removal";
|
||||
"smartclean.expectation.advanced" = "Inspect carefully";
|
||||
|
||||
"apps.screen.title" = "Apps";
|
||||
"apps.screen.subtitle" = "Review each app's footprint, leftovers, and recovery path before you uninstall it.";
|
||||
"apps.callout.default.title" = "Review the uninstall plan before you remove anything";
|
||||
"apps.callout.default.detail" = "Atlas lists large app footprints first, then builds an uninstall plan so you can review leftovers and recovery expectations.";
|
||||
"apps.callout.preview.title" = "The uninstall plan is ready";
|
||||
"apps.callout.preview.detail" = "Review the steps below to confirm what will be removed, what stays recoverable, and which leftovers need extra attention.";
|
||||
"apps.maintenance.title" = "App Maintenance";
|
||||
"apps.maintenance.subtitle" = "Refresh the local app inventory and review high-impact uninstall candidates first.";
|
||||
"apps.inventory.title" = "App Inventory";
|
||||
"apps.inventory.subtitle" = "Refresh the inventory, then choose one app at a time instead of making uninstall decisions in a long list.";
|
||||
"apps.loading.title" = "Refreshing app footprints";
|
||||
"apps.metric.listed.title" = "Apps Listed";
|
||||
"apps.metric.listed.detail" = "Installed apps Atlas can inspect and turn into uninstall plans.";
|
||||
"apps.metric.footprint.title" = "Total App Footprint";
|
||||
"apps.metric.footprint.detail" = "Combined size across the current app inventory.";
|
||||
"apps.metric.leftovers.title" = "Leftover Files";
|
||||
"apps.metric.leftovers.detail" = "Extra files Atlas can include in the uninstall plan before anything is removed.";
|
||||
"apps.refresh.action" = "Refresh App Footprints";
|
||||
"apps.refresh.running" = "Refreshing…";
|
||||
"apps.refresh.hint" = "Refreshes the installed app inventory and recalculates footprints.";
|
||||
"apps.preview.title" = "Uninstall Plan";
|
||||
"apps.preview.metric.size.title" = "Estimated Space";
|
||||
"apps.preview.metric.size.detail" = "Estimated space this uninstall plan would remove, including leftovers.";
|
||||
"apps.preview.metric.actions.title" = "Plan Steps";
|
||||
"apps.preview.metric.actions.detail" = "Every uninstall step is listed before Atlas removes anything.";
|
||||
"apps.preview.metric.recoverable.title" = "Recoverable Steps";
|
||||
"apps.preview.metric.recoverable.detail" = "These steps stay visible in History and Recovery when supported.";
|
||||
"apps.preview.callout.safe.title" = "This uninstall plan stays mostly recoverable";
|
||||
"apps.preview.callout.safe.detail" = "Atlas preserves a recovery path for the selected app and related files where possible.";
|
||||
"apps.preview.callout.review.title" = "Some steps in this uninstall plan need a closer review";
|
||||
"apps.preview.callout.review.detail" = "Review each step so the uninstall and leftover cleanup match your expectations.";
|
||||
"apps.preview.row.recoverable" = "Recoverable through History when supported.";
|
||||
"apps.preview.row.review" = "Run only after review.";
|
||||
"apps.preview.action" = "Review Plan";
|
||||
"apps.preview.running" = "Building Plan…";
|
||||
"apps.preview.hint" = "Builds the uninstall plan for this app before anything is removed.";
|
||||
"apps.uninstall.action" = "Run Uninstall";
|
||||
"apps.uninstall.running" = "Uninstalling…";
|
||||
"apps.uninstall.hint" = "Runs the reviewed uninstall plan for this app and tracks recoverable items when supported.";
|
||||
"apps.list.title" = "Installed App Footprints";
|
||||
"apps.list.subtitle" = "Review app footprint, bundle ID, leftover count, and uninstall actions in one place.";
|
||||
"apps.list.empty.title" = "No matching apps";
|
||||
"apps.list.empty.detail" = "Try another search term or refresh the app list to inspect the latest uninstall candidates.";
|
||||
"apps.list.row.footnote" = "%@ • %d leftover files";
|
||||
"apps.list.row.leftovers" = "%d leftover files";
|
||||
"apps.browser.title" = "Review One App at a Time";
|
||||
"apps.browser.subtitle" = "Pick an app from the grouped list, inspect its footprint, then build the uninstall plan before you run it.";
|
||||
"apps.group.large" = "Large Footprints";
|
||||
"apps.group.leftovers" = "Has Leftovers";
|
||||
"apps.group.other" = "Other Apps";
|
||||
"apps.detail.title" = "App Details";
|
||||
"apps.detail.empty.title" = "Select an app";
|
||||
"apps.detail.empty.detail" = "Choose an app from the list to inspect its footprint and build an uninstall plan.";
|
||||
"apps.detail.size" = "App footprint";
|
||||
"apps.detail.leftovers" = "Leftover files";
|
||||
"apps.detail.path" = "Bundle path";
|
||||
"apps.detail.callout.preview.title" = "Build the uninstall plan first";
|
||||
"apps.detail.callout.preview.detail" = "Atlas keeps uninstall confidence high by showing the exact steps before anything is removed.";
|
||||
"apps.detail.callout.ready.title" = "This app is ready for reviewed uninstall";
|
||||
"apps.detail.callout.ready.detail" = "The plan below shows what will be removed and what remains recoverable.";
|
||||
|
||||
"history.screen.title" = "History";
|
||||
"history.screen.subtitle" = "See what ran, what changed, and what you can still restore before recovery retention expires.";
|
||||
"history.callout.empty.title" = "History is ready even when recovery is empty";
|
||||
"history.callout.empty.detail" = "Your audit trail still records completed work, and future recoverable actions will appear here automatically.";
|
||||
"history.callout.expiring.title" = "Some recovery items expire soon";
|
||||
"history.callout.expiring.detail" = "Open the recovery list first so you can restore anything you still need before the retention window closes.";
|
||||
"history.callout.running.title" = "A recent task is still in progress";
|
||||
"history.callout.running.detail" = "Keep the timeline open if you want to confirm when it finishes and whether it creates new recovery items.";
|
||||
"history.callout.recovery.title" = "Recovery-first cleanup is active";
|
||||
"history.callout.recovery.detail" = "Each recoverable action stays visible until its retention window ends, so you can reverse decisions with confidence.";
|
||||
"history.metric.activity.title" = "Visible events";
|
||||
"history.metric.activity.detail.empty" = "Run a scan or cleanup action to build the audit trail.";
|
||||
"history.metric.activity.detail.latest" = "Latest update %@";
|
||||
"history.metric.running.title" = "In progress";
|
||||
"history.metric.running.detail.none" = "No tasks are still running.";
|
||||
"history.metric.running.detail.active" = "Running tasks stay pinned in the timeline until they finish.";
|
||||
"history.metric.recovery.title" = "Recoverable now";
|
||||
"history.metric.recovery.detail.none" = "Nothing is waiting in recovery.";
|
||||
"history.metric.recovery.detail.available" = "%@ total size available to restore.";
|
||||
"history.browser.title" = "Browse History";
|
||||
"history.browser.subtitle" = "Start with what you can still restore, then open the archive when you need older task records.";
|
||||
"history.browser.section.recovery" = "Can Restore";
|
||||
"history.browser.section.archive" = "Archive";
|
||||
"history.browser.summary.archive.one" = "1 archived task record";
|
||||
"history.browser.summary.archive.other" = "%d archived task records";
|
||||
"history.browser.summary.recovery.one" = "1 recoverable item";
|
||||
"history.browser.summary.recovery.other" = "%d recoverable items";
|
||||
"history.runs.title" = "Archive";
|
||||
"history.runs.subtitle" = "Current and past task records grouped so older activity does not overwhelm the page.";
|
||||
"history.runs.empty.title" = "No matching tasks";
|
||||
"history.runs.empty.detail" = "Run a scan or clear the current search to inspect the latest task timeline.";
|
||||
"history.timeline.latest" = "Latest";
|
||||
"history.timeline.meta.started" = "Started %@";
|
||||
"history.timeline.meta.finished" = "Finished %@";
|
||||
"history.timeline.meta.running" = "Still in progress";
|
||||
"history.recovery.title" = "Recoverable Items";
|
||||
"history.recovery.subtitle" = "Check the original location and retention window before restoring.";
|
||||
"history.recovery.empty.title" = "No matching recovery items";
|
||||
"history.recovery.empty.detail" = "Recovery-first deletion is active, but no items currently match the search.";
|
||||
"history.recovery.badge.available" = "Recoverable";
|
||||
"history.recovery.badge.expiring" = "Expiring soon";
|
||||
"history.recovery.filter.all" = "All";
|
||||
"history.recovery.filter.expiring" = "Expiring";
|
||||
"history.recovery.filtered.empty.title" = "No items in this filter";
|
||||
"history.recovery.filtered.empty.detail" = "Try another recovery category or clear the current filter to see everything that is still recoverable.";
|
||||
"history.recovery.group.expiring" = "Expiring Soon";
|
||||
"history.recovery.group.apps" = "Apps";
|
||||
"history.recovery.group.developer" = "Developer";
|
||||
"history.recovery.group.browsers" = "Browsers";
|
||||
"history.recovery.group.system" = "System";
|
||||
"history.recovery.group.other" = "Other";
|
||||
"history.recovery.meta.deleted" = "Deleted %@";
|
||||
"history.recovery.meta.expires" = "Recoverable until %@";
|
||||
"history.recovery.meta.noexpiry" = "Retention window active";
|
||||
"history.recovery.path.label" = "Original location";
|
||||
"history.archive.group.active" = "Still Running";
|
||||
"history.archive.group.recent" = "Recent";
|
||||
"history.archive.group.older" = "Older Archive";
|
||||
"history.detail.title" = "Details";
|
||||
"history.detail.empty.title" = "Select an item";
|
||||
"history.detail.empty.detail" = "Choose a timeline event or recovery item to inspect its details here.";
|
||||
"history.detail.task.status" = "Status";
|
||||
"history.detail.task.started" = "Started";
|
||||
"history.detail.task.finished" = "Finished";
|
||||
"history.detail.task.finished.running" = "Still in progress";
|
||||
"history.detail.task.callout.queued.title" = "This task is queued";
|
||||
"history.detail.task.callout.queued.detail" = "It has been accepted into the workflow, but execution has not started yet.";
|
||||
"history.detail.task.callout.running.title" = "This task is still running";
|
||||
"history.detail.task.callout.running.detail" = "Keep this detail open if you want to confirm when the work completes.";
|
||||
"history.detail.task.callout.completed.title" = "This task finished successfully";
|
||||
"history.detail.task.callout.completed.detail" = "Use the summary and timestamps below to confirm what changed.";
|
||||
"history.detail.task.callout.failed.title" = "This task did not finish cleanly";
|
||||
"history.detail.task.callout.failed.detail" = "Review the summary and recent activity before retrying the workflow.";
|
||||
"history.detail.recovery.size" = "Size";
|
||||
"history.detail.recovery.deleted" = "Deleted";
|
||||
"history.detail.recovery.window" = "Retention window";
|
||||
"history.detail.recovery.window.open" = "Still recoverable";
|
||||
"history.detail.recovery.callout.available.title" = "This item is still recoverable";
|
||||
"history.detail.recovery.callout.available.detail" = "Restore it whenever you are ready while the retention window remains open.";
|
||||
"history.detail.recovery.callout.expiring.title" = "Restore soon if you still need this";
|
||||
"history.detail.recovery.callout.expiring.detail" = "This recovery item is close to the end of its retention window.";
|
||||
"history.restore.action" = "Restore";
|
||||
"history.restore.running" = "Restoring…";
|
||||
"history.restore.hint" = "Restores this item while its recovery window is still open.";
|
||||
"history.run.footnote.finished" = "Started %@ • Finished %@";
|
||||
"history.run.footnote.running" = "Started %@ • Still in progress";
|
||||
"history.recovery.footnote.deleted" = "Deleted %@";
|
||||
"history.recovery.footnote.expires" = "Recoverable until %@";
|
||||
|
||||
"permissions.screen.title" = "Permissions";
|
||||
"permissions.screen.subtitle" = "Explain why access matters, keep limited mode useful, and open System Settings only when a workflow truly needs it.";
|
||||
"permissions.callout.ready.title" = "Core access is ready";
|
||||
"permissions.callout.ready.detail" = "The permissions required for the current workflows are already available. The remaining permissions can wait until the related workflow actually needs them.";
|
||||
"permissions.callout.limited.title" = "Atlas is still in limited mode";
|
||||
"permissions.callout.limited.detail" = "At least one permission required for the current workflows is still unavailable. You can keep browsing and doing partial scans, but deeper actions stay limited until the status refreshes.";
|
||||
"permissions.next.title" = "Next Step";
|
||||
"permissions.next.subtitle" = "Show the one access decision that matters most right now, then keep the rest in context.";
|
||||
"permissions.next.missing.title" = "Next: %@";
|
||||
"permissions.next.ready.title" = "No urgent permission work";
|
||||
"permissions.next.ready.detail" = "%d of %d tracked permissions are already available.";
|
||||
"permissions.controls.title" = "Access Overview";
|
||||
"permissions.controls.subtitle" = "Check the current access snapshot and understand why each permission matters before you open System Settings.";
|
||||
"permissions.loading.title" = "Refreshing permission status";
|
||||
"permissions.metric.granted.title" = "Granted";
|
||||
"permissions.metric.granted.detail" = "Permissions Atlas can already rely on for the current workflow set.";
|
||||
"permissions.metric.required.title" = "Required Now";
|
||||
"permissions.metric.required.detail" = "The core permissions that decide whether Atlas can leave limited mode.";
|
||||
"permissions.metric.later.title" = "Not Needed Yet";
|
||||
"permissions.metric.later.detail" = "These can stay off without putting Atlas into limited mode until a related workflow asks for them.";
|
||||
"permissions.metric.tracked.title" = "Tracked Permissions";
|
||||
"permissions.metric.tracked.detail" = "The minimum set Atlas surfaces for the frozen MVP workflows.";
|
||||
"permissions.refresh" = "Check Permission Status";
|
||||
"permissions.refresh.hint" = "Checks the current permission snapshot without opening System Settings.";
|
||||
"permissions.requiredSection.title" = "Required Now";
|
||||
"permissions.requiredSection.subtitle" = "These permissions decide whether Atlas can fully support the current workflows.";
|
||||
"permissions.optionalSection.title" = "Can Wait";
|
||||
"permissions.optionalSection.subtitle" = "Keep these collapsed until a related workflow actually needs them.";
|
||||
"permissions.optionalSection.disclosure" = "Optional permissions";
|
||||
"permissions.optionalSection.count.one" = "%d pending";
|
||||
"permissions.optionalSection.count.other" = "%d pending";
|
||||
"permissions.status.title" = "Permission Details";
|
||||
"permissions.status.subtitle" = "Each card explains what the permission unlocks, when it matters, and whether it can wait.";
|
||||
"permissions.empty.title" = "No matching permission states";
|
||||
"permissions.empty.detail" = "Refresh permissions or clear the current search to inspect the access model.";
|
||||
"permissions.row.ready" = "Already available for workflows that depend on this permission.";
|
||||
"permissions.row.required" = "This permission is required for the current core workflows.";
|
||||
"permissions.row.optional" = "This permission can wait until a related workflow actually needs it.";
|
||||
"permissions.status.required" = "Needs Access";
|
||||
"permissions.status.optional" = "Can Wait";
|
||||
"permissions.grant.action" = "Open System Settings";
|
||||
"permissions.grant.notifications" = "Request Notifications";
|
||||
"permissions.support.fullDiskAccess" = "Needed only when you want deeper coverage in protected Library locations. If you just enabled it, fully quit and reopen Atlas before checking again.";
|
||||
"permissions.support.accessibility" = "Needed later when Atlas must close running apps safely before uninstalling.";
|
||||
"permissions.support.notifications" = "Optional, but useful for long-running task and recovery reminders.";
|
||||
|
||||
"settings.screen.title" = "Settings";
|
||||
"settings.screen.subtitle" = "Adjust language, recovery retention, notifications, exclusions, and legal information in one place.";
|
||||
"settings.callout.title" = "Atlas stores state locally and keeps destructive work auditable";
|
||||
"settings.callout.detail" = "Recovery retention, notifications, exclusions, acknowledgements, and notices are all visible here so users can understand how the product behaves before they rely on it.";
|
||||
"settings.panel.title" = "Preference Center";
|
||||
"settings.panel.subtitle" = "Switch between active preferences, recovery behavior, and trust information without scrolling through one long page.";
|
||||
"settings.panel.general" = "General";
|
||||
"settings.panel.recovery" = "Recovery";
|
||||
"settings.panel.trust" = "Trust";
|
||||
"settings.general.title" = "General";
|
||||
"settings.general.subtitle" = "Set the language, recovery retention, and notifications you use day to day.";
|
||||
"settings.language.title" = "Interface Language";
|
||||
"settings.language.detail" = "Atlas currently supports Simplified Chinese and English.";
|
||||
"settings.language.picker" = "Select language";
|
||||
"settings.language.hint" = "Switch between Simplified Chinese and English. Simplified Chinese is the default.";
|
||||
"settings.retention.title" = "Recovery retention";
|
||||
"settings.retention.value" = "%d days";
|
||||
"settings.retention.detail" = "Recoverable cleanup stays available until this window expires.";
|
||||
"settings.retention.adjust" = "Adjust retention";
|
||||
"settings.retention.hint" = "Changes how many days recoverable items remain available.";
|
||||
"settings.recoveryPanel.title" = "Recovery behavior";
|
||||
"settings.recoveryPanel.subtitle" = "Control how long recoverable results stay available and which paths stay out of plans.";
|
||||
"settings.notifications.title" = "Task notifications";
|
||||
"settings.notifications.detail" = "Helpful when scans, plan updates, or cleanup take long enough to leave the foreground.";
|
||||
"settings.notifications.toggle" = "Enable task notifications";
|
||||
"settings.notifications.hint" = "Turns task and recovery notifications on or off.";
|
||||
"settings.distribution.title" = "Distribution";
|
||||
"settings.distribution.value" = "Developer ID + Notarization";
|
||||
"settings.distribution.detail" = "The frozen MVP assumes direct distribution rather than a Mac App Store release.";
|
||||
"settings.exclusions.title" = "Rules & Exclusions";
|
||||
"settings.exclusions.subtitle" = "These paths stay out of scan results and cleanup plans.";
|
||||
"settings.exclusions.empty.title" = "No exclusions configured";
|
||||
"settings.exclusions.empty.detail" = "Atlas will scan the default coverage set until you add exclusions in a later iteration.";
|
||||
"settings.exclusions.row.subtitle" = "Excluded from cleanup recommendations and plans.";
|
||||
"settings.trust.title" = "Trust";
|
||||
"settings.trust.subtitle" = "These promises explain how Atlas keeps cleanup and recovery safe to use.";
|
||||
"settings.trust.ack.title" = "Open-source acknowledgement";
|
||||
"settings.trust.ack.subtitle" = "Visible in-app so users can understand the product lineage.";
|
||||
"settings.trust.notices.title" = "Third-party notices";
|
||||
"settings.trust.notices.subtitle" = "Visible in-app alongside the acknowledgement material.";
|
||||
"settings.trust.destructive.title" = "Destructive actions";
|
||||
"settings.trust.destructive.subtitle" = "Destructive work is presented as recoverable, reviewable, and auditable whenever possible.";
|
||||
"settings.trust.destructive.badge" = "Recovery-first";
|
||||
"settings.trust.documents.title" = "Reference Documents";
|
||||
"settings.trust.documents.subtitle" = "Open acknowledgements and third-party notices only when you need the full text.";
|
||||
"settings.trust.documents.ack" = "View Acknowledgement";
|
||||
"settings.trust.documents.notices" = "View Notices";
|
||||
"settings.legal.title" = "Legal";
|
||||
"settings.legal.subtitle" = "Acknowledgements and third-party notices";
|
||||
"settings.acknowledgement.title" = "Acknowledgement";
|
||||
"settings.acknowledgement.subtitle" = "The in-app attribution text users can read without leaving the product.";
|
||||
"settings.notices.title" = "Third-Party Notices";
|
||||
"settings.notices.subtitle" = "A concise notice surface for shipped third-party attributions.";
|
||||
@@ -0,0 +1,561 @@
|
||||
"app.name" = "Atlas for Mac";
|
||||
"language.zhHans" = "简体中文";
|
||||
"language.en" = "English";
|
||||
|
||||
"route.overview.title" = "概览";
|
||||
"route.overview.subtitle" = "查看健康摘要、预计可释放空间和下一步安全操作。";
|
||||
"route.smartclean.title" = "智能清理";
|
||||
"route.smartclean.subtitle" = "先生成并复核清理计划,再决定是否执行。";
|
||||
"route.apps.title" = "应用";
|
||||
"route.apps.subtitle" = "先查看应用占用、残留和卸载计划,再决定是否移除。";
|
||||
"route.history.title" = "历史";
|
||||
"route.history.subtitle" = "跟踪任务、结果和恢复入口。";
|
||||
"route.permissions.title" = "权限";
|
||||
"route.permissions.subtitle" = "先解释原因,再请求访问。";
|
||||
"route.settings.title" = "设置";
|
||||
"route.settings.subtitle" = "偏好、排除项、致谢和通知。";
|
||||
|
||||
"risk.safe" = "安全";
|
||||
"risk.review" = "复核";
|
||||
"risk.advanced" = "高级";
|
||||
|
||||
"taskkind.scan" = "智能清理扫描";
|
||||
"taskkind.executePlan" = "执行清理计划";
|
||||
"taskkind.uninstallApp" = "卸载应用";
|
||||
"taskkind.restore" = "恢复项目";
|
||||
"taskkind.inspectPermissions" = "检查权限状态";
|
||||
|
||||
"taskstatus.queued" = "排队中";
|
||||
"taskstatus.running" = "进行中";
|
||||
"taskstatus.completed" = "已完成";
|
||||
"taskstatus.failed" = "失败";
|
||||
"taskstatus.cancelled" = "已取消";
|
||||
|
||||
"permission.fullDiskAccess" = "完全磁盘访问";
|
||||
"permission.accessibility" = "辅助功能";
|
||||
"permission.notifications" = "通知";
|
||||
|
||||
"category.developer" = "开发";
|
||||
"category.system" = "系统";
|
||||
"category.apps" = "应用";
|
||||
"category.browsers" = "浏览器";
|
||||
|
||||
"settings.acknowledgement.body" = "Atlas for Mac 包含源自开源项目 Mole(作者 tw93 及贡献者)的软件,并依据 MIT 许可证使用。Atlas for Mac 是独立产品,与原作者不存在关联或背书关系。";
|
||||
"settings.notices.body" = "本产品包含源自开源项目 Mole(作者 tw93 及贡献者)的软件,并依据 MIT 许可证使用。";
|
||||
"fixture.finding.derivedData.title" = "Xcode 派生数据";
|
||||
"fixture.finding.derivedData.detail" = "来自旧项目的构建产物和索引。";
|
||||
"fixture.finding.browserCaches.title" = "浏览器缓存";
|
||||
"fixture.finding.browserCaches.detail" = "低风险的 WebKit 和 Chromium 缓存目录。";
|
||||
"fixture.finding.oldRuntimes.title" = "旧模拟器运行时";
|
||||
"fixture.finding.oldRuntimes.detail" = "需要删除前先复核的闲置 iOS 模拟器资源。";
|
||||
"fixture.finding.launchAgents.title" = "启动代理残留";
|
||||
"fixture.finding.launchAgents.detail" = "与已删除应用或旧实验相关的后台辅助项。";
|
||||
|
||||
"fixture.plan.reclaimCommonClutter.title" = "回收常见杂项";
|
||||
"fixture.plan.item.moveDerivedData.title" = "将 Xcode 派生数据移入废纸篓";
|
||||
"fixture.plan.item.moveDerivedData.detail" = "保留恢复路径,可在历史和恢复中找回。";
|
||||
"fixture.plan.item.reviewRuntimes.title" = "复核模拟器运行时";
|
||||
"fixture.plan.item.reviewRuntimes.detail" = "执行前先让 worker 确认计划细节。";
|
||||
"fixture.plan.item.inspectAgents.title" = "检查启动代理";
|
||||
"fixture.plan.item.inspectAgents.detail" = "移除受权限影响的项目之前,需要先通过 helper 校验。";
|
||||
|
||||
"fixture.task.scan.summary" = "已扫描 214 个位置,并生成预计可释放 35.3 GB 的清理计划。";
|
||||
"fixture.task.execute.summary" = "正在将安全缓存项移入恢复区。";
|
||||
"fixture.task.permissions.summary" = "权限状态已刷新,无需立即弹窗请求。";
|
||||
|
||||
"fixture.recovery.chromeCache.title" = "Chrome 缓存包";
|
||||
"fixture.recovery.chromeCache.detail" = "来自上一次智能清理的可恢复缓存包。";
|
||||
"fixture.recovery.chromeCache.payload" = "从上一次智能清理中恢复的缓存数据。";
|
||||
"fixture.recovery.simulatorSupport.title" = "旧版模拟器设备支持";
|
||||
"fixture.recovery.simulatorSupport.detail" = "这是仍需复核的项目,保留一周后才会清除。";
|
||||
"fixture.recovery.simulatorSupport.payload" = "等待复核的模拟器支持文件。";
|
||||
|
||||
"fixture.permission.fullDiskAccess.rationale" = "只有在扫描系统级缓存、应用残留和受保护的 Library 路径时才需要。";
|
||||
"fixture.permission.accessibility.rationale" = "卸载流程需要安全关闭正在运行的应用时,再请求此权限即可。";
|
||||
"fixture.permission.notifications.rationale" = "用于长任务完成通知和恢复提醒。";
|
||||
|
||||
"fixture.health.optimization.dns.title" = "DNS 与 Spotlight 检查";
|
||||
"fixture.health.optimization.dns.detail" = "刷新 DNS 缓存并确认 Spotlight 状态。";
|
||||
"fixture.health.optimization.finder.title" = "Finder 缓存刷新";
|
||||
"fixture.health.optimization.finder.detail" = "刷新 QuickLook 缩略图与图标服务缓存。";
|
||||
"fixture.health.optimization.memory.title" = "内存优化";
|
||||
"fixture.health.optimization.memory.detail" = "释放非活跃内存以改善系统响应。";
|
||||
|
||||
"fixture.storage.downloads.title" = "下载归档";
|
||||
"fixture.storage.downloads.age" = "大部分文件已超过 90 天";
|
||||
"fixture.storage.movies.title" = "影片导出目录";
|
||||
"fixture.storage.movies.age" = "最近 6 个月内生成的大型媒体文件";
|
||||
"fixture.storage.installers.title" = "未使用的安装器";
|
||||
"fixture.storage.installers.age" = "大部分磁盘镜像已超过 30 天";
|
||||
"application.error.workerRejected" = "Worker 拒绝了请求(%@):%@";
|
||||
"xpc.error.encodingFailed" = "无法编码后台请求:%@";
|
||||
"xpc.error.decodingFailed" = "无法解析后台响应:%@";
|
||||
"xpc.error.invalidResponse" = "后台工作组件返回了无效响应。请完全退出并重新打开 Atlas;若仍失败,请重新安装当前版本。";
|
||||
"xpc.error.connectionUnavailable" = "后台工作组件不可用。请完全退出 Atlas,并从“应用程序”文件夹重新打开当前安装版本;若仍失败,请删除旧版本后重新安装。系统详情:%@";
|
||||
"xpc.error.timedOut" = "后台工作组件响应超时(%.1f 秒)。请重试;若持续出现,请重新打开 Atlas。";
|
||||
"application.plan.inspectPrivileged" = "检查 %@ 的受权限影响清理项";
|
||||
"application.plan.reviewFinding" = "复核 %@";
|
||||
"application.plan.reviewSelected.one" = "复核 1 个已选发现项";
|
||||
"application.plan.reviewSelected.other" = "复核 %d 个已选发现项";
|
||||
"application.scan.completed" = "智能清理扫描已完成,并已生成清理计划。";
|
||||
"application.preview.updated.one" = "已根据 1 个操作更新清理计划。";
|
||||
"application.preview.updated.other" = "已根据 %d 个操作更新清理计划。";
|
||||
"application.plan.executed" = "清理计划已执行完成。";
|
||||
"application.recovery.completed" = "恢复操作已完成。";
|
||||
"application.apps.loaded.one" = "已载入 1 个应用占用项。";
|
||||
"application.apps.loaded.other" = "已载入 %d 个应用占用项。";
|
||||
"application.apps.previewUpdated" = "已更新 %@ 的卸载计划。";
|
||||
"application.apps.uninstallCompleted" = "应用卸载已完成。";
|
||||
|
||||
"infrastructure.permission.fullDiskAccess.granted" = "Atlas 已可检查更广范围的受保护用户数据位置。";
|
||||
"infrastructure.permission.fullDiskAccess.needed" = "只有在扫描 Mail、Safari 或 Messages 等受保护的 Library 区域时才需要。若你刚在系统设置里开启,请完全退出并重新打开 Atlas 后再检查权限状态。";
|
||||
"infrastructure.permission.accessibility.granted" = "Atlas 已可协调关闭应用,以支持更安全的卸载流程。";
|
||||
"infrastructure.permission.accessibility.needed" = "当卸载流程需要安全关闭正在运行的应用时,再请求此权限即可。";
|
||||
"infrastructure.permission.notifications.granted" = "Atlas 已可为长任务发送完成和恢复提醒。";
|
||||
"infrastructure.permission.notifications.needed" = "用于长任务完成通知和恢复提醒。";
|
||||
"infrastructure.scan.completed.one" = "已扫描 1 组发现项,并生成清理计划。";
|
||||
"infrastructure.scan.completed.other" = "已扫描 %d 组发现项,并生成清理计划。";
|
||||
"infrastructure.execute.summary.clean.one" = "已将 1 个智能清理项目移入恢复区。";
|
||||
"infrastructure.execute.summary.clean.other" = "已将 %d 个智能清理项目移入恢复区。";
|
||||
"infrastructure.execute.summary.clean.mixed" = "已将 %d 个智能清理项目移入恢复区;仍有 %d 个高级项目需复核。";
|
||||
"infrastructure.restore.summary.one" = "已将 1 个项目恢复回工作区。";
|
||||
"infrastructure.restore.summary.other" = "已将 %d 个项目恢复回工作区。";
|
||||
"infrastructure.apps.loaded.one" = "已载入 1 个应用占用项。";
|
||||
"infrastructure.apps.loaded.other" = "已载入 %d 个应用占用项。";
|
||||
"infrastructure.apps.preview.summary" = "已为 %@ 生成卸载计划。";
|
||||
"infrastructure.apps.uninstall.summary" = "已将 %@ 及其残留移入恢复区。";
|
||||
"infrastructure.recovery.app.detail.one" = "可恢复的卸载包,包含 1 个残留项目。";
|
||||
"infrastructure.recovery.app.detail.other" = "可恢复的卸载包,包含 %d 个残留项目。";
|
||||
"infrastructure.plan.review.one" = "复核 1 个已选发现项";
|
||||
"infrastructure.plan.review.other" = "复核 %d 个已选发现项";
|
||||
"infrastructure.plan.uninstall.title" = "卸载 %@";
|
||||
"infrastructure.plan.uninstall.moveBundle.title" = "将 %@ 应用包移入恢复区";
|
||||
"infrastructure.plan.uninstall.moveBundle.detail" = "将位于 %@ 的应用包移入废纸篓,同时在历史中保留本次卸载记录。";
|
||||
"infrastructure.plan.uninstall.archive.one" = "归档 1 个残留项目";
|
||||
"infrastructure.plan.uninstall.archive.other" = "归档 %d 个残留项目";
|
||||
"infrastructure.plan.uninstall.archive.detail" = "支持文件、缓存和启动项仍会通过恢复历史保留可追溯性。";
|
||||
"infrastructure.action.reviewUninstall" = "复核 %@ 的卸载计划";
|
||||
"infrastructure.action.inspectPrivileged" = "检查 %@ 的受权限影响清理项";
|
||||
"infrastructure.action.archiveRecovery" = "将 %@ 归档到恢复区";
|
||||
"infrastructure.action.moveToTrash" = "将 %@ 移入废纸篓";
|
||||
"app.search.prompt" = "搜索发现项、应用、历史";
|
||||
"app.search.hint" = "搜索当前页面中可见的内容。";
|
||||
"app.search.prompt.route" = "搜索 %@";
|
||||
"app.search.hint.route" = "搜索 %@ 中当前可见的内容。";
|
||||
|
||||
"commands.navigate.menu" = "导航";
|
||||
"commands.actions.menu" = "操作";
|
||||
"commands.taskcenter.open" = "打开任务中心";
|
||||
"commands.taskcenter.close" = "关闭任务中心";
|
||||
"commands.actions.refreshCurrent" = "刷新当前页面";
|
||||
"commands.actions.runScan" = "运行智能清理扫描";
|
||||
"commands.actions.refreshApps" = "刷新应用占用";
|
||||
"commands.actions.refreshPermissions" = "检查权限状态";
|
||||
"commands.actions.refreshHealth" = "刷新系统快照";
|
||||
|
||||
"toolbar.taskcenter" = "任务中心";
|
||||
"toolbar.taskcenter.help" = "打开最近任务活动(⌘7)";
|
||||
"toolbar.taskcenter.accessibilityLabel" = "打开任务中心";
|
||||
"toolbar.taskcenter.accessibilityHint" = "显示最近任务活动,并可快速跳转到历史页面。";
|
||||
"toolbar.permissions" = "权限";
|
||||
"toolbar.permissions.help" = "检查权限状态(⌥⌘P)";
|
||||
"toolbar.permissions.accessibilityLabel" = "打开权限并检查状态";
|
||||
"toolbar.permissions.accessibilityHint" = "进入权限页面并检查当前权限快照。";
|
||||
"toolbar.settings" = "设置";
|
||||
"toolbar.settings.help" = "打开设置(⌘6)";
|
||||
"toolbar.settings.accessibilityLabel" = "打开设置";
|
||||
"toolbar.settings.accessibilityHint" = "进入偏好、保留策略、通知和说明页面。";
|
||||
"sidebar.route.hint" = "键盘快捷键 Command-%@。";
|
||||
|
||||
"common.days" = "天";
|
||||
"common.enabled" = "已开启";
|
||||
"common.disabled" = "已关闭";
|
||||
"common.granted" = "已授予";
|
||||
"common.neededLater" = "暂不需要";
|
||||
"common.recoverable" = "可恢复";
|
||||
"common.manualReview" = "需复核";
|
||||
"common.visible" = "可见";
|
||||
|
||||
"model.scan.ready" = "准备开始扫描并生成清理计划。";
|
||||
"model.scan.submitting" = "正在开始智能清理扫描并生成清理计划…";
|
||||
"model.apps.ready" = "准备检查应用占用并生成卸载计划。";
|
||||
"model.apps.refreshing" = "正在刷新应用占用并更新卸载计划…";
|
||||
"model.permissions.ready" = "需要时可随时检查权限状态。";
|
||||
"model.permissions.refreshing" = "正在检查权限状态…";
|
||||
"model.permissions.summary.one" = "%d / %d 个跟踪权限当前可用。";
|
||||
"model.permissions.summary.other" = "%d / %d 个跟踪权限当前可用。";
|
||||
"model.taskcenter.none" = "当前没有活动任务";
|
||||
"model.taskcenter.matching.one" = "1 个匹配任务";
|
||||
"model.taskcenter.matching.other" = "%d 个匹配任务";
|
||||
"model.taskcenter.active.one" = "1 个活动任务";
|
||||
"model.taskcenter.active.other" = "%d 个活动任务";
|
||||
|
||||
"taskcenter.title" = "任务中心";
|
||||
"taskcenter.callout.empty.title" = "当前没有匹配的任务活动";
|
||||
"taskcenter.callout.empty.detail" = "运行扫描、执行清理计划、卸载应用或恢复项目后,任务时间线就会出现在这里。";
|
||||
"taskcenter.callout.active.title" = "最近任务活动会集中显示在这里";
|
||||
"taskcenter.callout.active.detail" = "想看完整审计轨迹和可恢复项目时,可以打开历史页面。";
|
||||
"taskcenter.empty.title" = "还没有任务";
|
||||
"taskcenter.empty.detail" = "试试新的搜索词,或者运行一次智能清理扫描来填充任务时间线。";
|
||||
"taskcenter.openHistory" = "打开历史";
|
||||
"taskcenter.openHistory.hint" = "进入历史页面,查看完整审计轨迹和恢复项。";
|
||||
"taskcenter.timeline.finished" = "开始于 %@ • 结束于 %@";
|
||||
"taskcenter.timeline.running" = "开始于 %@ • 仍在进行中";
|
||||
|
||||
"overview.screen.title" = "概览";
|
||||
"overview.screen.subtitle" = "先看最重要的信息,再在理解取舍的前提下执行下一步安全维护。";
|
||||
"overview.callout.ready.title" = "Atlas 已为当前主流程做好准备";
|
||||
"overview.callout.ready.detail" = "当前主流程所需的核心权限已经就绪。像辅助功能、通知这类可稍后授予的权限,不会再把状态标记为受限。";
|
||||
"overview.callout.limited.title" = "你仍然可以在受限模式下继续使用";
|
||||
"overview.callout.limited.detail" = "当前仍缺少至少一项主流程必需权限,因此 Atlas 会保持受限模式;补齐后就会自动恢复为就绪状态。";
|
||||
"overview.metric.reclaimable.title" = "预计可释放空间";
|
||||
"overview.metric.reclaimable.detail" = "基于当前清理计划和最近的工作区状态估算。执行后会按剩余项目重新计算。";
|
||||
"overview.metric.findings.title" = "待处理发现项";
|
||||
"overview.metric.findings.detail" = "在真正执行前,Atlas 会先将其分为安全、复核和高级三个分区。";
|
||||
"overview.metric.permissions.title" = "权限就绪度";
|
||||
"overview.metric.permissions.ready" = "Atlas 已具备当前流程所需的权限。";
|
||||
"overview.metric.permissions.limited" = "仍有主流程必需权限未就绪,因此当前显示为受限模式。";
|
||||
"overview.snapshot.title" = "系统快照";
|
||||
"overview.snapshot.subtitle" = "高信号健康数据,以及最适合优先处理的维护建议。";
|
||||
"overview.snapshot.loading.title" = "正在刷新系统快照";
|
||||
"overview.snapshot.loading.detail" = "Atlas 正在收集最新健康数据,然后更新推荐内容。";
|
||||
"overview.snapshot.memory.title" = "已用内存";
|
||||
"overview.snapshot.memory.detail" = "用于快速判断内存压力和长期后台负载。";
|
||||
"overview.snapshot.disk.title" = "磁盘占用";
|
||||
"overview.snapshot.disk.detail" = "当前系统卷已使用 %@ / %@ GB。";
|
||||
"overview.snapshot.uptime.title" = "运行时长";
|
||||
"overview.snapshot.uptime.detail" = "有助于发现陈旧缓存、长时间会话和重启时机。";
|
||||
"overview.snapshot.callout.warning.title" = "当前存储压力较高,值得尽快处理";
|
||||
"overview.snapshot.callout.warning.detail" = "建议先从智能清理中的“安全”分区开始,在不扩大风险的前提下释放空间。";
|
||||
"overview.snapshot.callout.ok.title" = "当前系统状态稳定,适合进行一次安全清理";
|
||||
"overview.snapshot.callout.ok.detail" = "先看下方维护建议,再在需要时进入智能清理做更深一步的规划。";
|
||||
"overview.snapshot.empty.title" = "暂时无法获取系统快照";
|
||||
"overview.snapshot.empty.detail" = "Atlas 仍然可以显示已持久化的发现项和恢复数据,并等待下一次健康刷新。";
|
||||
"overview.actions.title" = "推荐操作";
|
||||
"overview.actions.subtitle" = "当前工作区里最值得优先处理的高价值发现项。";
|
||||
"overview.actions.empty.title" = "没有匹配的发现项";
|
||||
"overview.actions.empty.detail" = "重新运行智能清理,或者清空当前搜索,以重新生成推荐操作列表。";
|
||||
"overview.activity.title" = "最近活动";
|
||||
"overview.activity.subtitle" = "查看 Atlas 最近做了什么,以及哪些操作仍可恢复。";
|
||||
"overview.activity.empty.title" = "最近没有活动";
|
||||
"overview.activity.empty.detail" = "下一次扫描、执行或恢复操作后,这里就会出现任务时间线。";
|
||||
"overview.activity.timeline.finished" = "开始于 %@ • 结束于 %@";
|
||||
"overview.activity.timeline.running" = "开始于 %@ • 仍在进行中";
|
||||
"overview.risk.safe" = "低风险清理";
|
||||
"overview.risk.review" = "建议删除前先复核";
|
||||
"overview.risk.advanced" = "建议高级检查";
|
||||
|
||||
"smartclean.screen.title" = "智能清理";
|
||||
"smartclean.screen.subtitle" = "先把扫描结果变成清晰的清理计划,再决定是否执行任何具有破坏性的操作。";
|
||||
"smartclean.controls.title" = "扫描与计划";
|
||||
"smartclean.controls.subtitle" = "先运行扫描,再更新计划,然后选择下一步最安全的动作。";
|
||||
"smartclean.loading.scan" = "正在扫描可回收杂项";
|
||||
"smartclean.loading.execute" = "正在执行已复核的清理计划";
|
||||
"smartclean.action.runScan" = "运行扫描";
|
||||
"smartclean.action.runScan.hint" = "开始新的智能清理扫描,并重建当前清理计划。";
|
||||
"smartclean.action.refreshPreview" = "更新计划";
|
||||
"smartclean.action.refreshPreview.hint" = "基于当前发现项重新生成清理计划,不会立即执行任何操作。";
|
||||
"smartclean.action.execute" = "执行计划";
|
||||
"smartclean.action.execute.hint" = "执行已复核的清理计划,并在历史中保留可恢复项目。";
|
||||
"smartclean.primary.scan.title" = "先运行新的扫描";
|
||||
"smartclean.primary.scan.detail" = "当你还没有当前可复核的计划时,扫描应该是唯一主动作。";
|
||||
"smartclean.primary.refresh.title" = "先更新当前计划";
|
||||
"smartclean.primary.refresh.detail" = "在信任这份计划之前,先刷新它,尤其是在发现项或验证状态发生变化后。";
|
||||
"smartclean.primary.execute.title" = "执行已复核的计划";
|
||||
"smartclean.primary.execute.detail" = "当前计划已经可用,因此执行应成为唯一主动作。";
|
||||
"smartclean.metric.previewSize.title" = "预计释放空间";
|
||||
"smartclean.metric.previewSize.detail" = "当前清理计划预计可释放的空间。执行后会按剩余项目重新计算。";
|
||||
"smartclean.metric.actions.title" = "已选操作";
|
||||
"smartclean.metric.actions.detail" = "Atlas 会在真正修改之前先列出每一个步骤。";
|
||||
"smartclean.metric.review.title" = "需要复核";
|
||||
"smartclean.metric.review.none" = "这份计划中的所有项目都可恢复。";
|
||||
"smartclean.metric.review.some" = "这些项目在执行前值得再看一眼。";
|
||||
"smartclean.preview.title" = "当前清理计划";
|
||||
"smartclean.execution.real" = "可直接执行";
|
||||
"smartclean.execution.reviewOnly" = "仅供复核";
|
||||
"smartclean.execution.coverage.full" = "这份计划中的 %d 个步骤都可直接执行";
|
||||
"smartclean.execution.coverage.partial" = "这份计划中有 %d/%d 个步骤可直接执行";
|
||||
"smartclean.execution.coverage.full.detail" = "这些步骤会真正移动到废纸篓,并在有恢复路径时支持恢复。";
|
||||
"smartclean.execution.coverage.partial.detail" = "其余 %d 个步骤仍需复核,或当前还不支持直接执行。";
|
||||
"smartclean.preview.metric.space.title" = "这份计划预计释放";
|
||||
"smartclean.preview.metric.space.detail.one" = "按当前 1 个计划步骤估算。";
|
||||
"smartclean.preview.metric.space.detail.other" = "按当前 %d 个计划步骤估算。";
|
||||
"smartclean.preview.callout.safe.title" = "当前计划主要来自“安全”分区";
|
||||
"smartclean.preview.callout.safe.detail" = "大多数已选步骤都可以在历史和恢复中找回。";
|
||||
"smartclean.preview.empty.detail" = "运行一次扫描或更新计划,把当前发现项变成具体的清理步骤。若刚执行完计划,这里只显示剩余项目。";
|
||||
"smartclean.preview.callout.review.detail" = "建议在执行前检查高亮步骤,确认哪些仍可恢复、哪些需要额外判断。";
|
||||
"smartclean.preview.empty.title" = "还没有清理计划";
|
||||
"smartclean.preview.empty.detail" = "运行一次扫描或更新计划,把当前发现项变成具体的清理步骤。";
|
||||
"smartclean.empty.title" = "没有匹配的发现项";
|
||||
"smartclean.empty.detail" = "清空当前搜索,或重新运行一次扫描来填充智能清理分区。";
|
||||
"smartclean.status.scanning" = "正在扫描可回收杂项";
|
||||
"smartclean.status.executing" = "正在应用已复核的清理方案";
|
||||
"smartclean.status.empty" = "运行新的扫描以生成清理计划";
|
||||
"smartclean.status.cached" = "当前计划尚未重新验证";
|
||||
"smartclean.status.revalidationFailed" = "未能更新当前计划";
|
||||
"smartclean.cached.title" = "这份计划来自上一次结果";
|
||||
"smartclean.revalidationFailed.title" = "重新验证失败,以下仍是上一次计划";
|
||||
"smartclean.cached.detail" = "请先重新运行扫描或更新计划,再执行。当前显示的只是上一次保存的计划,不能直接视为当前可执行结果。";
|
||||
"smartclean.status.empty.detail" = "Atlas 会先把扫描结果变成清理计划,再执行任何操作,方便你先复核风险和恢复方式。";
|
||||
"smartclean.status.ready" = "先复核计划,再执行";
|
||||
"smartclean.status.ready.detail" = "当前已有 %d 个发现项。建议先从“安全”分区开始,再复核“复核”和“高级”分区中的项目。";
|
||||
"smartclean.support.removeCache" = "适合作为可恢复的清理步骤。";
|
||||
"smartclean.support.removeApp" = "卸载前先复核占用和残留,更容易形成可预期结果。";
|
||||
"smartclean.support.archiveFile" = "在支持的情况下,Atlas 会将此类项目保留在可恢复路径中。";
|
||||
"smartclean.support.inspectPermission" = "在 Atlas 能安全继续之前,需要额外确认。";
|
||||
"smartclean.section.safe" = "高置信度的清理候选项,通常也更容易恢复。";
|
||||
"smartclean.section.review" = "建议删除前快速复核一次。";
|
||||
"smartclean.section.advanced" = "可能较敏感的项目,值得做更审慎的决定。";
|
||||
"smartclean.expectation.safe" = "通常可恢复";
|
||||
"smartclean.expectation.review" = "删除前先复核";
|
||||
"smartclean.expectation.advanced" = "建议谨慎检查";
|
||||
|
||||
"apps.screen.title" = "应用";
|
||||
"apps.screen.subtitle" = "先检查每个应用的占用、残留和恢复路径,再决定是否卸载。";
|
||||
"apps.callout.default.title" = "先复核卸载计划,再决定是否移除";
|
||||
"apps.callout.default.detail" = "Atlas 会先列出占用较大的应用,再生成卸载计划,方便你检查残留和恢复预期。";
|
||||
"apps.callout.preview.title" = "卸载计划已准备好";
|
||||
"apps.callout.preview.detail" = "请先复核下面的步骤,确认会删除什么、哪些项目可恢复、哪些残留需要额外注意。";
|
||||
"apps.maintenance.title" = "应用维护";
|
||||
"apps.maintenance.subtitle" = "刷新本地应用清单,并优先复核占用较大的卸载候选项。";
|
||||
"apps.inventory.title" = "应用清单";
|
||||
"apps.inventory.subtitle" = "先刷新清单,再一次只处理一个应用,而不是在长列表里直接做卸载决定。";
|
||||
"apps.loading.title" = "正在刷新应用占用";
|
||||
"apps.metric.listed.title" = "已列出应用";
|
||||
"apps.metric.listed.detail" = "Atlas 当前可以检查并生成卸载计划的已安装应用数量。";
|
||||
"apps.metric.footprint.title" = "总应用占用";
|
||||
"apps.metric.footprint.detail" = "当前应用清单的总体空间占用。";
|
||||
"apps.metric.leftovers.title" = "残留文件";
|
||||
"apps.metric.leftovers.detail" = "Atlas 会在卸载前先把这些附加文件纳入计划,方便你逐项确认。";
|
||||
"apps.refresh.action" = "刷新应用占用";
|
||||
"apps.refresh.running" = "正在刷新…";
|
||||
"apps.refresh.hint" = "刷新已安装应用清单,并重新计算占用。";
|
||||
"apps.preview.title" = "卸载计划";
|
||||
"apps.preview.metric.size.title" = "预计释放空间";
|
||||
"apps.preview.metric.size.detail" = "执行这份卸载计划后,预计可释放的空间,包含残留文件。";
|
||||
"apps.preview.metric.actions.title" = "计划步骤";
|
||||
"apps.preview.metric.actions.detail" = "Atlas 会在真正移除前先列出每一个卸载步骤。";
|
||||
"apps.preview.metric.recoverable.title" = "可恢复步骤";
|
||||
"apps.preview.metric.recoverable.detail" = "支持恢复的步骤会在历史和恢复中保留。";
|
||||
"apps.preview.callout.safe.title" = "这份卸载计划大多可恢复";
|
||||
"apps.preview.callout.safe.detail" = "在支持的情况下,Atlas 会为所选应用及相关文件保留恢复路径。";
|
||||
"apps.preview.callout.review.title" = "这份卸载计划中仍有步骤需要复核";
|
||||
"apps.preview.callout.review.detail" = "建议逐项查看计划,确保卸载结果和残留清理方式符合你的预期。";
|
||||
"apps.preview.row.recoverable" = "支持时可通过历史恢复。";
|
||||
"apps.preview.row.review" = "执行前需复核。";
|
||||
"apps.preview.action" = "查看计划";
|
||||
"apps.preview.running" = "正在生成计划…";
|
||||
"apps.preview.hint" = "先为这个应用生成卸载计划,再决定是否真正移除。";
|
||||
"apps.uninstall.action" = "执行卸载";
|
||||
"apps.uninstall.running" = "正在卸载…";
|
||||
"apps.uninstall.hint" = "执行已复核的卸载计划,并在支持时保留可恢复项目。";
|
||||
"apps.list.title" = "已安装应用占用";
|
||||
"apps.list.subtitle" = "在一个列表中查看应用占用、标识符、残留数量和卸载操作。";
|
||||
"apps.list.empty.title" = "没有匹配的应用";
|
||||
"apps.list.empty.detail" = "试试新的搜索词,或者刷新应用列表以检查最新的候选项。";
|
||||
"apps.list.row.footnote" = "%@ • 残留 %d 项";
|
||||
"apps.list.row.leftovers" = "残留 %d 项";
|
||||
"apps.browser.title" = "逐个复核应用";
|
||||
"apps.browser.subtitle" = "从分组列表里选择一个应用,先检查占用,再生成卸载计划并决定是否执行。";
|
||||
"apps.group.large" = "大体积应用";
|
||||
"apps.group.leftovers" = "有残留";
|
||||
"apps.group.other" = "其他应用";
|
||||
"apps.detail.title" = "应用详情";
|
||||
"apps.detail.empty.title" = "选择一个应用";
|
||||
"apps.detail.empty.detail" = "从左侧列表选择一个应用,以检查占用并生成卸载计划。";
|
||||
"apps.detail.size" = "应用占用";
|
||||
"apps.detail.leftovers" = "残留文件";
|
||||
"apps.detail.path" = "Bundle 路径";
|
||||
"apps.detail.callout.preview.title" = "先生成卸载计划";
|
||||
"apps.detail.callout.preview.detail" = "为了让卸载更可控,Atlas 会先展示准确步骤,再决定是否真正移除。";
|
||||
"apps.detail.callout.ready.title" = "这个应用已经可以在复核后卸载";
|
||||
"apps.detail.callout.ready.detail" = "下面的计划会说明将删除什么,以及哪些内容仍可恢复。";
|
||||
|
||||
"history.screen.title" = "历史";
|
||||
"history.screen.subtitle" = "查看执行过的任务、发生的变更,以及在恢复窗口关闭前仍可找回的项目。";
|
||||
"history.callout.empty.title" = "即使恢复区为空,历史仍然可用";
|
||||
"history.callout.empty.detail" = "审计轨迹仍会记录已完成的工作,下一次可恢复操作也会自动出现在这里。";
|
||||
"history.callout.expiring.title" = "有恢复项即将到期";
|
||||
"history.callout.expiring.detail" = "先查看恢复列表,确认是否有仍需找回的项目,避免在保留窗口关闭后再处理。";
|
||||
"history.callout.running.title" = "最近有任务仍在进行中";
|
||||
"history.callout.running.detail" = "保留时间线可帮助你确认任务何时完成,以及是否会产生新的可恢复项目。";
|
||||
"history.callout.recovery.title" = "恢复优先的清理策略已启用";
|
||||
"history.callout.recovery.detail" = "每个可恢复操作都会在保留期结束前保持可见,便于你有把握地撤销决定。";
|
||||
"history.metric.activity.title" = "当前记录";
|
||||
"history.metric.activity.detail.empty" = "运行一次扫描或清理操作后,这里会开始形成审计轨迹。";
|
||||
"history.metric.activity.detail.latest" = "最近更新 %@";
|
||||
"history.metric.running.title" = "进行中";
|
||||
"history.metric.running.detail.none" = "当前没有仍在运行的任务。";
|
||||
"history.metric.running.detail.active" = "正在运行的任务会固定显示在时间线中,直到完成。";
|
||||
"history.metric.recovery.title" = "可恢复项";
|
||||
"history.metric.recovery.detail.none" = "当前没有等待恢复的项目。";
|
||||
"history.metric.recovery.detail.available" = "共 %@,仍可恢复。";
|
||||
"history.browser.title" = "浏览历史";
|
||||
"history.browser.subtitle" = "先看仍可恢复的内容,需要回溯旧任务时再打开归档,避免信息混在一起。";
|
||||
"history.browser.section.recovery" = "可恢复";
|
||||
"history.browser.section.archive" = "归档";
|
||||
"history.browser.summary.archive.one" = "1 条归档任务记录";
|
||||
"history.browser.summary.archive.other" = "%d 条归档任务记录";
|
||||
"history.browser.summary.recovery.one" = "1 个可恢复项目";
|
||||
"history.browser.summary.recovery.other" = "%d 个可恢复项目";
|
||||
"history.runs.title" = "归档";
|
||||
"history.runs.subtitle" = "把当前和过往任务分组展示,避免旧记录把页面拖得太长。";
|
||||
"history.runs.empty.title" = "没有匹配的任务";
|
||||
"history.runs.empty.detail" = "运行一次扫描,或者清空当前搜索,以查看最新任务时间线。";
|
||||
"history.timeline.latest" = "最新";
|
||||
"history.timeline.meta.started" = "开始于 %@";
|
||||
"history.timeline.meta.finished" = "结束于 %@";
|
||||
"history.timeline.meta.running" = "仍在进行中";
|
||||
"history.recovery.title" = "可恢复项目";
|
||||
"history.recovery.subtitle" = "恢复前先确认原始位置和保留窗口。";
|
||||
"history.recovery.empty.title" = "没有匹配的恢复项";
|
||||
"history.recovery.empty.detail" = "恢复优先的删除策略已启用,但当前搜索下没有匹配的项目。";
|
||||
"history.recovery.badge.available" = "可恢复";
|
||||
"history.recovery.badge.expiring" = "即将到期";
|
||||
"history.recovery.filter.all" = "全部";
|
||||
"history.recovery.filter.expiring" = "即将到期";
|
||||
"history.recovery.filtered.empty.title" = "这个筛选下没有项目";
|
||||
"history.recovery.filtered.empty.detail" = "试试其他恢复分类,或清空当前筛选来查看所有仍可恢复的项目。";
|
||||
"history.recovery.group.expiring" = "即将到期";
|
||||
"history.recovery.group.apps" = "应用";
|
||||
"history.recovery.group.developer" = "开发者";
|
||||
"history.recovery.group.browsers" = "浏览器";
|
||||
"history.recovery.group.system" = "系统";
|
||||
"history.recovery.group.other" = "其他";
|
||||
"history.recovery.meta.deleted" = "删除于 %@";
|
||||
"history.recovery.meta.expires" = "可恢复至 %@";
|
||||
"history.recovery.meta.noexpiry" = "保留窗口仍然开放";
|
||||
"history.recovery.path.label" = "原始位置";
|
||||
"history.archive.group.active" = "仍在运行";
|
||||
"history.archive.group.recent" = "近期记录";
|
||||
"history.archive.group.older" = "更早归档";
|
||||
"history.detail.title" = "详情";
|
||||
"history.detail.empty.title" = "选择一个项目";
|
||||
"history.detail.empty.detail" = "在左侧选择一条时间线记录或一个恢复项,即可在这里查看详情。";
|
||||
"history.detail.task.status" = "状态";
|
||||
"history.detail.task.started" = "开始时间";
|
||||
"history.detail.task.finished" = "结束时间";
|
||||
"history.detail.task.finished.running" = "仍在进行中";
|
||||
"history.detail.task.callout.queued.title" = "任务已进入队列";
|
||||
"history.detail.task.callout.queued.detail" = "系统已经接受这项任务,但执行尚未真正开始。";
|
||||
"history.detail.task.callout.running.title" = "任务仍在运行中";
|
||||
"history.detail.task.callout.running.detail" = "如果你想确认任务何时完成,可以先保留这个详情视图。";
|
||||
"history.detail.task.callout.completed.title" = "任务已成功完成";
|
||||
"history.detail.task.callout.completed.detail" = "可通过下面的摘要和时间信息确认这次变更实际做了什么。";
|
||||
"history.detail.task.callout.failed.title" = "任务未能完整结束";
|
||||
"history.detail.task.callout.failed.detail" = "建议先检查摘要和最近活动,再决定是否重新执行。";
|
||||
"history.detail.recovery.size" = "大小";
|
||||
"history.detail.recovery.deleted" = "删除时间";
|
||||
"history.detail.recovery.window" = "保留窗口";
|
||||
"history.detail.recovery.window.open" = "仍可恢复";
|
||||
"history.detail.recovery.callout.available.title" = "这个项目仍可恢复";
|
||||
"history.detail.recovery.callout.available.detail" = "只要保留窗口还在,你可以在准备好后随时恢复。";
|
||||
"history.detail.recovery.callout.expiring.title" = "如果还需要它,请尽快恢复";
|
||||
"history.detail.recovery.callout.expiring.detail" = "这个恢复项已经接近保留窗口的结束时间。";
|
||||
"history.restore.action" = "恢复";
|
||||
"history.restore.running" = "正在恢复…";
|
||||
"history.restore.hint" = "只要恢复窗口仍然开放,就可以把这个项目恢复回工作区。";
|
||||
"history.run.footnote.finished" = "开始于 %@ • 结束于 %@";
|
||||
"history.run.footnote.running" = "开始于 %@ • 仍在进行中";
|
||||
"history.recovery.footnote.deleted" = "删除于 %@";
|
||||
"history.recovery.footnote.expires" = "可恢复至 %@";
|
||||
|
||||
"permissions.screen.title" = "权限";
|
||||
"permissions.screen.subtitle" = "先解释为什么需要访问,保持受限模式可用,并且只在具体流程真正需要时再打开系统设置。";
|
||||
"permissions.callout.ready.title" = "核心访问能力已就绪";
|
||||
"permissions.callout.ready.detail" = "当前主流程所需的权限已经齐备。其余权限即使暂未授予,也只会在相关流程真正需要时再提示。";
|
||||
"permissions.callout.limited.title" = "当前仍处于受限模式";
|
||||
"permissions.callout.limited.detail" = "至少还有一项主流程必需权限未就绪。你仍可继续浏览和部分扫描,但更深层操作会保持受限,直到权限状态刷新为止。";
|
||||
"permissions.next.title" = "下一步";
|
||||
"permissions.next.subtitle" = "只突出当前最重要的权限决策,其余状态留在上下文中查看。";
|
||||
"permissions.next.missing.title" = "下一步:%@";
|
||||
"permissions.next.ready.title" = "当前没有紧急权限事项";
|
||||
"permissions.next.ready.detail" = "当前已具备 %d/%d 项跟踪权限。";
|
||||
"permissions.controls.title" = "权限概览";
|
||||
"permissions.controls.subtitle" = "检查当前权限快照,并在打开系统设置前理解每项权限的用途。";
|
||||
"permissions.loading.title" = "正在刷新权限状态";
|
||||
"permissions.metric.granted.title" = "已授予";
|
||||
"permissions.metric.granted.detail" = "Atlas 当前已可依赖的权限数量。";
|
||||
"permissions.metric.required.title" = "当前必需";
|
||||
"permissions.metric.required.detail" = "决定是否退出受限模式的核心权限。";
|
||||
"permissions.metric.later.title" = "暂不需要";
|
||||
"permissions.metric.later.detail" = "这些权限暂未授予也不会让 Atlas 进入受限模式,只有相关流程需要时才会用到。";
|
||||
"permissions.metric.tracked.title" = "跟踪权限";
|
||||
"permissions.metric.tracked.detail" = "冻结 MVP 流程当前展示的最小权限集合。";
|
||||
"permissions.refresh" = "检查权限状态";
|
||||
"permissions.refresh.hint" = "检查当前权限快照,而不会直接打开系统设置。";
|
||||
"permissions.requiredSection.title" = "当前必需";
|
||||
"permissions.requiredSection.subtitle" = "这些权限决定了 Atlas 是否能完整支持当前主流程。";
|
||||
"permissions.optionalSection.title" = "可稍后处理";
|
||||
"permissions.optionalSection.subtitle" = "先折叠这些权限,等相关流程真正需要时再展开。";
|
||||
"permissions.optionalSection.disclosure" = "可选权限";
|
||||
"permissions.optionalSection.count.one" = "%d 项待处理";
|
||||
"permissions.optionalSection.count.other" = "%d 项待处理";
|
||||
"permissions.status.title" = "权限详情";
|
||||
"permissions.status.subtitle" = "每张卡片都会说明这项权限能解锁什么、什么时候需要,以及是否可以稍后再处理。";
|
||||
"permissions.empty.title" = "没有匹配的权限状态";
|
||||
"permissions.empty.detail" = "刷新权限,或清空当前搜索,以查看当前访问模型。";
|
||||
"permissions.row.ready" = "依赖这项权限的流程现在已经可以运行。";
|
||||
"permissions.row.required" = "当前主流程需要这项权限。";
|
||||
"permissions.row.optional" = "这项权限可以等相关流程真正需要时再授予。";
|
||||
"permissions.status.required" = "需要授权";
|
||||
"permissions.status.optional" = "可稍后授权";
|
||||
"permissions.grant.action" = "打开系统设置";
|
||||
"permissions.grant.notifications" = "请求通知权限";
|
||||
"permissions.support.fullDiskAccess" = "只有在你想扫描更深层的受保护 Library 位置时才需要。若刚开启,请完全退出并重新打开 Atlas,再回来检查。";
|
||||
"permissions.support.accessibility" = "只有在 Atlas 需要先安全关闭正在运行的应用再卸载时才需要。";
|
||||
"permissions.support.notifications" = "不是必须项,但对长任务提醒和恢复提醒很有帮助。";
|
||||
|
||||
"settings.screen.title" = "设置";
|
||||
"settings.screen.subtitle" = "在这里调整语言、恢复保留期、通知、排除项,并查看致谢与说明。";
|
||||
"settings.callout.title" = "Atlas 会将状态保存在本地,并让破坏性操作保持可审计";
|
||||
"settings.callout.detail" = "恢复保留策略、通知、排除项、致谢和说明都集中在这里,方便你在真正依赖产品前先理解其行为。";
|
||||
"settings.panel.title" = "偏好中心";
|
||||
"settings.panel.subtitle" = "在活跃设置、恢复行为和信任信息之间切换,而不是在一个长页面里反复滚动。";
|
||||
"settings.panel.general" = "通用";
|
||||
"settings.panel.recovery" = "恢复";
|
||||
"settings.panel.trust" = "信任";
|
||||
"settings.general.title" = "通用";
|
||||
"settings.general.subtitle" = "设置日常维护会用到的语言、恢复保留期和通知。";
|
||||
"settings.language.title" = "界面语言";
|
||||
"settings.language.detail" = "当前优先支持简体中文和英文切换。";
|
||||
"settings.language.picker" = "选择界面语言";
|
||||
"settings.language.hint" = "在简体中文和英文之间切换,默认语言为简体中文。";
|
||||
"settings.retention.title" = "恢复保留时长";
|
||||
"settings.retention.value" = "%d 天";
|
||||
"settings.retention.detail" = "可恢复的清理结果会在这个时间窗口内保留。";
|
||||
"settings.retention.adjust" = "调整保留时长";
|
||||
"settings.retention.hint" = "更改可恢复项目的保留天数。";
|
||||
"settings.recoveryPanel.title" = "恢复行为";
|
||||
"settings.recoveryPanel.subtitle" = "控制可恢复结果保留多久,以及哪些路径不进入计划。";
|
||||
"settings.notifications.title" = "任务通知";
|
||||
"settings.notifications.detail" = "适合在扫描、更新计划或清理需要较长时间时提醒你。";
|
||||
"settings.notifications.toggle" = "启用任务通知";
|
||||
"settings.notifications.hint" = "打开或关闭任务和恢复通知。";
|
||||
"settings.distribution.title" = "分发方式";
|
||||
"settings.distribution.value" = "Developer ID + Notarization";
|
||||
"settings.distribution.detail" = "冻结 MVP 假设采用直接分发,而不是 Mac App Store。";
|
||||
"settings.exclusions.title" = "规则与排除项";
|
||||
"settings.exclusions.subtitle" = "这些路径不会出现在扫描结果和清理计划里。";
|
||||
"settings.exclusions.empty.title" = "还没有配置排除项";
|
||||
"settings.exclusions.empty.detail" = "在后续迭代前,Atlas 会先使用默认覆盖范围进行扫描。";
|
||||
"settings.exclusions.row.subtitle" = "不会纳入清理建议和计划。";
|
||||
"settings.trust.title" = "信任";
|
||||
"settings.trust.subtitle" = "这些承诺帮助你理解 Atlas 如何安全地处理清理与恢复。";
|
||||
"settings.trust.ack.title" = "开源致谢";
|
||||
"settings.trust.ack.subtitle" = "在应用内可见,帮助用户理解产品来源。";
|
||||
"settings.trust.notices.title" = "第三方说明";
|
||||
"settings.trust.notices.subtitle" = "与致谢内容一起在应用内展示。";
|
||||
"settings.trust.destructive.title" = "破坏性操作";
|
||||
"settings.trust.destructive.subtitle" = "Atlas 会尽量把具有破坏性的操作做成可恢复、可复核、可追溯。";
|
||||
"settings.trust.destructive.badge" = "恢复优先";
|
||||
"settings.trust.documents.title" = "参考文档";
|
||||
"settings.trust.documents.subtitle" = "只有在需要完整文本时再打开致谢和第三方说明。";
|
||||
"settings.trust.documents.ack" = "查看致谢";
|
||||
"settings.trust.documents.notices" = "查看第三方说明";
|
||||
"settings.legal.title" = "法律信息";
|
||||
"settings.legal.subtitle" = "致谢与第三方声明";
|
||||
"settings.acknowledgement.title" = "致谢";
|
||||
"settings.acknowledgement.subtitle" = "用户无需离开产品,就能查看应用内致谢文本。";
|
||||
"settings.notices.title" = "第三方说明";
|
||||
"settings.notices.subtitle" = "用于展示随产品一起分发的第三方说明信息。";
|
||||
@@ -0,0 +1,44 @@
|
||||
import XCTest
|
||||
@testable import AtlasDomain
|
||||
|
||||
final class AtlasDomainTests: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
AtlasL10n.setCurrentLanguage(.zhHans)
|
||||
}
|
||||
|
||||
func testPrimaryRoutesMatchFrozenMVP() {
|
||||
XCTAssertEqual(
|
||||
AtlasRoute.allCases.map(\.title),
|
||||
["概览", "智能清理", "应用", "历史", "权限", "设置"]
|
||||
)
|
||||
}
|
||||
|
||||
func testScaffoldFixturesExposeRecoveryItems() {
|
||||
XCTAssertFalse(AtlasScaffoldFixtures.recoveryItems.isEmpty)
|
||||
XCTAssertGreaterThan(AtlasScaffoldFixtures.findings.map(\.bytes).reduce(0, +), 0)
|
||||
}
|
||||
|
||||
|
||||
func testSettingsDecodeDefaultsLanguageToChineseWhenMissing() throws {
|
||||
let data = Data("""
|
||||
{
|
||||
"recoveryRetentionDays": 7,
|
||||
"notificationsEnabled": true,
|
||||
"excludedPaths": []
|
||||
}
|
||||
""".utf8)
|
||||
|
||||
let settings = try JSONDecoder().decode(AtlasSettings.self, from: data)
|
||||
|
||||
XCTAssertEqual(settings.language, .zhHans)
|
||||
XCTAssertEqual(settings.acknowledgementText, AtlasL10n.acknowledgement(language: .zhHans))
|
||||
}
|
||||
|
||||
func testOnlyFullDiskAccessIsRequiredForCurrentWorkflows() {
|
||||
XCTAssertTrue(PermissionKind.fullDiskAccess.isRequiredForCurrentWorkflows)
|
||||
XCTAssertFalse(PermissionKind.accessibility.isRequiredForCurrentWorkflows)
|
||||
XCTAssertFalse(PermissionKind.notifications.isRequiredForCurrentWorkflows)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user