From 88c910c6edf02a85ab2ab30aa9ac707e9d027f39 Mon Sep 17 00:00:00 2001 From: Daniel Saidi Date: Tue, 30 Apr 2024 11:54:28 +0200 Subject: [PATCH] Add button types and localize components --- Package.swift | 4 +- RELEASE_NOTES.md | 13 + Sources/SwiftUIKit/Buttons/Button+Init.swift | 34 ++ .../SwiftUIKit/Buttons/Button+Standard.swift | 126 ++++++ .../SwiftUIKit/Extensions/Label+Init.swift | 13 +- .../SwiftUIKit/Extensions/View+Label.swift | 4 +- .../SwiftUIKit/Extensions/View+Prefers.swift | 6 +- Sources/SwiftUIKit/Lists/ListActionRow.swift | 68 +-- .../SwiftUIKit/Lists/ListButtonGroup.swift | 10 +- .../SwiftUIKit/Lists/ListButtonStyle.swift | 2 +- Sources/SwiftUIKit/Lists/ListCard.swift | 4 +- Sources/SwiftUIKit/Lists/ListDragHandle.swift | 2 +- Sources/SwiftUIKit/Lists/ListHeader.swift | 22 +- .../SwiftUIKit/Lists/ListSectionTitle.swift | 14 +- Sources/SwiftUIKit/Lists/ListSelectItem.swift | 9 +- .../SwiftUIKit/Lists/ListShelfSection.swift | 26 +- Sources/SwiftUIKit/Lists/ListSubtitle.swift | 15 +- .../Loading/DotLoadingAnimationText.swift | 14 +- .../Navigation/NavigationButton.swift | 15 +- .../Navigation/NavigationLinkArrow.swift | 10 +- .../SwiftUIKit/Pages/PageView+Previews.swift | 2 +- .../ProcessInfo+SwiftPreviewInspector.swift | 4 +- .../Progress/CircularProgressBar.swift | 2 +- .../Resources/Localizable.xcstrings | 414 ++++++++++++++++++ .../Text/TextFieldClearButton.swift | 8 +- .../SwiftUIKit/Views/FetchedDataView.swift | 4 +- .../_Deprecated/List+Deprecated.swift | 2 +- 27 files changed, 733 insertions(+), 114 deletions(-) create mode 100644 Sources/SwiftUIKit/Buttons/Button+Init.swift create mode 100644 Sources/SwiftUIKit/Buttons/Button+Standard.swift create mode 100644 Sources/SwiftUIKit/Resources/Localizable.xcstrings diff --git a/Package.swift b/Package.swift index ca5cdae65d..4fdbc1325a 100644 --- a/Package.swift +++ b/Package.swift @@ -4,6 +4,7 @@ import PackageDescription let package = Package( name: "SwiftUIKit", + defaultLocalization: "en", platforms: [ .iOS(.v15), .tvOS(.v15), @@ -21,7 +22,8 @@ let package = Package( targets: [ .target( name: "SwiftUIKit", - dependencies: [] + dependencies: [], + resources: [.process("Resources")] ), .testTarget( name: "SwiftUIKitTests", diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d19fe793f0..b445ee1f14 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,6 +4,19 @@ SwiftUIKit makes its best effort to honor semver, but breaking changes can occur +## 4.3 + +This version adds localization support, and makes more types use `LocalizedStringKey` instead of `String`. + +This version also adds standard button types, which makes it easier to create standard button types. + +### ✨ New features + +* `Button+Init` adds a new button initializer. +* `Button+Standard` adds new standard button types. + + + ## 4.2.3 ### 💡 Behavior changes diff --git a/Sources/SwiftUIKit/Buttons/Button+Init.swift b/Sources/SwiftUIKit/Buttons/Button+Init.swift new file mode 100644 index 0000000000..f75eb62f3c --- /dev/null +++ b/Sources/SwiftUIKit/Buttons/Button+Init.swift @@ -0,0 +1,34 @@ +// +// Button+Init.swift +// SwiftUIKit +// +// Created by Daniel Saidi on 2024-04-30. +// Copyright © 2024 Daniel Saidi. All rights reserved. +// + +import SwiftUI + +public extension Button where Label == SwiftUI.Label { + + /// This initializer lets you use buttons with less code. + init( + _ text: LocalizedStringKey, + _ icon: Image, + _ bundle: Bundle = .main, + action: @escaping () -> Void + ) { + self.init(action: action) { + Label( + title: { Text(text, bundle: bundle) }, + icon: { icon } + ) + } + } +} + +#Preview { + + Button("Preview.Button", .symbol("checkmark"), .module) { + print("Tapped!") + } +} diff --git a/Sources/SwiftUIKit/Buttons/Button+Standard.swift b/Sources/SwiftUIKit/Buttons/Button+Standard.swift new file mode 100644 index 0000000000..c5e6a05b8f --- /dev/null +++ b/Sources/SwiftUIKit/Buttons/Button+Standard.swift @@ -0,0 +1,126 @@ +// +// Button+Standard.swift +// SwiftUIKit +// +// Created by Daniel Saidi on 2024-04-30. +// Copyright © 2024 Daniel Saidi. All rights reserved. +// + +import SwiftUI + +public extension Button { + + /// This is a standard add button. + init( + _ type: StandardType, + _ title: LocalizedStringKey? = nil, + _ icon: Image? = nil, + bundle: Bundle? = nil, + action: @escaping () -> Void + ) where Label == SwiftUI.Label { + self.init(role: type.role, action: action) { + Label( + title: { Text(title ?? type.title, bundle: title == nil ? .module : bundle) }, + icon: { icon ?? type.image } + ) + } + } + + /// This enum defines standard button types and provides + /// standard localized texts and icons. + enum StandardType: String, CaseIterable, Identifiable { + case add, addFavorite, addToFavorites, + cancel, call, copy, + delete, deselect, done, + edit, email, + ok, + paste, + removeFavorite, removeFromFavorites, + select, share + } +} + +public extension Button.StandardType { + + var id: String{ rawValue } + + var image: Image? { + guard let imageName else { return nil } + return .symbol(imageName) + } + + var imageName: String? { + switch self { + case .add: "plus" + case .addFavorite: "star.circle" + case .addToFavorites: "star.circle" + case .cancel: "xmark" + case .call: "phone" + case .copy: "doc.on.doc" + case .delete: "trash" + case .deselect: "checkmark.circle.fill" + case .done: "checkmark" + case .edit: "pencil" + case .email: "envelope" + case .ok: "checkmark" + case .paste: "clipboard" + case .removeFavorite: "star.circle.fill" + case .removeFromFavorites: "star.circle.fill" + case .select: "checkmark.circle" + case .share: "square.and.arrow.up" + } + } + + var role: ButtonRole? { + switch self { + case .cancel: .cancel + case .delete: .destructive + default: nil + } + } + + var title: LocalizedStringKey { + switch self { + case .add: "Button.Add" + case .addFavorite: "Button.AddFavorite" + case .addToFavorites: "Button.AddToFavorites" + case .call: "Button.Call" + case .cancel: "Button.Cancel" + case .copy: "Button.Copy" + case .deselect: "Button.Deselect" + case .edit: "Button.Edit" + case .email: "Button.Email" + case .delete: "Button.Delete" + case .done: "Button.Done" + case .ok: "Button.OK" + case .paste: "Button.Paste" + case .removeFavorite: "Button.RemoveFavorite" + case .removeFromFavorites: "Button.RemoveFromFavorites" + case .select: "Button.Select" + case .share: "Button.Share" + } + } +} + +#Preview { + + @ViewBuilder + func buttons() -> some View { + Section { + ForEach(Button.StandardType.allCases) { + Button($0) {} + } + } + } + + return List { + buttons() + buttons().labelStyle(.titleOnly) + buttons().labelStyle(.iconOnly) + } + .toolbar { + ToolbarItemGroup { + buttons() + } + } +} diff --git a/Sources/SwiftUIKit/Extensions/Label+Init.swift b/Sources/SwiftUIKit/Extensions/Label+Init.swift index 95a580bac9..a2fac76b04 100644 --- a/Sources/SwiftUIKit/Extensions/Label+Init.swift +++ b/Sources/SwiftUIKit/Extensions/Label+Init.swift @@ -20,11 +20,20 @@ public extension Label where Icon == Image, Title == Text { } /// Create a label with a string and a plain image icon. - init(_ text: LocalizedStringKey, _ image: Image) { + init( + _ text: LocalizedStringKey, + _ image: Image, + _ bundle: Bundle? = nil + ) { self.init { - Text(text) + Text(text, bundle: bundle) } icon: { image } } } + +#Preview { + + Label("Preview.Label", .symbol("checkmark"), .module) +} diff --git a/Sources/SwiftUIKit/Extensions/View+Label.swift b/Sources/SwiftUIKit/Extensions/View+Label.swift index 963568577d..e15ce2f89f 100644 --- a/Sources/SwiftUIKit/Extensions/View+Label.swift +++ b/Sources/SwiftUIKit/Extensions/View+Label.swift @@ -42,9 +42,9 @@ public extension View { VStack { Color.red - .label("Red") + .label("Preview.Label", bundle: .module) Image.symbol("checkmark") - .label("Test") + .label("Preview.Label", bundle: .module) } .frame(width: 200, height: 100) } diff --git a/Sources/SwiftUIKit/Extensions/View+Prefers.swift b/Sources/SwiftUIKit/Extensions/View+Prefers.swift index 565fe74ba5..99d83b9e5b 100644 --- a/Sources/SwiftUIKit/Extensions/View+Prefers.swift +++ b/Sources/SwiftUIKit/Extensions/View+Prefers.swift @@ -39,9 +39,9 @@ public extension View { VStack { #if os(iOS) || os(macOS) - Menu("Test") { - Button("1") {} - Button("2") {} + Menu("Preview.Menu") { + Button("Preview.Button.\(1)") {} + Button("Preview.Button.\(2)") {} } .prefersMenuOrderFixed() #endif diff --git a/Sources/SwiftUIKit/Lists/ListActionRow.swift b/Sources/SwiftUIKit/Lists/ListActionRow.swift index 84ca84d77b..d437e823f1 100644 --- a/Sources/SwiftUIKit/Lists/ListActionRow.swift +++ b/Sources/SwiftUIKit/Lists/ListActionRow.swift @@ -23,49 +23,32 @@ public struct ListActionRow: View { /// - title: The row title. /// - text: The row text. /// - action: The ``ListAction`` to use. - /// - hideIfEmpty: Whether or not to hide the view if the text is empty, by default `false`. /// - trailingView: An optional trailing view to apply to the view. public init( - title: String, - text: String, - action: ListAction?, - hideIfEmpty: Bool = false + title: LocalizedStringKey, + text: LocalizedStringKey, + bundle: Bundle? = nil, + action: ListAction? ) { self.title = title self.text = text + self.bundle = bundle self.action = action - self.hideIfEmpty = hideIfEmpty } - private let text: String - private let title: String - private let hideIfEmpty: Bool + private let title: LocalizedStringKey + private let text: LocalizedStringKey + private let bundle: Bundle? private let action: ListAction? public var body: some View { - if hasEmptyText && hideIfEmpty { - EmptyView() - } else { - stack - } - } -} - -private extension ListActionRow { - - var hasEmptyText: Bool { - text.trimmingCharacters(in: .whitespaces) - .isEmpty - } - - var stack: some View { HStack(spacing: 10) { VStack(alignment: .leading, spacing: 5) { - Text(title) + Text(title, bundle: bundle) .lineLimit(1) .font(.footnote) .foregroundColor(.secondary) - Text(text) + Text(text, bundle: bundle) } if let action { @@ -73,7 +56,6 @@ private extension ListActionRow { action.button } } - .padding(.vertical, 3) } } @@ -81,36 +63,32 @@ private extension ListActionRow { List { ListActionRow( - title: "Title 1", - text: "Text 1", + title: "Preview.Title.\(1)", + text: "Preview.Text.\(1)", + bundle: .module, action: .call(phoneNumber: "1234") ) ListActionRow( - title: "Title 2", - text: "Text 2", + title: "Preview.Title.\(2)", + text: "Preview.Text.\(2)", + bundle: .module, action: .copy("") ) .buttonStyle(.borderedProminent) ListActionRow( - title: "Title 3", - text: "Long\nmultuline\ntext that could have been entered in a text editor.", + title: "Preview.Title.\(3)", + text: "Preview.Text.Long", + bundle: .module, action: .email(address: "") ) ListActionRow( - title: "Title 4", - text: "", - action: nil, - hideIfEmpty: true - ) - - ListActionRow( - title: "Title 5", - text: "", - action: nil, - hideIfEmpty: false + title: "Preview.Title.\(4)", + text: "Preview.Text.\(4)", + bundle: .module, + action: nil ) } } diff --git a/Sources/SwiftUIKit/Lists/ListButtonGroup.swift b/Sources/SwiftUIKit/Lists/ListButtonGroup.swift index c5fe55cb44..d42ae9427a 100644 --- a/Sources/SwiftUIKit/Lists/ListButtonGroup.swift +++ b/Sources/SwiftUIKit/Lists/ListButtonGroup.swift @@ -53,7 +53,7 @@ public struct ListButtonGroup: View { ListButtonGroup { HStack { - "Report Bug".previewButton(.bug) + "Bug".previewButton(.bug) "Camera".previewButton(.camera).disabled(true) "Photos".previewButton(.camera).opacity(0.5) "Feedback".previewButton(.feedback) @@ -61,10 +61,10 @@ public struct ListButtonGroup: View { } Section { - Text("Row") - Text("Row") - Text("Row") - Text("Row") + Text("Preview.Row") + Text("Preview.Row") + Text("Preview.Row") + Text("Preview.Row") } } } diff --git a/Sources/SwiftUIKit/Lists/ListButtonStyle.swift b/Sources/SwiftUIKit/Lists/ListButtonStyle.swift index 4cc6ba6229..5aa7c121ab 100644 --- a/Sources/SwiftUIKit/Lists/ListButtonStyle.swift +++ b/Sources/SwiftUIKit/Lists/ListButtonStyle.swift @@ -60,7 +60,7 @@ public extension ButtonStyle where Self == ListButtonStyle { var body: some View { List { ForEach(0...100, id: \.self) { index in - Button("Button \(index)") { + Button("Preview.Button.\(index)") { overlayText = "\(index) tapped!" } .buttonStyle(index == 0 ? .list : .list(pressedOpacity: 0.1)) diff --git a/Sources/SwiftUIKit/Lists/ListCard.swift b/Sources/SwiftUIKit/Lists/ListCard.swift index 506720585e..7c46c7d0cd 100644 --- a/Sources/SwiftUIKit/Lists/ListCard.swift +++ b/Sources/SwiftUIKit/Lists/ListCard.swift @@ -97,9 +97,7 @@ public extension ViewShadowStyle { ListCard { Color.red.frame(width: 200, height: 200) } contextMenu: { - Button {} label: { - Text("Press me") - } + Button("Preview.Button") {} } } } diff --git a/Sources/SwiftUIKit/Lists/ListDragHandle.swift b/Sources/SwiftUIKit/Lists/ListDragHandle.swift index dcb6373bbe..e0547247f9 100644 --- a/Sources/SwiftUIKit/Lists/ListDragHandle.swift +++ b/Sources/SwiftUIKit/Lists/ListDragHandle.swift @@ -50,7 +50,7 @@ private extension ListDragHandle { ForEach(1...10, id: \.self) { item in HStack { Label { - Text("Title \(item)") + Text("Preview.Item.\(item)", bundle: .module) } icon: { Color.red } diff --git a/Sources/SwiftUIKit/Lists/ListHeader.swift b/Sources/SwiftUIKit/Lists/ListHeader.swift index ac59f04b03..9cb0de2bc8 100644 --- a/Sources/SwiftUIKit/Lists/ListHeader.swift +++ b/Sources/SwiftUIKit/Lists/ListHeader.swift @@ -84,25 +84,29 @@ public extension Image { #Preview { - VStack { + func item() -> some View { + Text("Preview.Item", bundle: .module) + } + + return VStack { List { ListHeader { Color.red.frame(square: 150) } Section { - Text("Item") - Text("Item") - Text("Item") - Text("Item") + item() + item() + item() + item() } } List { Image(systemName: "face.smiling").listHeader(height: 75) Section { - Text("Item") - Text("Item") - Text("Item") - Text("Item") + item() + item() + item() + item() } } } diff --git a/Sources/SwiftUIKit/Lists/ListSectionTitle.swift b/Sources/SwiftUIKit/Lists/ListSectionTitle.swift index 5e949bd0ad..1ce4f49afb 100644 --- a/Sources/SwiftUIKit/Lists/ListSectionTitle.swift +++ b/Sources/SwiftUIKit/Lists/ListSectionTitle.swift @@ -18,18 +18,22 @@ import SwiftUI public struct ListSectionTitle: View { public init( - _ text: String, + _ text: LocalizedStringKey, + bundle: Bundle? = nil, withInsets: Bool = false ) { self.text = text + self.bundle = bundle self.applyInsets = withInsets } - private let text: String + private let text: LocalizedStringKey + private let bundle: Bundle? private let applyInsets: Bool public var body: some View { - Text(text.uppercased()) + Text(text, bundle: bundle) + .textCase(.uppercase) .foregroundColor(.secondary) .font(.footnote) .withGroupedListSectionHeaderInsets(if: applyInsets) @@ -54,8 +58,8 @@ private extension View { #Preview { List { - Section(header: Text("Foo bar")) { - ListSectionTitle("Foo bar") + Section(header: Text("Preview.SectionTitle", bundle: .module)) { + ListSectionTitle("Preview.SectionTitle", bundle: .module) } } } diff --git a/Sources/SwiftUIKit/Lists/ListSelectItem.swift b/Sources/SwiftUIKit/Lists/ListSelectItem.swift index 2355ce1597..246e3b216f 100644 --- a/Sources/SwiftUIKit/Lists/ListSelectItem.swift +++ b/Sources/SwiftUIKit/Lists/ListSelectItem.swift @@ -80,14 +80,19 @@ public struct ListSelectItem: View { ForEach(0...10, id: \.self) { index in Group { ListSelectItem(isSelected: index == selection) { - Label("Item \(index)", systemImage: "\(index).circle") + Image.symbol("\(index).circle") + .label( + "Preview.Item.\(index)", + bundle: .module + ) } ListSelectItem( isSelected: index == selection, selectIndicator: Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) ) { - Label("Item \(index)", systemImage: "\(index).circle") + Image.symbol("\(index).circle") + .label("Preview.Item.\(index)", bundle: .module) } } #if os(iOS) || os(macOS) || os(visionOS) diff --git a/Sources/SwiftUIKit/Lists/ListShelfSection.swift b/Sources/SwiftUIKit/Lists/ListShelfSection.swift index b2e934bd5f..15becd6a38 100644 --- a/Sources/SwiftUIKit/Lists/ListShelfSection.swift +++ b/Sources/SwiftUIKit/Lists/ListShelfSection.swift @@ -61,35 +61,41 @@ public struct ListShelfSection: View { struct PreviewSection: View { + func button(_ index: Int) -> some View { + Button(action: {}) { + Text("Preview.Button.\(index)", bundle: .module) + } + } + var body: some View { ListShelfSection { - ListSectionTitle("Section") + ListSectionTitle("Preview.SectionTitle", bundle: .module) } content: { Group { Button {} label: { ListCard { Color.red } contextMenu: { - Button("1") {} - Button("2") {} - Button("3") {} + button(1) + button(2) + button(3) } } ListCard { Color.green } contextMenu: { - Button("1") {} - Button("2") {} - Button("3") {} + button(1) + button(2) + button(3) } ListCard { Color.blue } contextMenu: { - Button("1") {} - Button("2") {} - Button("3") {} + button(1) + button(2) + button(3) } } .buttonStyle(.listCard) diff --git a/Sources/SwiftUIKit/Lists/ListSubtitle.swift b/Sources/SwiftUIKit/Lists/ListSubtitle.swift index 85989ecf66..bd2b1ce5af 100644 --- a/Sources/SwiftUIKit/Lists/ListSubtitle.swift +++ b/Sources/SwiftUIKit/Lists/ListSubtitle.swift @@ -11,14 +11,19 @@ import SwiftUI /// This view can be used as a trailing list row subtitle. public struct ListSubtitle: View { - public init(_ text: String) { + public init( + _ text: LocalizedStringKey, + bundle: Bundle? = nil + ) { self.text = text + self.bundle = bundle } - private let text: String + private let text: LocalizedStringKey + private let bundle: Bundle? public var body: some View { - Text(text) + Text(text, bundle: bundle) .font(.footnote) .foregroundColor(.secondary) .lineLimit(1) @@ -30,12 +35,12 @@ public struct ListSubtitle: View { List { HStack { Label { - Text("Title") + Text("Preview.Label", bundle: .module) } icon: { Color.red } Spacer() - ListSubtitle("Subtitle") + ListSubtitle("Preview.Subtitle", bundle: .module) } } } diff --git a/Sources/SwiftUIKit/Loading/DotLoadingAnimationText.swift b/Sources/SwiftUIKit/Loading/DotLoadingAnimationText.swift index eda7b7eba3..7eff49ec10 100644 --- a/Sources/SwiftUIKit/Loading/DotLoadingAnimationText.swift +++ b/Sources/SwiftUIKit/Loading/DotLoadingAnimationText.swift @@ -24,22 +24,25 @@ public struct DotLoadingAnimationText: View { /// - dotCount: The max number of dots, by default `3`. /// - interval: The timer tick interval in seconds, by default `0.8`. public init( - text: String, + text: LocalizedStringKey, + bundle: Bundle? = nil, dotCount: Int = 3, interval: Double = 0.8 ) { self.text = text + self.bundle = bundle self.dotCount = dotCount self.interval = interval } - private let text: String + private let text: LocalizedStringKey + private let bundle: Bundle? private let dotCount: Int private let interval: Double public var body: some View { HStack(spacing: 0) { - Text(text) + Text(text, bundle: bundle) Text(staticDotString) } .opacity(0) @@ -72,5 +75,8 @@ private extension DotLoadingAnimationText { #Preview { - DotLoadingAnimationText(text: "Testing") + DotLoadingAnimationText( + text: "Preview.LoadingNoDots", + bundle: .module + ) } diff --git a/Sources/SwiftUIKit/Navigation/NavigationButton.swift b/Sources/SwiftUIKit/Navigation/NavigationButton.swift index 71efb043cc..fdf0582dc6 100644 --- a/Sources/SwiftUIKit/Navigation/NavigationButton.swift +++ b/Sources/SwiftUIKit/Navigation/NavigationButton.swift @@ -46,12 +46,17 @@ public struct NavigationButton: View { var body: some View { NavigationView { List { - Text("Is toggled: \(isToggled ? 1 : 0)") - NavigationLink("Navigation link") { - Text("HEJ") - }.offset() + Text("Preview.Toggled.\(isToggled ? 1 : 0)", bundle: .module) + + NavigationLink { + Text("Preview.Text", bundle: .module) + } label: { + Text("Preview.Text", bundle: .module) + } + .offset() + NavigationButton(action: { isToggled.toggle() }, content: { - Text("Navigation Button") + Text("Preview.Button", bundle: .module) }) } }.foregroundColor(.red) diff --git a/Sources/SwiftUIKit/Navigation/NavigationLinkArrow.swift b/Sources/SwiftUIKit/Navigation/NavigationLinkArrow.swift index aac35f3534..a521f0d193 100644 --- a/Sources/SwiftUIKit/Navigation/NavigationLinkArrow.swift +++ b/Sources/SwiftUIKit/Navigation/NavigationLinkArrow.swift @@ -60,11 +60,17 @@ private extension NavigationLinkArrow { NavigationView { List { - NavigationLink("Test") { + NavigationLink { + Text("Preview.Text", bundle: .module) + } label: { + Text("Preview.Link", bundle: .module) + } + + NavigationLink("Preview.Link") { Color.red } NavigationButton {} content: { - Text("HEJ") + Text("Preview.Text", bundle: .module) } NavigationLinkArrow() } diff --git a/Sources/SwiftUIKit/Pages/PageView+Previews.swift b/Sources/SwiftUIKit/Pages/PageView+Previews.swift index bb09713d6c..b6ab45c36d 100644 --- a/Sources/SwiftUIKit/Pages/PageView+Previews.swift +++ b/Sources/SwiftUIKit/Pages/PageView+Previews.swift @@ -27,7 +27,7 @@ private struct PreviewPage: View { var body: some View { type.color.opacity(0.5) - .overlay(Text("A \(type.rawValue) page") + .overlay(Text("Preview.Page.\(type.rawValue)", bundle: .module) .foregroundColor(.white) ) } diff --git a/Sources/SwiftUIKit/Previews/ProcessInfo+SwiftPreviewInspector.swift b/Sources/SwiftUIKit/Previews/ProcessInfo+SwiftPreviewInspector.swift index 24001bc27d..9d19ae5ab8 100644 --- a/Sources/SwiftUIKit/Previews/ProcessInfo+SwiftPreviewInspector.swift +++ b/Sources/SwiftUIKit/Previews/ProcessInfo+SwiftPreviewInspector.swift @@ -26,8 +26,8 @@ public extension ProcessInfo { #Preview { VStack { - Text("Is SwiftUI preview?") + Text("Preview.IsSwiftUIPreview", bundle: .module) .font(.title) - Text("\(ProcessInfo.processInfo.isSwiftUIPreview ? "Yes" : "No")") + Text("\(ProcessInfo.processInfo.isSwiftUIPreview ? "Preview.Yes" : "Preview.No")", bundle: .module) } } diff --git a/Sources/SwiftUIKit/Progress/CircularProgressBar.swift b/Sources/SwiftUIKit/Progress/CircularProgressBar.swift index aaae916bd7..2fca0482b3 100644 --- a/Sources/SwiftUIKit/Progress/CircularProgressBar.swift +++ b/Sources/SwiftUIKit/Progress/CircularProgressBar.swift @@ -103,7 +103,7 @@ private extension CircularProgressBar { .shadow(.sticker) .circularProgressBarStyle(.noText) - Button("Progress!") { + Button("Preview.Progress") { progress += 0.1 } } diff --git a/Sources/SwiftUIKit/Resources/Localizable.xcstrings b/Sources/SwiftUIKit/Resources/Localizable.xcstrings new file mode 100644 index 0000000000..e9c968a740 --- /dev/null +++ b/Sources/SwiftUIKit/Resources/Localizable.xcstrings @@ -0,0 +1,414 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "" : { + + }, + " " : { + + }, + "%@" : { + + }, + "%@:" : { + + }, + "%lld" : { + + }, + "1" : { + + }, + "Button.Add" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add" + } + } + } + }, + "Button.AddFavorite" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add Favorite" + } + } + } + }, + "Button.AddToFavorites" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add To Favorites" + } + } + } + }, + "Button.Call" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Call" + } + } + } + }, + "Button.Cancel" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" + } + } + } + }, + "Button.Copy" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy" + } + } + } + }, + "Button.Delete" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + } + } + }, + "Button.Deselect" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deselect" + } + } + } + }, + "Button.Done" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Done" + } + } + } + }, + "Button.Edit" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit" + } + } + } + }, + "Button.Email" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Email" + } + } + } + }, + "Button.OK" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" + } + } + } + }, + "Button.Paste" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paste" + } + } + } + }, + "Button.RemoveFavorite" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remove Favorite" + } + } + } + }, + "Button.RemoveFromFavorites" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remove From Favorites" + } + } + } + }, + "Button.Select" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select" + } + } + } + }, + "Button.Share" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Share" + } + } + } + }, + "Preview.Button" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Button" + } + } + } + }, + "Preview.Button.%lld" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Button %d" + } + } + } + }, + "Preview.IsSwiftUIPreview" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Is SwiftUI Preview?" + } + } + } + }, + "Preview.Item" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item" + } + } + } + }, + "Preview.Item.%lld" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Item %d" + } + } + } + }, + "Preview.Label" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Label" + } + } + } + }, + "Preview.Link" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Link" + } + } + } + }, + "Preview.Loading" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loading..." + } + } + } + }, + "Preview.LoadingNoDots" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Loading" + } + } + } + }, + "Preview.Menu" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menu" + } + } + } + }, + "Preview.NoData" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No Data" + } + } + } + }, + "Preview.Page.%@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "A %@ page" + } + } + } + }, + "Preview.Placeholder" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Placeholder..." + } + } + } + }, + "Preview.Progress" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Progress" + } + } + } + }, + "Preview.Row" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Row" + } + } + } + }, + "Preview.SectionTitle" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Section Title" + } + } + } + }, + "Preview.Subtitle" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Subtitle" + } + } + } + }, + "Preview.Text" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Text" + } + } + } + }, + "Preview.Text.%lld" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Text %d" + } + } + } + }, + "Preview.Text.Long" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "A long text that is meant to display how multi-line content is handled in this preview." + } + } + } + }, + "Preview.Title.%lld" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Title %d" + } + } + } + }, + "Preview.Toggled.%lld" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Is Toggled: %d" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Sources/SwiftUIKit/Text/TextFieldClearButton.swift b/Sources/SwiftUIKit/Text/TextFieldClearButton.swift index 7de1990a28..070539e280 100644 --- a/Sources/SwiftUIKit/Text/TextFieldClearButton.swift +++ b/Sources/SwiftUIKit/Text/TextFieldClearButton.swift @@ -66,12 +66,16 @@ public extension TextField { @State private var text = "" + + var placeholder: String { + .init(localized: "Preview.Placeholder", bundle: .module) + } var body: some View { VStack { - TextField("Test", text: $text) + TextField(placeholder, text: $text) .withClearButton(for: $text) - TextField("Test", text: $text) + TextField(placeholder, text: $text) .withClearButton( for: $text, .bouncy(duration: 1, extraBounce: 0.1) diff --git a/Sources/SwiftUIKit/Views/FetchedDataView.swift b/Sources/SwiftUIKit/Views/FetchedDataView.swift index b3c1db7098..2abf30d025 100644 --- a/Sources/SwiftUIKit/Views/FetchedDataView.swift +++ b/Sources/SwiftUIKit/Views/FetchedDataView.swift @@ -63,8 +63,8 @@ public struct FetchedDataView AnyView = { Text($0).any() } - let loadingView = Text("Loading...") - let noDataView = Text("No data") + let loadingView = Text("Preview.Loading") + let noDataView = Text("Preview.NoData") var body: some View { Group { diff --git a/Sources/SwiftUIKit/_Deprecated/List+Deprecated.swift b/Sources/SwiftUIKit/_Deprecated/List+Deprecated.swift index b72d871fe9..09de70a71c 100644 --- a/Sources/SwiftUIKit/_Deprecated/List+Deprecated.swift +++ b/Sources/SwiftUIKit/_Deprecated/List+Deprecated.swift @@ -10,7 +10,7 @@ public extension ListActionRow { hideIfEmpty: Bool = false, trailingView: @escaping () -> TrailingView ) { - self.init(title: title, text: text, action: nil, hideIfEmpty: hideIfEmpty) + self.init(title: .init(title), text: .init(text), action: nil) } } #endif