From 02f34190802be6d1c939bf009b136d7c85479a37 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 10 Sep 2026 21:36:54 +0900 Subject: [PATCH 1/4] =?UTF-8?q?chore:=20=EC=83=89=EC=83=81=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AccentColor.colorset/Contents.json | 6 +-- .../AppBackground.colorset/Contents.json | 12 +++--- .../Border.colorset/Contents.json | 10 ++--- .../DangerContainer.colorset/Contents.json | 38 +++++++++++++++++++ .../InfoContainer.colorset/Contents.json | 38 +++++++++++++++++++ .../OnPrimaryContainer.colorset/Contents.json | 6 +-- .../PrimaryContainer.colorset/Contents.json | 8 ++-- .../Review.colorset/Contents.json | 38 +++++++++++++++++++ .../ReviewContainer.colorset/Contents.json | 38 +++++++++++++++++++ .../SuccessContainer.colorset/Contents.json | 38 +++++++++++++++++++ .../Surface.colorset/Contents.json | 6 +-- .../SurfaceSecondary.colorset/Contents.json | 12 +++--- .../TextPrimary.colorset/Contents.json | 12 +++--- .../TextSecondary.colorset/Contents.json | 12 +++--- .../TextTertiary.colorset/Contents.json | 12 +++--- .../WarningContainer.colorset/Contents.json | 38 +++++++++++++++++++ .../Sources/Common/ColorAsset.swift | 6 +++ 17 files changed, 282 insertions(+), 48 deletions(-) create mode 100644 Application/Presentation/PresentationShared/Resources/Assets.xcassets/DangerContainer.colorset/Contents.json create mode 100644 Application/Presentation/PresentationShared/Resources/Assets.xcassets/InfoContainer.colorset/Contents.json create mode 100644 Application/Presentation/PresentationShared/Resources/Assets.xcassets/Review.colorset/Contents.json create mode 100644 Application/Presentation/PresentationShared/Resources/Assets.xcassets/ReviewContainer.colorset/Contents.json create mode 100644 Application/Presentation/PresentationShared/Resources/Assets.xcassets/SuccessContainer.colorset/Contents.json create mode 100644 Application/Presentation/PresentationShared/Resources/Assets.xcassets/WarningContainer.colorset/Contents.json diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/AccentColor.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/AccentColor.colorset/Contents.json index 23b9cc2b..d3f61b9f 100644 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/AccentColor.colorset/Contents.json +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/AccentColor.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0xA8", - "green": "0xA5", - "red": "0x1B" + "blue": "0xA6", + "green": "0xA0", + "red": "0x1E" } }, "idiom": "universal" diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/AppBackground.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/AppBackground.colorset/Contents.json index 0347f24f..cb3f5157 100644 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/AppBackground.colorset/Contents.json +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/AppBackground.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0xFA", - "green": "0xF9", - "red": "0xF6" + "blue": "0xF6", + "green": "0xF4", + "red": "0xF2" } }, "idiom": "universal" @@ -23,9 +23,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0x16", - "green": "0x14", - "red": "0x0F" + "blue": "0x13", + "green": "0x10", + "red": "0x0E" } }, "idiom": "universal" diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Border.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Border.colorset/Contents.json index c166657c..0f649cb4 100644 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Border.colorset/Contents.json +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Border.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0xF0", + "blue": "0xEB", "green": "0xE8", - "red": "0xE2" + "red": "0xE5" } }, "idiom": "universal" @@ -23,9 +23,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0x30", - "green": "0x2C", - "red": "0x24" + "blue": "0x43", + "green": "0x36", + "red": "0x2E" } }, "idiom": "universal" diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/DangerContainer.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/DangerContainer.colorset/Contents.json new file mode 100644 index 00000000..912bd8a0 --- /dev/null +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/DangerContainer.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0xEE", + "green": "0xEC", + "red": "0xFE" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0x22", + "green": "0x1E", + "red": "0x45" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/InfoContainer.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/InfoContainer.colorset/Contents.json new file mode 100644 index 00000000..df9c5263 --- /dev/null +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/InfoContainer.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0xFF", + "green": "0xF3", + "red": "0xE8" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0x4A", + "green": "0x38", + "red": "0x16" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/OnPrimaryContainer.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/OnPrimaryContainer.colorset/Contents.json index c0ceeb36..73257c63 100644 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/OnPrimaryContainer.colorset/Contents.json +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/OnPrimaryContainer.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0xDD", - "green": "0xD7", - "red": "0x67" + "blue": "0xBF", + "green": "0xD4", + "red": "0x2D" } }, "idiom": "universal" diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/PrimaryContainer.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/PrimaryContainer.colorset/Contents.json index be688072..b963049c 100644 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/PrimaryContainer.colorset/Contents.json +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/PrimaryContainer.colorset/Contents.json @@ -5,7 +5,7 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0xF8", + "blue": "0xF7", "green": "0xF7", "red": "0xE6" } @@ -23,9 +23,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0x39", - "green": "0x37", - "red": "0x00" + "blue": "0x3D", + "green": "0x3A", + "red": "0x16" } }, "idiom": "universal" diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Review.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Review.colorset/Contents.json new file mode 100644 index 00000000..eae205e6 --- /dev/null +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Review.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0xAD", + "green": "0x44", + "red": "0x8E" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0xFC", + "green": "0x84", + "red": "0xC0" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/ReviewContainer.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/ReviewContainer.colorset/Contents.json new file mode 100644 index 00000000..6bb5e503 --- /dev/null +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/ReviewContainer.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0xFC", + "green": "0xED", + "red": "0xF3" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0x54", + "green": "0x2D", + "red": "0x3A" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/SuccessContainer.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/SuccessContainer.colorset/Contents.json new file mode 100644 index 00000000..188a2bf5 --- /dev/null +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/SuccessContainer.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0xEE", + "green": "0xF8", + "red": "0xE6" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0x28", + "green": "0x31", + "red": "0x13" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Surface.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Surface.colorset/Contents.json index 94815123..a7c49ad2 100644 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Surface.colorset/Contents.json +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Surface.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0x1E", - "green": "0x1C", - "red": "0x17" + "blue": "0x2C", + "green": "0x24", + "red": "0x1F" } }, "idiom": "universal" diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/SurfaceSecondary.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/SurfaceSecondary.colorset/Contents.json index 9e86ecdd..478ea04b 100644 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/SurfaceSecondary.colorset/Contents.json +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/SurfaceSecondary.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0xF9", - "green": "0xF5", - "red": "0xF1" + "blue": "0xF8", + "green": "0xF6", + "red": "0xF4" } }, "idiom": "universal" @@ -23,9 +23,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0x22", - "green": "0x20", - "red": "0x1B" + "blue": "0x3B", + "green": "0x30", + "red": "0x28" } }, "idiom": "universal" diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextPrimary.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextPrimary.colorset/Contents.json index 2d37ab02..0fdb83b4 100644 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextPrimary.colorset/Contents.json +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextPrimary.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0x2A", - "green": "0x17", - "red": "0x0F" + "blue": "0x28", + "green": "0x1F", + "red": "0x19" } }, "idiom": "universal" @@ -23,9 +23,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0xE6", - "green": "0xE3", - "red": "0xDF" + "blue": "0xFC", + "green": "0xFA", + "red": "0xF8" } }, "idiom": "universal" diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextSecondary.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextSecondary.colorset/Contents.json index 5ce8eb03..5f055f1f 100644 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextSecondary.colorset/Contents.json +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextSecondary.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0x69", - "green": "0x55", - "red": "0x47" + "blue": "0x68", + "green": "0x59", + "red": "0x4E" } }, "idiom": "universal" @@ -23,9 +23,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0xC9", - "green": "0xC9", - "red": "0xBC" + "blue": "0xB8", + "green": "0xA3", + "red": "0x94" } }, "idiom": "universal" diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextTertiary.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextTertiary.colorset/Contents.json index 12d817e1..faadef61 100644 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextTertiary.colorset/Contents.json +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextTertiary.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0x8B", - "green": "0x74", - "red": "0x64" + "blue": "0xA1", + "green": "0x95", + "red": "0x8B" } }, "idiom": "universal" @@ -23,9 +23,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0x94", - "green": "0x93", - "red": "0x87" + "blue": "0x8B", + "green": "0x74", + "red": "0x64" } }, "idiom": "universal" diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/WarningContainer.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/WarningContainer.colorset/Contents.json new file mode 100644 index 00000000..00ae8e5c --- /dev/null +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/WarningContainer.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors": [ + { + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0xE5", + "green": "0xF4", + "red": "0xFF" + } + }, + "idiom": "universal" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "color": { + "color-space": "srgb", + "components": { + "alpha": "1.000", + "blue": "0x16", + "green": "0x2F", + "red": "0x3D" + } + }, + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift b/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift index ae96b3cb..a6b15a0d 100644 --- a/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift +++ b/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift @@ -12,16 +12,22 @@ public enum ColorAsset { case appBackground case border case danger + case dangerContainer case info + case infoContainer case onPrimaryContainer case primaryContainer + case review + case reviewContainer case success + case successContainer case surface case surfaceSecondary case textPrimary case textSecondary case textTertiary case warning + case warningContainer fileprivate var name: String { let name = String(describing: self) From 237e0ca1111d3ae010cf4ea7af8a7976bece8e55 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 10 Sep 2026 22:20:51 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=EB=A1=9C=EC=BB=AC=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EC=A0=9C=EC=9D=B4=EC=85=98=20=EC=9B=90=EB=B3=B8=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=EB=B0=8F=20Preview=20=EB=B2=88=EB=93=A4=20?= =?UTF-8?q?=EC=B0=B8=EC=A1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Application/App/Project.swift | 2 +- Application/Core/Sources/SystemTheme.swift | 11 ++-- .../Entry/Sources/Login/LoginFeature.swift | 10 ++-- .../Entry/Sources/Login/LoginView.swift | 12 ++--- .../Entry/Sources/Main/MainFeature.swift | 6 +-- .../Entry/Sources/Main/MainView.swift | 14 ++--- .../Entry/Sources/Root/RootFeature.swift | 12 ++--- .../Entry/Tests/Login/LoginFeatureTests.swift | 10 ++-- .../Entry/Tests/Main/MainFeatureTests.swift | 6 +-- .../Tests/Root/RootFeatureTestSupport.swift | 23 ++++++-- .../Entry/Tests/Root/RootFeatureTests.swift | 11 +++- .../Home/Category/CategoryManageFeature.swift | 16 +++--- .../Home/Category/CategoryManageView.swift | 6 +-- .../Home/Home/HomeFeature+Effects.swift | 10 ++-- .../HomeTab/Sources/Home/Home/HomeView.swift | 42 +++++++++------ .../Sources/Home/Search/SearchFeature.swift | 6 +-- .../Sources/Home/Search/SearchView.swift | 22 ++++---- .../Sources/Home/Structure/WebPageItem.swift | 6 ++- .../Tests/Home/HomeFeatureTestSupport.swift | 4 +- .../Search/SearchFeatureTestDoubles.swift | 6 +-- .../Sources/PushNotificationItem.swift | 10 ++-- .../Sources/PushNotificationListFeature.swift | 6 +-- .../Sources/PushNotificationListView.swift | 26 +++++----- .../PushNotificationQuery+Presentation.swift | 11 ++-- .../Tests/PushNotificationItemTests.swift | 29 +++++++++-- .../PushNotificationListTestAssertions.swift | 2 +- .../PushNotificationListTestSupport.swift | 2 +- .../Resources}/Localizable.xcstrings | 10 +++- .../Common/Component/RelativeTimeText.swift | 10 ++-- .../Common/Component/TagCopyMenuView.swift | 2 +- .../Common/Component/ToolbarButtons.swift | 4 +- .../Sources/Common/TodoInfoSheetView.swift | 12 ++--- .../Todo/SystemTodoCategoryItem.swift | 16 +++--- .../Todo/TodoQuery+Presentation.swift | 20 +++---- .../Todo/Detail/TodoDetailFeature.swift | 6 +-- .../Todo/Detail/TodoDetailPreviewView.swift | 4 +- .../Sources/Todo/Detail/TodoDetailView.swift | 20 +++---- .../Todo/Editor/TodoEditorFeature.swift | 10 ++-- .../Sources/Todo/Editor/TodoEditorView.swift | 43 +++++++++------ .../Todo/List/TodoListFeature+Effects.swift | 6 +-- .../Sources/Todo/List/TodoListView.swift | 33 ++++++------ .../Tests/Todo/TodoDetailFeatureTests.swift | 6 +-- .../Todo/TodoEditorFeatureTestDoubles.swift | 6 +-- .../Todo/TodoListFeatureTestDoubles.swift | 6 +-- .../Profile/ProfileFeature+State.swift | 3 +- .../Sources/Profile/ProfileFeature.swift | 4 +- .../Profile/ProfileRegularDetailView.swift | 2 +- .../Sources/Profile/ProfileView.swift | 14 ++--- .../Sources/Settings/AccountFeature.swift | 39 +++++++++----- .../Sources/Settings/AccountView.swift | 10 ++-- .../PushNotificationSettingsFeature.swift | 6 +-- .../PushNotificationSettingsView.swift | 8 +-- .../Sources/Settings/SettingsFeature.swift | 40 ++++++++------ .../Sources/Settings/SettingsView.swift | 25 ++++----- .../Sources/Settings/ThemeView.swift | 9 ++-- .../Structure/Profile/ActivityKindItem.swift | 7 +-- .../Tests/Settings/AccountFeatureTests.swift | 48 +++++++++++------ ...PushNotificationSettingsFeatureTests.swift | 6 +-- .../Tests/Settings/SettingsFeatureTests.swift | 46 +++++++++------- Application/Presentation/Project.swift | 5 +- .../TodayTab/Sources/Today/TodayFeature.swift | 22 ++++---- .../TodayTab/Sources/Today/TodayView.swift | 52 +++++++++++-------- 62 files changed, 511 insertions(+), 370 deletions(-) rename Application/{App/Sources/Resource => Presentation/PresentationShared/Resources}/Localizable.xcstrings (99%) diff --git a/Application/App/Project.swift b/Application/App/Project.swift index c75bcec4..76eb7249 100644 --- a/Application/App/Project.swift +++ b/Application/App/Project.swift @@ -22,7 +22,7 @@ let project = Project( "Sources/Resource/Assets.xcassets", "../Presentation/PresentationShared/Resources/Assets.xcassets", "Sources/Resource/GoogleService-Info.plist", - "Sources/Resource/Localizable.xcstrings", + "../Presentation/PresentationShared/Resources/Localizable.xcstrings", ], entitlements: .file(path: "Sources/Resource/DevLog.entitlements"), scripts: [ diff --git a/Application/Core/Sources/SystemTheme.swift b/Application/Core/Sources/SystemTheme.swift index db2602a7..00a5fb9a 100644 --- a/Application/Core/Sources/SystemTheme.swift +++ b/Application/Core/Sources/SystemTheme.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Foundation public enum SystemTheme: String, Identifiable { case automatic @@ -17,13 +18,17 @@ public enum SystemTheme: String, Identifiable { } public var localizedName: String { + localizedName(in: .main) + } + + public func localizedName(in bundle: Bundle) -> String { switch self { case .automatic: - return NSLocalizedString("system_theme_automatic", comment: "System theme: automatic") + return String(localized: "system_theme_automatic", bundle: bundle) case .light: - return NSLocalizedString("system_theme_light", comment: "System theme: light") + return String(localized: "system_theme_light", bundle: bundle) case .dark: - return NSLocalizedString("system_theme_dark", comment: "System theme: dark") + return String(localized: "system_theme_dark", bundle: bundle) } } diff --git a/Application/Presentation/Entry/Sources/Login/LoginFeature.swift b/Application/Presentation/Entry/Sources/Login/LoginFeature.swift index 373876e9..07b7b6b8 100644 --- a/Application/Presentation/Entry/Sources/Login/LoginFeature.swift +++ b/Application/Presentation/Entry/Sources/Login/LoginFeature.swift @@ -119,18 +119,18 @@ private extension LoginFeature { switch alertType { case .emailUnavailable: - title = String(localized: "login_alert_email_unavailable_title") - message = String(localized: "login_alert_email_unavailable_message") + title = String(localized: "login_alert_email_unavailable_title", bundle: PresentationResources.bundle) + message = String(localized: "login_alert_email_unavailable_message", bundle: PresentationResources.bundle) case .error: - title = String(localized: "common_error_title") - message = String(localized: "common_error_message") + title = String(localized: "common_error_title", bundle: PresentationResources.bundle) + message = String(localized: "common_error_message", bundle: PresentationResources.bundle) } return AlertState { TextState(title) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { TextState(message) diff --git a/Application/Presentation/Entry/Sources/Login/LoginView.swift b/Application/Presentation/Entry/Sources/Login/LoginView.swift index b4309041..40e9c03c 100644 --- a/Application/Presentation/Entry/Sources/Login/LoginView.swift +++ b/Application/Presentation/Entry/Sources/Login/LoginView.swift @@ -34,11 +34,11 @@ struct LoginView: View { Spacer() VStack(spacing: 24) { - Text(String(localized: "login_intro_title")) + Text(String(localized: "login_intro_title", bundle: PresentationResources.bundle)) .font(.title) .fontWeight(.heavy) .multilineTextAlignment(.center) - Text(String(localized: "login_intro_description")) + Text(String(localized: "login_intro_description", bundle: PresentationResources.bundle)) .padding(.horizontal) .foregroundStyle(Color(asset: .textTertiary)) .multilineTextAlignment(.center) @@ -49,23 +49,23 @@ struct LoginView: View { signInButton( provider: .google, logo: Image("Google", bundle: PresentationResources.bundle), - text: String(localized: "login_google_sign_in") + text: String(localized: "login_google_sign_in", bundle: PresentationResources.bundle) ) signInButton( provider: .github, logo: Image("Github", bundle: PresentationResources.bundle), - text: String(localized: "login_github_sign_in") + text: String(localized: "login_github_sign_in", bundle: PresentationResources.bundle) ) signInButton( provider: .apple, logo: Image("Apple", bundle: PresentationResources.bundle), - text: String(localized: "login_apple_sign_in") + text: String(localized: "login_apple_sign_in", bundle: PresentationResources.bundle) ) } .padding(.bottom, 30) - Text(String(localized: "login_terms_notice")) + Text(String(localized: "login_terms_notice", bundle: PresentationResources.bundle)) .font(.caption2) .foregroundStyle(Color.gray) .multilineTextAlignment(.center) diff --git a/Application/Presentation/Entry/Sources/Main/MainFeature.swift b/Application/Presentation/Entry/Sources/Main/MainFeature.swift index aa4df388..18466735 100644 --- a/Application/Presentation/Entry/Sources/Main/MainFeature.swift +++ b/Application/Presentation/Entry/Sources/Main/MainFeature.swift @@ -135,13 +135,13 @@ private extension MainFeature { static func alertState() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "main_alert_badge_error_message")) + TextState(String(localized: "main_alert_badge_error_message", bundle: PresentationResources.bundle)) } } } diff --git a/Application/Presentation/Entry/Sources/Main/MainView.swift b/Application/Presentation/Entry/Sources/Main/MainView.swift index 1e3dc84c..227ed3cc 100644 --- a/Application/Presentation/Entry/Sources/Main/MainView.swift +++ b/Application/Presentation/Entry/Sources/Main/MainView.swift @@ -209,7 +209,7 @@ struct MainView: View { homeDestinationView(homeRoute) } else { ContentUnavailableView( - String(localized: "home_select_detail"), + String(localized: "home_select_detail", bundle: PresentationResources.bundle), systemImage: "house" ) } @@ -280,7 +280,7 @@ struct MainView: View { todayDestinationView(todayRoute) } else { ContentUnavailableView( - String(localized: "today_select_detail"), + String(localized: "today_select_detail", bundle: PresentationResources.bundle), systemImage: "sun.max" ) } @@ -319,7 +319,7 @@ struct MainView: View { .id(todoId) } else { ContentUnavailableView( - String(localized: "push_notifications_select_detail"), + String(localized: "push_notifications_select_detail", bundle: PresentationResources.bundle), systemImage: "bell.badge" ) .background(Color(.systemGroupedBackground).ignoresSafeArea()) @@ -387,13 +387,13 @@ private extension MainTab { var title: String { switch self { case .home: - String(localized: "nav_home") + String(localized: "nav_home", bundle: PresentationResources.bundle) case .today: - String(localized: "nav_today") + String(localized: "nav_today", bundle: PresentationResources.bundle) case .notification: - String(localized: "nav_notifications") + String(localized: "nav_notifications", bundle: PresentationResources.bundle) case .profile: - String(localized: "nav_profile") + String(localized: "nav_profile", bundle: PresentationResources.bundle) } } diff --git a/Application/Presentation/Entry/Sources/Root/RootFeature.swift b/Application/Presentation/Entry/Sources/Root/RootFeature.swift index 0e090251..c240444d 100644 --- a/Application/Presentation/Entry/Sources/Root/RootFeature.swift +++ b/Application/Presentation/Entry/Sources/Root/RootFeature.swift @@ -292,25 +292,25 @@ private extension RootFeature { static func appUpdateAlertState() -> AlertState { AlertState { - TextState(String(localized: "root_app_update_title")) + TextState(String(localized: "root_app_update_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(action: .tapUpdateButton) { - TextState(String(localized: "root_app_update_action")) + TextState(String(localized: "root_app_update_action", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "root_app_update_message")) + TextState(String(localized: "root_app_update_message", bundle: PresentationResources.bundle)) } } static func networkDisconnectedAlertState() -> AlertState { AlertState { - TextState(String(localized: "root_network_disconnected_title")) + TextState(String(localized: "root_network_disconnected_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "root_network_disconnected_message")) + TextState(String(localized: "root_network_disconnected_message", bundle: PresentationResources.bundle)) } } } diff --git a/Application/Presentation/Entry/Tests/Login/LoginFeatureTests.swift b/Application/Presentation/Entry/Tests/Login/LoginFeatureTests.swift index 224937a2..c1d25ceb 100644 --- a/Application/Presentation/Entry/Tests/Login/LoginFeatureTests.swift +++ b/Application/Presentation/Entry/Tests/Login/LoginFeatureTests.swift @@ -92,8 +92,8 @@ struct LoginFeatureTests { } #expect(driver.alert == expectedAlert( - title: String(localized: "login_alert_email_unavailable_title"), - message: String(localized: "login_alert_email_unavailable_message") + title: String(localized: "login_alert_email_unavailable_title", bundle: PresentationResources.bundle), + message: String(localized: "login_alert_email_unavailable_message", bundle: PresentationResources.bundle) )) } @@ -110,8 +110,8 @@ struct LoginFeatureTests { } #expect(driver.alert == expectedAlert( - title: String(localized: "common_error_title"), - message: String(localized: "common_error_message") + title: String(localized: "common_error_title", bundle: PresentationResources.bundle), + message: String(localized: "common_error_message", bundle: PresentationResources.bundle) )) } @@ -203,7 +203,7 @@ private func expectedAlert( TextState(title) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { TextState(message) diff --git a/Application/Presentation/Entry/Tests/Main/MainFeatureTests.swift b/Application/Presentation/Entry/Tests/Main/MainFeatureTests.swift index 404fab2a..5e5a7392 100644 --- a/Application/Presentation/Entry/Tests/Main/MainFeatureTests.swift +++ b/Application/Presentation/Entry/Tests/Main/MainFeatureTests.swift @@ -216,13 +216,13 @@ private final class MainStateManagementReference { private func expectedMainErrorAlert() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "main_alert_badge_error_message")) + TextState(String(localized: "main_alert_badge_error_message", bundle: PresentationResources.bundle)) } } diff --git a/Application/Presentation/Entry/Tests/Root/RootFeatureTestSupport.swift b/Application/Presentation/Entry/Tests/Root/RootFeatureTestSupport.swift index 9cf7efec..1009c3ef 100644 --- a/Application/Presentation/Entry/Tests/Root/RootFeatureTestSupport.swift +++ b/Application/Presentation/Entry/Tests/Root/RootFeatureTestSupport.swift @@ -148,8 +148,14 @@ func verifyNetworkDisconnectedAlert(adapter: some RootStateDriving) async { #expect( adapter.snapshot == RootStateSnapshot( - alertTitle: String(localized: "root_network_disconnected_title"), - alertMessage: String(localized: "root_network_disconnected_message"), + alertTitle: String( + localized: "root_network_disconnected_title", + bundle: PresentationResources.bundle + ), + alertMessage: String( + localized: "root_network_disconnected_message", + bundle: PresentationResources.bundle + ), isNetworkConnected: false, signIn: nil, theme: .automatic, @@ -221,14 +227,21 @@ func verifyObservedInitialValues(adapter: some RootStateDriving) async { return snapshot.signIn == false && !snapshot.isNetworkConnected && snapshot.theme == .dark - && snapshot.alertTitle == String(localized: "root_network_disconnected_title") + && snapshot.alertTitle + == String(localized: "root_network_disconnected_title", bundle: PresentationResources.bundle) } #expect( adapter.snapshot == RootStateSnapshot( - alertTitle: String(localized: "root_network_disconnected_title"), - alertMessage: String(localized: "root_network_disconnected_message"), + alertTitle: String( + localized: "root_network_disconnected_title", + bundle: PresentationResources.bundle + ), + alertMessage: String( + localized: "root_network_disconnected_message", + bundle: PresentationResources.bundle + ), isNetworkConnected: false, signIn: false, theme: .dark, diff --git a/Application/Presentation/Entry/Tests/Root/RootFeatureTests.swift b/Application/Presentation/Entry/Tests/Root/RootFeatureTests.swift index e99ca7e3..49a39fe6 100644 --- a/Application/Presentation/Entry/Tests/Root/RootFeatureTests.swift +++ b/Application/Presentation/Entry/Tests/Root/RootFeatureTests.swift @@ -7,6 +7,7 @@ import Core import Foundation +import PresentationShared import Testing @MainActor @@ -142,8 +143,14 @@ struct RootFeatureTests { await adapter.onAppear() - #expect(adapter.snapshot.alertTitle == String(localized: "root_app_update_title")) - #expect(adapter.snapshot.alertMessage == String(localized: "root_app_update_message")) + #expect( + adapter.snapshot.alertTitle + == String(localized: "root_app_update_title", bundle: PresentationResources.bundle) + ) + #expect( + adapter.snapshot.alertMessage + == String(localized: "root_app_update_message", bundle: PresentationResources.bundle) + ) } @Test("업데이트 버튼은 App Store 열기를 요청한다") diff --git a/Application/Presentation/HomeTab/Sources/Home/Category/CategoryManageFeature.swift b/Application/Presentation/HomeTab/Sources/Home/Category/CategoryManageFeature.swift index 59f95bf6..8a41b306 100644 --- a/Application/Presentation/HomeTab/Sources/Home/Category/CategoryManageFeature.swift +++ b/Application/Presentation/HomeTab/Sources/Home/Category/CategoryManageFeature.swift @@ -28,13 +28,13 @@ struct CategoryManageFeature { } var navigationTitle: String { isEditing - ? String(localized: "todo_manage_edit_category_title") - : String(localized: "todo_manage_add_category_title") + ? String(localized: "todo_manage_edit_category_title", bundle: PresentationResources.bundle) + : String(localized: "todo_manage_add_category_title", bundle: PresentationResources.bundle) } var submitTitle: String { isEditing - ? String(localized: "todo_manage_save") - : String(localized: "todo_add") + ? String(localized: "todo_manage_save", bundle: PresentationResources.bundle) + : String(localized: "todo_add", bundle: PresentationResources.bundle) } var placeholder: String { category.name @@ -213,16 +213,16 @@ private struct CategoryManageSheetFeature: Reducer { private extension CategoryManageFeature { static func deleteAlertState(for item: TodoCategoryItem) -> AlertState { AlertState { - TextState(String(localized: "todo_manage_delete_category_title")) + TextState(String(localized: "todo_manage_delete_category_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_cancel")) + TextState(String(localized: "common_cancel", bundle: PresentationResources.bundle)) } ButtonState(role: .destructive, action: .confirmDeleteUserCategory(item)) { - TextState(String(localized: "common_delete")) + TextState(String(localized: "common_delete", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "todo_manage_delete_category_message")) + TextState(String(localized: "todo_manage_delete_category_message", bundle: PresentationResources.bundle)) } } } diff --git a/Application/Presentation/HomeTab/Sources/Home/Category/CategoryManageView.swift b/Application/Presentation/HomeTab/Sources/Home/Category/CategoryManageView.swift index f3a69db6..16bb96c2 100644 --- a/Application/Presentation/HomeTab/Sources/Home/Category/CategoryManageView.swift +++ b/Application/Presentation/HomeTab/Sources/Home/Category/CategoryManageView.swift @@ -49,7 +49,7 @@ struct CategoryManageView: View { .listRowInsets(EdgeInsets()) } .environment(\.editMode, .constant(.active)) - .navigationTitle(String(localized: "nav_todo_manage")) + .navigationTitle(String(localized: "nav_todo_manage", bundle: PresentationResources.bundle)) .navigationBarTitleDisplayMode(.inline) .navigationBarBackButtonHidden() .sheet(item: $store.scope(state: \.categorySheet, action: \.categorySheet)) { sheetStore in @@ -69,7 +69,7 @@ struct CategoryManageView: View { Button { store.send(.tapDoneButton, animation: .default) } label: { - Text(String(localized: "profile_done")) + Text(String(localized: "profile_done", bundle: PresentationResources.bundle)) } } } @@ -127,7 +127,7 @@ private struct CategoryManageSheet: View { .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(String(localized: "common_close")) { + Button(String(localized: "common_close", bundle: PresentationResources.bundle)) { store.send(.tapCloseButton) } } diff --git a/Application/Presentation/HomeTab/Sources/Home/Home/HomeFeature+Effects.swift b/Application/Presentation/HomeTab/Sources/Home/Home/HomeFeature+Effects.swift index 8a0a3635..4d26fac5 100644 --- a/Application/Presentation/HomeTab/Sources/Home/Home/HomeFeature+Effects.swift +++ b/Application/Presentation/HomeTab/Sources/Home/Home/HomeFeature+Effects.swift @@ -183,18 +183,18 @@ extension HomeFeature { switch type { case .invalidURL: - title = String(localized: "home_invalid_url_title") - message = String(localized: "home_invalid_url_message") + title = String(localized: "home_invalid_url_title", bundle: PresentationResources.bundle) + message = String(localized: "home_invalid_url_message", bundle: PresentationResources.bundle) case .error: - title = String(localized: "common_error_title") - message = String(localized: "common_error_message") + title = String(localized: "common_error_title", bundle: PresentationResources.bundle) + message = String(localized: "common_error_message", bundle: PresentationResources.bundle) } return AlertState { TextState(title) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { TextState(message) diff --git a/Application/Presentation/HomeTab/Sources/Home/Home/HomeView.swift b/Application/Presentation/HomeTab/Sources/Home/Home/HomeView.swift index a8c64c9c..cbdbcb39 100644 --- a/Application/Presentation/HomeTab/Sources/Home/Home/HomeView.swift +++ b/Application/Presentation/HomeTab/Sources/Home/Home/HomeView.swift @@ -33,7 +33,7 @@ public struct HomeView: View { webPageSection } .listStyle(.insetGrouped) - .navigationTitle(String(localized: "nav_home")) + .navigationTitle(String(localized: "nav_home", bundle: PresentationResources.bundle)) .toolbar { toolbar } .prominentAlert(store, state: \.alert, action: \.alert) .sheet(item: $store.scope(state: \.sheet, action: \.sheet), content: sheetContent) @@ -53,7 +53,7 @@ public struct HomeView: View { } }, header: { HStack { - Text("TODO") + Text("TODO", bundle: PresentationResources.bundle) .foregroundStyle(Color.primary) .font(.title2) .bold() @@ -77,7 +77,7 @@ public struct HomeView: View { } else if store.recentTodos.isEmpty { HStack { Spacer() - Text(String(localized: "home_recent_empty")) + Text(String(localized: "home_recent_empty", bundle: PresentationResources.bundle)) .font(.callout) Spacer() } @@ -88,7 +88,7 @@ public struct HomeView: View { } } header: { HStack { - Text(String(localized: "home_recent_title")) + Text(String(localized: "home_recent_title", bundle: PresentationResources.bundle)) .foregroundStyle(Color.primary) .font(.title2.bold()) Spacer() @@ -109,7 +109,7 @@ public struct HomeView: View { } label: { HStack { Spacer() - Text(String(localized: "home_web_refresh_required")) + Text(String(localized: "home_web_refresh_required", bundle: PresentationResources.bundle)) .font(.callout) .multilineTextAlignment(.center) Spacer() @@ -119,7 +119,7 @@ public struct HomeView: View { } else if webPages.isEmpty { HStack { Spacer() - Text(String(localized: "home_web_empty")) + Text(String(localized: "home_web_empty", bundle: PresentationResources.bundle)) .font(.callout) Spacer() } @@ -131,7 +131,7 @@ public struct HomeView: View { } } header: { HStack { - Text("Web Page") + Text("Web Page", bundle: PresentationResources.bundle) .foregroundStyle(Color.primary) .font(.title2.bold()) Spacer() @@ -187,7 +187,7 @@ public struct HomeView: View { } } } header: { - Text("TODO") + Text("TODO", bundle: PresentationResources.bundle) .foregroundStyle(Color(.label)) } @@ -203,7 +203,7 @@ public struct HomeView: View { } .listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) } header: { - Text("Web Page") + Text("Web Page", bundle: PresentationResources.bundle) .foregroundStyle(Color(.label)) } } @@ -213,17 +213,25 @@ public struct HomeView: View { Form { Section { TextField( - "https://", - text: $store.webPageURLInput + "", + text: $store.webPageURLInput, + prompt: Text("https://", bundle: PresentationResources.bundle) ) .textInputAutocapitalization(.never) .keyboardType(.URL) } footer: { - Text(String(localized: "home_webpage_input_message")) + Text(String(localized: "home_webpage_input_message", bundle: PresentationResources.bundle)) } } .scrollDisabled(true) - .navigationTitle(Text(String(localized: "home_webpage_input_title"))) + .navigationTitle( + Text( + String( + localized: "home_webpage_input_title", + bundle: PresentationResources.bundle + ) + ) + ) .navigationBarTitleDisplayMode(.inline) // 설정 안하면 섹션 위에 내비게이션 large 만큼 영역 먹음 .toolbar { if store.isAppending { @@ -235,14 +243,14 @@ public struct HomeView: View { } } else { ToolbarItem(placement: .topBarTrailing) { - Button(String(localized: "home_add")) { + Button(String(localized: "home_add", bundle: PresentationResources.bundle)) { store.send(.view(.addWebPage)) } } } } } - .navigationTitle(Text(String(localized: "nav_home_content"))) + .navigationTitle(Text(String(localized: "nav_home_content", bundle: PresentationResources.bundle))) .navigationBarTitleDisplayMode(.inline) // 설정 안하면 섹션 위에 내비게이션 large 만큼 영역 먹음 .toolbar { ToolbarItem(placement: .topBarLeading) { @@ -342,7 +350,7 @@ public struct HomeView: View { store.send(.view(.deleteWebPage(item))) presentDeleteWebPageToast(item.url.absoluteString) } label: { - Label(String(localized: "common_delete"), systemImage: "trash") + Label(String(localized: "common_delete", bundle: PresentationResources.bundle), systemImage: "trash") } } } @@ -382,7 +390,7 @@ public struct HomeView: View { private func presentDeleteWebPageToast(_ urlString: String) { ToastPresenter.present( - message: String(localized: "common_undo"), + message: String(localized: "common_undo", bundle: PresentationResources.bundle), systemImage: "arrow.uturn.left", duration: 5, font: .caption, diff --git a/Application/Presentation/HomeTab/Sources/Home/Search/SearchFeature.swift b/Application/Presentation/HomeTab/Sources/Home/Search/SearchFeature.swift index 6e8ec718..b2f2401c 100644 --- a/Application/Presentation/HomeTab/Sources/Home/Search/SearchFeature.swift +++ b/Application/Presentation/HomeTab/Sources/Home/Search/SearchFeature.swift @@ -283,13 +283,13 @@ private extension SearchFeature { static func alertState() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } } diff --git a/Application/Presentation/HomeTab/Sources/Home/Search/SearchView.swift b/Application/Presentation/HomeTab/Sources/Home/Search/SearchView.swift index a78d3b35..d65e6044 100644 --- a/Application/Presentation/HomeTab/Sources/Home/Search/SearchView.swift +++ b/Application/Presentation/HomeTab/Sources/Home/Search/SearchView.swift @@ -78,7 +78,7 @@ struct SearchView: View { text: $store.searchQuery, isPresented: $store.isSearching, placement: .navigationBarDrawer(displayMode: .always), - prompt: Text(String(localized: "search_prompt")) + prompt: Text(String(localized: "search_prompt", bundle: PresentationResources.bundle)) ) .onSubmit(of: .search) { store.send(.addRecentQuery(store.searchQuery)) @@ -88,7 +88,7 @@ struct SearchView: View { private var searchInstruction: some View { VStack { Spacer() - Text(String(localized: "search_instruction")) + Text(String(localized: "search_instruction", bundle: PresentationResources.bundle)) .foregroundStyle(Color.gray) Spacer() } @@ -98,7 +98,7 @@ struct SearchView: View { private var emptySearchResult: some View { VStack { Spacer() - Text(String(localized: "search_empty")) + Text(String(localized: "search_empty", bundle: PresentationResources.bundle)) .foregroundStyle(Color.gray) Spacer() } @@ -108,10 +108,10 @@ struct SearchView: View { private var hashGuide: some View { VStack(spacing: 8) { Spacer() - Text(String(localized: "search_hash_guide_title")) + Text(String(localized: "search_hash_guide_title", bundle: PresentationResources.bundle)) .font(.headline) .foregroundStyle(Color(.label)) - Text(String(localized: "search_hash_guide_message")) + Text(String(localized: "search_hash_guide_message", bundle: PresentationResources.bundle)) .font(.subheadline) .foregroundStyle(Color.gray) .multilineTextAlignment(.center) @@ -137,7 +137,7 @@ struct SearchView: View { let todos = store.visibleTodos return VStack(alignment: .leading, spacing: 12) { - Text("Todos") + Text("Todos", bundle: PresentationResources.bundle) .font(.headline) .foregroundStyle(Color(.label)) Divider() @@ -148,7 +148,7 @@ struct SearchView: View { } .padding(.top, -12) if store.shouldShowMoreTodos { - Button(String(localized: "search_show_more")) { + Button(String(localized: "search_show_more", bundle: PresentationResources.bundle)) { store.send(.setShowAllTodos(true)) } .font(.subheadline) @@ -164,7 +164,7 @@ struct SearchView: View { let pages = store.visibleWebPages return VStack(alignment: .leading, spacing: 12) { - Text("Web Pages") + Text("Web Pages", bundle: PresentationResources.bundle) .font(.headline) .foregroundStyle(Color(.label)) Divider() @@ -175,7 +175,7 @@ struct SearchView: View { } .padding(.top, -12) if store.shouldShowMoreWebPages { - Button(String(localized: "search_show_more")) { + Button(String(localized: "search_show_more", bundle: PresentationResources.bundle)) { store.send(.setShowAllWebPages(true)) } .font(.subheadline) @@ -211,11 +211,11 @@ struct SearchView: View { private var recentQueries: some View { VStack(alignment: .leading, spacing: 12) { HStack { - Text(String(localized: "search_recent")) + Text(String(localized: "search_recent", bundle: PresentationResources.bundle)) .font(.headline) .foregroundStyle(Color(.label)) Spacer() - Button(String(localized: "search_clear_all")) { + Button(String(localized: "search_clear_all", bundle: PresentationResources.bundle)) { store.send(.clearRecentQueries) } .font(.subheadline) diff --git a/Application/Presentation/HomeTab/Sources/Home/Structure/WebPageItem.swift b/Application/Presentation/HomeTab/Sources/Home/Structure/WebPageItem.swift index 741c875b..fdc4184e 100644 --- a/Application/Presentation/HomeTab/Sources/Home/Structure/WebPageItem.swift +++ b/Application/Presentation/HomeTab/Sources/Home/Structure/WebPageItem.swift @@ -7,6 +7,7 @@ import SwiftUI import Domain +import PresentationShared public struct WebPageItem: Identifiable, Hashable { private let metadata: WebPage @@ -17,7 +18,10 @@ public struct WebPageItem: Identifiable, Hashable { } public var id: String { metadata.id } - public var title: String { metadata.title ?? String(localized: "web_page_missing_title") } + public var title: String { + metadata.title + ?? String(localized: "web_page_missing_title", bundle: PresentationResources.bundle) + } public var url: URL { metadata.url } public var displayURL: String { metadata.displayURL.absoluteString } public var imageURL: URL? { metadata.imageURL } diff --git a/Application/Presentation/HomeTab/Tests/Home/HomeFeatureTestSupport.swift b/Application/Presentation/HomeTab/Tests/Home/HomeFeatureTestSupport.swift index fb82b3fc..32b7c333 100644 --- a/Application/Presentation/HomeTab/Tests/Home/HomeFeatureTestSupport.swift +++ b/Application/Presentation/HomeTab/Tests/Home/HomeFeatureTestSupport.swift @@ -33,10 +33,10 @@ struct HomeStoreTestAdapter { var showAlert: Bool { store.state.alert != nil } var alertType: HomeFeature.AlertType? { guard let title = store.state.alert?.title else { return nil } - if title == TextState(String(localized: "home_invalid_url_title")) { + if title == TextState(String(localized: "home_invalid_url_title", bundle: PresentationResources.bundle)) { return .invalidURL } - if title == TextState(String(localized: "common_error_title")) { + if title == TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) { return .error } return nil diff --git a/Application/Presentation/HomeTab/Tests/Search/SearchFeatureTestDoubles.swift b/Application/Presentation/HomeTab/Tests/Search/SearchFeatureTestDoubles.swift index 782e25a1..051c69c9 100644 --- a/Application/Presentation/HomeTab/Tests/Search/SearchFeatureTestDoubles.swift +++ b/Application/Presentation/HomeTab/Tests/Search/SearchFeatureTestDoubles.swift @@ -284,12 +284,12 @@ func makeSearchWebPage( func expectedSearchErrorAlert() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } diff --git a/Application/Presentation/NotificationTab/Sources/PushNotificationItem.swift b/Application/Presentation/NotificationTab/Sources/PushNotificationItem.swift index ca4623b3..80d2cb4f 100644 --- a/Application/Presentation/NotificationTab/Sources/PushNotificationItem.swift +++ b/Application/Presentation/NotificationTab/Sources/PushNotificationItem.swift @@ -7,6 +7,7 @@ import Foundation import Domain +import PresentationShared public struct PushNotificationItem: Identifiable, Hashable { public let id: String @@ -24,7 +25,7 @@ public struct PushNotificationItem: Identifiable, Hashable { if let legacy { return legacy.title } - return String(localized: "push_notification_todo_due_title") + return String(localized: "push_notification_todo_due_title", bundle: PresentationResources.bundle) } public var body: String { @@ -33,10 +34,13 @@ public struct PushNotificationItem: Identifiable, Hashable { } guard let todoTitle, !todoTitle.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { - return String(localized: "push_notification_todo_due_tomorrow_without_title") + return String( + localized: "push_notification_todo_due_tomorrow_without_title", + bundle: PresentationResources.bundle + ) } return String.localizedStringWithFormat( - String(localized: "push_notification_todo_due_tomorrow_format"), + String(localized: "push_notification_todo_due_tomorrow_format", bundle: PresentationResources.bundle), todoTitle ) } diff --git a/Application/Presentation/NotificationTab/Sources/PushNotificationListFeature.swift b/Application/Presentation/NotificationTab/Sources/PushNotificationListFeature.swift index 96ece740..a8ce5808 100644 --- a/Application/Presentation/NotificationTab/Sources/PushNotificationListFeature.swift +++ b/Application/Presentation/NotificationTab/Sources/PushNotificationListFeature.swift @@ -391,13 +391,13 @@ private extension PushNotificationListFeature { static func alertState() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } diff --git a/Application/Presentation/NotificationTab/Sources/PushNotificationListView.swift b/Application/Presentation/NotificationTab/Sources/PushNotificationListView.swift index b3488f5b..e126286d 100644 --- a/Application/Presentation/NotificationTab/Sources/PushNotificationListView.swift +++ b/Application/Presentation/NotificationTab/Sources/PushNotificationListView.swift @@ -44,7 +44,7 @@ public struct PushNotificationListView: View { headerOffset = max(0, -offset) } .safeAreaInset(edge: .top) { safeAreaHeader } - .navigationTitle(String(localized: "nav_push_notifications")) + .navigationTitle(String(localized: "nav_push_notifications", bundle: PresentationResources.bundle)) } .prominentAlert(store, state: \.alert, action: \.alert) .sheet(item: sheetStore) { store in @@ -71,7 +71,7 @@ public struct PushNotificationListView: View { if notifications.isEmpty { HStack { Spacer() - Text(String(localized: "push_notifications_empty")) + Text(String(localized: "push_notifications_empty", bundle: PresentationResources.bundle)) .foregroundStyle(Color.gray) Spacer() } @@ -179,14 +179,14 @@ public struct PushNotificationListView: View { Menu { Text( String.localizedStringWithFormat( - String(localized: "push_filters_applied_format"), + String(localized: "push_filters_applied_format", bundle: PresentationResources.bundle), Int64(store.appliedFilterCount) ) ) Button(role: .destructive) { store.send(.view(.resetFilters)) } label: { - Text(String(localized: "push_clear_all_filters")) + Text(String(localized: "push_clear_all_filters", bundle: PresentationResources.bundle)) } } label: { HStack(spacing: 6) { @@ -205,7 +205,7 @@ public struct PushNotificationListView: View { let condition = store.query.sortOrder == .oldest Text( String.localizedStringWithFormat( - String(localized: "push_sort_format"), + String(localized: "push_sort_format", bundle: PresentationResources.bundle), store.query.sortOrder.title ) ) @@ -220,12 +220,12 @@ public struct PushNotificationListView: View { Text(option.title).tag(option) } } label: { - Text(String(localized: "push_period")) + Text(String(localized: "push_period", bundle: PresentationResources.bundle)) } } label: { let condition = store.query.timeFilter == .none HStack { - Text(String(localized: "push_period")) + Text(String(localized: "push_period", bundle: PresentationResources.bundle)) Image(systemName: "chevron.down") } .foregroundStyle(condition ? Color(.label) : .white) @@ -238,7 +238,7 @@ public struct PushNotificationListView: View { } } label: { let condition = store.query.unreadOnly - Text(String(localized: "push_unread")) + Text(String(localized: "push_unread", bundle: PresentationResources.bundle)) .foregroundStyle(condition ? .white : Color(.label)) .adaptiveButtonStyle(color: condition ? .blue : .clear) } @@ -335,25 +335,25 @@ public struct PushNotificationListView: View { if seconds < 60 { return String.localizedStringWithFormat( - String(localized: "push_time_seconds_ago_format"), + String(localized: "push_time_seconds_ago_format", bundle: PresentationResources.bundle), Int64(max(0, seconds)) ) } else if seconds < 3600 { let minutes = seconds / 60 return String.localizedStringWithFormat( - String(localized: "push_time_minutes_ago_format"), + String(localized: "push_time_minutes_ago_format", bundle: PresentationResources.bundle), Int64(minutes) ) } else if seconds < 86400 { let hours = seconds / 3600 return String.localizedStringWithFormat( - String(localized: "push_time_hours_ago_format"), + String(localized: "push_time_hours_ago_format", bundle: PresentationResources.bundle), Int64(hours) ) } else { let days = seconds / 86400 return String.localizedStringWithFormat( - String(localized: "push_time_days_ago_format"), + String(localized: "push_time_days_ago_format", bundle: PresentationResources.bundle), Int64(days) ) } @@ -388,7 +388,7 @@ public struct PushNotificationListView: View { private func presentDeleteNotificationToast(_ notificationId: String) { ToastPresenter.present( - message: String(localized: "common_undo"), + message: String(localized: "common_undo", bundle: PresentationResources.bundle), systemImage: "arrow.uturn.left", duration: 5, font: .caption, diff --git a/Application/Presentation/NotificationTab/Sources/PushNotificationQuery+Presentation.swift b/Application/Presentation/NotificationTab/Sources/PushNotificationQuery+Presentation.swift index 215c21f4..7964ebc9 100644 --- a/Application/Presentation/NotificationTab/Sources/PushNotificationQuery+Presentation.swift +++ b/Application/Presentation/NotificationTab/Sources/PushNotificationQuery+Presentation.swift @@ -7,14 +7,15 @@ import Core import Foundation +import PresentationShared public extension PushNotificationQuery.SortOrder { var title: String { switch self { case .latest: - return String(localized: "push_sort_latest") + return String(localized: "push_sort_latest", bundle: PresentationResources.bundle) case .oldest: - return String(localized: "push_sort_oldest") + return String(localized: "push_sort_oldest", bundle: PresentationResources.bundle) } } } @@ -23,15 +24,15 @@ public extension PushNotificationQuery.TimeFilter { var title: String { switch self { case .none: - return String(localized: "push_timefilter_all") + return String(localized: "push_timefilter_all", bundle: PresentationResources.bundle) case .hours(let value): return String.localizedStringWithFormat( - String(localized: "push_timefilter_hours_format"), + String(localized: "push_timefilter_hours_format", bundle: PresentationResources.bundle), Int64(value) ) case .days(let value): return String.localizedStringWithFormat( - String(localized: "push_timefilter_days_format"), + String(localized: "push_timefilter_days_format", bundle: PresentationResources.bundle), Int64(value) ) } diff --git a/Application/Presentation/NotificationTab/Tests/PushNotificationItemTests.swift b/Application/Presentation/NotificationTab/Tests/PushNotificationItemTests.swift index 70680136..f1a69776 100644 --- a/Application/Presentation/NotificationTab/Tests/PushNotificationItemTests.swift +++ b/Application/Presentation/NotificationTab/Tests/PushNotificationItemTests.swift @@ -6,6 +6,7 @@ // import Foundation +import PresentationShared import Testing @testable import NotificationTab @@ -19,10 +20,13 @@ struct PushNotificationItemTests { ) let item = PushNotificationItem(from: notification) - #expect(item.title == String(localized: "push_notification_todo_due_title")) + #expect( + item.title + == String(localized: "push_notification_todo_due_title", bundle: PresentationResources.bundle) + ) #expect( item.body == String.localizedStringWithFormat( - String(localized: "push_notification_todo_due_tomorrow_format"), + String(localized: "push_notification_todo_due_tomorrow_format", bundle: PresentationResources.bundle), "테스트 작성" ) ) @@ -33,8 +37,17 @@ struct PushNotificationItemTests { let notification = makePushNotification(id: "notification-1", number: 1) let item = PushNotificationItem(from: notification) - #expect(item.title == String(localized: "push_notification_todo_due_title")) - #expect(item.body == String(localized: "push_notification_todo_due_tomorrow_without_title")) + #expect( + item.title + == String(localized: "push_notification_todo_due_title", bundle: PresentationResources.bundle) + ) + #expect( + item.body + == String( + localized: "push_notification_todo_due_tomorrow_without_title", + bundle: PresentationResources.bundle + ) + ) } @Test("공백으로만 구성된 Todo 제목을 제목 없는 알림으로 표시한다") @@ -46,7 +59,13 @@ struct PushNotificationItemTests { ) let item = PushNotificationItem(from: notification) - #expect(item.body == String(localized: "push_notification_todo_due_tomorrow_without_title")) + #expect( + item.body + == String( + localized: "push_notification_todo_due_tomorrow_without_title", + bundle: PresentationResources.bundle + ) + ) } @Test("기존 알림 문서의 저장 문구를 유지한다") diff --git a/Application/Presentation/NotificationTab/Tests/PushNotificationListTestAssertions.swift b/Application/Presentation/NotificationTab/Tests/PushNotificationListTestAssertions.swift index dd3860ef..f5481e17 100644 --- a/Application/Presentation/NotificationTab/Tests/PushNotificationListTestAssertions.swift +++ b/Application/Presentation/NotificationTab/Tests/PushNotificationListTestAssertions.swift @@ -203,7 +203,7 @@ func verifyDeleteUndoAndFinishToast( let deletedNotifications = adapter.notifications let toastMessage = ToastPresenter.item?.message #expect(deletedNotifications.first?.isHidden == true) - #expect(toastMessage == String(localized: "common_undo")) + #expect(toastMessage == String(localized: "common_undo", bundle: PresentationResources.bundle)) await waitUntilMainActor { deleteUseCaseSpy.calledNotificationIds == ["notification-1"] diff --git a/Application/Presentation/NotificationTab/Tests/PushNotificationListTestSupport.swift b/Application/Presentation/NotificationTab/Tests/PushNotificationListTestSupport.swift index da138051..ca6d789c 100644 --- a/Application/Presentation/NotificationTab/Tests/PushNotificationListTestSupport.swift +++ b/Application/Presentation/NotificationTab/Tests/PushNotificationListTestSupport.swift @@ -161,7 +161,7 @@ struct PushNotificationListStoreTestAdapter: PushNotificationListStateDriving { private func presentDeleteNotificationToast(_ notificationId: String) { ToastPresenter.present( - message: String(localized: "common_undo"), + message: String(localized: "common_undo", bundle: PresentationResources.bundle), systemImage: "arrow.uturn.left", duration: 5, font: .caption, diff --git a/Application/App/Sources/Resource/Localizable.xcstrings b/Application/Presentation/PresentationShared/Resources/Localizable.xcstrings similarity index 99% rename from Application/App/Sources/Resource/Localizable.xcstrings rename to Application/Presentation/PresentationShared/Resources/Localizable.xcstrings index 99ce4437..d3ec50e5 100644 --- a/Application/App/Sources/Resource/Localizable.xcstrings +++ b/Application/Presentation/PresentationShared/Resources/Localizable.xcstrings @@ -1,6 +1,14 @@ { "sourceLanguage" : "ko", "strings" : { + "#%@" : { + "comment" : "A badge displaying the number of a todo list item. The argument is the item's number.", + "isCommentAutoGenerated" : true + }, + "%@" : { + "comment" : "A badge that shows the number of items that match the current filter.", + "isCommentAutoGenerated" : true + }, "account_alert_already_linked_message" : { "extractionState" : "manual", "localizations" : { @@ -3652,5 +3660,5 @@ } } }, - "version" : "1.0" + "version" : "1.1" } \ No newline at end of file diff --git a/Application/Presentation/PresentationShared/Sources/Common/Component/RelativeTimeText.swift b/Application/Presentation/PresentationShared/Sources/Common/Component/RelativeTimeText.swift index d32bc5c3..0f97acef 100644 --- a/Application/Presentation/PresentationShared/Sources/Common/Component/RelativeTimeText.swift +++ b/Application/Presentation/PresentationShared/Sources/Common/Component/RelativeTimeText.swift @@ -26,7 +26,7 @@ public struct RelativeTimeText: View { TimelineView(.periodic(from: .now, by: 1.0)) { context in Text( String.localizedStringWithFormat( - String(localized: "relative_time_updated_format"), + String(localized: "relative_time_updated_format", bundle: PresentationResources.bundle), relativeTimeText(from: date, now: context.date) ) ) @@ -40,25 +40,25 @@ public struct RelativeTimeText: View { if seconds < 60 { return String.localizedStringWithFormat( - String(localized: "relative_time_seconds_ago_format"), + String(localized: "relative_time_seconds_ago_format", bundle: PresentationResources.bundle), Int64(max(0, seconds)) ) } else if seconds < 3600 { let minutes = seconds / 60 return String.localizedStringWithFormat( - String(localized: "relative_time_minutes_ago_format"), + String(localized: "relative_time_minutes_ago_format", bundle: PresentationResources.bundle), Int64(minutes) ) } else if seconds < 86400 { let hours = seconds / 3600 return String.localizedStringWithFormat( - String(localized: "relative_time_hours_ago_format"), + String(localized: "relative_time_hours_ago_format", bundle: PresentationResources.bundle), Int64(hours) ) } else { let days = seconds / 86400 return String.localizedStringWithFormat( - String(localized: "relative_time_days_ago_format"), + String(localized: "relative_time_days_ago_format", bundle: PresentationResources.bundle), Int64(days) ) } diff --git a/Application/Presentation/PresentationShared/Sources/Common/Component/TagCopyMenuView.swift b/Application/Presentation/PresentationShared/Sources/Common/Component/TagCopyMenuView.swift index b91cdf0b..28087f79 100644 --- a/Application/Presentation/PresentationShared/Sources/Common/Component/TagCopyMenuView.swift +++ b/Application/Presentation/PresentationShared/Sources/Common/Component/TagCopyMenuView.swift @@ -96,7 +96,7 @@ extension TagCopyMenuCoordinator: UIEditMenuInteractionDelegate { suggestedActions: [UIMenuElement] ) -> UIMenu? { let tagText = tagText - let copyAction = UIAction(title: String(localized: "common_copy")) { _ in + let copyAction = UIAction(title: String(localized: "common_copy", bundle: PresentationResources.bundle)) { _ in UIPasteboard.general.string = tagText } return UIMenu(children: [copyAction]) diff --git a/Application/Presentation/PresentationShared/Sources/Common/Component/ToolbarButtons.swift b/Application/Presentation/PresentationShared/Sources/Common/Component/ToolbarButtons.swift index d5eb7219..ddc1008e 100644 --- a/Application/Presentation/PresentationShared/Sources/Common/Component/ToolbarButtons.swift +++ b/Application/Presentation/PresentationShared/Sources/Common/Component/ToolbarButtons.swift @@ -25,7 +25,7 @@ public struct ToolbarLeadingButton: ToolbarContent { Button { action?() } label: { - Text(String(localized: "common_cancel")) + Text(String(localized: "common_cancel", bundle: PresentationResources.bundle)) } } } @@ -57,7 +57,7 @@ public struct ToolbarTrailingButton: ToolbarContent { Button { action?() } label: { - Text(String(localized: "common_confirm")) + Text(String(localized: "common_confirm", bundle: PresentationResources.bundle)) .bold() } .disabled(isDisabled) diff --git a/Application/Presentation/PresentationShared/Sources/Common/TodoInfoSheetView.swift b/Application/Presentation/PresentationShared/Sources/Common/TodoInfoSheetView.swift index a23bec96..8063a712 100644 --- a/Application/Presentation/PresentationShared/Sources/Common/TodoInfoSheetView.swift +++ b/Application/Presentation/PresentationShared/Sources/Common/TodoInfoSheetView.swift @@ -20,7 +20,7 @@ struct TodoInfoSheetView: View { ScrollView { LazyVStack(spacing: 32) { VStack(alignment: .leading) { - Text(String(localized: "todo_info_created_at")) + Text(String(localized: "todo_info_created_at", bundle: PresentationResources.bundle)) .font(.subheadline) .foregroundStyle(.white) HStack(spacing: 8) { @@ -38,7 +38,7 @@ struct TodoInfoSheetView: View { Divider() } VStack(alignment: .leading) { - Text(String(localized: "todo_info_due_date")) + Text(String(localized: "todo_info_due_date", bundle: PresentationResources.bundle)) .font(.subheadline) .foregroundStyle(.secondary) HStack(spacing: 8) { @@ -47,7 +47,7 @@ struct TodoInfoSheetView: View { Text( dueDate? .formatted(date: .abbreviated, time: .omitted) - ?? String(localized: "todo_info_no_due_date") + ?? String(localized: "todo_info_no_due_date", bundle: PresentationResources.bundle) ) .foregroundStyle(dueDate == nil ? .secondary : .primary) Spacer() @@ -61,7 +61,7 @@ struct TodoInfoSheetView: View { Divider() } VStack(alignment: .leading) { - Text(String(localized: "todo_info_completed_at")) + Text(String(localized: "todo_info_completed_at", bundle: PresentationResources.bundle)) .font(.subheadline) .foregroundStyle(.secondary) HStack(spacing: 8) { @@ -70,7 +70,7 @@ struct TodoInfoSheetView: View { Text( completedAt? .formatted(date: .abbreviated, time: .omitted) - ?? String(localized: "todo_info_not_completed") + ?? String(localized: "todo_info_not_completed", bundle: PresentationResources.bundle) ) .foregroundStyle(.white) Spacer() @@ -84,7 +84,7 @@ struct TodoInfoSheetView: View { Divider() } VStack(alignment: .leading) { - Text(String(localized: "todo_tags")) + Text(String(localized: "todo_tags", bundle: PresentationResources.bundle)) .font(.subheadline) .foregroundStyle(.secondary) Divider() diff --git a/Application/Presentation/PresentationShared/Sources/Structure/Todo/SystemTodoCategoryItem.swift b/Application/Presentation/PresentationShared/Sources/Structure/Todo/SystemTodoCategoryItem.swift index ae16bdd7..fad908d5 100644 --- a/Application/Presentation/PresentationShared/Sources/Structure/Todo/SystemTodoCategoryItem.swift +++ b/Application/Presentation/PresentationShared/Sources/Structure/Todo/SystemTodoCategoryItem.swift @@ -32,14 +32,14 @@ public struct SystemTodoCategoryItem: Identifiable, Hashable { public var localizedName: String { switch systemTodoCategory { - case .issue: return NSLocalizedString("todo_category_issue", comment: "Todo category: Issue") - case .feature: return NSLocalizedString("todo_category_feature", comment: "Todo category: Feature") - case .improvement: return NSLocalizedString("todo_category_improvement", comment: "Todo category: Improvement") - case .review: return NSLocalizedString("todo_category_review", comment: "Todo category: Review") - case .test: return NSLocalizedString("todo_category_test", comment: "Todo category: Test") - case .doc: return NSLocalizedString("todo_category_doc", comment: "Todo category: Documentation") - case .research: return NSLocalizedString("todo_category_research", comment: "Todo category: Research") - case .etc: return NSLocalizedString("todo_category_etc", comment: "Todo category: Etc") + case .issue: return String(localized: "todo_category_issue", bundle: PresentationResources.bundle) + case .feature: return String(localized: "todo_category_feature", bundle: PresentationResources.bundle) + case .improvement: return String(localized: "todo_category_improvement", bundle: PresentationResources.bundle) + case .review: return String(localized: "todo_category_review", bundle: PresentationResources.bundle) + case .test: return String(localized: "todo_category_test", bundle: PresentationResources.bundle) + case .doc: return String(localized: "todo_category_doc", bundle: PresentationResources.bundle) + case .research: return String(localized: "todo_category_research", bundle: PresentationResources.bundle) + case .etc: return String(localized: "todo_category_etc", bundle: PresentationResources.bundle) } } diff --git a/Application/Presentation/PresentationShared/Sources/Structure/Todo/TodoQuery+Presentation.swift b/Application/Presentation/PresentationShared/Sources/Structure/Todo/TodoQuery+Presentation.swift index b09f4e31..28083ec0 100644 --- a/Application/Presentation/PresentationShared/Sources/Structure/Todo/TodoQuery+Presentation.swift +++ b/Application/Presentation/PresentationShared/Sources/Structure/Todo/TodoQuery+Presentation.swift @@ -12,15 +12,15 @@ public extension TodoQuery.SortTarget { var title: String { switch self { case .createdAt: - return String(localized: "todo_sort_created") + return String(localized: "todo_sort_created", bundle: PresentationResources.bundle) case .completedAt: - return String(localized: "profile_activity_completed") + return String(localized: "profile_activity_completed", bundle: PresentationResources.bundle) case .deletedAt: - return String(localized: "profile_activity_deleted") + return String(localized: "profile_activity_deleted", bundle: PresentationResources.bundle) case .updatedAt: - return String(localized: "todo_sort_updated") + return String(localized: "todo_sort_updated", bundle: PresentationResources.bundle) case .dueDate: - return String(localized: "todo_sort_due_date") + return String(localized: "todo_sort_due_date", bundle: PresentationResources.bundle) } } } @@ -29,9 +29,9 @@ public extension TodoQuery.SortOrder { var title: String { switch self { case .latest: - return String(localized: "todo_sort_latest") + return String(localized: "todo_sort_latest", bundle: PresentationResources.bundle) case .oldest: - return String(localized: "todo_sort_oldest") + return String(localized: "todo_sort_oldest", bundle: PresentationResources.bundle) } } } @@ -40,11 +40,11 @@ public extension TodoQuery.CompletionFilter { var title: String { switch self { case .all: - return String(localized: "todo_completion_all") + return String(localized: "todo_completion_all", bundle: PresentationResources.bundle) case .incomplete: - return String(localized: "todo_completion_incomplete") + return String(localized: "todo_completion_incomplete", bundle: PresentationResources.bundle) case .completed: - return String(localized: "todo_completion_completed") + return String(localized: "todo_completion_completed", bundle: PresentationResources.bundle) } } } diff --git a/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailFeature.swift b/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailFeature.swift index 34352112..78a824f9 100644 --- a/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailFeature.swift +++ b/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailFeature.swift @@ -256,13 +256,13 @@ private extension TodoDetailFeature { static func alertState() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } } diff --git a/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailPreviewView.swift b/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailPreviewView.swift index c69db75c..b94151b6 100644 --- a/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailPreviewView.swift +++ b/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailPreviewView.swift @@ -25,11 +25,11 @@ struct TodoDetailPreviewView: View { } else if store.alert != nil { ContentUnavailableView { Label( - String(localized: "common_error_title"), + String(localized: "common_error_title", bundle: PresentationResources.bundle), systemImage: "exclamationmark.triangle" ) } description: { - Text(String(localized: "common_error_message")) + Text(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } else { LoadingView() diff --git a/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailView.swift b/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailView.swift index 9cb1e449..4179840f 100644 --- a/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailView.swift +++ b/Application/Presentation/PresentationShared/Sources/Todo/Detail/TodoDetailView.swift @@ -65,7 +65,7 @@ public struct TodoDetailView: View { Button { openTodoEditor() } label: { - Text(String(localized: "todo_edit")) + Text(String(localized: "todo_edit", bundle: PresentationResources.bundle)) } } } @@ -131,28 +131,28 @@ private struct TodoDetailInfoSheetView: View { var body: some View { NavigationStack { List { - Section(String(localized: "todo_options_section")) { + Section(String(localized: "todo_options_section", bundle: PresentationResources.bundle)) { HStack { - Text(String(localized: "todo_category")) + Text(String(localized: "todo_category", bundle: PresentationResources.bundle)) Spacer() Text(TodoCategoryItem(from: todo.category).localizedName) .foregroundStyle(.secondary) } statusRow( - title: String(localized: "todo_completed"), + title: String(localized: "todo_completed", bundle: PresentationResources.bundle), systemImage: todo.isCompleted ? "checkmark.circle.fill" : "circle", color: todo.isCompleted ? .green : .secondary ) statusRow( - title: String(localized: "todo_pinned"), + title: String(localized: "todo_pinned", bundle: PresentationResources.bundle), systemImage: todo.isPinned ? "star.fill" : "star", color: todo.isPinned ? .orange : .secondary ) HStack { - Text(String(localized: "todo_due_date")) + Text(String(localized: "todo_due_date", bundle: PresentationResources.bundle)) Spacer() @@ -160,15 +160,15 @@ private struct TodoDetailInfoSheetView: View { Tag(dueDateText(for: dueDate), isEditing: false) .padding(.vertical, -4) } else { - Text(String(localized: "todo_none")) + Text(String(localized: "todo_none", bundle: PresentationResources.bundle)) .foregroundStyle(.secondary) } } } - Section(String(localized: "todo_tags")) { + Section(String(localized: "todo_tags", bundle: PresentationResources.bundle)) { if todo.tags.isEmpty { - Text(String(localized: "todo_no_tags")) + Text(String(localized: "todo_no_tags", bundle: PresentationResources.bundle)) .foregroundStyle(.secondary) .padding(.vertical, 4) } else { @@ -176,7 +176,7 @@ private struct TodoDetailInfoSheetView: View { } } } - .navigationTitle(String(localized: "todo_details")) + .navigationTitle(String(localized: "todo_details", bundle: PresentationResources.bundle)) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarLeadingButton { diff --git a/Application/Presentation/PresentationShared/Sources/Todo/Editor/TodoEditorFeature.swift b/Application/Presentation/PresentationShared/Sources/Todo/Editor/TodoEditorFeature.swift index 0436f7af..2790085f 100644 --- a/Application/Presentation/PresentationShared/Sources/Todo/Editor/TodoEditorFeature.swift +++ b/Application/Presentation/PresentationShared/Sources/Todo/Editor/TodoEditorFeature.swift @@ -46,12 +46,12 @@ public struct TodoEditorFeature { var navigationTitle: String { if originalDraft == nil { return String.localizedStringWithFormat( - String(localized: "todo_editor_new_format"), + String(localized: "todo_editor_new_format", bundle: PresentationResources.bundle), category.localizedName ) } - return String(localized: "todo_edit") + return String(localized: "todo_edit", bundle: PresentationResources.bundle) } public var hasChanges: Bool { guard let originalDraft else { return true } @@ -344,13 +344,13 @@ private extension TodoEditorFeature { static func alertState() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } } diff --git a/Application/Presentation/PresentationShared/Sources/Todo/Editor/TodoEditorView.swift b/Application/Presentation/PresentationShared/Sources/Todo/Editor/TodoEditorView.swift index 68ecace5..4abf64bb 100644 --- a/Application/Presentation/PresentationShared/Sources/Todo/Editor/TodoEditorView.swift +++ b/Application/Presentation/PresentationShared/Sources/Todo/Editor/TodoEditorView.swift @@ -142,7 +142,13 @@ public struct TodoEditorView: View { TextField( "", text: $store.title, - prompt: Text(String(localized: "todo_editor_title_required")).foregroundColor(Color.secondary), + prompt: Text( + String( + localized: "todo_editor_title_required", + bundle: PresentationResources.bundle + ) + ) + .foregroundColor(Color.secondary), ) .font(.title2) .frame(height: 30) @@ -164,9 +170,9 @@ public struct TodoEditorView: View { } ) ) { - Text(String(localized: "todo_write")) + Text(String(localized: "todo_write", bundle: PresentationResources.bundle)) .tag(TodoEditorFeature.EditorTab.editor) - Text(String(localized: "todo_preview")) + Text(String(localized: "todo_preview", bundle: PresentationResources.bundle)) .tag(TodoEditorFeature.EditorTab.preview) } .pickerStyle(.segmented) @@ -179,7 +185,10 @@ public struct TodoEditorView: View { markdownHint UIKitTextEditor( text: $store.content, - placeholder: String(localized: "todo_editor_description_optional") + placeholder: String( + localized: "todo_editor_description_optional", + bundle: PresentationResources.bundle + ) ) .focused($field, equals: .content) } @@ -201,16 +210,16 @@ public struct TodoEditorView: View { } private var markdownHint: some View { - Text(String(localized: "todo_editor_markdown_hint")) + Text(String(localized: "todo_editor_markdown_hint", bundle: PresentationResources.bundle)) .font(.caption) .foregroundStyle(.secondary) } private var previewPlaceholder: some View { VStack(alignment: .leading, spacing: 6) { - Text(String(localized: "todo_editor_markdown_preview_title")) + Text(String(localized: "todo_editor_markdown_preview_title", bundle: PresentationResources.bundle)) .font(.subheadline.weight(.semibold)) - Text(String(localized: "todo_editor_markdown_preview_message")) + Text(String(localized: "todo_editor_markdown_preview_message", bundle: PresentationResources.bundle)) .font(.footnote) .foregroundStyle(.secondary) } @@ -290,9 +299,9 @@ private struct TodoEditorInfoSheetView: View { var body: some View { NavigationStack { List { - Section(String(localized: "todo_options_section")) { + Section(String(localized: "todo_options_section", bundle: PresentationResources.bundle)) { Picker( - String(localized: "todo_category"), + String(localized: "todo_category", bundle: PresentationResources.bundle), selection: Binding( get: { store.category.id }, set: { categoryId in @@ -313,7 +322,7 @@ private struct TodoEditorInfoSheetView: View { } Toggle( - String(localized: "todo_completed"), + String(localized: "todo_completed", bundle: PresentationResources.bundle), isOn: Binding( get: { store.isCompleted }, set: { store.send(.setCompleted($0)) } @@ -322,7 +331,7 @@ private struct TodoEditorInfoSheetView: View { .tint(.blue) Toggle( - String(localized: "todo_pinned"), + String(localized: "todo_pinned", bundle: PresentationResources.bundle), isOn: Binding( get: { store.isPinned }, set: { store.send(.binding(.set(\.isPinned, $0))) } @@ -333,10 +342,10 @@ private struct TodoEditorInfoSheetView: View { dueDateControl } - Section(String(localized: "todo_tags")) { + Section(String(localized: "todo_tags", bundle: PresentationResources.bundle)) { HStack(spacing: 12) { TextField( - String(localized: "todo_add"), + String(localized: "todo_add", bundle: PresentationResources.bundle), text: $store.tagText ) .frame(height: UIFont.preferredFont(forTextStyle: .title2).lineHeight) @@ -359,7 +368,7 @@ private struct TodoEditorInfoSheetView: View { } if store.tags.isEmpty { - Text(String(localized: "todo_no_tags")) + Text(String(localized: "todo_no_tags", bundle: PresentationResources.bundle)) .foregroundStyle(.secondary) .padding(.vertical, 4) } else { @@ -371,7 +380,7 @@ private struct TodoEditorInfoSheetView: View { } } } - .navigationTitle(String(localized: "todo_details")) + .navigationTitle(String(localized: "todo_details", bundle: PresentationResources.bundle)) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarLeadingButton { @@ -387,7 +396,7 @@ private struct TodoEditorInfoSheetView: View { set: { store.send(.binding(.set(\.dueDate, $0))) } )) { HStack { - Text(String(localized: "todo_due_date")) + Text(String(localized: "todo_due_date", bundle: PresentationResources.bundle)) .foregroundStyle(.primary) Spacer() if let dueDate = store.dueDate { @@ -396,7 +405,7 @@ private struct TodoEditorInfoSheetView: View { } .padding(.vertical, -4) } else { - Text(String(localized: "todo_none")) + Text(String(localized: "todo_none", bundle: PresentationResources.bundle)) .foregroundStyle(.secondary) } } diff --git a/Application/Presentation/PresentationShared/Sources/Todo/List/TodoListFeature+Effects.swift b/Application/Presentation/PresentationShared/Sources/Todo/List/TodoListFeature+Effects.swift index fc868d4e..f7f2be59 100644 --- a/Application/Presentation/PresentationShared/Sources/Todo/List/TodoListFeature+Effects.swift +++ b/Application/Presentation/PresentationShared/Sources/Todo/List/TodoListFeature+Effects.swift @@ -129,13 +129,13 @@ extension TodoListFeature { static func alertState() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } diff --git a/Application/Presentation/PresentationShared/Sources/Todo/List/TodoListView.swift b/Application/Presentation/PresentationShared/Sources/Todo/List/TodoListView.swift index 794aa2f5..89bb3945 100644 --- a/Application/Presentation/PresentationShared/Sources/Todo/List/TodoListView.swift +++ b/Application/Presentation/PresentationShared/Sources/Todo/List/TodoListView.swift @@ -50,7 +50,7 @@ public struct TodoListView: View { placement: .navigationBarDrawer(displayMode: .always), prompt: Text( String.localizedStringWithFormat( - String(localized: "todo_list_search_prompt_format"), + String(localized: "todo_list_search_prompt_format", bundle: PresentationResources.bundle), TodoCategoryItem(from: store.category).localizedName ) ) @@ -100,7 +100,7 @@ public struct TodoListView: View { if visibleTodos.isEmpty, !store.state.isLoading { HStack { Spacer() - Text(String(localized: "todo_list_empty")) + Text(String(localized: "todo_list_empty", bundle: PresentationResources.bundle)) .foregroundStyle(Color.gray) Spacer() } @@ -197,7 +197,7 @@ public struct TodoListView: View { placement: .navigationBarDrawer(displayMode: .always), prompt: Text( String.localizedStringWithFormat( - String(localized: "todo_list_search_prompt_format"), + String(localized: "todo_list_search_prompt_format", bundle: PresentationResources.bundle), TodoCategoryItem(from: store.category).localizedName ) ) @@ -231,7 +231,7 @@ public struct TodoListView: View { private func presentDeleteTodoToast(_ todoId: String) { ToastPresenter.present( - message: String(localized: "common_undo"), + message: String(localized: "common_undo", bundle: PresentationResources.bundle), systemImage: "arrow.uturn.left", duration: 5, action: { @@ -254,7 +254,7 @@ public struct TodoListView: View { if store.state.searchText.isEmpty { Text( String.localizedStringWithFormat( - String(localized: "todo_list_search_instruction_format"), + String(localized: "todo_list_search_instruction_format", bundle: PresentationResources.bundle), TodoCategoryItem(from: store.category).localizedName ) ) @@ -264,7 +264,7 @@ public struct TodoListView: View { LoadingView() } else if searchResults.isEmpty { Spacer() - Text(String(localized: "todo_list_search_empty")) + Text(String(localized: "todo_list_search_empty", bundle: PresentationResources.bundle)) .foregroundStyle(Color.gray) .frame(maxWidth: .infinity) Spacer() @@ -285,7 +285,7 @@ public struct TodoListView: View { .padding(.horizontal, 16) if !store.state.showAllSearchResults, limit < searchResults.count { - Button(String(localized: "todo_list_show_more")) { + Button(String(localized: "todo_list_show_more", bundle: PresentationResources.bundle)) { store.send(.binding(.set(\.showAllSearchResults, true))) } .font(.subheadline) @@ -305,14 +305,17 @@ public struct TodoListView: View { Menu { Text( String.localizedStringWithFormat( - String(localized: "todo_list_filters_applied_format"), + String( + localized: "todo_list_filters_applied_format", + bundle: PresentationResources.bundle + ), Int64(store.appliedFilterCount) ) ) Button(role: .destructive) { store.send(.view(.resetFilters)) } label: { - Text(String(localized: "todo_list_clear_filters")) + Text(String(localized: "todo_list_clear_filters", bundle: PresentationResources.bundle)) } } label: { HStack(spacing: 6) { @@ -329,21 +332,21 @@ public struct TodoListView: View { Text(option.title).tag(option) } } label: { - Text(String(localized: "todo_list_sort_by")) + Text(String(localized: "todo_list_sort_by", bundle: PresentationResources.bundle)) } Picker(selection: $store.query.sortOrder) { ForEach([TodoQuery.SortOrder.latest, .oldest], id: \.self) { option in Text(option.title).tag(option) } } label: { - Text(String(localized: "todo_list_sort_order")) + Text(String(localized: "todo_list_sort_order", bundle: PresentationResources.bundle)) } } label: { let condition = store.state.query.sortTarget == .createdAt && store.state.query.sortOrder == .latest HStack { Text( String.localizedStringWithFormat( - String(localized: "todo_list_sort_format"), + String(localized: "todo_list_sort_format", bundle: PresentationResources.bundle), store.state.query.sortTarget.title, store.state.query.sortOrder.title ) @@ -356,7 +359,7 @@ public struct TodoListView: View { Menu { Toggle(isOn: $store.query.isPinned) { - Text(String(localized: "todo_pinned")) + Text(String(localized: "todo_pinned", bundle: PresentationResources.bundle)) } Picker(selection: $store.query.completionFilter) { @@ -364,12 +367,12 @@ public struct TodoListView: View { Text(option.title).tag(option) } } label: { - Text(String(localized: "todo_list_completion_status")) + Text(String(localized: "todo_list_completion_status", bundle: PresentationResources.bundle)) } } label: { let condition = store.state.query.isPinned || store.state.query.completionFilter != .all HStack { - Text(String(localized: "todo_list_filter_options")) + Text(String(localized: "todo_list_filter_options", bundle: PresentationResources.bundle)) Image(systemName: "chevron.down") } .foregroundStyle(condition ? .white : Color(.label)) diff --git a/Application/Presentation/PresentationShared/Tests/Todo/TodoDetailFeatureTests.swift b/Application/Presentation/PresentationShared/Tests/Todo/TodoDetailFeatureTests.swift index cd58ae8e..608f3bbd 100644 --- a/Application/Presentation/PresentationShared/Tests/Todo/TodoDetailFeatureTests.swift +++ b/Application/Presentation/PresentationShared/Tests/Todo/TodoDetailFeatureTests.swift @@ -330,13 +330,13 @@ private func todoDetailState( private func expectedErrorAlert() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } diff --git a/Application/Presentation/PresentationShared/Tests/Todo/TodoEditorFeatureTestDoubles.swift b/Application/Presentation/PresentationShared/Tests/Todo/TodoEditorFeatureTestDoubles.swift index 91239621..f757124d 100644 --- a/Application/Presentation/PresentationShared/Tests/Todo/TodoEditorFeatureTestDoubles.swift +++ b/Application/Presentation/PresentationShared/Tests/Todo/TodoEditorFeatureTestDoubles.swift @@ -303,13 +303,13 @@ enum TodoEditorTestError: Error { func expectedTodoEditorErrorAlert() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } diff --git a/Application/Presentation/PresentationShared/Tests/Todo/TodoListFeatureTestDoubles.swift b/Application/Presentation/PresentationShared/Tests/Todo/TodoListFeatureTestDoubles.swift index 5e1ca72e..77ae8cf9 100644 --- a/Application/Presentation/PresentationShared/Tests/Todo/TodoListFeatureTestDoubles.swift +++ b/Application/Presentation/PresentationShared/Tests/Todo/TodoListFeatureTestDoubles.swift @@ -164,13 +164,13 @@ final class TodoListStoreTestAdapter { func expectedTodoListErrorAlert() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } diff --git a/Application/Presentation/ProfileTab/Sources/Profile/ProfileFeature+State.swift b/Application/Presentation/ProfileTab/Sources/Profile/ProfileFeature+State.swift index 73f85462..3a600d8e 100644 --- a/Application/Presentation/ProfileTab/Sources/Profile/ProfileFeature+State.swift +++ b/Application/Presentation/ProfileTab/Sources/Profile/ProfileFeature+State.swift @@ -7,6 +7,7 @@ import Core import Foundation +import PresentationShared extension ProfileFeature.State { var isLoading: Bool { @@ -19,7 +20,7 @@ extension ProfileFeature.State { let month = Calendar.current.component(.month, from: start) let quarter = ((month - 1) / 3) + 1 return String.localizedStringWithFormat( - String(localized: "profile_year_quarter_format"), + String(localized: "profile_year_quarter_format", bundle: PresentationResources.bundle), String(year), String(quarter) ) diff --git a/Application/Presentation/ProfileTab/Sources/Profile/ProfileFeature.swift b/Application/Presentation/ProfileTab/Sources/Profile/ProfileFeature.swift index e374e36e..be8c0502 100644 --- a/Application/Presentation/ProfileTab/Sources/Profile/ProfileFeature.swift +++ b/Application/Presentation/ProfileTab/Sources/Profile/ProfileFeature.swift @@ -304,10 +304,10 @@ private extension ProfileFeature { TextState("") } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } } diff --git a/Application/Presentation/ProfileTab/Sources/Profile/ProfileRegularDetailView.swift b/Application/Presentation/ProfileTab/Sources/Profile/ProfileRegularDetailView.swift index 0e52d21d..afa295af 100644 --- a/Application/Presentation/ProfileTab/Sources/Profile/ProfileRegularDetailView.swift +++ b/Application/Presentation/ProfileTab/Sources/Profile/ProfileRegularDetailView.swift @@ -26,7 +26,7 @@ public struct ProfileRegularDetailView: View { ) } else { ContentUnavailableView( - String(localized: "profile_select_detail"), + String(localized: "profile_select_detail", bundle: PresentationResources.bundle), systemImage: "person.crop.circle" ) } diff --git a/Application/Presentation/ProfileTab/Sources/Profile/ProfileView.swift b/Application/Presentation/ProfileTab/Sources/Profile/ProfileView.swift index 4cadab18..38a92020 100644 --- a/Application/Presentation/ProfileTab/Sources/Profile/ProfileView.swift +++ b/Application/Presentation/ProfileTab/Sources/Profile/ProfileView.swift @@ -105,7 +105,7 @@ public struct ProfileView: View { TextField( text: $store.statusMessage ) { - Text(String(localized: "profile_status_placeholder")) + Text(String(localized: "profile_status_placeholder", bundle: PresentationResources.bundle)) } .frame(height: UIFont.preferredFont(forTextStyle: .body).lineHeight) .focused($focused) @@ -132,7 +132,7 @@ public struct ProfileView: View { focused = false store.send(.willUpdateStatusMessage) } label: { - Text(String(localized: "profile_done")) + Text(String(localized: "profile_done", bundle: PresentationResources.bundle)) } .transition(.move(edge: .trailing).combined(with: .opacity)) } @@ -143,7 +143,7 @@ public struct ProfileView: View { private var activityHeatmapSection: some View { VStack(alignment: .leading, spacing: 16) { HStack { - Text(String(localized: "profile_quarterly_activity")) + Text(String(localized: "profile_quarterly_activity", bundle: PresentationResources.bundle)) .font(.headline) Spacer() if !store.isViewingCurrentQuarter { @@ -246,7 +246,7 @@ public struct ProfileView: View { NavigationStack { VStack(alignment: .leading, spacing: 20) { HStack { - Text(String(localized: "profile_year")) + Text(String(localized: "profile_year", bundle: PresentationResources.bundle)) .font(.subheadline) .foregroundStyle(.secondary) Spacer() @@ -272,7 +272,7 @@ public struct ProfileView: View { Spacer(minLength: 0) } .padding(20) - .navigationTitle(String(localized: "profile_select_quarter")) + .navigationTitle(String(localized: "profile_select_quarter", bundle: PresentationResources.bundle)) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarTrailingButton { @@ -296,7 +296,7 @@ public struct ProfileView: View { } label: { Text( String.localizedStringWithFormat( - String(localized: "profile_quarter_format"), + String(localized: "profile_quarter_format", bundle: PresentationResources.bundle), Int64(quarter) ) ) @@ -323,7 +323,7 @@ public struct ProfileView: View { .bold() if activities.isEmpty { - Text(String(localized: "profile_activity_none")) + Text(String(localized: "profile_activity_none", bundle: PresentationResources.bundle)) .font(.caption) .foregroundStyle(.secondary) .frame(maxWidth: .infinity, alignment: .center) diff --git a/Application/Presentation/ProfileTab/Sources/Settings/AccountFeature.swift b/Application/Presentation/ProfileTab/Sources/Settings/AccountFeature.swift index 0eae963e..1249d465 100644 --- a/Application/Presentation/ProfileTab/Sources/Settings/AccountFeature.swift +++ b/Application/Presentation/ProfileTab/Sources/Settings/AccountFeature.swift @@ -176,7 +176,12 @@ private extension AccountFeature { return } - await ToastPresenter.present(message: String(localized: "account_toast_link_success")) + await ToastPresenter.present( + message: String( + localized: "account_toast_link_success", + bundle: PresentationResources.bundle + ) + ) let providers = try await fetchProvidersUseCase.execute() await send(.setProviders( currentProvider: providers.currentProvider, @@ -195,7 +200,12 @@ private extension AccountFeature { await send(.loading(.begin(target: .default, mode: .delayed))) do { try await unlinkProviderUseCase.execute(provider) - await ToastPresenter.present(message: String(localized: "account_toast_unlink_success")) + await ToastPresenter.present( + message: String( + localized: "account_toast_unlink_success", + bundle: PresentationResources.bundle + ) + ) let providers = try await fetchProvidersUseCase.execute() await send(.setProviders( currentProvider: providers.currentProvider, @@ -234,27 +244,30 @@ private extension AccountFeature { switch type { case .linkEmailNotFound: - title = String(localized: "account_alert_email_unavailable_title") - message = String(localized: "account_alert_email_unavailable_message") + title = String(localized: "account_alert_email_unavailable_title", bundle: PresentationResources.bundle) + message = String(localized: "account_alert_email_unavailable_message", bundle: PresentationResources.bundle) case .linkEmailMismatch: - title = String(localized: "account_alert_cannot_link_title") - message = String(localized: "account_alert_cannot_link_message") + title = String(localized: "account_alert_cannot_link_title", bundle: PresentationResources.bundle) + message = String(localized: "account_alert_cannot_link_message", bundle: PresentationResources.bundle) case .linkCredentialAlreadyInUse: - title = String(localized: "account_alert_already_linked_title") - message = String(localized: "account_alert_already_linked_message") + title = String(localized: "account_alert_already_linked_title", bundle: PresentationResources.bundle) + message = String(localized: "account_alert_already_linked_message", bundle: PresentationResources.bundle) case .githubEmailConflict: - title = String(localized: "account_alert_github_email_conflict_title") - message = String(localized: "account_alert_github_email_conflict_message") + title = String(localized: "account_alert_github_email_conflict_title", bundle: PresentationResources.bundle) + message = String( + localized: "account_alert_github_email_conflict_message", + bundle: PresentationResources.bundle + ) case .error: - title = String(localized: "common_error_title") - message = String(localized: "common_error_message") + title = String(localized: "common_error_title", bundle: PresentationResources.bundle) + message = String(localized: "common_error_message", bundle: PresentationResources.bundle) } return AlertState { TextState(title) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { TextState(message) diff --git a/Application/Presentation/ProfileTab/Sources/Settings/AccountView.swift b/Application/Presentation/ProfileTab/Sources/Settings/AccountView.swift index 285146aa..a665c049 100644 --- a/Application/Presentation/ProfileTab/Sources/Settings/AccountView.swift +++ b/Application/Presentation/ProfileTab/Sources/Settings/AccountView.swift @@ -15,14 +15,14 @@ struct AccountView: View { var body: some View { List { - Section(String(localized: "account_current_section")) { + Section(String(localized: "account_current_section", bundle: PresentationResources.bundle)) { HStack { if let provider = store.currentProvider { providerContent(provider) } } } - Section(String(localized: "account_social_section")) { + Section(String(localized: "account_social_section", bundle: PresentationResources.bundle)) { let providers = AuthProvider.allCases.filter { $0 != store.currentProvider } ForEach(providers, id: \.self) { provider in let isConnected = store.connectedProviders.contains(provider) @@ -38,8 +38,8 @@ struct AccountView: View { } } label: { Text(isConnected - ? String(localized: "account_disconnect") - : String(localized: "account_connect")) + ? String(localized: "account_disconnect", bundle: PresentationResources.bundle) + : String(localized: "account_connect", bundle: PresentationResources.bundle)) .font(.caption.weight(.semibold)) .foregroundStyle(.white) .padding(.horizontal, 12) @@ -64,7 +64,7 @@ struct AccountView: View { } .scrollDisabled(true) .listStyle(.insetGrouped) - .navigationTitle(String(localized: "nav_account")) + .navigationTitle(String(localized: "nav_account", bundle: PresentationResources.bundle)) .onAppear { store.send(.onAppear) } .prominentAlert(store, state: \.alert, action: \.alert) .background { diff --git a/Application/Presentation/ProfileTab/Sources/Settings/PushNotificationSettingsFeature.swift b/Application/Presentation/ProfileTab/Sources/Settings/PushNotificationSettingsFeature.swift index 639f7064..d596664b 100644 --- a/Application/Presentation/ProfileTab/Sources/Settings/PushNotificationSettingsFeature.swift +++ b/Application/Presentation/ProfileTab/Sources/Settings/PushNotificationSettingsFeature.swift @@ -231,13 +231,13 @@ private extension PushNotificationSettingsFeature { static func alertState() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } } diff --git a/Application/Presentation/ProfileTab/Sources/Settings/PushNotificationSettingsView.swift b/Application/Presentation/ProfileTab/Sources/Settings/PushNotificationSettingsView.swift index 4b5b02b7..6b213450 100644 --- a/Application/Presentation/ProfileTab/Sources/Settings/PushNotificationSettingsView.swift +++ b/Application/Presentation/ProfileTab/Sources/Settings/PushNotificationSettingsView.swift @@ -15,7 +15,7 @@ struct PushNotificationSettingsView: View { List { Section(content: { HStack { - Text(String(localized: "push_settings_enable")) + Text(String(localized: "push_settings_enable", bundle: PresentationResources.bundle)) Spacer() if store.isLoading && store.activeLoadingRow == .enable { ProgressView() @@ -27,7 +27,7 @@ struct PushNotificationSettingsView: View { } } }, footer: { - Text(String(localized: "push_settings_footer")) + Text(String(localized: "push_settings_footer", bundle: PresentationResources.bundle)) .multilineTextAlignment(.leading) }) Section { @@ -52,7 +52,7 @@ struct PushNotificationSettingsView: View { } } HStack { - Text(String(localized: "push_settings_custom")) + Text(String(localized: "push_settings_custom", bundle: PresentationResources.bundle)) Spacer() Text(formattedTimeString(store.viewPushNotificationTime)) .foregroundStyle(.secondary) @@ -68,7 +68,7 @@ struct PushNotificationSettingsView: View { .opacity(store.pushNotificationEnable ? 1.0 : 0.2) } .listStyle(.insetGrouped) - .navigationTitle(String(localized: "nav_push_settings")) + .navigationTitle(String(localized: "nav_push_settings", bundle: PresentationResources.bundle)) .onAppear { store.send(.fetchSettings) } .prominentAlert(store, state: \.alert, action: \.alert) .sheet(item: $store.scope(state: \.timePicker, action: \.timePicker)) { timePickerStore in diff --git a/Application/Presentation/ProfileTab/Sources/Settings/SettingsFeature.swift b/Application/Presentation/ProfileTab/Sources/Settings/SettingsFeature.swift index 03d3a026..d479ab65 100644 --- a/Application/Presentation/ProfileTab/Sources/Settings/SettingsFeature.swift +++ b/Application/Presentation/ProfileTab/Sources/Settings/SettingsFeature.swift @@ -347,52 +347,62 @@ private extension SettingsFeature { switch type { case .signOut: return AlertState { - TextState(String(localized: "settings_alert_sign_out_title")) + TextState(String(localized: "settings_alert_sign_out_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_cancel")) + TextState(String(localized: "common_cancel", bundle: PresentationResources.bundle)) } ButtonState(role: .destructive, action: .tapSignOutButton) { - TextState(String(localized: "common_confirm")) + TextState(String(localized: "common_confirm", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "settings_alert_sign_out_message")) + TextState(String(localized: "settings_alert_sign_out_message", bundle: PresentationResources.bundle)) } case .deleteAuth: return AlertState { - TextState(String(localized: "settings_alert_delete_account_title")) + TextState( + String( + localized: "settings_alert_delete_account_title", + bundle: PresentationResources.bundle + ) + ) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_cancel")) + TextState(String(localized: "common_cancel", bundle: PresentationResources.bundle)) } ButtonState(role: .destructive, action: .tapDeleteAuthButton) { - TextState(String(localized: "settings_delete_account_action")) + TextState(String(localized: "settings_delete_account_action", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "settings_alert_delete_account_message")) + TextState( + String( + localized: "settings_alert_delete_account_message", + bundle: PresentationResources.bundle + ) + ) } case .error: return AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } case .removeCache: return AlertState { - TextState(String(localized: "settings_alert_clear_temp_title")) + TextState(String(localized: "settings_alert_clear_temp_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_cancel")) + TextState(String(localized: "common_cancel", bundle: PresentationResources.bundle)) } ButtonState(role: .destructive, action: .confirmRemoveCache) { - TextState(String(localized: "common_confirm")) + TextState(String(localized: "common_confirm", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "settings_alert_clear_temp_message")) + TextState(String(localized: "settings_alert_clear_temp_message", bundle: PresentationResources.bundle)) } } } diff --git a/Application/Presentation/ProfileTab/Sources/Settings/SettingsView.swift b/Application/Presentation/ProfileTab/Sources/Settings/SettingsView.swift index 717a896f..0e53421b 100644 --- a/Application/Presentation/ProfileTab/Sources/Settings/SettingsView.swift +++ b/Application/Presentation/ProfileTab/Sources/Settings/SettingsView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Domain import PresentationShared struct SettingsView: View { @@ -20,10 +21,10 @@ struct SettingsView: View { router.push(.theme) } label: { HStack { - Text(String(localized: "settings_theme")) + Text(String(localized: "settings_theme", bundle: PresentationResources.bundle)) .foregroundStyle(Color.primary) Spacer() - Text(store.theme.localizedName) + Text(store.theme.localizedName(in: PresentationResources.bundle)) .foregroundStyle(Color.gray) } } @@ -31,7 +32,7 @@ struct SettingsView: View { Button { router.push(.pushNotification) } label: { - Text(String(localized: "settings_notifications")) + Text(String(localized: "settings_notifications", bundle: PresentationResources.bundle)) .foregroundStyle(connected ? Color.primary : Color.secondary) } .disabled(!connected) @@ -41,7 +42,7 @@ struct SettingsView: View { store.send(.tapRemoveCacheButton) } label: { HStack { - Text(String(localized: "settings_clear_temp_data")) + Text(String(localized: "settings_clear_temp_data", bundle: PresentationResources.bundle)) .foregroundStyle(dirSize == 0 ? Color.secondary : .primary) Spacer() if store.activeLoadingRow == .removeCache { @@ -59,7 +60,7 @@ struct SettingsView: View { Section { if let appVersion = store.appVersion { HStack { - Text(String(localized: "settings_version")) + Text(String(localized: "settings_version", bundle: PresentationResources.bundle)) Spacer() Text(appVersion) } @@ -67,16 +68,16 @@ struct SettingsView: View { if let policyString = store.policyURL, let url = URL(string: policyString) { Link(destination: url) { - Text(String(localized: "settings_privacy_policy")) + Text(String(localized: "settings_privacy_policy", bundle: PresentationResources.bundle)) .foregroundColor(Color.blue) } } if let betaTestURL = store.betaTestURL { Link(destination: betaTestURL) { VStack(alignment: .leading) { - Text(String(localized: "settings_join_beta")) + Text(String(localized: "settings_join_beta", bundle: PresentationResources.bundle)) .foregroundStyle(Color.primary) - Text(String(localized: "settings_join_beta_subtitle")) + Text(String(localized: "settings_join_beta_subtitle", bundle: PresentationResources.bundle)) .foregroundStyle(Color.gray) .font(.caption) } @@ -88,14 +89,14 @@ struct SettingsView: View { Button { router.push(.account) } label: { - Text(String(localized: "settings_account")) + Text(String(localized: "settings_account", bundle: PresentationResources.bundle)) } .disabled(!connected) Button { store.send(.setAlert(.signOut)) } label: { HStack { - Text(String(localized: "settings_sign_out")) + Text(String(localized: "settings_sign_out", bundle: PresentationResources.bundle)) .foregroundStyle(.red) Spacer() if store.activeLoadingRow == .signOut { @@ -115,7 +116,7 @@ struct SettingsView: View { ProgressView() .tint(.red) } else { - Text(String(localized: "settings_delete_account")) + Text(String(localized: "settings_delete_account", bundle: PresentationResources.bundle)) .foregroundStyle(.red) .font(.headline) } @@ -124,7 +125,7 @@ struct SettingsView: View { Spacer() } } - .navigationTitle(String(localized: "nav_settings")) + .navigationTitle(String(localized: "nav_settings", bundle: PresentationResources.bundle)) .navigationBarTitleDisplayMode(.inline) .prominentAlert(store, state: \.alert, action: \.alert) .onAppear { diff --git a/Application/Presentation/ProfileTab/Sources/Settings/ThemeView.swift b/Application/Presentation/ProfileTab/Sources/Settings/ThemeView.swift index 73f9a996..9fb90be5 100644 --- a/Application/Presentation/ProfileTab/Sources/Settings/ThemeView.swift +++ b/Application/Presentation/ProfileTab/Sources/Settings/ThemeView.swift @@ -7,6 +7,7 @@ import SwiftUI import Core +import PresentationShared struct ThemeView: View { @Binding var theme: SystemTheme @@ -17,7 +18,7 @@ struct ThemeView: View { theme = .automatic }) { HStack { - Text(SystemTheme.automatic.localizedName) + Text(SystemTheme.automatic.localizedName(in: PresentationResources.bundle)) .foregroundStyle(Color.primary) Spacer() if theme == .automatic { @@ -29,7 +30,7 @@ struct ThemeView: View { theme = .light }) { HStack { - Text(SystemTheme.light.localizedName) + Text(SystemTheme.light.localizedName(in: PresentationResources.bundle)) .foregroundStyle(Color.primary) Spacer() if theme == .light { @@ -41,7 +42,7 @@ struct ThemeView: View { theme = .dark }) { HStack { - Text(SystemTheme.dark.localizedName) + Text(SystemTheme.dark.localizedName(in: PresentationResources.bundle)) .foregroundStyle(Color.primary) Spacer() if theme == .dark { @@ -53,7 +54,7 @@ struct ThemeView: View { .listStyle(.insetGrouped) .toolbar { ToolbarItem(placement: .principal) { - Text(String(localized: "nav_theme")) + Text(String(localized: "nav_theme", bundle: PresentationResources.bundle)) .bold() } } diff --git a/Application/Presentation/ProfileTab/Sources/Structure/Profile/ActivityKindItem.swift b/Application/Presentation/ProfileTab/Sources/Structure/Profile/ActivityKindItem.swift index eef45612..e6d0f58b 100644 --- a/Application/Presentation/ProfileTab/Sources/Structure/Profile/ActivityKindItem.swift +++ b/Application/Presentation/ProfileTab/Sources/Structure/Profile/ActivityKindItem.swift @@ -7,6 +7,7 @@ import SwiftUI import Core +import PresentationShared public struct ActivityKindItem: Identifiable, Hashable { private let activityKind: ActivityKind @@ -26,11 +27,11 @@ public struct ActivityKindItem: Identifiable, Hashable { public var title: String { switch activityKind { case .created: - return String(localized: "profile_activity_created") + return String(localized: "profile_activity_created", bundle: PresentationResources.bundle) case .completed: - return String(localized: "profile_activity_completed") + return String(localized: "profile_activity_completed", bundle: PresentationResources.bundle) case .deleted: - return String(localized: "profile_activity_deleted") + return String(localized: "profile_activity_deleted", bundle: PresentationResources.bundle) } } diff --git a/Application/Presentation/ProfileTab/Tests/Settings/AccountFeatureTests.swift b/Application/Presentation/ProfileTab/Tests/Settings/AccountFeatureTests.swift index a258fe4a..415b2a1d 100644 --- a/Application/Presentation/ProfileTab/Tests/Settings/AccountFeatureTests.swift +++ b/Application/Presentation/ProfileTab/Tests/Settings/AccountFeatureTests.swift @@ -153,8 +153,8 @@ struct AccountFeatureTests { } #expect(driver.alert == expectedAlert( - title: String(localized: "common_error_title"), - message: String(localized: "common_error_message") + title: String(localized: "common_error_title", bundle: PresentationResources.bundle), + message: String(localized: "common_error_message", bundle: PresentationResources.bundle) )) } @@ -163,28 +163,46 @@ struct AccountFeatureTests { let scenarios = [ AccountLinkFailureScenario( error: AuthError.linkEmailNotFound, - title: String(localized: "account_alert_email_unavailable_title"), - message: String(localized: "account_alert_email_unavailable_message") + title: String( + localized: "account_alert_email_unavailable_title", + bundle: PresentationResources.bundle + ), + message: String( + localized: "account_alert_email_unavailable_message", + bundle: PresentationResources.bundle + ) ), AccountLinkFailureScenario( error: AuthError.linkEmailMismatch, - title: String(localized: "account_alert_cannot_link_title"), - message: String(localized: "account_alert_cannot_link_message") + title: String(localized: "account_alert_cannot_link_title", bundle: PresentationResources.bundle), + message: String(localized: "account_alert_cannot_link_message", bundle: PresentationResources.bundle) ), AccountLinkFailureScenario( error: AuthError.linkCredentialAlreadyInUse, - title: String(localized: "account_alert_already_linked_title"), - message: String(localized: "account_alert_already_linked_message") + title: String( + localized: "account_alert_already_linked_title", + bundle: PresentationResources.bundle + ), + message: String( + localized: "account_alert_already_linked_message", + bundle: PresentationResources.bundle + ) ), AccountLinkFailureScenario( error: AuthError.githubEmailConflict, - title: String(localized: "account_alert_github_email_conflict_title"), - message: String(localized: "account_alert_github_email_conflict_message") + title: String( + localized: "account_alert_github_email_conflict_title", + bundle: PresentationResources.bundle + ), + message: String( + localized: "account_alert_github_email_conflict_message", + bundle: PresentationResources.bundle + ) ), AccountLinkFailureScenario( error: AccountTestError.failure, - title: String(localized: "common_error_title"), - message: String(localized: "common_error_message") + title: String(localized: "common_error_title", bundle: PresentationResources.bundle), + message: String(localized: "common_error_message", bundle: PresentationResources.bundle) ) ] @@ -234,8 +252,8 @@ struct AccountFeatureTests { } #expect(driver.alert == expectedAlert( - title: String(localized: "common_error_title"), - message: String(localized: "common_error_message") + title: String(localized: "common_error_title", bundle: PresentationResources.bundle), + message: String(localized: "common_error_message", bundle: PresentationResources.bundle) )) } @@ -415,7 +433,7 @@ private func expectedAlert( TextState(title) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { TextState(message) diff --git a/Application/Presentation/ProfileTab/Tests/Settings/PushNotificationSettingsFeatureTests.swift b/Application/Presentation/ProfileTab/Tests/Settings/PushNotificationSettingsFeatureTests.swift index 6e0e1f8e..257eb1db 100644 --- a/Application/Presentation/ProfileTab/Tests/Settings/PushNotificationSettingsFeatureTests.swift +++ b/Application/Presentation/ProfileTab/Tests/Settings/PushNotificationSettingsFeatureTests.swift @@ -483,12 +483,12 @@ private func makeDate( private func expectedPushNotificationSettingsErrorAlert() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } diff --git a/Application/Presentation/ProfileTab/Tests/Settings/SettingsFeatureTests.swift b/Application/Presentation/ProfileTab/Tests/Settings/SettingsFeatureTests.swift index 96236428..3662e8a0 100644 --- a/Application/Presentation/ProfileTab/Tests/Settings/SettingsFeatureTests.swift +++ b/Application/Presentation/ProfileTab/Tests/Settings/SettingsFeatureTests.swift @@ -103,8 +103,14 @@ struct SettingsFeatureTests { #expect(adapter.showAlert) #expect(adapter.alertType == .removeCache) - #expect(adapter.alertTitle == String(localized: "settings_alert_clear_temp_title")) - #expect(adapter.alertMessage == String(localized: "settings_alert_clear_temp_message")) + #expect( + adapter.alertTitle + == String(localized: "settings_alert_clear_temp_title", bundle: PresentationResources.bundle) + ) + #expect( + adapter.alertMessage + == String(localized: "settings_alert_clear_temp_message", bundle: PresentationResources.bundle) + ) } @Test("캐시 삭제 확인에 성공하면 캐시를 비우고 크기를 다시 조회한다") @@ -134,8 +140,8 @@ struct SettingsFeatureTests { await adapter.confirmRemoveCache() #expect(adapter.showAlert) - #expect(adapter.alertTitle == String(localized: "common_error_title")) - #expect(adapter.alertMessage == String(localized: "common_error_message")) + #expect(adapter.alertTitle == String(localized: "common_error_title", bundle: PresentationResources.bundle)) + #expect(adapter.alertMessage == String(localized: "common_error_message", bundle: PresentationResources.bundle)) #expect(adapter.activeLoadingRow == nil) } @@ -183,7 +189,7 @@ struct SettingsFeatureTests { #expect(deleteSpy.executeCallCount == 1) #expect(adapter.showAlert) - #expect(adapter.alertTitle == String(localized: "common_error_title")) + #expect(adapter.alertTitle == String(localized: "common_error_title", bundle: PresentationResources.bundle)) #expect(adapter.activeLoadingRow == nil) } } @@ -341,52 +347,52 @@ private func expectedSettingsAlert( switch type { case .signOut: return AlertState { - TextState(String(localized: "settings_alert_sign_out_title")) + TextState(String(localized: "settings_alert_sign_out_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_cancel")) + TextState(String(localized: "common_cancel", bundle: PresentationResources.bundle)) } ButtonState(role: .destructive, action: .tapSignOutButton) { - TextState(String(localized: "common_confirm")) + TextState(String(localized: "common_confirm", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "settings_alert_sign_out_message")) + TextState(String(localized: "settings_alert_sign_out_message", bundle: PresentationResources.bundle)) } case .deleteAuth: return AlertState { - TextState(String(localized: "settings_alert_delete_account_title")) + TextState(String(localized: "settings_alert_delete_account_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_cancel")) + TextState(String(localized: "common_cancel", bundle: PresentationResources.bundle)) } ButtonState(role: .destructive, action: .tapDeleteAuthButton) { - TextState(String(localized: "settings_delete_account_action")) + TextState(String(localized: "settings_delete_account_action", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "settings_alert_delete_account_message")) + TextState(String(localized: "settings_alert_delete_account_message", bundle: PresentationResources.bundle)) } case .error: return AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } case .removeCache: return AlertState { - TextState(String(localized: "settings_alert_clear_temp_title")) + TextState(String(localized: "settings_alert_clear_temp_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_cancel")) + TextState(String(localized: "common_cancel", bundle: PresentationResources.bundle)) } ButtonState(role: .destructive, action: .confirmRemoveCache) { - TextState(String(localized: "common_confirm")) + TextState(String(localized: "common_confirm", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "settings_alert_clear_temp_message")) + TextState(String(localized: "settings_alert_clear_temp_message", bundle: PresentationResources.bundle)) } } } diff --git a/Application/Presentation/Project.swift b/Application/Presentation/Project.swift index 3025ed98..068feeda 100644 --- a/Application/Presentation/Project.swift +++ b/Application/Presentation/Project.swift @@ -34,7 +34,10 @@ let project = Project( bundleId: "com.opfic.DevLog.PresentationShared", infoPlist: .file(path: frameworkInfoPlistPath), sources: ["PresentationShared/Sources/**/*.swift"], - resources: ["PresentationShared/Resources/Assets.xcassets"], + resources: [ + "PresentationShared/Resources/Assets.xcassets", + "PresentationShared/Resources/Localizable.xcstrings", + ], scripts: [ DevLogScripts.swiftLint( sourcePath: "PresentationShared/Sources", diff --git a/Application/Presentation/TodayTab/Sources/Today/TodayFeature.swift b/Application/Presentation/TodayTab/Sources/Today/TodayFeature.swift index ad15644d..f0a9d024 100644 --- a/Application/Presentation/TodayTab/Sources/Today/TodayFeature.swift +++ b/Application/Presentation/TodayTab/Sources/Today/TodayFeature.swift @@ -73,49 +73,49 @@ struct TodayFeature { return TodayFeature.makeSection( category: .focused, - title: String(localized: "today_section_focused"), + title: String(localized: "today_section_focused", bundle: PresentationResources.bundle), items: items.focused ) + TodayFeature.makeSection( category: .overdue, - title: String(localized: "today_section_overdue"), + title: String(localized: "today_section_overdue", bundle: PresentationResources.bundle), items: items.overdue ) + TodayFeature.makeSection( category: .dueSoon, title: String.localizedStringWithFormat( - String(localized: "today_section_due_soon_format"), + String(localized: "today_section_due_soon_format", bundle: PresentationResources.bundle), Int64(TodayFeature.upcomingWindowDays) ), items: items.dueSoon ) + TodayFeature.makeSection( category: .later, - title: String(localized: "today_section_later"), + title: String(localized: "today_section_later", bundle: PresentationResources.bundle), items: items.later ) + TodayFeature.makeSection( category: .unscheduled, - title: String(localized: "today_section_unscheduled"), + title: String(localized: "today_section_unscheduled", bundle: PresentationResources.bundle), items: items.unscheduled ) case .focused: return TodayFeature.makeSection( category: .focused, - title: String(localized: "today_section_focused"), + title: String(localized: "today_section_focused", bundle: PresentationResources.bundle), items: items.focused ) case .overdue: return TodayFeature.makeSection( category: .overdue, - title: String(localized: "today_section_overdue"), + title: String(localized: "today_section_overdue", bundle: PresentationResources.bundle), items: items.overdue ) case .dueSoon: return TodayFeature.makeSection( category: .dueSoon, title: String.localizedStringWithFormat( - String(localized: "today_section_due_soon_format"), + String(localized: "today_section_due_soon_format", bundle: PresentationResources.bundle), Int64(TodayFeature.upcomingWindowDays) ), items: items.dueSoon @@ -350,13 +350,13 @@ private extension TodayFeature { static func alertState() -> AlertState { AlertState { - TextState(String(localized: "common_error_title")) + TextState(String(localized: "common_error_title", bundle: PresentationResources.bundle)) } actions: { ButtonState(role: .cancel) { - TextState(String(localized: "common_close")) + TextState(String(localized: "common_close", bundle: PresentationResources.bundle)) } } message: { - TextState(String(localized: "common_error_message")) + TextState(String(localized: "common_error_message", bundle: PresentationResources.bundle)) } } } diff --git a/Application/Presentation/TodayTab/Sources/Today/TodayView.swift b/Application/Presentation/TodayTab/Sources/Today/TodayView.swift index c5fe1126..fe6fecd7 100644 --- a/Application/Presentation/TodayTab/Sources/Today/TodayView.swift +++ b/Application/Presentation/TodayTab/Sources/Today/TodayView.swift @@ -36,7 +36,7 @@ public struct TodayView: View { } } .listStyle(.insetGrouped) - .navigationTitle(String(localized: "nav_today")) + .navigationTitle(String(localized: "nav_today", bundle: PresentationResources.bundle)) .toolbar { toolbarContent } .background(NavigationBarConfigurator()) .refreshable { await store.send(.refresh).finish() } @@ -83,7 +83,7 @@ public struct TodayView: View { ToolbarItem(placement: .topBarTrailing) { Menu { Picker( - String(localized: "today_due_visibility_label"), + String(localized: "today_due_visibility_label", bundle: PresentationResources.bundle), selection: $store.displayOptions.dueDateVisibility ) { ForEach(TodayDisplayOptions.DueDateVisibility.allCases, id: \.self) { option in @@ -92,13 +92,13 @@ public struct TodayView: View { } Toggle( - String(localized: "today_pinned_only"), + String(localized: "today_pinned_only", bundle: PresentationResources.bundle), isOn: $store.displayOptions.isFocusedOnly ) .tint(.orange) if store.displayOptions.focusVisibility == .focusedOnly { - Text(String(localized: "today_pinned_only_description")) + Text(String(localized: "today_pinned_only_description", bundle: PresentationResources.bundle)) .font(.caption) } } label: { @@ -141,7 +141,13 @@ public struct TodayView: View { Button { store.send(.completeTodo(item)) } label: { - Label(String(localized: "today_complete_action"), systemImage: "checkmark") + Label( + String( + localized: "today_complete_action", + bundle: PresentationResources.bundle + ), + systemImage: "checkmark" + ) } .tint(.green) } @@ -179,28 +185,28 @@ public struct TodayView: View { case .all: if store.todos.isEmpty { return EmptyStateContent( - title: String(localized: "today_empty_all_title"), - message: String(localized: "today_empty_all_message") + title: String(localized: "today_empty_all_title", bundle: PresentationResources.bundle), + message: String(localized: "today_empty_all_message", bundle: PresentationResources.bundle) ) } return EmptyStateContent( - title: String(localized: "today_empty_filtered_title"), - message: String(localized: "today_empty_filtered_message") + title: String(localized: "today_empty_filtered_title", bundle: PresentationResources.bundle), + message: String(localized: "today_empty_filtered_message", bundle: PresentationResources.bundle) ) case .focused: return EmptyStateContent( - title: String(localized: "today_empty_focused_title"), - message: String(localized: "today_empty_focused_message") + title: String(localized: "today_empty_focused_title", bundle: PresentationResources.bundle), + message: String(localized: "today_empty_focused_message", bundle: PresentationResources.bundle) ) case .overdue: return EmptyStateContent( - title: String(localized: "today_empty_overdue_title"), - message: String(localized: "today_empty_overdue_message") + title: String(localized: "today_empty_overdue_title", bundle: PresentationResources.bundle), + message: String(localized: "today_empty_overdue_message", bundle: PresentationResources.bundle) ) case .dueSoon: return EmptyStateContent( - title: String(localized: "today_empty_due_soon_title"), - message: String(localized: "today_empty_due_soon_message") + title: String(localized: "today_empty_due_soon_title", bundle: PresentationResources.bundle), + message: String(localized: "today_empty_due_soon_message", bundle: PresentationResources.bundle) ) } } @@ -215,11 +221,11 @@ private extension TodayDisplayOptions.DueDateVisibility { var title: String { switch self { case .all: - return String(localized: "today_due_visibility_all") + return String(localized: "today_due_visibility_all", bundle: PresentationResources.bundle) case .withDueDateOnly: - return String(localized: "today_due_visibility_with_due") + return String(localized: "today_due_visibility_with_due", bundle: PresentationResources.bundle) case .withoutDueDateOnly: - return String(localized: "today_due_visibility_without_due") + return String(localized: "today_due_visibility_without_due", bundle: PresentationResources.bundle) } } } @@ -228,13 +234,13 @@ private extension TodayFeature.SectionScope { var title: String { switch self { case .all: - return String(localized: "today_summary_all") + return String(localized: "today_summary_all", bundle: PresentationResources.bundle) case .focused: - return String(localized: "today_summary_focused") + return String(localized: "today_summary_focused", bundle: PresentationResources.bundle) case .overdue: - return String(localized: "today_summary_overdue") + return String(localized: "today_summary_overdue", bundle: PresentationResources.bundle) case .dueSoon: - return String(localized: "today_summary_due_soon") + return String(localized: "today_summary_due_soon", bundle: PresentationResources.bundle) } } @@ -334,7 +340,7 @@ private struct TodayTodoRow: View { if dueDay < today { return DueDateBadge( - text: String(localized: "today_due_overdue"), + text: String(localized: "today_due_overdue", bundle: PresentationResources.bundle), textColor: .red, backgroundColor: Color.red.opacity(0.12) ) From 98595cc35d678669fa504210b122f50916df9291 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 10 Sep 2026 22:45:48 +0900 Subject: [PATCH 3/4] =?UTF-8?q?chore:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=EB=B3=84=20=EC=83=89=EC=83=81=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Review.colorset/Contents.json | 38 ------------------- .../ReviewContainer.colorset/Contents.json | 38 ------------------- .../Sources/Common/ColorAsset.swift | 2 - 3 files changed, 78 deletions(-) delete mode 100644 Application/Presentation/PresentationShared/Resources/Assets.xcassets/Review.colorset/Contents.json delete mode 100644 Application/Presentation/PresentationShared/Resources/Assets.xcassets/ReviewContainer.colorset/Contents.json diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Review.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Review.colorset/Contents.json deleted file mode 100644 index eae205e6..00000000 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/Review.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors": [ - { - "color": { - "color-space": "srgb", - "components": { - "alpha": "1.000", - "blue": "0xAD", - "green": "0x44", - "red": "0x8E" - } - }, - "idiom": "universal" - }, - { - "appearances": [ - { - "appearance": "luminosity", - "value": "dark" - } - ], - "color": { - "color-space": "srgb", - "components": { - "alpha": "1.000", - "blue": "0xFC", - "green": "0x84", - "red": "0xC0" - } - }, - "idiom": "universal" - } - ], - "info": { - "author": "xcode", - "version": 1 - } -} diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/ReviewContainer.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/ReviewContainer.colorset/Contents.json deleted file mode 100644 index 6bb5e503..00000000 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/ReviewContainer.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors": [ - { - "color": { - "color-space": "srgb", - "components": { - "alpha": "1.000", - "blue": "0xFC", - "green": "0xED", - "red": "0xF3" - } - }, - "idiom": "universal" - }, - { - "appearances": [ - { - "appearance": "luminosity", - "value": "dark" - } - ], - "color": { - "color-space": "srgb", - "components": { - "alpha": "1.000", - "blue": "0x54", - "green": "0x2D", - "red": "0x3A" - } - }, - "idiom": "universal" - } - ], - "info": { - "author": "xcode", - "version": 1 - } -} diff --git a/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift b/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift index a6b15a0d..e92e113f 100644 --- a/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift +++ b/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift @@ -17,8 +17,6 @@ public enum ColorAsset { case infoContainer case onPrimaryContainer case primaryContainer - case review - case reviewContainer case success case successContainer case surface From d3db987e6ef6371b8013898d62a62ef4685e09e1 Mon Sep 17 00:00:00 2001 From: opficdev Date: Fri, 11 Sep 2026 00:04:27 +0900 Subject: [PATCH 4/4] =?UTF-8?q?chore:=20=EB=8B=A4=ED=81=AC=20=EC=83=89?= =?UTF-8?q?=EC=83=81=20=EB=B3=B4=EC=A0=95=20=EB=B0=8F=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20=EA=B8=80=EC=9E=90=EC=83=89=20=EC=96=B4=EC=85=8B=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/Component/LoginButton.swift | 2 +- .../SuccessContainer.colorset/Contents.json | 6 +-- .../TextPrimary.colorset/Contents.json | 38 ------------------- .../Sources/Common/ColorAsset.swift | 1 - 4 files changed, 4 insertions(+), 43 deletions(-) delete mode 100644 Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextPrimary.colorset/Contents.json diff --git a/Application/Presentation/Entry/Sources/Common/Component/LoginButton.swift b/Application/Presentation/Entry/Sources/Common/Component/LoginButton.swift index 2f54e825..c8ad9c5f 100644 --- a/Application/Presentation/Entry/Sources/Common/Component/LoginButton.swift +++ b/Application/Presentation/Entry/Sources/Common/Component/LoginButton.swift @@ -36,7 +36,7 @@ struct LoginButton: View { .tint(Color(asset: .accentColor)) } else { Text(text) - .foregroundStyle(Color(asset: .textPrimary)) + .foregroundStyle(.primary) } } .font(.system(.body)) diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/SuccessContainer.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/SuccessContainer.colorset/Contents.json index 188a2bf5..e9abb0a8 100644 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/SuccessContainer.colorset/Contents.json +++ b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/SuccessContainer.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space": "srgb", "components": { "alpha": "1.000", - "blue": "0x28", - "green": "0x31", - "red": "0x13" + "blue": "0x2B", + "green": "0x35", + "red": "0x14" } }, "idiom": "universal" diff --git a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextPrimary.colorset/Contents.json b/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextPrimary.colorset/Contents.json deleted file mode 100644 index 0fdb83b4..00000000 --- a/Application/Presentation/PresentationShared/Resources/Assets.xcassets/TextPrimary.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors": [ - { - "color": { - "color-space": "srgb", - "components": { - "alpha": "1.000", - "blue": "0x28", - "green": "0x1F", - "red": "0x19" - } - }, - "idiom": "universal" - }, - { - "appearances": [ - { - "appearance": "luminosity", - "value": "dark" - } - ], - "color": { - "color-space": "srgb", - "components": { - "alpha": "1.000", - "blue": "0xFC", - "green": "0xFA", - "red": "0xF8" - } - }, - "idiom": "universal" - } - ], - "info": { - "author": "xcode", - "version": 1 - } -} diff --git a/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift b/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift index e92e113f..b0b2be49 100644 --- a/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift +++ b/Application/Presentation/PresentationShared/Sources/Common/ColorAsset.swift @@ -21,7 +21,6 @@ public enum ColorAsset { case successContainer case surface case surfaceSecondary - case textPrimary case textSecondary case textTertiary case warning