mirror of
https://github.com/X1a0He/Adobe-Downloader.git
synced 2025-11-25 11:18:53 +08:00
feat: Call Setup through Helper to install and get progress.
This commit is contained in:
@@ -30,9 +30,9 @@
|
||||
filePath = "Adobe Downloader/Utils/InstallManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "143"
|
||||
endingLineNumber = "143"
|
||||
landmarkName = "install(at:progressHandler:logHandler:)"
|
||||
startingLineNumber = "132"
|
||||
endingLineNumber = "132"
|
||||
landmarkName = "retry(at:progressHandler:logHandler:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
|
||||
@@ -11,6 +11,8 @@ import ServiceManagement
|
||||
|
||||
@objc protocol HelperToolProtocol {
|
||||
func executeCommand(_ command: String, withReply reply: @escaping (String) -> Void)
|
||||
func startInstallation(_ command: String, withReply reply: @escaping (String) -> Void)
|
||||
func getInstallationOutput(withReply reply: @escaping (String) -> Void)
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
@@ -198,7 +200,7 @@ class PrivilegedHelperManager: NSObject {
|
||||
completion(false, "获取授权失败")
|
||||
case .getAdminFail:
|
||||
completion(false, "获取管理员权限失败")
|
||||
case let .blessError(code):
|
||||
case .blessError(_):
|
||||
completion(false, "安装失败: \(result.alertContent)")
|
||||
}
|
||||
}
|
||||
@@ -323,6 +325,44 @@ class PrivilegedHelperManager: NSObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func executeInstallation(_ command: String, progress: @escaping (String) -> Void) async throws {
|
||||
guard let connection = connectToHelper() else {
|
||||
throw NSError(domain: "HelperError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Could not connect to helper"])
|
||||
}
|
||||
|
||||
guard let helper = connection.remoteObjectProxyWithErrorHandler({ error in
|
||||
self.connectionState = .disconnected
|
||||
}) as? HelperToolProtocol else {
|
||||
throw NSError(domain: "HelperError", code: -2, userInfo: [NSLocalizedDescriptionKey: "Could not get helper proxy"])
|
||||
}
|
||||
|
||||
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
|
||||
helper.startInstallation(command) { result in
|
||||
if result == "Started" {
|
||||
continuation.resume()
|
||||
} else {
|
||||
continuation.resume(throwing: NSError(domain: "HelperError", code: -3, userInfo: [NSLocalizedDescriptionKey: result]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while true {
|
||||
let output = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<String, Error>) in
|
||||
helper.getInstallationOutput { result in
|
||||
continuation.resume(returning: result)
|
||||
}
|
||||
}
|
||||
|
||||
if output == "Completed" {
|
||||
break
|
||||
} else if !output.isEmpty {
|
||||
progress(output)
|
||||
}
|
||||
|
||||
try await Task.sleep(nanoseconds: 100_000_000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PrivilegedHelperManager {
|
||||
|
||||
@@ -34,8 +34,6 @@ actor InstallManager {
|
||||
|
||||
private func executeInstallation(
|
||||
at appPath: URL,
|
||||
withSudo: Bool = true,
|
||||
password: String? = nil,
|
||||
progressHandler: @escaping (Double, String) -> Void,
|
||||
logHandler: @escaping (String) -> Void
|
||||
) async throws {
|
||||
@@ -44,89 +42,64 @@ actor InstallManager {
|
||||
}
|
||||
|
||||
let driverPath = appPath.appendingPathComponent("driver.xml").path
|
||||
let installProcess = Process()
|
||||
|
||||
if withSudo {
|
||||
installProcess.executableURL = URL(fileURLWithPath: "/usr/bin/sudo")
|
||||
installProcess.arguments = password != nil ? ["-S"] : []
|
||||
installProcess.arguments?.append(contentsOf: [setupPath, "--install=1", "--driverXML=\(driverPath)"])
|
||||
} else {
|
||||
installProcess.executableURL = URL(fileURLWithPath: setupPath)
|
||||
installProcess.arguments = ["--install=1", "--driverXML=\(driverPath)"]
|
||||
}
|
||||
|
||||
let commandString = "sudo \"\(setupPath)\" --install=1 --driverXML=\"\(driverPath)\""
|
||||
print(commandString)
|
||||
|
||||
let outputPipe = Pipe()
|
||||
installProcess.standardOutput = outputPipe
|
||||
installProcess.standardError = outputPipe
|
||||
|
||||
if let password = password {
|
||||
let inputPipe = Pipe()
|
||||
installProcess.standardInput = inputPipe
|
||||
try inputPipe.fileHandleForWriting.write(contentsOf: "\(password)\n".data(using: .utf8)!)
|
||||
inputPipe.fileHandleForWriting.closeFile()
|
||||
}
|
||||
|
||||
installationProcess = installProcess
|
||||
let installCommand = "\"\(setupPath)\" --install=1 --driverXML=\"\(driverPath)\""
|
||||
|
||||
await MainActor.run {
|
||||
progressHandler(0.0, withSudo ? String(localized: "正在准备安装...") : String(localized: "正在重试安装..."))
|
||||
progressHandler(0.0, String(localized: "正在准备安装..."))
|
||||
}
|
||||
|
||||
try installProcess.run()
|
||||
|
||||
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
|
||||
Task.detached {
|
||||
do {
|
||||
for try await line in outputPipe.fileHandleForReading.bytes.lines {
|
||||
await MainActor.run { logHandler(line) }
|
||||
try await PrivilegedHelperManager.shared.executeInstallation(installCommand) { output in
|
||||
Task { @MainActor in
|
||||
logHandler(output)
|
||||
if let range = output.range(of: "Exit Code: (-?[0-9]+)", options: .regularExpression),
|
||||
let codeStr = output[range].split(separator: ":").last?.trimmingCharacters(in: .whitespaces),
|
||||
let exitCode = Int(codeStr) {
|
||||
|
||||
if line.contains("incorrect password") || line.contains("sudo: 1 incorrect password attempt") {
|
||||
installProcess.terminate()
|
||||
continuation.resume(throwing: InstallError.permissionDenied)
|
||||
return
|
||||
}
|
||||
|
||||
if let range = line.range(of: "Exit Code: (-?[0-9]+)", options: .regularExpression),
|
||||
let codeStr = line[range].split(separator: ":").last?.trimmingCharacters(in: .whitespaces),
|
||||
let code = Int32(codeStr) {
|
||||
if code != 0 {
|
||||
let errorMessage = code == -1
|
||||
? String(localized: "安装程序调用失败,Setup 组件未被处理,请联系开发者")
|
||||
: String(localized: "(退出代码: \(code))")
|
||||
|
||||
installProcess.terminate()
|
||||
continuation.resume(throwing: InstallError.installationFailed(errorMessage))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if let progress = await self.parseProgress(from: line) {
|
||||
await MainActor.run {
|
||||
progressHandler(progress.progress, progress.status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
installProcess.waitUntilExit()
|
||||
|
||||
if installProcess.terminationStatus == 0 {
|
||||
if exitCode == 0 {
|
||||
progressHandler(1.0, String(localized: "安装完成"))
|
||||
continuation.resume()
|
||||
} else {
|
||||
let errorMessage = String(localized: "(退出代码: \(installProcess.terminationStatus))")
|
||||
let errorMessage: String
|
||||
switch exitCode {
|
||||
case 107:
|
||||
errorMessage = String(localized: "安装失败: 架构或版本不一致 (退出代码: \(exitCode))")
|
||||
case 103:
|
||||
errorMessage = String(localized: "安装失败: 权限问题 (退出代码: \(exitCode))")
|
||||
case 182:
|
||||
errorMessage = String(localized: "安装失败: 安装文件不完整或损坏 (退出代码: \(exitCode))")
|
||||
case -1:
|
||||
errorMessage = String(localized: "安装失败: Setup 组件未被处理 (退出代码: \(exitCode))")
|
||||
default:
|
||||
errorMessage = String(localized: "安装失败 (退出代码: \(exitCode))")
|
||||
}
|
||||
progressHandler(0.0, errorMessage)
|
||||
continuation.resume(throwing: InstallError.installationFailed(errorMessage))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if let progress = await self.parseProgress(from: output) {
|
||||
progressHandler(progress, String(localized: "正在安装..."))
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await MainActor.run {
|
||||
progressHandler(1.0, String(localized: "安装完成"))
|
||||
}
|
||||
|
||||
private func parseProgress(from output: String) -> Double? {
|
||||
if let range = output.range(of: "Progress: ([0-9]{1,3})%", options: .regularExpression),
|
||||
let progressStr = output[range].split(separator: ":").last?.trimmingCharacters(in: .whitespaces),
|
||||
let progressValue = Double(progressStr.replacingOccurrences(of: "%", with: "")) {
|
||||
return progressValue / 100.0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func install(
|
||||
@@ -134,100 +107,31 @@ actor InstallManager {
|
||||
progressHandler: @escaping (Double, String) -> Void,
|
||||
logHandler: @escaping (String) -> Void
|
||||
) async throws {
|
||||
self.progressHandler = progressHandler
|
||||
|
||||
let password = try await requestPassword()
|
||||
try await executeInstallation(
|
||||
at: appPath,
|
||||
withSudo: true,
|
||||
password: password,
|
||||
progressHandler: progressHandler,
|
||||
logHandler: logHandler
|
||||
)
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
PrivilegedHelperManager.shared.executeCommand("pkill -f Setup") { _ in }
|
||||
}
|
||||
|
||||
func getInstallCommand(for driverPath: String) -> String {
|
||||
return "sudo \"\(setupPath)\" --install=1 --driverXML=\"\(driverPath)\""
|
||||
}
|
||||
|
||||
func retry(
|
||||
at appPath: URL,
|
||||
progressHandler: @escaping (Double, String) -> Void,
|
||||
logHandler: @escaping (String) -> Void
|
||||
) async throws {
|
||||
self.progressHandler = progressHandler
|
||||
let password = try await requestPassword()
|
||||
try await executeInstallation(
|
||||
at: appPath,
|
||||
withSudo: true,
|
||||
password: password,
|
||||
progressHandler: progressHandler,
|
||||
logHandler: logHandler
|
||||
)
|
||||
}
|
||||
|
||||
private func requestPassword() async throws -> String {
|
||||
let authProcess = Process()
|
||||
authProcess.executableURL = URL(fileURLWithPath: "/usr/bin/osascript")
|
||||
let authScript = """
|
||||
tell application "System Events"
|
||||
display dialog "请输入管理员密码以继续安装(Please enter the password to continue the installation)" default answer "" with hidden answer ¬
|
||||
buttons {"取消", "确定"} default button "确定" ¬
|
||||
with icon caution ¬
|
||||
with title "需要管理员权限"
|
||||
if button returned of result is "确定" then
|
||||
return text returned of result
|
||||
else
|
||||
error "用户取消了操作"
|
||||
end if
|
||||
end tell
|
||||
"""
|
||||
|
||||
let authPipe = Pipe()
|
||||
authProcess.standardOutput = authPipe
|
||||
authProcess.standardError = Pipe()
|
||||
authProcess.arguments = ["-e", authScript]
|
||||
|
||||
try authProcess.run()
|
||||
authProcess.waitUntilExit()
|
||||
|
||||
if authProcess.terminationStatus != 0 {
|
||||
throw InstallError.cancelled
|
||||
}
|
||||
|
||||
guard let passwordData = try? authPipe.fileHandleForReading.readToEnd(),
|
||||
let password = String(data: passwordData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!password.isEmpty else {
|
||||
throw InstallError.cancelled
|
||||
}
|
||||
|
||||
return password
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
installationProcess?.terminate()
|
||||
}
|
||||
|
||||
private func parseProgress(from line: String) -> (progress: Double, status: String)? {
|
||||
if let range = line.range(of: "Exit Code: (-?[0-9]+)", options: .regularExpression),
|
||||
let codeStr = line[range].split(separator: ":").last?.trimmingCharacters(in: .whitespaces),
|
||||
let exitCode = Int(codeStr) {
|
||||
return exitCode == 0 ? (1.0, String(localized: "安装完成")) : nil
|
||||
}
|
||||
|
||||
if let range = line.range(of: "Progress: ([0-9]{1,3})%", options: .regularExpression),
|
||||
let progressStr = line[range].split(separator: ":").last?.trimmingCharacters(in: .whitespaces),
|
||||
let progressValue = Double(progressStr.replacingOccurrences(of: "%", with: "")) {
|
||||
return (progressValue / 100.0, String("正在安装..."))
|
||||
}
|
||||
|
||||
if line.contains("Installing packages") {
|
||||
return (0.0, String(localized: "正在安装包..."))
|
||||
} else if line.contains("Preparing") {
|
||||
return (0.0, String(localized: "正在准备..."))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getInstallCommand(for driverPath: String) -> String {
|
||||
return "sudo \"\(setupPath)\" --install=1 --driverXML=\"\(driverPath)\""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,23 +93,14 @@ class ModifySetup {
|
||||
let setupPath = "/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup"
|
||||
|
||||
let commands = [
|
||||
// 第一个替换命令
|
||||
"""
|
||||
perl -0777pi -e 'BEGIN{$/=\\1e8} s|\\x55\\x48\\x89\\xE5\\x53\\x50\\x48\\x89\\xFB\\x48\\x8B\\x05\\x70\\xC7\\x03\\x00\\x48\\x8B\\x00\\x48\\x89\\x45\\xF0\\xE8\\x24\\xD7\\xFE\\xFF\\x48\\x83\\xC3\\x08\\x48\\x39\\xD8\\x0F|\\x6A\\x01\\x58\\xC3\\x53\\x50\\x48\\x89\\xFB\\x48\\x8B\\x05\\x70\\xC7\\x03\\x00\\x48\\x8B\\x00\\x48\\x89\\x45\\xF0\\xE8\\x24\\xD7\\xFE\\xFF\\x48\\x83\\xC3\\x08\\x48\\x39\\xD8\\x0F|gs' '\(setupPath)'
|
||||
""",
|
||||
|
||||
// 第二个替换命令
|
||||
"""
|
||||
perl -0777pi -e 'BEGIN{$/=\\1e8} s|\\xFF\\xC3\\x00\\xD1\\xF4\\x4F\\x01\\xA9\\xFD\\x7B\\x02\\xA9\\xFD\\x83\\x00\\x91\\xF3\\x03\\x00\\xAA\\x1F\\x20\\x03\\xD5\\x68\\xA1\\x1D\\x58\\x08\\x01\\x40\\xF9\\xE8\\x07\\x00\\xF9|\\x20\\x00\\x80\\xD2\\xC0\\x03\\x5F\\xD6\\xFD\\x7B\\x02\\xA9\\xFD\\x83\\x00\\x91\\xF3\\x03\\x00\\xAA\\x1F\\x20\\x03\\xD5\\x68\\xA1\\x1D\\x58\\x08\\x01\\x40\\xF9\\xE8\\x07\\x00\\xF9|gs' '\(setupPath)'
|
||||
""",
|
||||
|
||||
// 移除签名
|
||||
"codesign --remove-signature '\(setupPath)'",
|
||||
|
||||
// 重新签名
|
||||
"codesign -f -s - --timestamp=none --all-architectures --deep '\(setupPath)'",
|
||||
|
||||
// 清除扩展属性
|
||||
"xattr -cr '\(setupPath)'"
|
||||
]
|
||||
|
||||
|
||||
@@ -9,11 +9,15 @@ import Foundation
|
||||
|
||||
@objc(HelperToolProtocol) protocol HelperToolProtocol {
|
||||
func executeCommand(_ command: String, withReply reply: @escaping (String) -> Void)
|
||||
func startInstallation(_ command: String, withReply reply: @escaping (String) -> Void)
|
||||
func getInstallationOutput(withReply reply: @escaping (String) -> Void)
|
||||
}
|
||||
|
||||
class HelperTool: NSObject, HelperToolProtocol {
|
||||
private let listener: NSXPCListener
|
||||
private var connections: Set<NSXPCConnection> = []
|
||||
private var currentTask: Process?
|
||||
private var outputPipe: Pipe?
|
||||
|
||||
override init() {
|
||||
listener = NSXPCListener(machServiceName: "com.x1a0he.macOS.Adobe-Downloader.helper")
|
||||
@@ -32,7 +36,6 @@ class HelperTool: NSObject, HelperToolProtocol {
|
||||
|
||||
func executeCommand(_ command: String, withReply reply: @escaping (String) -> Void) {
|
||||
print("[Adobe Downloader Helper] 收到执行命令请求: \(command)")
|
||||
print("[Adobe Downloader Helper] 当前进程权限: \(geteuid())")
|
||||
|
||||
let task = Process()
|
||||
let pipe = Pipe()
|
||||
@@ -42,27 +45,88 @@ class HelperTool: NSObject, HelperToolProtocol {
|
||||
task.arguments = ["-c", command]
|
||||
task.executableURL = URL(fileURLWithPath: "/bin/sh")
|
||||
|
||||
currentTask = task
|
||||
|
||||
do {
|
||||
print("[Adobe Downloader Helper] 开始执行命令")
|
||||
try task.run()
|
||||
task.waitUntilExit()
|
||||
|
||||
let status = task.terminationStatus
|
||||
print("[Adobe Downloader Helper] 命令执行完成,退出状态: \(status)")
|
||||
|
||||
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||
if let output = String(data: data, encoding: .utf8) {
|
||||
let trimmedOutput = output.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
print("[Adobe Downloader Helper] 命令执行成功,输出: \(trimmedOutput)")
|
||||
reply(trimmedOutput)
|
||||
reply(output.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||
} else {
|
||||
print("[Adobe Downloader Helper] 无法解码命令输出")
|
||||
reply("Error: Could not decode command output")
|
||||
}
|
||||
} catch {
|
||||
print("[Adobe Downloader Helper] 命令执行失败: \(error)")
|
||||
reply("Error: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
currentTask = nil
|
||||
}
|
||||
|
||||
func startInstallation(_ command: String, withReply reply: @escaping (String) -> Void) {
|
||||
print("[Adobe Downloader Helper] 收到安装请求: \(command)")
|
||||
|
||||
let task = Process()
|
||||
let pipe = Pipe()
|
||||
|
||||
task.standardOutput = pipe
|
||||
task.standardError = pipe
|
||||
task.arguments = ["-c", command]
|
||||
task.executableURL = URL(fileURLWithPath: "/bin/sh")
|
||||
|
||||
currentTask = task
|
||||
outputPipe = pipe
|
||||
|
||||
do {
|
||||
try task.run()
|
||||
reply("Started")
|
||||
} catch {
|
||||
currentTask = nil
|
||||
outputPipe = nil
|
||||
reply("Error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
func getInstallationOutput(withReply reply: @escaping (String) -> Void) {
|
||||
guard let pipe = outputPipe else {
|
||||
reply("")
|
||||
return
|
||||
}
|
||||
|
||||
let data = pipe.fileHandleForReading.availableData
|
||||
if data.isEmpty {
|
||||
if let task = currentTask, !task.isRunning {
|
||||
currentTask = nil
|
||||
outputPipe = nil
|
||||
reply("Completed")
|
||||
} else {
|
||||
reply("")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if let output = String(data: data, encoding: .utf8) {
|
||||
let lines = output.components(separatedBy: .newlines)
|
||||
.filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
|
||||
.joined(separator: "\n")
|
||||
if !lines.isEmpty {
|
||||
reply(lines)
|
||||
} else {
|
||||
reply("")
|
||||
}
|
||||
} else {
|
||||
reply("")
|
||||
}
|
||||
}
|
||||
|
||||
func terminateCurrentTask() {
|
||||
if let pipe = outputPipe {
|
||||
pipe.fileHandleForReading.readabilityHandler = nil
|
||||
}
|
||||
currentTask?.terminate()
|
||||
currentTask = nil
|
||||
outputPipe = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,16 +14,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"(退出代码: %d)" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "(Exit Code: %d)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/" : {
|
||||
|
||||
},
|
||||
@@ -611,6 +601,21 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"安装失败 (退出代码: %lld)" : {
|
||||
|
||||
},
|
||||
"安装失败: Setup 组件未被处理 (退出代码: %lld)" : {
|
||||
|
||||
},
|
||||
"安装失败: 安装文件不完整或损坏 (退出代码: %lld)" : {
|
||||
|
||||
},
|
||||
"安装失败: 权限问题 (退出代码: %lld)" : {
|
||||
|
||||
},
|
||||
"安装失败: 架构或版本不一致 (退出代码: %lld)" : {
|
||||
|
||||
},
|
||||
"安装完成" : {
|
||||
"localizations" : {
|
||||
@@ -642,16 +647,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"安装程序调用失败,Setup 组件未被处理,请联系开发者" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "The installation program failed to call, please contact developer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"尝试使用不同的搜索关键词" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@@ -1031,16 +1026,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"正在准备..." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Preparing..."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"正在准备安装..." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@@ -1081,25 +1066,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"正在安装包..." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Installing packages..."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"正在重试安装..." : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Retrying installation..."
|
||||
}
|
||||
}
|
||||
}
|
||||
"正在安装..." : {
|
||||
|
||||
},
|
||||
"没有写入权限" : {
|
||||
"localizations" : {
|
||||
@@ -1142,17 +1110,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"目录:" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Directory: "
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"确定" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@@ -1165,31 +1122,9 @@
|
||||
},
|
||||
"确定要下载并安装 Setup 组件吗?" : {
|
||||
|
||||
},
|
||||
"确定要下载并安装 Setup 组件吗?这个操作需要管理员权限。" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Are you sure you want to download and install the Setup components? This operation requires administrator privileges."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"确定要重新处理 Setup 组件吗?" : {
|
||||
|
||||
},
|
||||
"确定要重新处理 Setup 组件吗?这个操作需要管理员权限。" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Are you sure you want to reprocess the Setup components? This operation requires administrator privileges."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"确认" : {
|
||||
"localizations" : {
|
||||
@@ -1384,17 +1319,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"语言:" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Language: "
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"请求超时,请检查网络连接后重试" : {
|
||||
"comment" : "Network timeout",
|
||||
"localizations" : {
|
||||
|
||||
Reference in New Issue
Block a user