diff --git a/ios/Sources/GutenbergKit/Sources/EditorLocalization.swift b/ios/Sources/GutenbergKit/Sources/EditorLocalization.swift new file mode 100644 index 000000000..039f4f0b8 --- /dev/null +++ b/ios/Sources/GutenbergKit/Sources/EditorLocalization.swift @@ -0,0 +1,50 @@ +import Foundation + +/// Enum representing all localizable strings in the editor. +public enum EditorLocalizableString { + // MARK: - Block Inserter + case showMore + case showLess + case search + case insertBlock + + // MARK: - Media + case failedToInsertMedia + + // MARK: - Patterns + case patterns + case noPatternsFound + case insertPattern + case patternsCategoryUncategorized + case patternsCategoryAll +} + +/// Provides localized strings for the editor. +/// +/// Usage: +/// ```swift +/// let text = EditorLocalization[.showMore] +/// ``` +@MainActor +public final class EditorLocalization { + /// This is designed to be overridden by the host app to provide translations. + public static var localize: (EditorLocalizableString) -> String = { key in + switch key { + case .showMore: "Show More" + case .showLess: "Show Less" + case .search: "Search" + case .insertBlock: "Insert Block" + case .failedToInsertMedia: "Failed to insert media" + case .patterns: "Patterns" + case .noPatternsFound: "No Patterns Found" + case .insertPattern: "Insert Pattern" + case .patternsCategoryUncategorized: "Uncategorized" + case .patternsCategoryAll: "All" + } + } + + /// Convenience subscript for accessing localized strings. + public static subscript(key: EditorLocalizableString) -> String { + localize(key) + } +} diff --git a/ios/Sources/GutenbergKit/Sources/Media/MediaPickerController.swift b/ios/Sources/GutenbergKit/Sources/Media/MediaPickerController.swift index 17a2cf12e..a5256a2ec 100644 --- a/ios/Sources/GutenbergKit/Sources/Media/MediaPickerController.swift +++ b/ios/Sources/GutenbergKit/Sources/Media/MediaPickerController.swift @@ -5,7 +5,7 @@ public struct MediaPickerAction: Identifiable { public let title: String public let image: UIImage - init(id: String, title: String, image: UIImage) { + public init(id: String, title: String, image: UIImage) { self.id = id self.title = title self.image = image @@ -15,6 +15,11 @@ public struct MediaPickerAction: Identifiable { public struct MediaPickerActionGroup: Identifiable { public let id: String public let actions: [MediaPickerAction] + + public init(id: String, actions: [MediaPickerAction]) { + self.id = id + self.actions = actions + } } /// Configuration parameters for media picker behavior. @@ -38,6 +43,7 @@ public struct MediaPickerParameters { } } +@MainActor public protocol MediaPickerController { /// Returns a grouped list of media picker actions for the given parameters. func getActions(for parameters: MediaPickerParameters) -> [MediaPickerActionGroup] diff --git a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterBlockView.swift b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterBlockView.swift index 52a54c3b2..96b40aba3 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterBlockView.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterBlockView.swift @@ -34,8 +34,7 @@ struct BlockInserterBlockView: View { Button { onSelected() } label: { - // TODO: CMM-874 l10n - Label("Insert Block", systemImage: "plus") + Label(EditorLocalization[.insertBlock], systemImage: "plus") } } preview: { BlockDetailedView(block: block) diff --git a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterSectionView.swift b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterSectionView.swift index 047b4a645..3a035a7bf 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterSectionView.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterSectionView.swift @@ -83,8 +83,7 @@ struct BlockInserterSectionView: View { } } label: { HStack { - // TODO: CMM-874 add localization - Text(isExpanded ? "Show Less" : "Show More") + Text(isExpanded ? EditorLocalization[.showLess] : EditorLocalization[.showMore]) .font(.subheadline) .fontWeight(.medium) .foregroundStyle(Color.secondary) diff --git a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView.swift b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView.swift index ea845ec7f..30b48c900 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterView.swift @@ -201,8 +201,7 @@ struct BlockInserterView: View { Image(systemName: "magnifyingglass") .foregroundStyle(.secondary) .padding(.leading, 6) - /// TODO: CMM-874 - TextField("Search", text: $viewModel.searchText) + TextField(EditorLocalization[.search], text: $viewModel.searchText) .textFieldStyle(.plain) .autocorrectionDisabled() if !viewModel.searchText.isEmpty { diff --git a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterViewModel.swift b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterViewModel.swift index ae6aaa367..3da13cd3e 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterViewModel.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/BlockInserter/BlockInserterViewModel.swift @@ -78,8 +78,7 @@ class BlockInserterViewModel: ObservableObject { } if results.isEmpty { - // TODO: CMM-874 add localization - self.error = MediaError(message: anyError?.localizedDescription ?? "Failed to insert media") + self.error = MediaError(message: anyError?.localizedDescription ?? EditorLocalization[.failedToInsertMedia]) } return results diff --git a/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternCardView.swift b/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternCardView.swift index b8b7012ab..9476b799c 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternCardView.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternCardView.swift @@ -37,8 +37,7 @@ struct PatternCardView: View { Button { onSelected() } label: { - // TODO: CMM-874 l10n - Label("Insert Pattern", systemImage: "plus") + Label(EditorLocalization[.insertPattern], systemImage: "plus") } } preview: { PatternDetailedView(pattern: pattern, onSelected: onSelected, viewportWidth: viewportWidth) diff --git a/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternsView.swift b/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternsView.swift index afc41dce5..4381674f9 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternsView.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternsView.swift @@ -27,10 +27,9 @@ struct PatternsView: View { } var body: some View { - // TODO: CMM-874 l10n content .searchable(text: $viewModel.searchText) - .navigationTitle("Patterns") + .navigationTitle(EditorLocalization[.patterns]) .navigationBarTitleDisplayMode(.inline) .toolbar { toolbar @@ -41,12 +40,11 @@ struct PatternsView: View { private var content: some View { Group { if viewModel.sections.isEmpty { - // TODO: CMM-874 l10n - ContentUnavailableView( - "No Patterns Found", - systemImage: "square.grid.2x2", - description: Text(viewModel.searchText.isEmpty ? "There are no patterns available" : "Try a different search") - ) + if !viewModel.searchText.isEmpty { + ContentUnavailableView.search(text: viewModel.searchText) + } else { + ContentUnavailableView(EditorLocalization[.noPatternsFound], systemImage: "square.grid.2x2") + } } else { ScrollView { LazyVStack(spacing: 24) { diff --git a/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternsViewModel.swift b/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternsViewModel.swift index ffbdbf77b..51b71bfc2 100644 --- a/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternsViewModel.swift +++ b/ios/Sources/GutenbergKit/Sources/Views/Patterns/PatternsViewModel.swift @@ -32,8 +32,7 @@ final class PatternsViewModel: ObservableObject { var sections: [PatternSection] = categoryPatterns.map { category, patterns in let displayName: String if category == uncategorizedKey { - // TODO: CMM-874 - Localize "Uncategorized" - displayName = "Uncategorized" + displayName = EditorLocalization[.patternsCategoryUncategorized] } else { displayName = category.capitalized } @@ -63,11 +62,10 @@ final class PatternsViewModel: ObservableObject { } // Create "All" section at the top (no previews) - // TODO: CMM-874 - Localize "All" if !patterns.isEmpty { sections = [PatternSection( category: "all", - name: "All", + name: EditorLocalization[.patternsCategoryAll], patterns: patterns, showPreviews: false )] + sections