feat: update notification texts

This commit is contained in:
Kaede Akino committed 2026-09-27 19:28:04 +08:00
1 parent c907cbd83e
commit 039de457e2
6 files changed
+149 -142

No files matched your search

@@ -0,0 +1,42 @@
import Foundation
import TermindApplication
import TermindPresentation
import Testing
@Suite
struct CommandNotificationContentTests {
@Test(arguments: [0, 1, nil] as [Int?], ["npm\nrun\tbuild", nil, " \n\t"] as [String?])
func completionIsOneSentenceWithTheActualResult(exitCode: Int?, command: String?) {
let completion = TerminalCommandCompletion(command: command, exitCode: exitCode, duration: 12)
let duration = Duration.seconds(12).formatted(.units(allowed: [.hours, .minutes, .seconds]))
let expected: String
if command == "npm\nrun\tbuild" {
let preview = "npm run build"
switch exitCode {
case 0: expected = String(localized: "“\(preview)” completed successfully in \(duration).")
case .some:
expected = String(localized: "“\(preview)” failed with exit code \(1) after \(duration).")
case nil: expected = String(localized: "“\(preview)” finished after \(duration).")
}
} else {
switch exitCode {
case 0: expected = String(localized: "Command completed successfully in \(duration).")
case .some: expected = String(localized: "Command failed with exit code \(1) after \(duration).")
case nil: expected = String(localized: "Command finished after \(duration).")
}
}
let body = CommandNotificationContent.body(completion)
#expect(body == expected)
#expect(!body.contains("\n"))
}
@Test func longCommandKeepsTheFailureCodeAndDurationAfterTheEllipsis() {
let completion = TerminalCommandCompletion(
command: String(repeating: "字", count: 170), exitCode: 127, duration: 12)
let preview = String(repeating: "字", count: 159) + "…"
let duration = Duration.seconds(12).formatted(.units(allowed: [.hours, .minutes, .seconds]))
#expect(
CommandNotificationContent.body(completion)
== String(localized: "“\(preview)” failed with exit code \(127) after \(duration)."))
}
}
+91 -115
View File
@@ -6171,19 +6171,53 @@
}
}
},
"Command completed" : {
"Command completed successfully in %@." : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "命令已完成"
"value" : "命令执行成功,用时%@。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "指令已完成"
"value" : "指令執行成功,用時%@。"
}
}
}
},
"Command failed with exit code %lld after %@." : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "命令执行失败,退出码为%lld,用时%@。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "指令執行失敗,結束代碼為%lld,用時%@。"
}
}
}
},
"Command finished after %@." : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "命令执行结束,用时%@。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "指令執行結束,用時%@。"
}
}
}
@@ -6347,29 +6381,6 @@
}
}
},
"Completed successfully in %@." : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Completed successfully in %@."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "执行成功,用时%@。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "執行成功,用時%@。"
}
}
}
},
"Completed without output" : {
"extractionState" : "manual",
"localizations" : {
@@ -11542,29 +11553,6 @@
}
}
},
"Failed with exit code %lld after %@." : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Failed with exit code %lld after %@."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "执行失败,退出码为%lld,用时%@。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "執行失敗,結束代碼為%lld,用時%@。"
}
}
}
},
"Family Shared" : {
"comment" : "A label for a family-shared subscription.",
"extractionState" : "manual",
@@ -11928,29 +11916,6 @@
}
}
},
"Finished after %@." : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Finished after %@."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "执行结束,用时%@。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "執行結束,用時%@。"
}
}
}
},
"Flow Control" : {
"extractionState" : "manual",
"localizations" : {
@@ -17981,23 +17946,6 @@
}
}
},
"Notifications can be delayed while iOS suspends Termind." : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "iOS挂起Termind时,通知可能延迟。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "iOS暫停Termind時,通知可能延遲。"
}
}
}
},
"Notify When" : {
"extractionState" : "manual",
"localizations" : {
@@ -18015,29 +17963,6 @@
}
}
},
"Notify when all transfers finish, excluding paused transfers. Unfocused means no app window is in the foreground." : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Notify when all transfers finish, excluding paused transfers. Unfocused means no app window is in the foreground."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "所有传输结束时通知,暂停的传输不计入。未聚焦表示应用没有处于前台的窗口。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "所有傳輸結束時通知,暫停的傳輸不計入。未聚焦表示應用程式沒有處於前景的視窗。"
}
}
}
},
"OK" : {
"extractionState" : "manual",
"localizations" : {
@@ -22331,19 +22256,19 @@
}
}
},
"Requires shell integration or OSC 133. Workspace dots also mark shorter commands." : {
"Requires shell integration or OSC 133." : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "需要Shell集成或OSC 133。工作区未读标记也会显示较短的命令。"
"value" : "需要Shell集成或OSC 133。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "需要Shell整合或OSC 133。工作區未讀標記也會顯示較短的指令。"
"value" : "需要Shell整合或OSC 133。"
}
}
}
@@ -34805,6 +34730,57 @@
}
}
}
},
"“%@” completed successfully in %@." : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "“%@”执行成功,用时%@。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "「%@」執行成功,用時%@。"
}
}
}
},
"“%@” failed with exit code %lld after %@." : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "“%@”执行失败,退出码为%lld,用时%@。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "「%@」執行失敗,結束代碼為%lld,用時%@。"
}
}
}
},
"“%@” finished after %@." : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "“%@”执行结束,用时%@。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "「%@」執行結束,用時%@。"
}
}
}
}
},
"version" : "1.2"
@@ -62,20 +62,13 @@ public struct NotificationSettingsFields: View {
} header: {
Text(AppStrings.Settings.terminal)
} footer: {
VStack(alignment: .leading) {
Text(AppStrings.CommandNotifications.explanation).fixedSize(horizontal: false, vertical: true)
#if os(iOS)
Text(AppStrings.CommandNotifications.mobileExplanation).fixedSize(horizontal: false, vertical: true)
#endif
}
Text(AppStrings.CommandNotifications.explanation).fixedSize(horizontal: false, vertical: true)
}
.disabled(!model.preferences.isEnabled)
Section {
modePicker(\.sftpMode)
} header: {
Text(AppStrings.SFTP.sftp)
} footer: {
Text(AppStrings.Notifications.sftpExplanation)
}
.disabled(!model.preferences.isEnabled)
.task { authorization = await service.authorization(request: false) }
@@ -14,20 +14,19 @@ extension AppStrings {
public static var denied: LocalizedStringResource { "Notifications are disabled in system settings." }
public static var unread: LocalizedStringResource { "Unread command completion" }
public static var explanation: LocalizedStringResource {
"Requires shell integration or OSC 133. Workspace dots also mark shorter commands."
"Requires shell integration or OSC 133."
}
public static var mobileExplanation: LocalizedStringResource {
"Notifications can be delayed while iOS suspends Termind."
public static func succeeded(_ command: String?, _ duration: String) -> LocalizedStringResource {
if let command { return "“\(command)” completed successfully in \(duration)." }
return "Command completed successfully in \(duration)."
}
public static var completed: LocalizedStringResource { "Command completed" }
public static func succeeded(_ duration: String) -> LocalizedStringResource {
"Completed successfully in \(duration)."
public static func failed(_ command: String?, _ code: Int, _ duration: String) -> LocalizedStringResource {
if let command { return "“\(command)” failed with exit code \(code) after \(duration)." }
return "Command failed with exit code \(code) after \(duration)."
}
public static func failed(_ code: Int, _ duration: String) -> LocalizedStringResource {
"Failed with exit code \(code) after \(duration)."
}
public static func finished(_ duration: String) -> LocalizedStringResource {
"Finished after \(duration)."
public static func finished(_ command: String?, _ duration: String) -> LocalizedStringResource {
if let command { return "“\(command)” finished after \(duration)." }
return "Command finished after \(duration)."
}
}
}
@@ -13,9 +13,6 @@ extension AppStrings {
public static var unfocused: LocalizedStringResource { "Unfocused" }
public static var markRead: LocalizedStringResource { "Mark as Read" }
public static var markUnread: LocalizedStringResource { "Mark as Unread" }
public static var sftpExplanation: LocalizedStringResource {
"Notify when all transfers finish, excluding paused transfers. Unfocused means no app window is in the foreground."
}
public static func transfersCompleted(succeeded: Int, failed: Int) -> LocalizedStringResource {
if failed == 0 {
return succeeded == 1 ? "The transfer is complete." : "All \(succeeded) transfers are complete."
@@ -4,13 +4,13 @@ import TermindApplication
public enum CommandNotificationContent {
public static func body(_ completion: TerminalCommandCompletion) -> String {
let duration = Duration.seconds(completion.duration).formatted(.units(allowed: [.hours, .minutes, .seconds]))
let command = completion.commandPreview
let result: LocalizedStringResource
switch completion.exitCode {
case 0: result = AppStrings.CommandNotifications.succeeded(duration)
case .some(let code): result = AppStrings.CommandNotifications.failed(code, duration)
case nil: result = AppStrings.CommandNotifications.finished(duration)
case 0: result = AppStrings.CommandNotifications.succeeded(command, duration)
case .some(let code): result = AppStrings.CommandNotifications.failed(command, code, duration)
case nil: result = AppStrings.CommandNotifications.finished(command, duration)
}
return (completion.commandPreview ?? String(localized: AppStrings.CommandNotifications.completed))
+ "\n" + String(localized: result)
return String(localized: result)
}
}