From 039de457e2ea53717add353194136867445242df Mon Sep 17 00:00:00 2001 From: Kaede Akino Date: Sun, 27 Sep 2026 19:28:04 +0800 Subject: [PATCH] feat: update notification texts --- .../CommandNotificationContentTests.swift | 42 ++++ Frameworks/Resources/Localizable.xcstrings | 206 ++++++++---------- .../NotificationSettingsFields.swift | 9 +- .../Strings/CommandNotifications.swift | 21 +- .../Strings/Notifications.swift | 3 - .../TerminalCommandNotificationContent.swift | 10 +- 6 files changed, 149 insertions(+), 142 deletions(-) create mode 100644 FrameworkTests/CommandNotificationContentTests.swift diff --git a/FrameworkTests/CommandNotificationContentTests.swift b/FrameworkTests/CommandNotificationContentTests.swift new file mode 100644 index 00000000..89cedcd3 --- /dev/null +++ b/FrameworkTests/CommandNotificationContentTests.swift @@ -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).")) + } +} diff --git a/Frameworks/Resources/Localizable.xcstrings b/Frameworks/Resources/Localizable.xcstrings index faff366d..9bfe7db5 100644 --- a/Frameworks/Resources/Localizable.xcstrings +++ b/Frameworks/Resources/Localizable.xcstrings @@ -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" diff --git a/Frameworks/TermindPresentation/NotificationSettingsFields.swift b/Frameworks/TermindPresentation/NotificationSettingsFields.swift index f2583755..692bf699 100644 --- a/Frameworks/TermindPresentation/NotificationSettingsFields.swift +++ b/Frameworks/TermindPresentation/NotificationSettingsFields.swift @@ -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) } diff --git a/Frameworks/TermindPresentation/Strings/CommandNotifications.swift b/Frameworks/TermindPresentation/Strings/CommandNotifications.swift index b4984e07..c890a877 100644 --- a/Frameworks/TermindPresentation/Strings/CommandNotifications.swift +++ b/Frameworks/TermindPresentation/Strings/CommandNotifications.swift @@ -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)." } } } diff --git a/Frameworks/TermindPresentation/Strings/Notifications.swift b/Frameworks/TermindPresentation/Strings/Notifications.swift index 98ffba92..7e2c013b 100644 --- a/Frameworks/TermindPresentation/Strings/Notifications.swift +++ b/Frameworks/TermindPresentation/Strings/Notifications.swift @@ -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." diff --git a/Frameworks/TermindPresentation/Terminal/TerminalCommandNotificationContent.swift b/Frameworks/TermindPresentation/Terminal/TerminalCommandNotificationContent.swift index 95bc9e4d..21d4de05 100644 --- a/Frameworks/TermindPresentation/Terminal/TerminalCommandNotificationContent.swift +++ b/Frameworks/TermindPresentation/Terminal/TerminalCommandNotificationContent.swift @@ -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) } }