From e88d1466d45ee1802a85f71e64e876d3cb8bca5c Mon Sep 17 00:00:00 2001 From: tvision106 Date: Tue, 14 Nov 2023 16:20:36 -0500 Subject: [PATCH 01/17] Update the localization --- App/App_macOS.swift | 12 +- App/SidebarViewController.swift | 10 +- App/SplitViewController.swift | 2 +- Kiwix.xcodeproj/project.pbxproj | 33 ++- Model/DownloadService.swift | 4 +- Model/Entities/Errors.swift | 6 +- Model/Utilities/String+Extension.swift | 32 +++ Support/en.lproj/Localizable.strings | 202 ++++++++++++++++++ Support/es.lproj/Localizable.strings | 8 + ViewModel/BrowserViewModel.swift | 8 +- Views/Bookmarks.swift | 6 +- Views/BrowserTab.swift | 2 +- Views/BuildingBlocks/ArticleCell.swift | 6 +- Views/BuildingBlocks/Attribute.swift | 4 +- Views/BuildingBlocks/DownloadTaskCell.swift | 4 +- Views/BuildingBlocks/FlavorTag.swift | 6 +- Views/BuildingBlocks/GridSection.swift | 4 +- .../LibraryLastRefreshTime.swift | 4 +- Views/BuildingBlocks/Message.swift | 2 +- Views/BuildingBlocks/SearchResultRow.swift | 4 +- Views/BuildingBlocks/SheetContent.swift | 2 +- Views/BuildingBlocks/TabLabel.swift | 4 +- .../ZimFileMissingIndicator.swift | 2 +- Views/BuildingBlocks/ZimFileRow.swift | 2 +- Views/Buttons/ArticleShortcutButtons.swift | 16 +- Views/Buttons/BookmarkButton.swift | 14 +- Views/Buttons/NavigationButtons.swift | 4 +- Views/Buttons/OutlineButton.swift | 10 +- Views/Buttons/TabsManagerButton.swift | 26 +-- Views/Commands.swift | 10 +- Views/Library/Library.swift | 14 +- Views/Library/ZimFileDetail.swift | 78 +++---- Views/Library/ZimFilesCategories.swift | 10 +- Views/Library/ZimFilesDownloads.swift | 6 +- Views/Library/ZimFilesNew.swift | 8 +- Views/Library/ZimFilesOpened.swift | 10 +- Views/SearchResults.swift | 22 +- Views/Settings/About.swift | 34 +-- Views/Settings/LanguageSelector.swift | 16 +- Views/Settings/Settings.swift | 76 +++---- Views/ViewModifiers/AlertHandler.swift | 2 +- Views/ViewModifiers/BookmarkContextMenu.swift | 4 +- Views/ViewModifiers/ExternalLinkHandler.swift | 10 +- Views/ViewModifiers/FileImport.swift | 6 +- Views/Welcome.swift | 12 +- WikiMed/es.lproj/LaunchScreen.strings | 1 + WikiMed/es.lproj/Main.strings | 1 + 47 files changed, 515 insertions(+), 244 deletions(-) create mode 100644 Model/Utilities/String+Extension.swift create mode 100644 Support/en.lproj/Localizable.strings create mode 100644 Support/es.lproj/Localizable.strings create mode 100644 WikiMed/es.lproj/LaunchScreen.strings create mode 100644 WikiMed/es.lproj/Main.strings diff --git a/App/App_macOS.swift b/App/App_macOS.swift index ce6efe2cf..2bbdcbf7e 100644 --- a/App/App_macOS.swift +++ b/App/App_macOS.swift @@ -32,10 +32,10 @@ struct Kiwix: App { }.commands { SidebarCommands() CommandGroup(replacing: .importExport) { - OpenFileButton(context: .command) { Text("Open...") } + OpenFileButton(context: .command) { Text("Open...".localized) } } CommandGroup(replacing: .newItem) { - Button("New Tab") { + Button("New Tab".localized) { guard let currentWindow = NSApp.keyWindow, let controller = currentWindow.windowController else { return } controller.newWindowForTab(nil) @@ -91,11 +91,11 @@ struct RootView: View { NavigationView { List(selection: $navigation.currentItem) { ForEach(primaryItems, id: \.self) { navigationItem in - Label(navigationItem.name, systemImage: navigationItem.icon) + Label(navigationItem.name.localized, systemImage: navigationItem.icon) } - Section("Library") { + Section("Library".localized) { ForEach(libraryItems, id: \.self) { navigationItem in - Label(navigationItem.name, systemImage: navigationItem.icon) + Label(navigationItem.name.localized, systemImage: navigationItem.icon) } } } @@ -106,7 +106,7 @@ struct RootView: View { responder.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil) } label: { Image(systemName: "sidebar.leading") - }.help("Show sidebar") + }.help("Show sidebar".localized) } switch navigation.currentItem { case .reading: diff --git a/App/SidebarViewController.swift b/App/SidebarViewController.swift index 836850f3a..a29afb156 100644 --- a/App/SidebarViewController.swift +++ b/App/SidebarViewController.swift @@ -86,7 +86,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl }, menu: UIMenu(children: [ UIAction( - title: "Close This Tab", + title: "Close This Tab".localized, image: UIImage(systemName: "xmark.square"), attributes: .destructive ) { [unowned self] _ in @@ -95,7 +95,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl splitViewController.navigationViewModel.deleteTab(tabID: tabID) }, UIAction( - title: "Close All Tabs", + title: "Close All Tabs".localized, image: UIImage(systemName: "xmark.square.fill"), attributes: .destructive ) { [unowned self] _ in @@ -188,11 +188,11 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl switch section { case .tabs: var config = UIListContentConfiguration.sidebarHeader() - config.text = "Tabs" + config.text = "Tabs".localized headerView.contentConfiguration = config case .library: var config = UIListContentConfiguration.sidebarHeader() - config.text = "Library" + config.text = "Library".localized headerView.contentConfiguration = config default: headerView.contentConfiguration = nil @@ -203,7 +203,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl guard let splitViewController = splitViewController as? SplitViewController, let item = dataSource.itemIdentifier(for: indexPath), case let .tab(tabID) = item else { return nil } - let action = UIContextualAction(style: .destructive, title: "Close") { _, _, _ in + let action = UIContextualAction(style: .destructive, title: "Close".localized) { _, _, _ in splitViewController.navigationViewModel.deleteTab(tabID: tabID) } action.image = UIImage(systemName: "xmark") diff --git a/App/SplitViewController.swift b/App/SplitViewController.swift index 1e8300320..45a733c40 100644 --- a/App/SplitViewController.swift +++ b/App/SplitViewController.swift @@ -128,7 +128,7 @@ class SplitViewController: UISplitViewController { let controller = UIHostingController(rootView: Settings()) setViewController(UINavigationController(rootViewController: controller), for: .secondary) default: - let controller = UIHostingController(rootView: Text("Not yet implemented")) + let controller = UIHostingController(rootView: Text("Not yet implemented".localized)) setViewController(UINavigationController(rootViewController: controller), for: .secondary) } } diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index 922d260db..aec5c37c2 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 8E4396462B02E455007F0BC4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8E4396492B02E455007F0BC4 /* Localizable.strings */; }; + 8E4396472B02E455007F0BC4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8E4396492B02E455007F0BC4 /* Localizable.strings */; }; + 8E43964C2B02E4C6007F0BC4 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */; }; + 8E43964D2B02E4C6007F0BC4 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */; }; 97008ABD2974A5BF0076E60C /* OPDSParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97008ABC2974A5BF0076E60C /* OPDSParserTests.swift */; }; 9709C0982A8E4C5700E4564C /* Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9709C0972A8E4C5700E4564C /* Commands.swift */; }; 97121EBE28849F0000371AEB /* ZimFileMissingIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97121EBC28849F0000371AEB /* ZimFileMissingIndicator.swift */; }; @@ -124,6 +128,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 8E4396442B02E443007F0BC4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Main.strings; sourceTree = ""; }; + 8E4396452B02E443007F0BC4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/LaunchScreen.strings; sourceTree = ""; }; + 8E4396482B02E455007F0BC4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + 8E43964A2B02E458007F0BC4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; 97008AB42974A5A70076E60C /* WikiMed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WikiMed.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97008ABA2974A5BF0076E60C /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 97008ABC2974A5BF0076E60C /* OPDSParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPDSParserTests.swift; sourceTree = ""; }; @@ -293,6 +302,7 @@ 9779A7E224567A5A00F6F6FF /* Log.swift */, 97B3BACD2736CE3500A23F49 /* URL.swift */, 9779A73A2456796B00F6F6FF /* WebKitHandler.swift */, + 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */, ); path = Utilities; sourceTree = ""; @@ -386,8 +396,8 @@ 974E7EE22930201500BDF59C /* ZimFileService */ = { isa = PBXGroup; children = ( - 974E7EE32930201500BDF59C /* ZimFileService.mm */, 974E7EE42930201500BDF59C /* ZimFileService.h */, + 974E7EE32930201500BDF59C /* ZimFileService.mm */, 974E7EE52930201500BDF59C /* ZimFileService.swift */, ); path = ZimFileService; @@ -549,6 +559,7 @@ 97E94B22271EF250005B0295 /* Kiwix.entitlements */, 9779A5D02456796A00F6F6FF /* Kiwix-Bridging-Header.h */, 970885D0271339A300C5795C /* wikipedia_dark.css */, + 8E4396492B02E455007F0BC4 /* Localizable.strings */, ); path = Support; sourceTree = ""; @@ -649,6 +660,7 @@ knownRegions = ( en, Base, + es, ); mainGroup = 97A2AB7F1C1B80FF00052E74; packageReferences = ( @@ -681,6 +693,7 @@ buildActionMask = 2147483647; files = ( 973A0DF72830929C00B41E71 /* Assets.xcassets in Resources */, + 8E4396462B02E455007F0BC4 /* Localizable.strings in Resources */, 979D3A7C284159BF00E396B8 /* injection.js in Resources */, 97DE2BAD283B133700C63D9B /* wikipedia_dark.css in Resources */, ); @@ -691,6 +704,7 @@ buildActionMask = 2147483647; files = ( 97B448A1210FBC2E0004B056 /* LaunchScreen.storyboard in Resources */, + 8E4396472B02E455007F0BC4 /* Localizable.strings in Resources */, 97B4489E210FBC2E0004B056 /* Assets.xcassets in Resources */, 97B4489C210FBC2C0004B056 /* Main.storyboard in Resources */, ); @@ -753,6 +767,7 @@ buildActionMask = 2147483647; files = ( 972DE4BB2814A5A4004FD9B9 /* Errors.swift in Sources */, + 8E43964C2B02E4C6007F0BC4 /* String+Extension.swift in Sources */, 9790CA5A28A05EBB00D39FC6 /* ZimFilesCategories.swift in Sources */, 97486D08284A42B90096E4DD /* SearchResultRow.swift in Sources */, 9753D949285B55F100A626CC /* DefaultKeys.swift in Sources */, @@ -836,6 +851,7 @@ buildActionMask = 2147483647; files = ( 97B44899210FBC2C0004B056 /* ViewController.swift in Sources */, + 8E43964D2B02E4C6007F0BC4 /* String+Extension.swift in Sources */, 97B44897210FBC2C0004B056 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -851,10 +867,20 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + 8E4396492B02E455007F0BC4 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 8E4396482B02E455007F0BC4 /* en */, + 8E43964A2B02E458007F0BC4 /* es */, + ); + name = Localizable.strings; + sourceTree = ""; + }; 97B4489A210FBC2C0004B056 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 97B4489B210FBC2C0004B056 /* Base */, + 8E4396442B02E443007F0BC4 /* es */, ); name = Main.storyboard; sourceTree = ""; @@ -863,6 +889,7 @@ isa = PBXVariantGroup; children = ( 97B448A0210FBC2E0004B056 /* Base */, + 8E4396452B02E443007F0BC4 /* es */, ); name = LaunchScreen.storyboard; sourceTree = ""; @@ -943,7 +970,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 119; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = L7HWM3SP3L; + DEVELOPMENT_TEAM = ""; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -989,7 +1016,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 119; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = L7HWM3SP3L; + DEVELOPMENT_TEAM = ""; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; diff --git a/Model/DownloadService.swift b/Model/DownloadService.swift index 39761c773..a51b0e1d9 100644 --- a/Model/DownloadService.swift +++ b/Model/DownloadService.swift @@ -191,10 +191,10 @@ class DownloadService: NSObject, URLSessionDelegate, URLSessionTaskDelegate, URL Database.shared.container.performBackgroundTask { context in // configure notification content let content = UNMutableNotificationContent() - content.title = "Download Completed" + content.title = "Download Completed".localized content.sound = .default if let zimFile = try? context.fetch(ZimFile.fetchRequest(fileID: zimFileID)).first { - content.body = "\(zimFile.name) has been downloaded successfully." + content.body = "%@ has been downloaded successfully.".localizedWithFormat(withArgs: zimFile.name) } // schedule notification diff --git a/Model/Entities/Errors.swift b/Model/Entities/Errors.swift index 45c9a26b1..46ce9d552 100644 --- a/Model/Entities/Errors.swift +++ b/Model/Entities/Errors.swift @@ -16,12 +16,12 @@ public enum LibraryRefreshError: LocalizedError { public var errorDescription: String? { switch self { case .retrieve(let description): - let prefix = NSLocalizedString("Error retrieving library data.", comment: "Library Refresh Error") + let prefix = "Error retrieving library data.".localized(withComment: "Library Refresh Error") return [prefix, description].compactMap({ $0 }).joined(separator: " ") case .parse: - return NSLocalizedString("Error parsing library data.", comment: "Library Refresh Error") + return "Error parsing library data.".localized(withComment: "Library Refresh Error") case .process: - return NSLocalizedString("Error processing library data.", comment: "Library Refresh Error") + return "Error processing library data.".localized(withComment: "Library Refresh Error") } } } diff --git a/Model/Utilities/String+Extension.swift b/Model/Utilities/String+Extension.swift new file mode 100644 index 000000000..9f1ad1364 --- /dev/null +++ b/Model/Utilities/String+Extension.swift @@ -0,0 +1,32 @@ +// +// String+Extension.swift +// Kiwix +// +// Created by tvision251 on 11/13/23. +// Copyright © 2023 Chris Li. All rights reserved. +// + +import Foundation + +extension String { + + var localized: String { + return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") + } + + func localized(withComment:String) -> String { + return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: withComment) + } + + func localizedWithFormat(withArgs: CVarArg...) -> String { + let format = NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") + + switch withArgs.count { + case 1: return String.localizedStringWithFormat(format, withArgs[0]) + case 2: return String.localizedStringWithFormat(format, withArgs[0], withArgs[1]) + default: return String.localizedStringWithFormat(format, withArgs) + } + } + +} + diff --git a/Support/en.lproj/Localizable.strings b/Support/en.lproj/Localizable.strings new file mode 100644 index 000000000..632378726 --- /dev/null +++ b/Support/en.lproj/Localizable.strings @@ -0,0 +1,202 @@ +/* + Localizable.strings + Kiwix + + Created by tvision251 on 11/13/23. + Copyright © 2023 Chris Li. All rights reserved. +*/ + +/* Library Refresh Error */ +"Error retrieving library data." = "Error retrieving library data."; +/* Library Refresh Error */ +"Error parsing library data." = "Error parsing library data."; +/* Library Refresh Error */ +"Error processing library data." = "Error processing library data."; + +"Download Completed" = "Download Completed"; +"%@ has been downloaded successfully." = "%@ has been downloaded successfully."; +"Done" = "Done"; +"Go Back" = "Go Back"; +"Go Forward" = "Go Forward"; +"Outline" = "Outline"; +"Show article outline" = "Show article outline"; +"No outline available" = "No outline available"; +"Remove Bookmark" = "Remove Bookmark"; +"Add Bookmark" = "Add Bookmark"; +"Show Bookmarks" = "Show Bookmarks"; +"Show bookmarks. Long press to bookmark or unbookmark the current article." = "Show bookmarks. Long press to bookmark or unbookmark the current article."; +"Main Article" = "Main Article"; +"Show main article" = "Show main article"; +"Random Article" = "Random Article"; +"Show random article" = "Show random article"; +"Random Page" = "Random Page"; +"Tabs" = "Tabs"; +"New Tab" = "New Tab"; +"Close This Tab" = "Close This Tab"; +"Close All Tabs" = "Close All Tabs"; +"Close Tab" = "Close Tab"; +"Library" = "Library"; +"Settings" = "Settings"; +"Tabs Manager" = "Tabs Manager"; +"Unable to load the article requested." = "Unable to load the article requested."; +"View" = "View"; +"Remove" = "Remove"; +"External Link" = "External Link"; +"Load the link" = "Load the link"; +"Cancel" = "Cancel"; +"An external link is tapped, do you wish to load the link?" = "An external link is tapped, do you wish to load the link?"; +"An external link is tapped. However, your current setting does not allow it to be loaded." = "An external link is tapped. However, your current setting does not allow it to be loaded."; +"Open a zim file" = "Open a zim file"; +"Unable to open file" = "Unable to open file"; +"%@ cannot be opened." = "%@ cannot be opened."; +"No snippet" = "No snippet"; +"Article Title" = "Article Title"; +"Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ +sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ +sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; +"Unknown" = "Unknown"; +"Yes" = "Yes"; +"No" = "No"; +"Failed" = "Failed"; +"Downloading..." = "Downloading..."; +"Paused" = "Paused"; +"everything except large media files like video/audio" = "everything except large media files like video/audio"; +"most pictures have been removed" = "most pictures have been removed"; +"only a subset of the text is available, probably the first section" = "only a subset of the text is available, probably the first section"; +"Header Text" = "Header Text"; +"Content" = "Content"; +"Just Now" = "Just Now"; +"Never" = "Never"; +"There is nothing to see" = "There is nothing to see"; +"Article Title" = "Article Title"; +"Zim file is missing." = "Zim file is missing."; +"articles" = "articles"; +"Select a zim file to see detail" = "Select a zim file to see detail"; +"Main Page" = "Main Page"; +"Copy URL" = "Copy URL"; +"Copy ID" = "Copy ID"; +"No opened zim file" = "No opened zim file"; +"Show Sidebar" = "Show Sidebar"; +"Open..." = "Open..."; +"Category" = "Category"; +"No zim file under this category." = "No zim file under this category."; +"No download tasks" = "No download tasks"; +"No new zim file" = "No new zim file"; +"Refresh" = "Refresh"; +"Name" = "Name"; +"Description" = "Description"; +"Actions" = "Actions"; +"Info" = "Info"; +"Locate" = "Locate"; +"Open Main Page" = "Open Main Page"; +"Reveal in Finder" = "Reveal in Finder"; +"Download using cellular" = "Download using cellular"; +"Unlink" = "Unlink"; +"Unlink %@" = "Unlink %@"; +"All bookmarked articles linked to this zim file will be deleted, \ +but the original file will remain in place." = "All bookmarked articles linked to this zim file will be deleted, \ +but the original file will remain in place."; +"Delete %@" = "Delete %@"; +"The zim file and all bookmarked articles linked to this zim file will be deleted." = "The zim file and all bookmarked articles linked to this zim file will be deleted."; +"Download" = "Download"; +"Space Warning" = "Space Warning"; +"There might not be enough space on your device for this zim file." = "There might not be enough space on your device for this zim file."; +"There would be less than 1GB space left after the zim file is downloaded." = "There would be less than 1GB space left after the zim file is downloaded."; +"Download Anyway" = "Download Anyway"; +"Language" = "Language"; +"Size" = "Size"; +"Created" = "Created"; +"Pictures" = "Pictures"; +"Videos" = "Videos"; +"Details" = "Details"; +"Requires Service Workers" = "Requires Service Workers"; +"Article Count" = "Article Count"; +"Media Count" = "Media Count"; +"ID" = "ID"; +"Try to Recover" = "Try to Recover"; +"Pause" = "Pause"; +"Resume" = "Resume"; +"Zim files requiring service workers are not supported." = "Zim files requiring service workers are not supported."; +"A very long description" = "A very long description"; +"Page zoom" = "Page zoom"; +"Reset" = "Reset"; +"External link" = "External link"; +"Search snippet" = "Search snippet"; +"Reading" = "Reading"; +"Catalog" = "Catalog"; +"Refresh Now" = "Refresh Now"; +"Last refresh" = "Last refresh"; +"Auto refresh" = "Auto refresh"; +"When enabled, the library catalog will be refreshed automatically when outdated." = "When enabled, the library catalog will be refreshed automatically when outdated."; +"Languages" = "Languages"; +"Library" = "Library"; +"Change will only apply to new download tasks." = "Change will only apply to new download tasks."; +"Refreshing..." = "Refreshing..."; +"Include zim files in backup" = "Include zim files in backup"; +"Backup" = "Backup"; +"Does not apply to files opened in place." = "Does not apply to files opened in place."; +"Misc" = "Misc"; +"Feedback" = "Feedback"; +"Rate the App" = "Rate the App"; +"About" = "About"; +"Count" = "Count"; +"No language" = "No language"; +"Showing" = "Showing"; +"Hiding" = "Hiding"; +"Sorting" = "Sorting"; +"Release" = "Release"; +"Dependencies" = "Dependencies"; +"License" = "License"; +"Version" = "Version"; +"Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ +It makes knowledge available to people with no or limited internet access. \ +The software as well as the content is free to use for anyone." = "Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ +It makes knowledge available to people with no or limited internet access. \ +The software as well as the content is free to use for anyone."; +"This app is released under the terms of the GNU General Public License version 3." = "This app is released under the terms of the GNU General Public License version 3."; +"Build" = "Build"; +"Our Website" = "Our Website"; +"Source" = "Source"; +"GNU General Public License v3" = "GNU General Public License v3"; +"Bookmarks" = "Bookmarks"; +"No bookmarks" = "No bookmarks"; +"No result" = "No result"; +"Recent Search" = "Recent Search"; +"Clear" = "Clear"; +"Clear Recent Searches" = "Clear Recent Searches"; +"Clear All" = "Clear All"; +"All recent search history will be removed." = "All recent search history will be removed."; +"Included in Search" = "Included in Search"; +"None" = "None"; +"All" = "All"; +"Open File" = "Open File"; +"Fetching..." = "Fetching..."; +"Fetch Catalog" = "Fetch Catalog"; +"Actual Size" = "Actual Size"; +"Zoom In" = "Zoom In"; +"Zoom Out" = "Zoom Out"; +"Not yet implemented" = "Not yet implemented"; +"Close" = "Close"; +"Map" = "Map"; +"Opened" = "Opened"; +"Categories" = "Categories"; +"New" = "New"; +"Downloads" = "Downloads"; +"A-Z" = "A-Z"; +"By Count" = "By Count"; +"Always Ask" = "Always Ask"; +"Always Load" = "Always Load"; +"Never Load" = "Never Load"; +"Wikipedia" = "Wikipedia"; +"Wikibooks" = "Wikibooks"; +"Wikinews" = "Wikinews"; +"Wikiquote" = "Wikiquote"; +"Wikisource" = "Wikisource"; +"Wikiversity" = "Wikiversity"; +"Wikivoyage" = "Wikivoyage"; +"Wiktionary" = "Wiktionary"; +"TED" = "TED"; +"Vikidia" = "Vikidia"; +"StackExchange" = "StackExchange"; +"Other" = "Other"; + diff --git a/Support/es.lproj/Localizable.strings b/Support/es.lproj/Localizable.strings new file mode 100644 index 000000000..e83d7f5c0 --- /dev/null +++ b/Support/es.lproj/Localizable.strings @@ -0,0 +1,8 @@ +/* + Localizable.strings + Kiwix + + Created by tvision251 on 11/13/23. + Copyright © 2023 Chris Li. All rights reserved. +*/ + diff --git a/ViewModel/BrowserViewModel.swift b/ViewModel/BrowserViewModel.swift index 2354f6b4d..7eaccd1e6 100644 --- a/ViewModel/BrowserViewModel.swift +++ b/ViewModel/BrowserViewModel.swift @@ -250,12 +250,12 @@ class BrowserViewModel: NSObject, ObservableObject, // open url actions.append( - UIAction(title: "Open", image: UIImage(systemName: "doc.text")) { _ in + UIAction(title: "Open".localized, image: UIImage(systemName: "doc.text")) { _ in webView.load(URLRequest(url: url)) } ) actions.append( - UIAction(title: "Open in New Tab", image: UIImage(systemName: "doc.badge.plus")) { _ in + UIAction(title: "Open in New Tab".localized, image: UIImage(systemName: "doc.badge.plus")) { _ in NotificationCenter.openURL(url, inNewTab: true) } ) @@ -266,11 +266,11 @@ class BrowserViewModel: NSObject, ObservableObject, let predicate = NSPredicate(format: "articleURL == %@", url as CVarArg) let request = Bookmark.fetchRequest(predicate: predicate) if let bookmarks = try? context.fetch(request), !bookmarks.isEmpty { - return UIAction(title: "Remove Bookmark", image: UIImage(systemName: "star.slash.fill")) { _ in + return UIAction(title: "Remove Bookmark".localized, image: UIImage(systemName: "star.slash.fill")) { _ in self.deleteBookmark(url: url) } } else { - return UIAction(title: "Bookmark", image: UIImage(systemName: "star")) { _ in + return UIAction(title: "Bookmark".localized, image: UIImage(systemName: "star")) { _ in self.createBookmark(url: url) } } diff --git a/Views/Bookmarks.swift b/Views/Bookmarks.swift index f24ab25d5..988023e86 100644 --- a/Views/Bookmarks.swift +++ b/Views/Bookmarks.swift @@ -36,14 +36,14 @@ struct Bookmarks: View { } .modifier(GridCommon()) .modifier(ToolbarRoleBrowser()) - .navigationTitle("Bookmarks") + .navigationTitle("Bookmarks".localized) .searchable(text: $searchText) .onChange(of: searchText) { searchText in bookmarks.nsPredicate = Bookmarks.buildPredicate(searchText: searchText) } .overlay { if bookmarks.isEmpty { - Message(text: "No bookmarks") + Message(text: "No bookmarks".localized) } } .toolbar { @@ -53,7 +53,7 @@ struct Bookmarks: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } diff --git a/Views/BrowserTab.swift b/Views/BrowserTab.swift index 9a080449e..aec014f54 100644 --- a/Views/BrowserTab.swift +++ b/Views/BrowserTab.swift @@ -22,7 +22,7 @@ struct BrowserTab: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } NavigationButtons() diff --git a/Views/BuildingBlocks/ArticleCell.swift b/Views/BuildingBlocks/ArticleCell.swift index f5681f328..fa8069c89 100644 --- a/Views/BuildingBlocks/ArticleCell.swift +++ b/Views/BuildingBlocks/ArticleCell.swift @@ -44,7 +44,7 @@ struct ArticleCell: View { if let snippet = snippet { Text(AttributedString(snippet)).lineLimit(4) } else if alwaysShowSnippet { - Text("No snippet").foregroundColor(.secondary) + Text("No snippet".localized).foregroundColor(.secondary) } }.font(.caption).multilineTextAlignment(.leading) Spacer(minLength: 0) @@ -64,12 +64,12 @@ struct ArticleCell: View { struct ArticleCell_Previews: PreviewProvider { static let result: SearchResult = { - let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title")! + let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! result.snippet = NSAttributedString(string: """ Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """ + """.localized ) return result }() diff --git a/Views/BuildingBlocks/Attribute.swift b/Views/BuildingBlocks/Attribute.swift index 038f9202d..98ff221b7 100644 --- a/Views/BuildingBlocks/Attribute.swift +++ b/Views/BuildingBlocks/Attribute.swift @@ -16,7 +16,7 @@ struct Attribute: View { HStack { Text(title) Spacer() - Text(detail ?? "Unknown").foregroundColor(.secondary) + Text(detail ?? "Unknown".localized).foregroundColor(.secondary) } } } @@ -30,7 +30,7 @@ struct AttributeBool: View { Text(title) Spacer() #if os(macOS) - Text(detail ? "Yes" : "No").foregroundColor(.secondary) + Text(detail ? "Yes".localized : "No".localized).foregroundColor(.secondary) #elseif os(iOS) if detail { Image(systemName: "checkmark.circle.fill").foregroundColor(.green) diff --git a/Views/BuildingBlocks/DownloadTaskCell.swift b/Views/BuildingBlocks/DownloadTaskCell.swift index f1fb7f1a0..530cd7ad2 100644 --- a/Views/BuildingBlocks/DownloadTaskCell.swift +++ b/Views/BuildingBlocks/DownloadTaskCell.swift @@ -41,9 +41,9 @@ struct DownloadTaskCell: View { } VStack(alignment: .leading, spacing: 4) { if downloadTask.error != nil { - Text("Failed") + Text("Failed".localized) } else if downloadTask.resumeData == nil { - Text("Downloading...") + Text("Downloading...".localized) } else { Text("Paused") } diff --git a/Views/BuildingBlocks/FlavorTag.swift b/Views/BuildingBlocks/FlavorTag.swift index ffe22e3b8..17e3c6827 100644 --- a/Views/BuildingBlocks/FlavorTag.swift +++ b/Views/BuildingBlocks/FlavorTag.swift @@ -41,11 +41,11 @@ struct FlavorTag: View { var help: String { switch flavor { case .max: - return "everything except large media files like video/audio" + return "everything except large media files like video/audio".localized case .noPic: - return "most pictures have been removed" + return "most pictures have been removed".localized case .mini: - return "only a subset of the text is available, probably the first section" + return "only a subset of the text is available, probably the first section".localized } } } diff --git a/Views/BuildingBlocks/GridSection.swift b/Views/BuildingBlocks/GridSection.swift index 6dd622d5d..d820a9151 100644 --- a/Views/BuildingBlocks/GridSection.swift +++ b/Views/BuildingBlocks/GridSection.swift @@ -29,8 +29,8 @@ struct GridSection: View { struct GridSection_Previews: PreviewProvider { static var previews: some View { - GridSection(title: "Header Text") { - Text("Content") + GridSection(title: "Header Text".localized) { + Text("Content".localized) } } } diff --git a/Views/BuildingBlocks/LibraryLastRefreshTime.swift b/Views/BuildingBlocks/LibraryLastRefreshTime.swift index 47156b964..9fbd4aa9d 100644 --- a/Views/BuildingBlocks/LibraryLastRefreshTime.swift +++ b/Views/BuildingBlocks/LibraryLastRefreshTime.swift @@ -16,12 +16,12 @@ struct LibraryLastRefreshTime: View { var body: some View { if let lastRefresh = lastRefresh { if Date().timeIntervalSince(lastRefresh) < 120 { - Text("Just Now") + Text("Just Now".localized) } else { Text(RelativeDateTimeFormatter().localizedString(for: lastRefresh, relativeTo: Date())) } } else { - Text("Never") + Text("Never".localized) } } } diff --git a/Views/BuildingBlocks/Message.swift b/Views/BuildingBlocks/Message.swift index 4992e79de..2b0e5a88d 100644 --- a/Views/BuildingBlocks/Message.swift +++ b/Views/BuildingBlocks/Message.swift @@ -26,7 +26,7 @@ struct Message: View { struct Message_Previews: PreviewProvider { static var previews: some View { - Message(text: "There is nothing to see") + Message(text: "There is nothing to see".localized) .frame(width: 250, height: 200) } } diff --git a/Views/BuildingBlocks/SearchResultRow.swift b/Views/BuildingBlocks/SearchResultRow.swift index 55b33dc7a..57edb5f07 100644 --- a/Views/BuildingBlocks/SearchResultRow.swift +++ b/Views/BuildingBlocks/SearchResultRow.swift @@ -38,12 +38,12 @@ struct SearchResultRow: View { struct SearchResultRow_Previews: PreviewProvider { static let result: SearchResult = { - let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title")! + let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! result.snippet = NSAttributedString(string: """ Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """ + """.localized ) return result }() diff --git a/Views/BuildingBlocks/SheetContent.swift b/Views/BuildingBlocks/SheetContent.swift index f7f3fa061..022ea301e 100644 --- a/Views/BuildingBlocks/SheetContent.swift +++ b/Views/BuildingBlocks/SheetContent.swift @@ -27,7 +27,7 @@ struct SheetContent: View { Button { dismiss() } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } diff --git a/Views/BuildingBlocks/TabLabel.swift b/Views/BuildingBlocks/TabLabel.swift index ebd8fa598..e31847ded 100644 --- a/Views/BuildingBlocks/TabLabel.swift +++ b/Views/BuildingBlocks/TabLabel.swift @@ -15,12 +15,12 @@ struct TabLabel: View { var body: some View { if let zimFile = tab.zimFile, let category = Category(rawValue: zimFile.category) { Label { - Text(tab.title ?? "New Tab").lineLimit(1) + Text(tab.title ?? "New Tab".localized).lineLimit(1) } icon: { Favicon(category: category, imageData: zimFile.faviconData).frame(width: 22, height: 22) } } else { - Label(tab.title ?? "New Tab", systemImage: "square") + Label(tab.title ?? "New Tab".localized, systemImage: "square") } } } diff --git a/Views/BuildingBlocks/ZimFileMissingIndicator.swift b/Views/BuildingBlocks/ZimFileMissingIndicator.swift index da7c3b3c1..2e0a23a8d 100644 --- a/Views/BuildingBlocks/ZimFileMissingIndicator.swift +++ b/Views/BuildingBlocks/ZimFileMissingIndicator.swift @@ -12,6 +12,6 @@ struct ZimFileMissingIndicator: View { var body: some View { Image(systemName: "exclamationmark.triangle.fill") .renderingMode(.original) - .help("Zim file is missing.") + .help("Zim file is missing.".localized) } } diff --git a/Views/BuildingBlocks/ZimFileRow.swift b/Views/BuildingBlocks/ZimFileRow.swift index 0fb4d3878..73510e563 100644 --- a/Views/BuildingBlocks/ZimFileRow.swift +++ b/Views/BuildingBlocks/ZimFileRow.swift @@ -30,7 +30,7 @@ struct ZimFileRow: View { Formatter.size.string(fromByteCount: zimFile.size), { if #available(iOS 15.0, *) { - return "\(zimFile.articleCount.formatted(.number.notation(.compactName))) articles" + return "\(zimFile.articleCount.formatted(.number.notation(.compactName)))" + "articles".localized } else { return Formatter.largeNumber(zimFile.articleCount) } diff --git a/Views/Buttons/ArticleShortcutButtons.swift b/Views/Buttons/ArticleShortcutButtons.swift index 79ee831de..7e4660473 100644 --- a/Views/Buttons/ArticleShortcutButtons.swift +++ b/Views/Buttons/ArticleShortcutButtons.swift @@ -40,10 +40,10 @@ struct ArticleShortcutButtons: View { browser.loadMainArticle() dismissSearch() } label: { - Label("Main Article", systemImage: "house") + Label("Main Article".localized, systemImage: "house") } .disabled(zimFiles.isEmpty) - .help("Show main article") + .help("Show main article".localized) #elseif os(iOS) Menu { ForEach(zimFiles) { zimFile in @@ -53,13 +53,13 @@ struct ArticleShortcutButtons: View { } } } label: { - Label("Main Article", systemImage: "house") + Label("Main Article".localized, systemImage: "house") } primaryAction: { browser.loadMainArticle() dismissSearch() } .disabled(zimFiles.isEmpty) - .help("Show main article") + .help("Show main article".localized) #endif } @@ -69,10 +69,10 @@ struct ArticleShortcutButtons: View { browser.loadRandomArticle() dismissSearch() } label: { - Label("Random Article", systemImage: "die.face.5") + Label("Random Article".localized, systemImage: "die.face.5") } .disabled(zimFiles.isEmpty) - .help("Show random article") + .help("Show random article".localized) #elseif os(iOS) Menu { ForEach(zimFiles) { zimFile in @@ -82,13 +82,13 @@ struct ArticleShortcutButtons: View { } } } label: { - Label("Random Page", systemImage: "die.face.5") + Label("Random Page".localized, systemImage: "die.face.5") } primaryAction: { browser.loadRandomArticle() dismissSearch() } .disabled(zimFiles.isEmpty) - .help("Show random article") + .help("Show random article".localized) #endif } } diff --git a/Views/Buttons/BookmarkButton.swift b/Views/Buttons/BookmarkButton.swift index 385084421..96b40565e 100644 --- a/Views/Buttons/BookmarkButton.swift +++ b/Views/Buttons/BookmarkButton.swift @@ -23,7 +23,7 @@ struct BookmarkButton: View { } } label: { Label { - Text(browser.articleBookmarked ? "Remove Bookmark" : "Add Bookmark") + Text(browser.articleBookmarked ? "Remove Bookmark".localized : "Add Bookmark".localized) } icon: { Image(systemName: browser.articleBookmarked ? "star.fill" : "star") .renderingMode(browser.articleBookmarked ? .original : .template) @@ -35,23 +35,23 @@ struct BookmarkButton: View { Button(role: .destructive) { browser.deleteBookmark() } label: { - Label("Remove Bookmark", systemImage: "star.slash.fill") + Label("Remove Bookmark".localized, systemImage: "star.slash.fill") } } else { Button { browser.createBookmark() } label: { - Label("Add Bookmark", systemImage: "star") + Label("Add Bookmark".localized, systemImage: "star") } } Button { isShowingBookmark = true } label: { - Label("Show Bookmarks", systemImage: "list.star") + Label("Show Bookmarks".localized, systemImage: "list.star") } } label: { Label { - Text("Show Bookmarks") + Text("Show Bookmarks".localized) } icon: { Image(systemName: browser.articleBookmarked ? "star.fill" : "star") .renderingMode(browser.articleBookmarked ? .original : .template) @@ -59,7 +59,7 @@ struct BookmarkButton: View { } primaryAction: { isShowingBookmark = true } - .help("Show bookmarks. Long press to bookmark or unbookmark the current article.") + .help("Show bookmarks. Long press to bookmark or unbookmark the current article.".localized) .popover(isPresented: $isShowingBookmark) { NavigationView { Bookmarks().navigationBarTitleDisplayMode(.inline).toolbar { @@ -67,7 +67,7 @@ struct BookmarkButton: View { Button { isShowingBookmark = false } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } diff --git a/Views/Buttons/NavigationButtons.swift b/Views/Buttons/NavigationButtons.swift index e30dec407..8f8f121de 100644 --- a/Views/Buttons/NavigationButtons.swift +++ b/Views/Buttons/NavigationButtons.swift @@ -29,7 +29,7 @@ struct NavigationButtons: View { browser.webView.goBack() dismissSearch() } label: { - Label("Go Back", systemImage: "chevron.left") + Label("Go Back".localized, systemImage: "chevron.left") }.disabled(!browser.canGoBack) } @@ -38,7 +38,7 @@ struct NavigationButtons: View { browser.webView.goForward() dismissSearch() } label: { - Label("Go Forward", systemImage: "chevron.right") + Label("Go Forward".localized, systemImage: "chevron.right") }.disabled(!browser.canGoForward) } } diff --git a/Views/Buttons/OutlineButton.swift b/Views/Buttons/OutlineButton.swift index 671b74d9c..3eb7ef874 100644 --- a/Views/Buttons/OutlineButton.swift +++ b/Views/Buttons/OutlineButton.swift @@ -25,10 +25,10 @@ struct OutlineButton: View { } } } label: { - Label("Outline", systemImage: "list.bullet") + Label("Outline".localized, systemImage: "list.bullet") } .disabled(browser.outlineItems.isEmpty) - .help("Show article outline") + .help("Show article outline".localized) #elseif os(iOS) Button { isShowingOutline = true @@ -36,12 +36,12 @@ struct OutlineButton: View { Image(systemName: "list.bullet") } .disabled(browser.outlineItems.isEmpty) - .help("Show article outline") + .help("Show article outline".localized) .popover(isPresented: $isShowingOutline) { NavigationView { Group { if browser.outlineItemTree.isEmpty { - Message(text: "No outline available") + Message(text: "No outline available".localized) } else { List(browser.outlineItemTree) { item in OutlineNode(item: item) { item in @@ -59,7 +59,7 @@ struct OutlineButton: View { Button { isShowingOutline = false } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } diff --git a/Views/Buttons/TabsManagerButton.swift b/Views/Buttons/TabsManagerButton.swift index f8bf203a4..212ce3024 100644 --- a/Views/Buttons/TabsManagerButton.swift +++ b/Views/Buttons/TabsManagerButton.swift @@ -29,18 +29,18 @@ struct TabsManagerButton: View { Button { navigation.createTab() } label: { - Label("New Tab", systemImage: "plus.square") + Label("New Tab".localized, systemImage: "plus.square") } Button(role: .destructive) { guard case .tab(let tabID) = navigation.currentItem else { return } navigation.deleteTab(tabID: tabID) } label: { - Label("Close This Tab", systemImage: "xmark.square") + Label("Close This Tab".localized, systemImage: "xmark.square") } Button(role: .destructive) { navigation.deleteAllTabs() } label: { - Label("Close All Tabs", systemImage: "xmark.square.fill") + Label("Close All Tabs".localized, systemImage: "xmark.square.fill") } } Section { @@ -54,16 +54,16 @@ struct TabsManagerButton: View { Button { presentedSheet = .library } label: { - Label("Library", systemImage: "folder") + Label("Library".localized, systemImage: "folder") } Button { presentedSheet = .settings } label: { - Label("Settings", systemImage: "gear") + Label("Settings".localized, systemImage: "gear") } } } label: { - Label("Tabs Manager", systemImage: "square.stack") + Label("Tabs Manager".localized, systemImage: "square.stack") } primaryAction: { presentedSheet = .tabsManager } @@ -76,7 +76,7 @@ struct TabsManagerButton: View { Button { self.presentedSheet = nil } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } @@ -90,7 +90,7 @@ struct TabsManagerButton: View { Button { self.presentedSheet = nil } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } @@ -129,12 +129,12 @@ struct TabManager: View { Button(role: .destructive) { navigation.deleteTab(tabID: tab.objectID) } label: { - Label("Close Tab", systemImage: "xmark") + Label("Close Tab".localized, systemImage: "xmark") } } } .listStyle(.plain) - .navigationTitle("Tabs") + .navigationTitle("Tabs".localized) .navigationBarTitleDisplayMode(.inline) .toolbar { Menu { @@ -142,15 +142,15 @@ struct TabManager: View { guard case let .tab(tabID) = navigation.currentItem else { return } navigation.deleteTab(tabID: tabID) } label: { - Label("Close This Tab", systemImage: "xmark.square") + Label("Close This Tab".localized, systemImage: "xmark.square") } Button(role: .destructive) { navigation.deleteAllTabs() } label: { - Label("Close All Tabs", systemImage: "xmark.square.fill") + Label("Close All Tabs".localized, systemImage: "xmark.square.fill") } } label: { - Label("New Tab", systemImage: "plus.square") + Label("New Tab".localized, systemImage: "plus.square") } primaryAction: { navigation.createTab() } diff --git a/Views/Commands.swift b/Views/Commands.swift index 7550ec0e4..61672c3df 100644 --- a/Views/Commands.swift +++ b/Views/Commands.swift @@ -54,10 +54,10 @@ struct NavigationCommands: View { @FocusedValue(\.browserViewModel) var browser: BrowserViewModel? var body: some View { - Button("Go Back") { browser?.webView.goBack() } + Button("Go Back".localized) { browser?.webView.goBack() } .keyboardShortcut("[") .disabled(canGoBack != true) - Button("Go Forward") { browser?.webView.goForward() } + Button("Go Forward".localized) { browser?.webView.goForward() } .keyboardShortcut("]") .disabled(canGoForward != true) } @@ -68,13 +68,13 @@ struct PageZoomCommands: View { @FocusedValue(\.browserViewModel) var browser: BrowserViewModel? var body: some View { - Button("Actual Size") { webViewPageZoom = 1 } + Button("Actual Size".localized) { webViewPageZoom = 1 } .keyboardShortcut("0") .disabled(webViewPageZoom == 1 || browser?.url == nil) - Button("Zoom In") { webViewPageZoom += 0.1 } + Button("Zoom In".localized) { webViewPageZoom += 0.1 } .keyboardShortcut("+") .disabled(webViewPageZoom >= 2 || browser?.url == nil) - Button("Zoom Out") { webViewPageZoom -= 0.1 } + Button("Zoom Out".localized) { webViewPageZoom -= 0.1 } .keyboardShortcut("-") .disabled(webViewPageZoom <= 0.5 || browser?.url == nil) } diff --git a/Views/Library/Library.swift b/Views/Library/Library.swift index 06d9770cd..b2786227e 100644 --- a/Views/Library/Library.swift +++ b/Views/Library/Library.swift @@ -32,7 +32,7 @@ struct Library: View { List(Category.allCases) { category in NavigationLink { ZimFilesCategory(category: .constant(category)) - .navigationTitle(category.name) + .navigationTitle(category.name.localized) .navigationBarTitleDisplayMode(.inline) } label: { HStack { @@ -42,7 +42,7 @@ struct Library: View { } } .listStyle(.plain) - .navigationTitle(NavigationItem.categories.name) + .navigationTitle(NavigationItem.categories.name.localized) case .downloads: ZimFilesDownloads() case .new: @@ -87,7 +87,7 @@ struct LibraryZimFileDetailSidePanel: ViewModifier { if let zimFile = viewModel.selectedZimFile { ZimFileDetail(zimFile: zimFile) } else { - Message(text: "Select a zim file to see detail").background(.thickMaterial) + Message(text: "Select a zim file to see detail".localized).background(.thickMaterial) } }.frame(width: 275).background(.ultraThinMaterial) } @@ -132,13 +132,13 @@ struct LibraryZimFileContext: ViewModifier { guard let url = ZimFileService.shared.getMainPageURL(zimFileID: zimFile.fileID) else { return } NotificationCenter.openURL(url, inNewTab: true) } label: { - Label("Main Page", systemImage: "house") + Label("Main Page".localized, systemImage: "house") } Button { guard let url = ZimFileService.shared.getRandomPageURL(zimFileID: zimFile.fileID) else { return } NotificationCenter.openURL(url, inNewTab: true) } label: { - Label("Random Page", systemImage: "die.face.5") + Label("Random Page".localized, systemImage: "die.face.5") } } @@ -153,7 +153,7 @@ struct LibraryZimFileContext: ViewModifier { UIPasteboard.general.setValue(downloadURL.absoluteString, forPasteboardType: UTType.url.identifier) #endif } label: { - Label("Copy URL", systemImage: "doc.on.doc") + Label("Copy URL".localized, systemImage: "doc.on.doc") } } Button { @@ -164,7 +164,7 @@ struct LibraryZimFileContext: ViewModifier { UIPasteboard.general.setValue(zimFile.fileID.uuidString, forPasteboardType: UTType.plainText.identifier) #endif } label: { - Label("Copy ID", systemImage: "barcode.viewfinder") + Label("Copy ID".localized, systemImage: "barcode.viewfinder") } } } diff --git a/Views/Library/ZimFileDetail.swift b/Views/Library/ZimFileDetail.swift index 72b0d9482..5ee91f1a1 100644 --- a/Views/Library/ZimFileDetail.swift +++ b/Views/Library/ZimFileDetail.swift @@ -25,10 +25,10 @@ struct ZimFileDetail: View { var body: some View { #if os(macOS) List { - Section("Name") { Text(zimFile.name).lineLimit(nil) }.collapsible(false) - Section("Description") { Text(zimFile.fileDescription).lineLimit(nil) }.collapsible(false) - Section("Actions") { actions }.collapsible(false) - Section("Info") { + Section("Name".localized) { Text(zimFile.name).lineLimit(nil) }.collapsible(false) + Section("Description".localized) { Text(zimFile.fileDescription).lineLimit(nil) }.collapsible(false) + Section("Actions".localized) { actions }.collapsible(false) + Section("Info".localized) { basicInfo boolInfo counts @@ -81,15 +81,15 @@ struct ZimFileDetail: View { if let downloadTask = zimFile.downloadTask { // zim file is being downloaded DownloadTaskDetail(downloadTask: downloadTask) } else if zimFile.isMissing { // zim file was opened, but is now missing - Action(title: "Locate") { isPresentingFileLocator = true } + Action(title: "Locate".localized) { isPresentingFileLocator = true } unlinkAction } else if zimFile.fileURLBookmark != nil { // zim file is opened - Action(title: "Open Main Page") { + Action(title: "Open Main Page".localized) { guard let url = ZimFileService.shared.getMainPageURL(zimFileID: zimFile.fileID) else { return } NotificationCenter.openURL(url, inNewTab: true) } #if os(macOS) - Action(title: "Reveal in Finder") { + Action(title: "Reveal in Finder".localized) { guard let url = ZimFileService.shared.getFileURL(zimFileID: zimFile.id) else { return } NSWorkspace.shared.activateFileViewerSelecting([url]) } @@ -105,23 +105,23 @@ struct ZimFileDetail: View { #endif } else if zimFile.downloadURL != nil { // zim file can be downloaded #if os(iOS) - Toggle("Download using cellular", isOn: $downloadUsingCellular) + Toggle("Download using cellular".localized, isOn: $downloadUsingCellular) #endif downloadAction } } var unlinkAction: some View { - Action(title: "Unlink", isDestructive: true) { + Action(title: "Unlink".localized, isDestructive: true) { isPresentingUnlinkAlert = true }.alert(isPresented: $isPresentingUnlinkAlert) { Alert( - title: Text("Unlink \(zimFile.name)"), + title: Text("Unlink %@".localizedWithFormat(withArgs: zimFile.name)), message: Text(""" All bookmarked articles linked to this zim file will be deleted, \ but the original file will remain in place. """), - primaryButton: .destructive(Text("Unlink")) { + primaryButton: .destructive(Text("Unlink".localized)) { LibraryOperations.unlink(zimFileID: zimFile.fileID) #if os(iOS) presentationMode.wrappedValue.dismiss() @@ -137,9 +137,9 @@ struct ZimFileDetail: View { isPresentingDeleteAlert = true }.alert(isPresented: $isPresentingDeleteAlert) { Alert( - title: Text("Delete \(zimFile.name)"), - message: Text("The zim file and all bookmarked articles linked to this zim file will be deleted."), - primaryButton: .destructive(Text("Delete")) { + title: Text("Delete %@".localizedWithFormat(withArgs: zimFile.name)), + message: Text("The zim file and all bookmarked articles linked to this zim file will be deleted.".localized), + primaryButton: .destructive(Text("Delete".localized)) { LibraryOperations.delete(zimFileID: zimFile.fileID) #if os(iOS) presentationMode.wrappedValue.dismiss() @@ -151,7 +151,7 @@ struct ZimFileDetail: View { } var downloadAction: some View { - Action(title: "Download") { + Action(title: "Download".localized) { if let freeSpace = freeSpace, zimFile.size >= freeSpace - 10^9 { isPresentingDownloadAlert = true } else { @@ -159,15 +159,15 @@ struct ZimFileDetail: View { } }.alert(isPresented: $isPresentingDownloadAlert) { Alert( - title: Text("Space Warning"), + title: Text("Space Warning".localized), message: Text({ if let freeSpace = freeSpace, zimFile.size > freeSpace { - return "There might not be enough space on your device for this zim file." + return "There might not be enough space on your device for this zim file.".localized } else { - return "There would be less than 1GB space left after the zim file is downloaded." + return "There would be less than 1GB space left after the zim file is downloaded.".localized } }()), - primaryButton: .default(Text("Download Anyway")) { + primaryButton: .default(Text("Download Anyway".localized)) { DownloadService.shared.start(zimFileID: zimFile.id, allowsCellularAccess: false) }, secondaryButton: .cancel() @@ -177,37 +177,37 @@ struct ZimFileDetail: View { @ViewBuilder var basicInfo: some View { - Attribute(title: "Language", detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) - Attribute(title: "Category", detail: Category(rawValue: zimFile.category)?.description) - Attribute(title: "Size", detail: Formatter.size.string(fromByteCount: zimFile.size)) - Attribute(title: "Created", detail: Formatter.dateMedium.string(from: zimFile.created)) + Attribute(title: "Language".localized, detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) + Attribute(title: "Category".localized, detail: Category(rawValue: zimFile.category)?.description) + Attribute(title: "Size".localized, detail: Formatter.size.string(fromByteCount: zimFile.size)) + Attribute(title: "Created".localized, detail: Formatter.dateMedium.string(from: zimFile.created)) } @ViewBuilder var boolInfo: some View { - AttributeBool(title: "Pictures", detail: zimFile.hasPictures) - AttributeBool(title: "Videos", detail: zimFile.hasVideos) - AttributeBool(title: "Details", detail: zimFile.hasDetails) + AttributeBool(title: "Pictures".localized, detail: zimFile.hasPictures) + AttributeBool(title: "Videos".localized, detail: zimFile.hasVideos) + AttributeBool(title: "Details".localized, detail: zimFile.hasDetails) if zimFile.requiresServiceWorkers { - AttributeBool(title: "Requires Service Workers", detail: zimFile.requiresServiceWorkers) + AttributeBool(title: "Requires Service Workers".localized, detail: zimFile.requiresServiceWorkers) } } @ViewBuilder var counts: some View { Attribute( - title: "Article Count", + title: "Article Count".localized, detail: Formatter.number.string(from: NSNumber(value: zimFile.articleCount)) ) Attribute( - title: "Media Count", + title: "Media Count".localized, detail: Formatter.number.string(from: NSNumber(value: zimFile.mediaCount)) ) } @ViewBuilder var id: some View { - Attribute(title: "ID", detail: String(zimFile.fileID.uuidString.prefix(8))) + Attribute(title: "ID".localized, detail: String(zimFile.fileID.uuidString.prefix(8))) } private var freeSpace: Int64? { @@ -237,27 +237,27 @@ private struct DownloadTaskDetail: View { @ObservedObject var downloadTask: DownloadTask var body: some View { - Action(title: "Cancel", isDestructive: true) { + Action(title: "Cancel".localized, isDestructive: true) { DownloadService.shared.cancel(zimFileID: downloadTask.fileID) } if let error = downloadTask.error { if downloadTask.resumeData != nil { - Action(title: "Try to Recover") { + Action(title: "Try to Recover".localized) { DownloadService.shared.resume(zimFileID: downloadTask.fileID) } } - Attribute(title: "Failed", detail: detail) + Attribute(title: "Failed".localized, detail: detail) Text(error) } else if downloadTask.resumeData == nil { - Action(title: "Pause") { + Action(title: "Pause".localized) { DownloadService.shared.pause(zimFileID: downloadTask.fileID) } - Attribute(title: "Downloading...", detail: detail) + Attribute(title: "Downloading...".localized, detail: detail) } else { - Action(title: "Resume") { + Action(title: "Resume".localized) { DownloadService.shared.resume(zimFileID: downloadTask.fileID) } - Attribute(title: "Paused", detail: detail) + Attribute(title: "Paused".localized, detail: detail) } } @@ -313,7 +313,7 @@ private struct Action: View { private struct ServiceWorkerWarning: View { var body: some View { Label { - Text("Zim files requiring service workers are not supported.") + Text("Zim files requiring service workers are not supported.".localized) } icon: { Image(systemName: "exclamationmark.triangle.fill").renderingMode(.original) } @@ -329,7 +329,7 @@ struct ZimFileDetail_Previews: PreviewProvider { zimFile.created = Date() zimFile.downloadURL = URL(string: "https://www.example.com") zimFile.fileID = UUID() - zimFile.fileDescription = "A very long description" + zimFile.fileDescription = "A very long description".localized zimFile.flavor = "max" zimFile.hasDetails = true zimFile.hasPictures = false diff --git a/Views/Library/ZimFilesCategories.swift b/Views/Library/ZimFilesCategories.swift index b58277097..f08c6298b 100644 --- a/Views/Library/ZimFilesCategories.swift +++ b/Views/Library/ZimFilesCategories.swift @@ -17,7 +17,7 @@ struct ZimFilesCategories: View { var body: some View { ZimFilesCategory(category: $selected) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.categories.name) + .navigationTitle(NavigationItem.categories.name.localized) .toolbar { #if os(iOS) ToolbarItem(placement: .navigationBarLeading) { @@ -25,13 +25,13 @@ struct ZimFilesCategories: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } #endif ToolbarItem { - Picker("Category", selection: $selected) { + Picker("Category".localized, selection: $selected) { ForEach(Category.allCases) { Text($0.name).tag($0) } @@ -91,7 +91,7 @@ private struct CategoryGrid: View { var body: some View { Group { if sections.isEmpty { - Message(text: "No zim file under this category.") + Message(text: "No zim file under this category.".localized) } else { LazyVGrid(columns: ([gridItem]), alignment: .leading, spacing: 12) { ForEach(sections) { section in @@ -187,7 +187,7 @@ private struct CategoryList: View { var body: some View { Group { if zimFiles.isEmpty { - Message(text: "No zim file under this category.") + Message(text: "No zim file under this category.".localized) } else { List(zimFiles, id: \.self, selection: $viewModel.selectedZimFile) { zimFile in ZimFileRow(zimFile) diff --git a/Views/Library/ZimFilesDownloads.swift b/Views/Library/ZimFilesDownloads.swift index 54c3f678d..ea83b8c0d 100644 --- a/Views/Library/ZimFilesDownloads.swift +++ b/Views/Library/ZimFilesDownloads.swift @@ -31,10 +31,10 @@ struct ZimFilesDownloads: View { } .modifier(GridCommon()) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.downloads.name) + .navigationTitle(NavigationItem.downloads.name.localized) .overlay { if downloadTasks.isEmpty { - Message(text: "No download tasks") + Message(text: "No download tasks".localized) } } .toolbar { @@ -44,7 +44,7 @@ struct ZimFilesDownloads: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } diff --git a/Views/Library/ZimFilesNew.swift b/Views/Library/ZimFilesNew.swift index 3ae5c3aeb..772db2764 100644 --- a/Views/Library/ZimFilesNew.swift +++ b/Views/Library/ZimFilesNew.swift @@ -39,7 +39,7 @@ struct ZimFilesNew: View { } .modifier(GridCommon()) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.new.name) + .navigationTitle(NavigationItem.new.name.localized) .searchable(text: $searchText) .onAppear { viewModel.start(isUserInitiated: false) @@ -52,7 +52,7 @@ struct ZimFilesNew: View { } .overlay { if zimFiles.isEmpty { - Message(text: "No new zim file") + Message(text: "No new zim file".localized) } } .toolbar { @@ -62,7 +62,7 @@ struct ZimFilesNew: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } @@ -77,7 +77,7 @@ struct ZimFilesNew: View { Button { viewModel.start(isUserInitiated: true) } label: { - Label("Refresh", systemImage: "arrow.triangle.2.circlepath.circle") + Label("Refresh".localized, systemImage: "arrow.triangle.2.circlepath.circle") } } } diff --git a/Views/Library/ZimFilesOpened.swift b/Views/Library/ZimFilesOpened.swift index 0b19b0159..dfdf3f70e 100644 --- a/Views/Library/ZimFilesOpened.swift +++ b/Views/Library/ZimFilesOpened.swift @@ -31,10 +31,10 @@ struct ZimFilesOpened: View { } .modifier(GridCommon(edges: .all)) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.opened.name) + .navigationTitle(NavigationItem.opened.name.localized) .overlay { if zimFiles.isEmpty { - Message(text: "No opened zim file") + Message(text: "No opened zim file".localized) } } // not using OpenFileButton here, because it does not work on iOS/iPadOS 15 when this view is in a modal @@ -53,7 +53,7 @@ struct ZimFilesOpened: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } @@ -68,8 +68,8 @@ struct ZimFilesOpened: View { } isFileImporterPresented = true } label: { - Label("Open...", systemImage: "plus") - }.help("Open a zim file") + Label("Open...".localized, systemImage: "plus") + }.help("Open a zim file".localized) } } } diff --git a/Views/SearchResults.swift b/Views/SearchResults.swift index c1192cf1f..a617a1fed 100644 --- a/Views/SearchResults.swift +++ b/Views/SearchResults.swift @@ -28,7 +28,7 @@ struct SearchResults: View { var body: some View { Group { if zimFiles.isEmpty { - Message(text: "No opened zim file") + Message(text: "No opened zim file".localized) } else if horizontalSizeClass == .regular { HStack(spacing: 0) { #if os(macOS) @@ -66,7 +66,7 @@ struct SearchResults: View { Spacer() } } else if viewModel.results.isEmpty { - Message(text: "No result") + Message(text: "No result".localized) } else { ScrollView { LazyVGrid(columns: [GridItem(.flexible(minimum: 300, maximum: 700), alignment: .center)]) { @@ -98,7 +98,7 @@ struct SearchResults: View { viewModel.searchText = searchText } }.swipeActions { - Button("Remove", role: .destructive) { + Button("Remove".localized, role: .destructive) { recentSearchTexts.removeAll { $0 == searchText } } } @@ -126,39 +126,39 @@ struct SearchResults: View { private var recentSearchHeader: some View { HStack { - Text("Recent Search") + Text("Recent Search".localized) Spacer() Button { isClearSearchConfirmationPresented = true } label: { - Text("Clear").font(.caption).fontWeight(.medium) - }.confirmationDialog("Clear Recent Searches", isPresented: $isClearSearchConfirmationPresented) { - Button("Clear All", role: .destructive) { + Text("Clear".localized).font(.caption).fontWeight(.medium) + }.confirmationDialog("Clear Recent Searches".localized, isPresented: $isClearSearchConfirmationPresented) { + Button("Clear All".localized, role: .destructive) { recentSearchTexts.removeAll() } } message: { - Text("All recent search history will be removed.") + Text("All recent search history will be removed.".localized) } } } private var searchFilterHeader: some View { HStack { - Text("Included in Search") + Text("Included in Search".localized) Spacer() if zimFiles.count == zimFiles.filter({ $0.includedInSearch }).count { Button { zimFiles.forEach { $0.includedInSearch = false } try? managedObjectContext.save() } label: { - Text("None").font(.caption).fontWeight(.medium) + Text("None".localized).font(.caption).fontWeight(.medium) } } else { Button { zimFiles.forEach { $0.includedInSearch = true } try? managedObjectContext.save() } label: { - Text("All").font(.caption).fontWeight(.medium) + Text("All".localized).font(.caption).fontWeight(.medium) } } } diff --git a/Views/Settings/About.swift b/Views/Settings/About.swift index 09ea7bf0f..c9a7407c6 100644 --- a/Views/Settings/About.swift +++ b/Views/Settings/About.swift @@ -17,27 +17,27 @@ struct About: View { var body: some View { #if os(macOS) VStack(spacing: 16) { - SettingSection(name: "About") { + SettingSection(name: "About".localized) { about ourWebsite } - SettingSection(name: "Release") { + SettingSection(name: "Release".localized) { release HStack { source license } } - SettingSection(name: "Dependencies", alignment: .top) { + SettingSection(name: "Dependencies".localized, alignment: .top) { Table(dependencies) { - TableColumn("Name", value: \.name) - TableColumn("License") { dependency in Text(dependency.license ?? "") } - TableColumn("Version", value: \.version) + TableColumn("Name".localized, value: \.name) + TableColumn("License".localized) { dependency in Text(dependency.license ?? "") } + TableColumn("Version".localized, value: \.version) }.tableStyle(.bordered(alternatesRowBackgrounds: true)) } } .padding() - .tabItem { Label("About", systemImage: "info.circle") } + .tabItem { Label("About".localized, systemImage: "info.circle") } .task { await getDependencies() } .onChange(of: externalLinkURL) { url in guard let url = url else { return } @@ -49,14 +49,14 @@ struct About: View { about ourWebsite } - Section("Release") { + Section("Release".localized) { release appVersion buildNumber source license } - Section("Dependencies") { + Section("Dependencies".localized) { ForEach(dependencies) { dependency in HStack { Text(dependency.name) @@ -70,7 +70,7 @@ struct About: View { } } } - .navigationTitle("About") + .navigationTitle("About".localized) .navigationBarTitleDisplayMode(.inline) .sheet(item: $externalLinkURL) { SafariView(url: $0) } .task { await getDependencies() } @@ -83,36 +83,36 @@ struct About: View { Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ It makes knowledge available to people with no or limited internet access. \ The software as well as the content is free to use for anyone. - """ + """.localized ) } private var release: some View { - Text("This app is released under the terms of the GNU General Public License version 3.") + Text("This app is released under the terms of the GNU General Public License version 3.".localized) } private var appVersion: some View { - Attribute(title: "Version", detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) + Attribute(title: "Version".localized, detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) } private var buildNumber: some View { - Attribute(title: "Build", detail: Bundle.main.infoDictionary?["CFBundleVersion"] as? String) + Attribute(title: "Build".localized, detail: Bundle.main.infoDictionary?["CFBundleVersion"] as? String) } private var ourWebsite: some View { - Button("Our Website") { + Button("Our Website".localized) { externalLinkURL = URL(string: "https://www.kiwix.org") } } private var source: some View { - Button("Source") { + Button("Source".localized) { externalLinkURL = URL(string: "https://github.com/kiwix/apple") } } private var license: some View { - Button("GNU General Public License v3") { + Button("GNU General Public License v3".localized) { externalLinkURL = URL(string: "https://www.gnu.org/licenses/gpl-3.0.en.html") } } diff --git a/Views/Settings/LanguageSelector.swift b/Views/Settings/LanguageSelector.swift index 8dcaf3870..d03751244 100644 --- a/Views/Settings/LanguageSelector.swift +++ b/Views/Settings/LanguageSelector.swift @@ -30,8 +30,8 @@ struct LanguageSelector: View { } }) }.width(14) - TableColumn("Name", value: \.name) - TableColumn("Count", value: \.count) { language in Text(language.count.formatted()) } + TableColumn("Name".localized, value: \.name) + TableColumn("Count".localized, value: \.count) { language in Text(language.count.formatted()) } } .tableStyle(.bordered(alternatesRowBackgrounds: true)) .onChange(of: sortOrder) { languages.sort(using: $0) } @@ -51,29 +51,29 @@ struct LanguageSelector: View { List { Section { if showing.isEmpty { - Text("No language").foregroundColor(.secondary) + Text("No language".localized).foregroundColor(.secondary) } else { ForEach(showing) { language in Button { hide(language) } label: { LanguageLabel(language: language) } } } - } header: { Text("Showing") } + } header: { Text("Showing".localized) } Section { ForEach(hiding) { language in Button { show(language) } label: { LanguageLabel(language: language) } } - } header: { Text("Hiding") } + } header: { Text("Hiding".localized) } } .listStyle(.insetGrouped) - .navigationTitle("Languages") + .navigationTitle("Languages".localized) .navigationBarTitleDisplayMode(.inline) .toolbar { Picker(selection: $sortingMode) { ForEach(LibraryLanguageSortingMode.allCases) { sortingMode in - Text(sortingMode.name).tag(sortingMode) + Text(sortingMode.name.localized).tag(sortingMode) } } label: { - Label("Sorting", systemImage: "arrow.up.arrow.down") + Label("Sorting".localized, systemImage: "arrow.up.arrow.down") }.pickerStyle(.menu) } .onAppear { diff --git a/Views/Settings/Settings.swift b/Views/Settings/Settings.swift index 71f652a37..bee1ddc50 100644 --- a/Views/Settings/Settings.swift +++ b/Views/Settings/Settings.swift @@ -18,30 +18,30 @@ struct ReadingSettings: View { var body: some View { VStack(spacing: 16) { - SettingSection(name: "Page zoom") { + SettingSection(name: "Page zoom".localized) { HStack { Stepper(webViewPageZoom.formatted(.percent), value: $webViewPageZoom, in: 0.5...2, step: 0.05) Spacer() - Button("Reset") { webViewPageZoom = 1 }.disabled(webViewPageZoom == 1) + Button("Reset".localized) { webViewPageZoom = 1 }.disabled(webViewPageZoom == 1) } } - SettingSection(name: "External link") { + SettingSection(name: "External link".localized) { Picker(selection: $externalLinkLoadingPolicy) { ForEach(ExternalLinkLoadingPolicy.allCases) { loadingPolicy in - Text(loadingPolicy.name).tag(loadingPolicy) + Text(loadingPolicy.name.localized).tag(loadingPolicy) } } label: { } } - SettingSection(name: "Search snippet") { + SettingSection(name: "Search snippet".localized) { Picker(selection: $searchResultSnippetMode) { ForEach(SearchResultSnippetMode.allCases) { snippetMode in - Text(snippetMode.name).tag(snippetMode) + Text(snippetMode.name.localized).tag(snippetMode) } } label: { } } } .padding() - .tabItem { Label("Reading", systemImage: "book") } + .tabItem { Label("Reading".localized, systemImage: "book") } } } @@ -51,30 +51,30 @@ struct LibrarySettings: View { var body: some View { VStack(spacing: 16) { - SettingSection(name: "Catalog") { + SettingSection(name: "Catalog".localized) { HStack(spacing: 6) { - Button("Refresh Now") { + Button("Refresh Now".localized) { library.start(isUserInitiated: true) }.disabled(library.isInProgress) if library.isInProgress { ProgressView().progressViewStyle(.circular).scaleEffect(0.5).frame(height: 1) } Spacer() - Text("Last refresh:").foregroundColor(.secondary) + Text("Last refresh".localized + ":").foregroundColor(.secondary) LibraryLastRefreshTime().foregroundColor(.secondary) } VStack(alignment: .leading) { - Toggle("Auto refresh", isOn: $libraryAutoRefresh) - Text("When enabled, the library catalog will be refreshed automatically when outdated.") + Toggle("Auto refresh".localized, isOn: $libraryAutoRefresh) + Text("When enabled, the library catalog will be refreshed automatically when outdated.".localized) .foregroundColor(.secondary) } } - SettingSection(name: "Languages", alignment: .top) { + SettingSection(name: "Languages".localized, alignment: .top) { LanguageSelector() } } .padding() - .tabItem { Label("Library", systemImage: "folder.badge.gearshape") } + .tabItem { Label("Library".localized, systemImage: "folder.badge.gearshape") } } } @@ -126,22 +126,22 @@ struct Settings: View { miscellaneous } .modifier(ToolbarRoleBrowser()) - .navigationTitle("Settings") + .navigationTitle("Settings".localized) } var readingSettings: some View { - Section("Reading") { + Section("Reading".localized) { Stepper(value: $webViewPageZoom, in: 0.5...2, step: 0.05) { - Text("Page zoom: \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") + Text("Page zoom".localized + ": \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") } - Picker("External link", selection: $externalLinkLoadingPolicy) { + Picker("External link".localized, selection: $externalLinkLoadingPolicy) { ForEach(ExternalLinkLoadingPolicy.allCases) { loadingPolicy in - Text(loadingPolicy.name).tag(loadingPolicy) + Text(loadingPolicy.name.localized).tag(loadingPolicy) } } - Picker("Search snippet", selection: $searchResultSnippetMode) { + Picker("Search snippet".localized, selection: $searchResultSnippetMode) { ForEach(SearchResultSnippetMode.allCases) { snippetMode in - Text(snippetMode.name).tag(snippetMode) + Text(snippetMode.name.localized).tag(snippetMode) } } } @@ -154,58 +154,58 @@ struct Settings: View { } label: { SelectedLanaguageLabel() } - Toggle("Download using cellular", isOn: $downloadUsingCellular) + Toggle("Download using cellular".localized, isOn: $downloadUsingCellular) } header: { - Text("Library") + Text("Library".localized) } footer: { - Text("Change will only apply to new download tasks.") + Text("Change will only apply to new download tasks.".localized) } } var catalogSettings: some View { Section { HStack { - Text("Last refresh") + Text("Last refresh".localized) Spacer() LibraryLastRefreshTime().foregroundColor(.secondary) } if library.isInProgress { HStack { - Text("Refreshing...").foregroundColor(.secondary) + Text("Refreshing...".localized).foregroundColor(.secondary) Spacer() ProgressView().progressViewStyle(.circular) } } else { - Button("Refresh Now") { + Button("Refresh Now".localized) { library.start(isUserInitiated: true) } } - Toggle("Auto refresh", isOn: $libraryAutoRefresh) + Toggle("Auto refresh".localized, isOn: $libraryAutoRefresh) } header: { - Text("Catalog") + Text("Catalog".localized) } footer: { - Text("When enabled, the library catalog will be refreshed automatically when outdated.") + Text("When enabled, the library catalog will be refreshed automatically when outdated.".localized) }.onChange(of: libraryAutoRefresh) { LibraryOperations.applyLibraryAutoRefreshSetting(isEnabled: $0) } } var backupSettings: some View { Section { - Toggle("Include zim files in backup", isOn: $backupDocumentDirectory) + Toggle("Include zim files in backup".localized, isOn: $backupDocumentDirectory) } header: { - Text("Backup") + Text("Backup".localized) } footer: { - Text("Does not apply to files opened in place.") + Text("Does not apply to files opened in place.".localized) }.onChange(of: backupDocumentDirectory) { LibraryOperations.applyFileBackupSetting(isEnabled: $0) } } var miscellaneous: some View { - Section("Misc") { - Button("Feedback") { UIApplication.shared.open(URL(string: "mailto:feedback@kiwix.org")!) } - Button("Rate the App") { + Section("Misc".lowercased) { + Button("Feedback".localized) { UIApplication.shared.open(URL(string: "mailto:feedback@kiwix.org")!) } + Button("Rate the App".localized) { let url = URL(string: "itms-apps://itunes.apple.com/us/app/kiwix/id997079563?action=write-review")! UIApplication.shared.open(url) } - NavigationLink("About") { About() } + NavigationLink("About".localized) { About() } } } } @@ -215,7 +215,7 @@ private struct SelectedLanaguageLabel: View { var body: some View { HStack { - Text("Languages") + Text("Languages".localized) Spacer() if languageCodes.count == 1, let languageCode = languageCodes.first, diff --git a/Views/ViewModifiers/AlertHandler.swift b/Views/ViewModifiers/AlertHandler.swift index 940a4ecc5..b305b0550 100644 --- a/Views/ViewModifiers/AlertHandler.swift +++ b/Views/ViewModifiers/AlertHandler.swift @@ -21,7 +21,7 @@ struct AlertHandler: ViewModifier { .alert(item: $activeAlert) { alert in switch alert { case .articleFailedToLoad: - return Alert(title: Text("Unable to load the article requested.")) + return Alert(title: Text("Unable to load the article requested.".localized)) } } } diff --git a/Views/ViewModifiers/BookmarkContextMenu.swift b/Views/ViewModifiers/BookmarkContextMenu.swift index 235e984dd..5620f604f 100644 --- a/Views/ViewModifiers/BookmarkContextMenu.swift +++ b/Views/ViewModifiers/BookmarkContextMenu.swift @@ -18,13 +18,13 @@ struct BookmarkContextMenu: ViewModifier { Button { NotificationCenter.openURL(bookmark.articleURL) } label: { - Label("View", systemImage: "doc.richtext") + Label("View".localized, systemImage: "doc.richtext") } Button(role: .destructive) { managedObjectContext.delete(bookmark) try? managedObjectContext.save() } label: { - Label("Remove", systemImage: "star.slash.fill") + Label("Remove".localized, systemImage: "star.slash.fill") } } } diff --git a/Views/ViewModifiers/ExternalLinkHandler.swift b/Views/ViewModifiers/ExternalLinkHandler.swift index a3a1d0cb4..aff6a6613 100644 --- a/Views/ViewModifiers/ExternalLinkHandler.swift +++ b/Views/ViewModifiers/ExternalLinkHandler.swift @@ -41,19 +41,19 @@ struct ExternalLinkHandler: ViewModifier { activeAlert = .notLoading } } - .alert("External Link", isPresented: $isAlertPresented, presenting: activeAlert) { alert in + .alert("External Link".localized, isPresented: $isAlertPresented, presenting: activeAlert) { alert in if case .ask(let url) = alert { - Button("Load the link") { + Button("Load the link".localized) { load(url: url) } - Button("Cancel", role: .cancel) { } + Button("Cancel".localized, role: .cancel) { } } } message: { alert in switch alert { case .ask: - Text("An external link is tapped, do you wish to load the link?") + Text("An external link is tapped, do you wish to load the link?".localized) case .notLoading: - Text("An external link is tapped. However, your current setting does not allow it to be loaded.") + Text("An external link is tapped. However, your current setting does not allow it to be loaded.".localized) } } #if os(iOS) diff --git a/Views/ViewModifiers/FileImport.swift b/Views/ViewModifiers/FileImport.swift index da40c6ea5..020d7cc6c 100644 --- a/Views/ViewModifiers/FileImport.swift +++ b/Views/ViewModifiers/FileImport.swift @@ -41,7 +41,7 @@ struct OpenFileButton: View { guard case let .success(urls) = result else { return } NotificationCenter.openFiles(urls, context: context) } - .help("Open a zim file") + .help("Open a zim file".localized) .keyboardShortcut("o") } } @@ -98,11 +98,11 @@ struct OpenFileHandler: ViewModifier { isAlertPresented = true activeAlert = .unableToOpen(filenames: invalidURLs.map({ $0.lastPathComponent })) } - }.alert("Unable to open file", isPresented: $isAlertPresented, presenting: activeAlert) { _ in + }.alert("Unable to open file".localized, isPresented: $isAlertPresented, presenting: activeAlert) { _ in } message: { alert in switch alert { case .unableToOpen(let filenames): - Text("\(ListFormatter.localizedString(byJoining: filenames)) cannot be opened.") + Text("%@ cannot be opened.".localizedWithFormat(withArgs: ListFormatter.localizedString(byJoining: filenames))) } } } diff --git a/Views/Welcome.swift b/Views/Welcome.swift index 3c079fe44..377c58e6f 100644 --- a/Views/Welcome.swift +++ b/Views/Welcome.swift @@ -67,7 +67,7 @@ struct Welcome: View { alignment: .leading, spacing: 12 ) { - GridSection(title: "Main Page") { + GridSection(title: "Main Page".localized) { ForEach(zimFiles) { zimFile in Button { guard let url = ZimFileService.shared.getMainPageURL(zimFileID: zimFile.fileID) else { return } @@ -78,7 +78,7 @@ struct Welcome: View { } } if !bookmarks.isEmpty { - GridSection(title: "Bookmarks") { + GridSection(title: "Bookmarks".localized) { ForEach(bookmarks.prefix(6)) { bookmark in Button { browser.load(url: bookmark.articleURL) @@ -112,7 +112,7 @@ struct Welcome: View { OpenFileButton(context: .onBoarding) { HStack { Spacer() - Text("Open File") + Text("Open File".localized) Spacer() }.padding(6) } @@ -123,15 +123,15 @@ struct Welcome: View { Spacer() if library.isInProgress { #if os(macOS) - Text("Fetching...") + Text("Fetching...".localized) #elseif os(iOS) HStack(spacing: 6) { ProgressView().frame(maxHeight: 10) - Text("Fetching...") + Text("Fetching...".localized) } #endif } else { - Text("Fetch Catalog") + Text("Fetch Catalog".localized) } Spacer() }.padding(6) diff --git a/WikiMed/es.lproj/LaunchScreen.strings b/WikiMed/es.lproj/LaunchScreen.strings new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/WikiMed/es.lproj/LaunchScreen.strings @@ -0,0 +1 @@ + diff --git a/WikiMed/es.lproj/Main.strings b/WikiMed/es.lproj/Main.strings new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/WikiMed/es.lproj/Main.strings @@ -0,0 +1 @@ + From 9a12338559937c9826c08e66c9b408225a410bf8 Mon Sep 17 00:00:00 2001 From: tvision106 Date: Tue, 14 Nov 2023 17:12:29 -0500 Subject: [PATCH 02/17] fix codefactor issue --- Model/Utilities/String+Extension.swift | 9 ++++----- Support/en.lproj/Localizable.strings | 8 ++++++-- ViewModel/BrowserViewModel.swift | 3 ++- Views/BuildingBlocks/ZimFileRow.swift | 3 ++- Views/Library/ZimFileDetail.swift | 8 ++++++-- Views/Settings/About.swift | 3 ++- Views/Settings/Settings.swift | 3 ++- Views/ViewModifiers/ExternalLinkHandler.swift | 5 ++++- Views/ViewModifiers/FileImport.swift | 3 ++- 9 files changed, 30 insertions(+), 15 deletions(-) diff --git a/Model/Utilities/String+Extension.swift b/Model/Utilities/String+Extension.swift index 9f1ad1364..e782c0d14 100644 --- a/Model/Utilities/String+Extension.swift +++ b/Model/Utilities/String+Extension.swift @@ -14,7 +14,7 @@ extension String { return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") } - func localized(withComment:String) -> String { + func localized(withComment: String) -> String { return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: withComment) } @@ -22,11 +22,10 @@ extension String { let format = NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") switch withArgs.count { - case 1: return String.localizedStringWithFormat(format, withArgs[0]) - case 2: return String.localizedStringWithFormat(format, withArgs[0], withArgs[1]) - default: return String.localizedStringWithFormat(format, withArgs) + case 1: return String.localizedStringWithFormat(format, withArgs[0]) + case 2: return String.localizedStringWithFormat(format, withArgs[0], withArgs[1]) + default: return String.localizedStringWithFormat(format, withArgs) } } } - diff --git a/Support/en.lproj/Localizable.strings b/Support/en.lproj/Localizable.strings index 632378726..fd8353f44 100644 --- a/Support/en.lproj/Localizable.strings +++ b/Support/en.lproj/Localizable.strings @@ -45,7 +45,9 @@ "Load the link" = "Load the link"; "Cancel" = "Cancel"; "An external link is tapped, do you wish to load the link?" = "An external link is tapped, do you wish to load the link?"; -"An external link is tapped. However, your current setting does not allow it to be loaded." = "An external link is tapped. However, your current setting does not allow it to be loaded."; +"An external link is tapped. \ +However, your current setting does not allow it to be loaded." = "An external link is tapped. \ +However, your current setting does not allow it to be loaded."; "Open a zim file" = "Open a zim file"; "Unable to open file" = "Unable to open file"; "%@ cannot be opened." = "%@ cannot be opened."; @@ -97,7 +99,9 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; but the original file will remain in place." = "All bookmarked articles linked to this zim file will be deleted, \ but the original file will remain in place."; "Delete %@" = "Delete %@"; -"The zim file and all bookmarked articles linked to this zim file will be deleted." = "The zim file and all bookmarked articles linked to this zim file will be deleted."; +"The zim file and all bookmarked articles \ +linked to this zim file will be deleted." = "The zim file and all bookmarked articles \ +linked to this zim file will be deleted."; "Download" = "Download"; "Space Warning" = "Space Warning"; "There might not be enough space on your device for this zim file." = "There might not be enough space on your device for this zim file."; diff --git a/ViewModel/BrowserViewModel.swift b/ViewModel/BrowserViewModel.swift index 7eaccd1e6..9de806141 100644 --- a/ViewModel/BrowserViewModel.swift +++ b/ViewModel/BrowserViewModel.swift @@ -266,7 +266,8 @@ class BrowserViewModel: NSObject, ObservableObject, let predicate = NSPredicate(format: "articleURL == %@", url as CVarArg) let request = Bookmark.fetchRequest(predicate: predicate) if let bookmarks = try? context.fetch(request), !bookmarks.isEmpty { - return UIAction(title: "Remove Bookmark".localized, image: UIImage(systemName: "star.slash.fill")) { _ in + return UIAction(title: "Remove Bookmark".localized, + image: UIImage(systemName: "star.slash.fill")) { _ in self.deleteBookmark(url: url) } } else { diff --git a/Views/BuildingBlocks/ZimFileRow.swift b/Views/BuildingBlocks/ZimFileRow.swift index 73510e563..df40aa9db 100644 --- a/Views/BuildingBlocks/ZimFileRow.swift +++ b/Views/BuildingBlocks/ZimFileRow.swift @@ -30,7 +30,8 @@ struct ZimFileRow: View { Formatter.size.string(fromByteCount: zimFile.size), { if #available(iOS 15.0, *) { - return "\(zimFile.articleCount.formatted(.number.notation(.compactName)))" + "articles".localized + return "\(zimFile.articleCount.formatted(.number.notation(.compactName)))" + + "articles".localized } else { return Formatter.largeNumber(zimFile.articleCount) } diff --git a/Views/Library/ZimFileDetail.swift b/Views/Library/ZimFileDetail.swift index 5ee91f1a1..4b58afaa7 100644 --- a/Views/Library/ZimFileDetail.swift +++ b/Views/Library/ZimFileDetail.swift @@ -138,7 +138,10 @@ struct ZimFileDetail: View { }.alert(isPresented: $isPresentingDeleteAlert) { Alert( title: Text("Delete %@".localizedWithFormat(withArgs: zimFile.name)), - message: Text("The zim file and all bookmarked articles linked to this zim file will be deleted.".localized), + message: Text(""" + The zim file and all bookmarked articles \ + linked to this zim file will be deleted. + """.localized), primaryButton: .destructive(Text("Delete".localized)) { LibraryOperations.delete(zimFileID: zimFile.fileID) #if os(iOS) @@ -177,7 +180,8 @@ struct ZimFileDetail: View { @ViewBuilder var basicInfo: some View { - Attribute(title: "Language".localized, detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) + Attribute(title: "Language".localized, + detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) Attribute(title: "Category".localized, detail: Category(rawValue: zimFile.category)?.description) Attribute(title: "Size".localized, detail: Formatter.size.string(fromByteCount: zimFile.size)) Attribute(title: "Created".localized, detail: Formatter.dateMedium.string(from: zimFile.created)) diff --git a/Views/Settings/About.swift b/Views/Settings/About.swift index c9a7407c6..72d5bed70 100644 --- a/Views/Settings/About.swift +++ b/Views/Settings/About.swift @@ -92,7 +92,8 @@ struct About: View { } private var appVersion: some View { - Attribute(title: "Version".localized, detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) + Attribute(title: "Version".localized, + detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) } private var buildNumber: some View { diff --git a/Views/Settings/Settings.swift b/Views/Settings/Settings.swift index bee1ddc50..cf6bbd853 100644 --- a/Views/Settings/Settings.swift +++ b/Views/Settings/Settings.swift @@ -132,7 +132,8 @@ struct Settings: View { var readingSettings: some View { Section("Reading".localized) { Stepper(value: $webViewPageZoom, in: 0.5...2, step: 0.05) { - Text("Page zoom".localized + ": \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") + Text("Page zoom".localized + + ": \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") } Picker("External link".localized, selection: $externalLinkLoadingPolicy) { ForEach(ExternalLinkLoadingPolicy.allCases) { loadingPolicy in diff --git a/Views/ViewModifiers/ExternalLinkHandler.swift b/Views/ViewModifiers/ExternalLinkHandler.swift index aff6a6613..f48bde1a4 100644 --- a/Views/ViewModifiers/ExternalLinkHandler.swift +++ b/Views/ViewModifiers/ExternalLinkHandler.swift @@ -53,7 +53,10 @@ struct ExternalLinkHandler: ViewModifier { case .ask: Text("An external link is tapped, do you wish to load the link?".localized) case .notLoading: - Text("An external link is tapped. However, your current setting does not allow it to be loaded.".localized) + Text(""" + An external link is tapped. \ + However, your current setting does not allow it to be loaded. + """.localized) } } #if os(iOS) diff --git a/Views/ViewModifiers/FileImport.swift b/Views/ViewModifiers/FileImport.swift index 020d7cc6c..c3dd86c45 100644 --- a/Views/ViewModifiers/FileImport.swift +++ b/Views/ViewModifiers/FileImport.swift @@ -102,7 +102,8 @@ struct OpenFileHandler: ViewModifier { } message: { alert in switch alert { case .unableToOpen(let filenames): - Text("%@ cannot be opened.".localizedWithFormat(withArgs: ListFormatter.localizedString(byJoining: filenames))) + Text("%@ cannot be opened.".localizedWithFormat(withArgs: + ListFormatter.localizedString(byJoining: filenames))) } } } From a5bc9053f508612a03ac8cb1d4f02e78e7016686 Mon Sep 17 00:00:00 2001 From: tvision106 Date: Tue, 14 Nov 2023 17:15:21 -0500 Subject: [PATCH 03/17] fix codefactor issue --- Views/ViewModifiers/FileImport.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Views/ViewModifiers/FileImport.swift b/Views/ViewModifiers/FileImport.swift index c3dd86c45..edd5f4263 100644 --- a/Views/ViewModifiers/FileImport.swift +++ b/Views/ViewModifiers/FileImport.swift @@ -102,8 +102,8 @@ struct OpenFileHandler: ViewModifier { } message: { alert in switch alert { case .unableToOpen(let filenames): - Text("%@ cannot be opened.".localizedWithFormat(withArgs: - ListFormatter.localizedString(byJoining: filenames))) + let name = ListFormatter.localizedString(byJoining: filenames) + Text("%@ cannot be opened.".localizedWithFormat(withArgs: name)) } } } From 3c61ff68220c8119917597700295e5c2b0658e67 Mon Sep 17 00:00:00 2001 From: tvision106 Date: Wed, 15 Nov 2023 13:24:44 -0500 Subject: [PATCH 04/17] fix some issues for long strings --- Support/en.lproj/Localizable.strings | 21 ++++++++----------- Views/BuildingBlocks/ArticleCell.swift | 7 +------ Views/BuildingBlocks/SearchResultRow.swift | 7 +------ Views/Library/ZimFileDetail.swift | 10 ++------- Views/Settings/About.swift | 8 +------ Views/ViewModifiers/ExternalLinkHandler.swift | 5 +---- 6 files changed, 15 insertions(+), 43 deletions(-) diff --git a/Support/en.lproj/Localizable.strings b/Support/en.lproj/Localizable.strings index fd8353f44..fbcd8a241 100644 --- a/Support/en.lproj/Localizable.strings +++ b/Support/en.lproj/Localizable.strings @@ -45,16 +45,14 @@ "Load the link" = "Load the link"; "Cancel" = "Cancel"; "An external link is tapped, do you wish to load the link?" = "An external link is tapped, do you wish to load the link?"; -"An external link is tapped. \ -However, your current setting does not allow it to be loaded." = "An external link is tapped. \ +"loc-extenral-alert" = "An external link is tapped. \ However, your current setting does not allow it to be loaded."; "Open a zim file" = "Open a zim file"; "Unable to open file" = "Unable to open file"; "%@ cannot be opened." = "%@ cannot be opened."; "No snippet" = "No snippet"; "Article Title" = "Article Title"; -"Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ -sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ +"loc-article-cell-template" = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; "Unknown" = "Unknown"; "Yes" = "Yes"; @@ -95,12 +93,10 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; "Download using cellular" = "Download using cellular"; "Unlink" = "Unlink"; "Unlink %@" = "Unlink %@"; -"All bookmarked articles linked to this zim file will be deleted, \ -but the original file will remain in place." = "All bookmarked articles linked to this zim file will be deleted, \ +"loc-ZimFileDetail-Alert-unlink" = "All bookmarked articles linked to this zim file will be deleted, \ but the original file will remain in place."; "Delete %@" = "Delete %@"; -"The zim file and all bookmarked articles \ -linked to this zim file will be deleted." = "The zim file and all bookmarked articles \ +"loc-ZimFileDetail-Alert-Delete" = "The zim file and all bookmarked articles \ linked to this zim file will be deleted."; "Download" = "Download"; "Space Warning" = "Space Warning"; @@ -152,9 +148,7 @@ linked to this zim file will be deleted."; "Dependencies" = "Dependencies"; "License" = "License"; "Version" = "Version"; -"Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ -It makes knowledge available to people with no or limited internet access. \ -The software as well as the content is free to use for anyone." = "Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ +"loc-About-description" = "Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ It makes knowledge available to people with no or limited internet access. \ The software as well as the content is free to use for anyone."; "This app is released under the terms of the GNU General Public License version 3." = "This app is released under the terms of the GNU General Public License version 3."; @@ -203,4 +197,7 @@ The software as well as the content is free to use for anyone."; "Vikidia" = "Vikidia"; "StackExchange" = "StackExchange"; "Other" = "Other"; - +"Disabled" = "Disabled"; +"First Paragraph" = "First Paragraph"; +"First Sentence" = "First Sentence"; +"Matches" = "Matches"; diff --git a/Views/BuildingBlocks/ArticleCell.swift b/Views/BuildingBlocks/ArticleCell.swift index fa8069c89..f947e7ddd 100644 --- a/Views/BuildingBlocks/ArticleCell.swift +++ b/Views/BuildingBlocks/ArticleCell.swift @@ -65,12 +65,7 @@ struct ArticleCell: View { struct ArticleCell_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """.localized - ) + result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) return result }() diff --git a/Views/BuildingBlocks/SearchResultRow.swift b/Views/BuildingBlocks/SearchResultRow.swift index 57edb5f07..249f72cd4 100644 --- a/Views/BuildingBlocks/SearchResultRow.swift +++ b/Views/BuildingBlocks/SearchResultRow.swift @@ -39,12 +39,7 @@ struct SearchResultRow: View { struct SearchResultRow_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """.localized - ) + result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) return result }() diff --git a/Views/Library/ZimFileDetail.swift b/Views/Library/ZimFileDetail.swift index 4b58afaa7..7bfafb0a0 100644 --- a/Views/Library/ZimFileDetail.swift +++ b/Views/Library/ZimFileDetail.swift @@ -117,10 +117,7 @@ struct ZimFileDetail: View { }.alert(isPresented: $isPresentingUnlinkAlert) { Alert( title: Text("Unlink %@".localizedWithFormat(withArgs: zimFile.name)), - message: Text(""" - All bookmarked articles linked to this zim file will be deleted, \ - but the original file will remain in place. - """), + message: Text("loc-ZimFileDetail-Alert-unlink".localized), primaryButton: .destructive(Text("Unlink".localized)) { LibraryOperations.unlink(zimFileID: zimFile.fileID) #if os(iOS) @@ -138,10 +135,7 @@ struct ZimFileDetail: View { }.alert(isPresented: $isPresentingDeleteAlert) { Alert( title: Text("Delete %@".localizedWithFormat(withArgs: zimFile.name)), - message: Text(""" - The zim file and all bookmarked articles \ - linked to this zim file will be deleted. - """.localized), + message: Text("loc-ZimFileDetail-Alert-Delete".localized), primaryButton: .destructive(Text("Delete".localized)) { LibraryOperations.delete(zimFileID: zimFile.fileID) #if os(iOS) diff --git a/Views/Settings/About.swift b/Views/Settings/About.swift index 72d5bed70..3db016d6c 100644 --- a/Views/Settings/About.swift +++ b/Views/Settings/About.swift @@ -78,13 +78,7 @@ struct About: View { } private var about: some View { - Text( - """ - Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ - It makes knowledge available to people with no or limited internet access. \ - The software as well as the content is free to use for anyone. - """.localized - ) + Text("loc-About-description".localized) } private var release: some View { diff --git a/Views/ViewModifiers/ExternalLinkHandler.swift b/Views/ViewModifiers/ExternalLinkHandler.swift index f48bde1a4..4d3860d46 100644 --- a/Views/ViewModifiers/ExternalLinkHandler.swift +++ b/Views/ViewModifiers/ExternalLinkHandler.swift @@ -53,10 +53,7 @@ struct ExternalLinkHandler: ViewModifier { case .ask: Text("An external link is tapped, do you wish to load the link?".localized) case .notLoading: - Text(""" - An external link is tapped. \ - However, your current setting does not allow it to be loaded. - """.localized) + Text("loc-extenral-alert".localized) } } #if os(iOS) From 4df733a179f0d841be40b1a85bd6fb37dc504f6d Mon Sep 17 00:00:00 2001 From: tvision106 Date: Wed, 15 Nov 2023 14:50:45 -0500 Subject: [PATCH 05/17] fix codefactor issue --- Views/BuildingBlocks/ArticleCell.swift | 2 +- Views/BuildingBlocks/SearchResultRow.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Views/BuildingBlocks/ArticleCell.swift b/Views/BuildingBlocks/ArticleCell.swift index f947e7ddd..a3a1b3f72 100644 --- a/Views/BuildingBlocks/ArticleCell.swift +++ b/Views/BuildingBlocks/ArticleCell.swift @@ -65,7 +65,7 @@ struct ArticleCell: View { struct ArticleCell_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) + result.snippet = NSAttributedString(string: "loc-article-cell-template".localized) return result }() diff --git a/Views/BuildingBlocks/SearchResultRow.swift b/Views/BuildingBlocks/SearchResultRow.swift index 249f72cd4..690a37328 100644 --- a/Views/BuildingBlocks/SearchResultRow.swift +++ b/Views/BuildingBlocks/SearchResultRow.swift @@ -39,7 +39,7 @@ struct SearchResultRow: View { struct SearchResultRow_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) + result.snippet = NSAttributedString(string: "loc-article-cell-template".localized) return result }() From 22c72ae2a2513561cdf73de9e3e0e1db8671c911 Mon Sep 17 00:00:00 2001 From: tvision106 Date: Tue, 14 Nov 2023 16:20:36 -0500 Subject: [PATCH 06/17] Update the localization --- App/App_macOS.swift | 12 +- App/SidebarViewController.swift | 10 +- App/SplitViewController.swift | 2 +- Kiwix.xcodeproj/project.pbxproj | 33 ++- Model/DownloadService.swift | 4 +- Model/Entities/Errors.swift | 6 +- Model/Utilities/String+Extension.swift | 32 +++ Support/en.lproj/Localizable.strings | 202 ++++++++++++++++++ Support/es.lproj/Localizable.strings | 8 + ViewModel/BrowserViewModel.swift | 8 +- Views/Bookmarks.swift | 6 +- Views/BrowserTab.swift | 2 +- Views/BuildingBlocks/ArticleCell.swift | 6 +- Views/BuildingBlocks/Attribute.swift | 4 +- Views/BuildingBlocks/DownloadTaskCell.swift | 4 +- Views/BuildingBlocks/FlavorTag.swift | 6 +- Views/BuildingBlocks/GridSection.swift | 4 +- .../LibraryLastRefreshTime.swift | 4 +- Views/BuildingBlocks/Message.swift | 2 +- Views/BuildingBlocks/SearchResultRow.swift | 4 +- Views/BuildingBlocks/SheetContent.swift | 2 +- Views/BuildingBlocks/TabLabel.swift | 4 +- .../ZimFileMissingIndicator.swift | 2 +- Views/BuildingBlocks/ZimFileRow.swift | 2 +- Views/Buttons/ArticleShortcutButtons.swift | 16 +- Views/Buttons/BookmarkButton.swift | 14 +- Views/Buttons/NavigationButtons.swift | 4 +- Views/Buttons/OutlineButton.swift | 10 +- Views/Buttons/TabsManagerButton.swift | 26 +-- Views/Commands.swift | 10 +- Views/Library/Library.swift | 14 +- Views/Library/ZimFileDetail.swift | 78 +++---- Views/Library/ZimFilesCategories.swift | 10 +- Views/Library/ZimFilesDownloads.swift | 6 +- Views/Library/ZimFilesNew.swift | 8 +- Views/Library/ZimFilesOpened.swift | 10 +- Views/SearchResults.swift | 22 +- Views/Settings/About.swift | 34 +-- Views/Settings/LanguageSelector.swift | 16 +- Views/Settings/Settings.swift | 76 +++---- Views/ViewModifiers/AlertHandler.swift | 2 +- Views/ViewModifiers/BookmarkContextMenu.swift | 4 +- Views/ViewModifiers/ExternalLinkHandler.swift | 10 +- Views/ViewModifiers/FileImport.swift | 6 +- Views/Welcome.swift | 12 +- WikiMed/es.lproj/LaunchScreen.strings | 1 + WikiMed/es.lproj/Main.strings | 1 + 47 files changed, 515 insertions(+), 244 deletions(-) create mode 100644 Model/Utilities/String+Extension.swift create mode 100644 Support/en.lproj/Localizable.strings create mode 100644 Support/es.lproj/Localizable.strings create mode 100644 WikiMed/es.lproj/LaunchScreen.strings create mode 100644 WikiMed/es.lproj/Main.strings diff --git a/App/App_macOS.swift b/App/App_macOS.swift index ce6efe2cf..2bbdcbf7e 100644 --- a/App/App_macOS.swift +++ b/App/App_macOS.swift @@ -32,10 +32,10 @@ struct Kiwix: App { }.commands { SidebarCommands() CommandGroup(replacing: .importExport) { - OpenFileButton(context: .command) { Text("Open...") } + OpenFileButton(context: .command) { Text("Open...".localized) } } CommandGroup(replacing: .newItem) { - Button("New Tab") { + Button("New Tab".localized) { guard let currentWindow = NSApp.keyWindow, let controller = currentWindow.windowController else { return } controller.newWindowForTab(nil) @@ -91,11 +91,11 @@ struct RootView: View { NavigationView { List(selection: $navigation.currentItem) { ForEach(primaryItems, id: \.self) { navigationItem in - Label(navigationItem.name, systemImage: navigationItem.icon) + Label(navigationItem.name.localized, systemImage: navigationItem.icon) } - Section("Library") { + Section("Library".localized) { ForEach(libraryItems, id: \.self) { navigationItem in - Label(navigationItem.name, systemImage: navigationItem.icon) + Label(navigationItem.name.localized, systemImage: navigationItem.icon) } } } @@ -106,7 +106,7 @@ struct RootView: View { responder.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil) } label: { Image(systemName: "sidebar.leading") - }.help("Show sidebar") + }.help("Show sidebar".localized) } switch navigation.currentItem { case .reading: diff --git a/App/SidebarViewController.swift b/App/SidebarViewController.swift index 836850f3a..a29afb156 100644 --- a/App/SidebarViewController.swift +++ b/App/SidebarViewController.swift @@ -86,7 +86,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl }, menu: UIMenu(children: [ UIAction( - title: "Close This Tab", + title: "Close This Tab".localized, image: UIImage(systemName: "xmark.square"), attributes: .destructive ) { [unowned self] _ in @@ -95,7 +95,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl splitViewController.navigationViewModel.deleteTab(tabID: tabID) }, UIAction( - title: "Close All Tabs", + title: "Close All Tabs".localized, image: UIImage(systemName: "xmark.square.fill"), attributes: .destructive ) { [unowned self] _ in @@ -188,11 +188,11 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl switch section { case .tabs: var config = UIListContentConfiguration.sidebarHeader() - config.text = "Tabs" + config.text = "Tabs".localized headerView.contentConfiguration = config case .library: var config = UIListContentConfiguration.sidebarHeader() - config.text = "Library" + config.text = "Library".localized headerView.contentConfiguration = config default: headerView.contentConfiguration = nil @@ -203,7 +203,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl guard let splitViewController = splitViewController as? SplitViewController, let item = dataSource.itemIdentifier(for: indexPath), case let .tab(tabID) = item else { return nil } - let action = UIContextualAction(style: .destructive, title: "Close") { _, _, _ in + let action = UIContextualAction(style: .destructive, title: "Close".localized) { _, _, _ in splitViewController.navigationViewModel.deleteTab(tabID: tabID) } action.image = UIImage(systemName: "xmark") diff --git a/App/SplitViewController.swift b/App/SplitViewController.swift index 1e8300320..45a733c40 100644 --- a/App/SplitViewController.swift +++ b/App/SplitViewController.swift @@ -128,7 +128,7 @@ class SplitViewController: UISplitViewController { let controller = UIHostingController(rootView: Settings()) setViewController(UINavigationController(rootViewController: controller), for: .secondary) default: - let controller = UIHostingController(rootView: Text("Not yet implemented")) + let controller = UIHostingController(rootView: Text("Not yet implemented".localized)) setViewController(UINavigationController(rootViewController: controller), for: .secondary) } } diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index 2e2bc8ae8..2aa1a10ab 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 8E4396462B02E455007F0BC4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8E4396492B02E455007F0BC4 /* Localizable.strings */; }; + 8E4396472B02E455007F0BC4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8E4396492B02E455007F0BC4 /* Localizable.strings */; }; + 8E43964C2B02E4C6007F0BC4 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */; }; + 8E43964D2B02E4C6007F0BC4 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */; }; 97008ABD2974A5BF0076E60C /* OPDSParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97008ABC2974A5BF0076E60C /* OPDSParserTests.swift */; }; 9709C0982A8E4C5700E4564C /* Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9709C0972A8E4C5700E4564C /* Commands.swift */; }; 97121EBE28849F0000371AEB /* ZimFileMissingIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97121EBC28849F0000371AEB /* ZimFileMissingIndicator.swift */; }; @@ -124,6 +128,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 8E4396442B02E443007F0BC4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Main.strings; sourceTree = ""; }; + 8E4396452B02E443007F0BC4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/LaunchScreen.strings; sourceTree = ""; }; + 8E4396482B02E455007F0BC4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + 8E43964A2B02E458007F0BC4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; 97008AB42974A5A70076E60C /* WikiMed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WikiMed.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97008ABA2974A5BF0076E60C /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 97008ABC2974A5BF0076E60C /* OPDSParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPDSParserTests.swift; sourceTree = ""; }; @@ -293,6 +302,7 @@ 9779A7E224567A5A00F6F6FF /* Log.swift */, 97B3BACD2736CE3500A23F49 /* URL.swift */, 9779A73A2456796B00F6F6FF /* WebKitHandler.swift */, + 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */, ); path = Utilities; sourceTree = ""; @@ -386,8 +396,8 @@ 974E7EE22930201500BDF59C /* ZimFileService */ = { isa = PBXGroup; children = ( - 974E7EE32930201500BDF59C /* ZimFileService.mm */, 974E7EE42930201500BDF59C /* ZimFileService.h */, + 974E7EE32930201500BDF59C /* ZimFileService.mm */, 974E7EE52930201500BDF59C /* ZimFileService.swift */, ); path = ZimFileService; @@ -549,6 +559,7 @@ 97E94B22271EF250005B0295 /* Kiwix.entitlements */, 9779A5D02456796A00F6F6FF /* Kiwix-Bridging-Header.h */, 970885D0271339A300C5795C /* wikipedia_dark.css */, + 8E4396492B02E455007F0BC4 /* Localizable.strings */, ); path = Support; sourceTree = ""; @@ -649,6 +660,7 @@ knownRegions = ( en, Base, + es, ); mainGroup = 97A2AB7F1C1B80FF00052E74; packageReferences = ( @@ -681,6 +693,7 @@ buildActionMask = 2147483647; files = ( 973A0DF72830929C00B41E71 /* Assets.xcassets in Resources */, + 8E4396462B02E455007F0BC4 /* Localizable.strings in Resources */, 979D3A7C284159BF00E396B8 /* injection.js in Resources */, 97DE2BAD283B133700C63D9B /* wikipedia_dark.css in Resources */, ); @@ -691,6 +704,7 @@ buildActionMask = 2147483647; files = ( 97B448A1210FBC2E0004B056 /* LaunchScreen.storyboard in Resources */, + 8E4396472B02E455007F0BC4 /* Localizable.strings in Resources */, 97B4489E210FBC2E0004B056 /* Assets.xcassets in Resources */, 97B4489C210FBC2C0004B056 /* Main.storyboard in Resources */, ); @@ -753,6 +767,7 @@ buildActionMask = 2147483647; files = ( 972DE4BB2814A5A4004FD9B9 /* Errors.swift in Sources */, + 8E43964C2B02E4C6007F0BC4 /* String+Extension.swift in Sources */, 9790CA5A28A05EBB00D39FC6 /* ZimFilesCategories.swift in Sources */, 97486D08284A42B90096E4DD /* SearchResultRow.swift in Sources */, 9753D949285B55F100A626CC /* DefaultKeys.swift in Sources */, @@ -836,6 +851,7 @@ buildActionMask = 2147483647; files = ( 97B44899210FBC2C0004B056 /* ViewController.swift in Sources */, + 8E43964D2B02E4C6007F0BC4 /* String+Extension.swift in Sources */, 97B44897210FBC2C0004B056 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -851,10 +867,20 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + 8E4396492B02E455007F0BC4 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 8E4396482B02E455007F0BC4 /* en */, + 8E43964A2B02E458007F0BC4 /* es */, + ); + name = Localizable.strings; + sourceTree = ""; + }; 97B4489A210FBC2C0004B056 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 97B4489B210FBC2C0004B056 /* Base */, + 8E4396442B02E443007F0BC4 /* es */, ); name = Main.storyboard; sourceTree = ""; @@ -863,6 +889,7 @@ isa = PBXVariantGroup; children = ( 97B448A0210FBC2E0004B056 /* Base */, + 8E4396452B02E443007F0BC4 /* es */, ); name = LaunchScreen.storyboard; sourceTree = ""; @@ -943,7 +970,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 119; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = L7HWM3SP3L; + DEVELOPMENT_TEAM = ""; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -989,7 +1016,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 119; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = L7HWM3SP3L; + DEVELOPMENT_TEAM = ""; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; diff --git a/Model/DownloadService.swift b/Model/DownloadService.swift index 39761c773..a51b0e1d9 100644 --- a/Model/DownloadService.swift +++ b/Model/DownloadService.swift @@ -191,10 +191,10 @@ class DownloadService: NSObject, URLSessionDelegate, URLSessionTaskDelegate, URL Database.shared.container.performBackgroundTask { context in // configure notification content let content = UNMutableNotificationContent() - content.title = "Download Completed" + content.title = "Download Completed".localized content.sound = .default if let zimFile = try? context.fetch(ZimFile.fetchRequest(fileID: zimFileID)).first { - content.body = "\(zimFile.name) has been downloaded successfully." + content.body = "%@ has been downloaded successfully.".localizedWithFormat(withArgs: zimFile.name) } // schedule notification diff --git a/Model/Entities/Errors.swift b/Model/Entities/Errors.swift index 45c9a26b1..46ce9d552 100644 --- a/Model/Entities/Errors.swift +++ b/Model/Entities/Errors.swift @@ -16,12 +16,12 @@ public enum LibraryRefreshError: LocalizedError { public var errorDescription: String? { switch self { case .retrieve(let description): - let prefix = NSLocalizedString("Error retrieving library data.", comment: "Library Refresh Error") + let prefix = "Error retrieving library data.".localized(withComment: "Library Refresh Error") return [prefix, description].compactMap({ $0 }).joined(separator: " ") case .parse: - return NSLocalizedString("Error parsing library data.", comment: "Library Refresh Error") + return "Error parsing library data.".localized(withComment: "Library Refresh Error") case .process: - return NSLocalizedString("Error processing library data.", comment: "Library Refresh Error") + return "Error processing library data.".localized(withComment: "Library Refresh Error") } } } diff --git a/Model/Utilities/String+Extension.swift b/Model/Utilities/String+Extension.swift new file mode 100644 index 000000000..9f1ad1364 --- /dev/null +++ b/Model/Utilities/String+Extension.swift @@ -0,0 +1,32 @@ +// +// String+Extension.swift +// Kiwix +// +// Created by tvision251 on 11/13/23. +// Copyright © 2023 Chris Li. All rights reserved. +// + +import Foundation + +extension String { + + var localized: String { + return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") + } + + func localized(withComment:String) -> String { + return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: withComment) + } + + func localizedWithFormat(withArgs: CVarArg...) -> String { + let format = NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") + + switch withArgs.count { + case 1: return String.localizedStringWithFormat(format, withArgs[0]) + case 2: return String.localizedStringWithFormat(format, withArgs[0], withArgs[1]) + default: return String.localizedStringWithFormat(format, withArgs) + } + } + +} + diff --git a/Support/en.lproj/Localizable.strings b/Support/en.lproj/Localizable.strings new file mode 100644 index 000000000..632378726 --- /dev/null +++ b/Support/en.lproj/Localizable.strings @@ -0,0 +1,202 @@ +/* + Localizable.strings + Kiwix + + Created by tvision251 on 11/13/23. + Copyright © 2023 Chris Li. All rights reserved. +*/ + +/* Library Refresh Error */ +"Error retrieving library data." = "Error retrieving library data."; +/* Library Refresh Error */ +"Error parsing library data." = "Error parsing library data."; +/* Library Refresh Error */ +"Error processing library data." = "Error processing library data."; + +"Download Completed" = "Download Completed"; +"%@ has been downloaded successfully." = "%@ has been downloaded successfully."; +"Done" = "Done"; +"Go Back" = "Go Back"; +"Go Forward" = "Go Forward"; +"Outline" = "Outline"; +"Show article outline" = "Show article outline"; +"No outline available" = "No outline available"; +"Remove Bookmark" = "Remove Bookmark"; +"Add Bookmark" = "Add Bookmark"; +"Show Bookmarks" = "Show Bookmarks"; +"Show bookmarks. Long press to bookmark or unbookmark the current article." = "Show bookmarks. Long press to bookmark or unbookmark the current article."; +"Main Article" = "Main Article"; +"Show main article" = "Show main article"; +"Random Article" = "Random Article"; +"Show random article" = "Show random article"; +"Random Page" = "Random Page"; +"Tabs" = "Tabs"; +"New Tab" = "New Tab"; +"Close This Tab" = "Close This Tab"; +"Close All Tabs" = "Close All Tabs"; +"Close Tab" = "Close Tab"; +"Library" = "Library"; +"Settings" = "Settings"; +"Tabs Manager" = "Tabs Manager"; +"Unable to load the article requested." = "Unable to load the article requested."; +"View" = "View"; +"Remove" = "Remove"; +"External Link" = "External Link"; +"Load the link" = "Load the link"; +"Cancel" = "Cancel"; +"An external link is tapped, do you wish to load the link?" = "An external link is tapped, do you wish to load the link?"; +"An external link is tapped. However, your current setting does not allow it to be loaded." = "An external link is tapped. However, your current setting does not allow it to be loaded."; +"Open a zim file" = "Open a zim file"; +"Unable to open file" = "Unable to open file"; +"%@ cannot be opened." = "%@ cannot be opened."; +"No snippet" = "No snippet"; +"Article Title" = "Article Title"; +"Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ +sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ +sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; +"Unknown" = "Unknown"; +"Yes" = "Yes"; +"No" = "No"; +"Failed" = "Failed"; +"Downloading..." = "Downloading..."; +"Paused" = "Paused"; +"everything except large media files like video/audio" = "everything except large media files like video/audio"; +"most pictures have been removed" = "most pictures have been removed"; +"only a subset of the text is available, probably the first section" = "only a subset of the text is available, probably the first section"; +"Header Text" = "Header Text"; +"Content" = "Content"; +"Just Now" = "Just Now"; +"Never" = "Never"; +"There is nothing to see" = "There is nothing to see"; +"Article Title" = "Article Title"; +"Zim file is missing." = "Zim file is missing."; +"articles" = "articles"; +"Select a zim file to see detail" = "Select a zim file to see detail"; +"Main Page" = "Main Page"; +"Copy URL" = "Copy URL"; +"Copy ID" = "Copy ID"; +"No opened zim file" = "No opened zim file"; +"Show Sidebar" = "Show Sidebar"; +"Open..." = "Open..."; +"Category" = "Category"; +"No zim file under this category." = "No zim file under this category."; +"No download tasks" = "No download tasks"; +"No new zim file" = "No new zim file"; +"Refresh" = "Refresh"; +"Name" = "Name"; +"Description" = "Description"; +"Actions" = "Actions"; +"Info" = "Info"; +"Locate" = "Locate"; +"Open Main Page" = "Open Main Page"; +"Reveal in Finder" = "Reveal in Finder"; +"Download using cellular" = "Download using cellular"; +"Unlink" = "Unlink"; +"Unlink %@" = "Unlink %@"; +"All bookmarked articles linked to this zim file will be deleted, \ +but the original file will remain in place." = "All bookmarked articles linked to this zim file will be deleted, \ +but the original file will remain in place."; +"Delete %@" = "Delete %@"; +"The zim file and all bookmarked articles linked to this zim file will be deleted." = "The zim file and all bookmarked articles linked to this zim file will be deleted."; +"Download" = "Download"; +"Space Warning" = "Space Warning"; +"There might not be enough space on your device for this zim file." = "There might not be enough space on your device for this zim file."; +"There would be less than 1GB space left after the zim file is downloaded." = "There would be less than 1GB space left after the zim file is downloaded."; +"Download Anyway" = "Download Anyway"; +"Language" = "Language"; +"Size" = "Size"; +"Created" = "Created"; +"Pictures" = "Pictures"; +"Videos" = "Videos"; +"Details" = "Details"; +"Requires Service Workers" = "Requires Service Workers"; +"Article Count" = "Article Count"; +"Media Count" = "Media Count"; +"ID" = "ID"; +"Try to Recover" = "Try to Recover"; +"Pause" = "Pause"; +"Resume" = "Resume"; +"Zim files requiring service workers are not supported." = "Zim files requiring service workers are not supported."; +"A very long description" = "A very long description"; +"Page zoom" = "Page zoom"; +"Reset" = "Reset"; +"External link" = "External link"; +"Search snippet" = "Search snippet"; +"Reading" = "Reading"; +"Catalog" = "Catalog"; +"Refresh Now" = "Refresh Now"; +"Last refresh" = "Last refresh"; +"Auto refresh" = "Auto refresh"; +"When enabled, the library catalog will be refreshed automatically when outdated." = "When enabled, the library catalog will be refreshed automatically when outdated."; +"Languages" = "Languages"; +"Library" = "Library"; +"Change will only apply to new download tasks." = "Change will only apply to new download tasks."; +"Refreshing..." = "Refreshing..."; +"Include zim files in backup" = "Include zim files in backup"; +"Backup" = "Backup"; +"Does not apply to files opened in place." = "Does not apply to files opened in place."; +"Misc" = "Misc"; +"Feedback" = "Feedback"; +"Rate the App" = "Rate the App"; +"About" = "About"; +"Count" = "Count"; +"No language" = "No language"; +"Showing" = "Showing"; +"Hiding" = "Hiding"; +"Sorting" = "Sorting"; +"Release" = "Release"; +"Dependencies" = "Dependencies"; +"License" = "License"; +"Version" = "Version"; +"Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ +It makes knowledge available to people with no or limited internet access. \ +The software as well as the content is free to use for anyone." = "Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ +It makes knowledge available to people with no or limited internet access. \ +The software as well as the content is free to use for anyone."; +"This app is released under the terms of the GNU General Public License version 3." = "This app is released under the terms of the GNU General Public License version 3."; +"Build" = "Build"; +"Our Website" = "Our Website"; +"Source" = "Source"; +"GNU General Public License v3" = "GNU General Public License v3"; +"Bookmarks" = "Bookmarks"; +"No bookmarks" = "No bookmarks"; +"No result" = "No result"; +"Recent Search" = "Recent Search"; +"Clear" = "Clear"; +"Clear Recent Searches" = "Clear Recent Searches"; +"Clear All" = "Clear All"; +"All recent search history will be removed." = "All recent search history will be removed."; +"Included in Search" = "Included in Search"; +"None" = "None"; +"All" = "All"; +"Open File" = "Open File"; +"Fetching..." = "Fetching..."; +"Fetch Catalog" = "Fetch Catalog"; +"Actual Size" = "Actual Size"; +"Zoom In" = "Zoom In"; +"Zoom Out" = "Zoom Out"; +"Not yet implemented" = "Not yet implemented"; +"Close" = "Close"; +"Map" = "Map"; +"Opened" = "Opened"; +"Categories" = "Categories"; +"New" = "New"; +"Downloads" = "Downloads"; +"A-Z" = "A-Z"; +"By Count" = "By Count"; +"Always Ask" = "Always Ask"; +"Always Load" = "Always Load"; +"Never Load" = "Never Load"; +"Wikipedia" = "Wikipedia"; +"Wikibooks" = "Wikibooks"; +"Wikinews" = "Wikinews"; +"Wikiquote" = "Wikiquote"; +"Wikisource" = "Wikisource"; +"Wikiversity" = "Wikiversity"; +"Wikivoyage" = "Wikivoyage"; +"Wiktionary" = "Wiktionary"; +"TED" = "TED"; +"Vikidia" = "Vikidia"; +"StackExchange" = "StackExchange"; +"Other" = "Other"; + diff --git a/Support/es.lproj/Localizable.strings b/Support/es.lproj/Localizable.strings new file mode 100644 index 000000000..e83d7f5c0 --- /dev/null +++ b/Support/es.lproj/Localizable.strings @@ -0,0 +1,8 @@ +/* + Localizable.strings + Kiwix + + Created by tvision251 on 11/13/23. + Copyright © 2023 Chris Li. All rights reserved. +*/ + diff --git a/ViewModel/BrowserViewModel.swift b/ViewModel/BrowserViewModel.swift index 2354f6b4d..7eaccd1e6 100644 --- a/ViewModel/BrowserViewModel.swift +++ b/ViewModel/BrowserViewModel.swift @@ -250,12 +250,12 @@ class BrowserViewModel: NSObject, ObservableObject, // open url actions.append( - UIAction(title: "Open", image: UIImage(systemName: "doc.text")) { _ in + UIAction(title: "Open".localized, image: UIImage(systemName: "doc.text")) { _ in webView.load(URLRequest(url: url)) } ) actions.append( - UIAction(title: "Open in New Tab", image: UIImage(systemName: "doc.badge.plus")) { _ in + UIAction(title: "Open in New Tab".localized, image: UIImage(systemName: "doc.badge.plus")) { _ in NotificationCenter.openURL(url, inNewTab: true) } ) @@ -266,11 +266,11 @@ class BrowserViewModel: NSObject, ObservableObject, let predicate = NSPredicate(format: "articleURL == %@", url as CVarArg) let request = Bookmark.fetchRequest(predicate: predicate) if let bookmarks = try? context.fetch(request), !bookmarks.isEmpty { - return UIAction(title: "Remove Bookmark", image: UIImage(systemName: "star.slash.fill")) { _ in + return UIAction(title: "Remove Bookmark".localized, image: UIImage(systemName: "star.slash.fill")) { _ in self.deleteBookmark(url: url) } } else { - return UIAction(title: "Bookmark", image: UIImage(systemName: "star")) { _ in + return UIAction(title: "Bookmark".localized, image: UIImage(systemName: "star")) { _ in self.createBookmark(url: url) } } diff --git a/Views/Bookmarks.swift b/Views/Bookmarks.swift index f24ab25d5..988023e86 100644 --- a/Views/Bookmarks.swift +++ b/Views/Bookmarks.swift @@ -36,14 +36,14 @@ struct Bookmarks: View { } .modifier(GridCommon()) .modifier(ToolbarRoleBrowser()) - .navigationTitle("Bookmarks") + .navigationTitle("Bookmarks".localized) .searchable(text: $searchText) .onChange(of: searchText) { searchText in bookmarks.nsPredicate = Bookmarks.buildPredicate(searchText: searchText) } .overlay { if bookmarks.isEmpty { - Message(text: "No bookmarks") + Message(text: "No bookmarks".localized) } } .toolbar { @@ -53,7 +53,7 @@ struct Bookmarks: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } diff --git a/Views/BrowserTab.swift b/Views/BrowserTab.swift index 9a080449e..aec014f54 100644 --- a/Views/BrowserTab.swift +++ b/Views/BrowserTab.swift @@ -22,7 +22,7 @@ struct BrowserTab: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } NavigationButtons() diff --git a/Views/BuildingBlocks/ArticleCell.swift b/Views/BuildingBlocks/ArticleCell.swift index f5681f328..fa8069c89 100644 --- a/Views/BuildingBlocks/ArticleCell.swift +++ b/Views/BuildingBlocks/ArticleCell.swift @@ -44,7 +44,7 @@ struct ArticleCell: View { if let snippet = snippet { Text(AttributedString(snippet)).lineLimit(4) } else if alwaysShowSnippet { - Text("No snippet").foregroundColor(.secondary) + Text("No snippet".localized).foregroundColor(.secondary) } }.font(.caption).multilineTextAlignment(.leading) Spacer(minLength: 0) @@ -64,12 +64,12 @@ struct ArticleCell: View { struct ArticleCell_Previews: PreviewProvider { static let result: SearchResult = { - let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title")! + let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! result.snippet = NSAttributedString(string: """ Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """ + """.localized ) return result }() diff --git a/Views/BuildingBlocks/Attribute.swift b/Views/BuildingBlocks/Attribute.swift index 038f9202d..98ff221b7 100644 --- a/Views/BuildingBlocks/Attribute.swift +++ b/Views/BuildingBlocks/Attribute.swift @@ -16,7 +16,7 @@ struct Attribute: View { HStack { Text(title) Spacer() - Text(detail ?? "Unknown").foregroundColor(.secondary) + Text(detail ?? "Unknown".localized).foregroundColor(.secondary) } } } @@ -30,7 +30,7 @@ struct AttributeBool: View { Text(title) Spacer() #if os(macOS) - Text(detail ? "Yes" : "No").foregroundColor(.secondary) + Text(detail ? "Yes".localized : "No".localized).foregroundColor(.secondary) #elseif os(iOS) if detail { Image(systemName: "checkmark.circle.fill").foregroundColor(.green) diff --git a/Views/BuildingBlocks/DownloadTaskCell.swift b/Views/BuildingBlocks/DownloadTaskCell.swift index f1fb7f1a0..530cd7ad2 100644 --- a/Views/BuildingBlocks/DownloadTaskCell.swift +++ b/Views/BuildingBlocks/DownloadTaskCell.swift @@ -41,9 +41,9 @@ struct DownloadTaskCell: View { } VStack(alignment: .leading, spacing: 4) { if downloadTask.error != nil { - Text("Failed") + Text("Failed".localized) } else if downloadTask.resumeData == nil { - Text("Downloading...") + Text("Downloading...".localized) } else { Text("Paused") } diff --git a/Views/BuildingBlocks/FlavorTag.swift b/Views/BuildingBlocks/FlavorTag.swift index ffe22e3b8..17e3c6827 100644 --- a/Views/BuildingBlocks/FlavorTag.swift +++ b/Views/BuildingBlocks/FlavorTag.swift @@ -41,11 +41,11 @@ struct FlavorTag: View { var help: String { switch flavor { case .max: - return "everything except large media files like video/audio" + return "everything except large media files like video/audio".localized case .noPic: - return "most pictures have been removed" + return "most pictures have been removed".localized case .mini: - return "only a subset of the text is available, probably the first section" + return "only a subset of the text is available, probably the first section".localized } } } diff --git a/Views/BuildingBlocks/GridSection.swift b/Views/BuildingBlocks/GridSection.swift index 6dd622d5d..d820a9151 100644 --- a/Views/BuildingBlocks/GridSection.swift +++ b/Views/BuildingBlocks/GridSection.swift @@ -29,8 +29,8 @@ struct GridSection: View { struct GridSection_Previews: PreviewProvider { static var previews: some View { - GridSection(title: "Header Text") { - Text("Content") + GridSection(title: "Header Text".localized) { + Text("Content".localized) } } } diff --git a/Views/BuildingBlocks/LibraryLastRefreshTime.swift b/Views/BuildingBlocks/LibraryLastRefreshTime.swift index 47156b964..9fbd4aa9d 100644 --- a/Views/BuildingBlocks/LibraryLastRefreshTime.swift +++ b/Views/BuildingBlocks/LibraryLastRefreshTime.swift @@ -16,12 +16,12 @@ struct LibraryLastRefreshTime: View { var body: some View { if let lastRefresh = lastRefresh { if Date().timeIntervalSince(lastRefresh) < 120 { - Text("Just Now") + Text("Just Now".localized) } else { Text(RelativeDateTimeFormatter().localizedString(for: lastRefresh, relativeTo: Date())) } } else { - Text("Never") + Text("Never".localized) } } } diff --git a/Views/BuildingBlocks/Message.swift b/Views/BuildingBlocks/Message.swift index 4992e79de..2b0e5a88d 100644 --- a/Views/BuildingBlocks/Message.swift +++ b/Views/BuildingBlocks/Message.swift @@ -26,7 +26,7 @@ struct Message: View { struct Message_Previews: PreviewProvider { static var previews: some View { - Message(text: "There is nothing to see") + Message(text: "There is nothing to see".localized) .frame(width: 250, height: 200) } } diff --git a/Views/BuildingBlocks/SearchResultRow.swift b/Views/BuildingBlocks/SearchResultRow.swift index 55b33dc7a..57edb5f07 100644 --- a/Views/BuildingBlocks/SearchResultRow.swift +++ b/Views/BuildingBlocks/SearchResultRow.swift @@ -38,12 +38,12 @@ struct SearchResultRow: View { struct SearchResultRow_Previews: PreviewProvider { static let result: SearchResult = { - let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title")! + let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! result.snippet = NSAttributedString(string: """ Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """ + """.localized ) return result }() diff --git a/Views/BuildingBlocks/SheetContent.swift b/Views/BuildingBlocks/SheetContent.swift index f7f3fa061..022ea301e 100644 --- a/Views/BuildingBlocks/SheetContent.swift +++ b/Views/BuildingBlocks/SheetContent.swift @@ -27,7 +27,7 @@ struct SheetContent: View { Button { dismiss() } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } diff --git a/Views/BuildingBlocks/TabLabel.swift b/Views/BuildingBlocks/TabLabel.swift index ebd8fa598..e31847ded 100644 --- a/Views/BuildingBlocks/TabLabel.swift +++ b/Views/BuildingBlocks/TabLabel.swift @@ -15,12 +15,12 @@ struct TabLabel: View { var body: some View { if let zimFile = tab.zimFile, let category = Category(rawValue: zimFile.category) { Label { - Text(tab.title ?? "New Tab").lineLimit(1) + Text(tab.title ?? "New Tab".localized).lineLimit(1) } icon: { Favicon(category: category, imageData: zimFile.faviconData).frame(width: 22, height: 22) } } else { - Label(tab.title ?? "New Tab", systemImage: "square") + Label(tab.title ?? "New Tab".localized, systemImage: "square") } } } diff --git a/Views/BuildingBlocks/ZimFileMissingIndicator.swift b/Views/BuildingBlocks/ZimFileMissingIndicator.swift index da7c3b3c1..2e0a23a8d 100644 --- a/Views/BuildingBlocks/ZimFileMissingIndicator.swift +++ b/Views/BuildingBlocks/ZimFileMissingIndicator.swift @@ -12,6 +12,6 @@ struct ZimFileMissingIndicator: View { var body: some View { Image(systemName: "exclamationmark.triangle.fill") .renderingMode(.original) - .help("Zim file is missing.") + .help("Zim file is missing.".localized) } } diff --git a/Views/BuildingBlocks/ZimFileRow.swift b/Views/BuildingBlocks/ZimFileRow.swift index 0fb4d3878..73510e563 100644 --- a/Views/BuildingBlocks/ZimFileRow.swift +++ b/Views/BuildingBlocks/ZimFileRow.swift @@ -30,7 +30,7 @@ struct ZimFileRow: View { Formatter.size.string(fromByteCount: zimFile.size), { if #available(iOS 15.0, *) { - return "\(zimFile.articleCount.formatted(.number.notation(.compactName))) articles" + return "\(zimFile.articleCount.formatted(.number.notation(.compactName)))" + "articles".localized } else { return Formatter.largeNumber(zimFile.articleCount) } diff --git a/Views/Buttons/ArticleShortcutButtons.swift b/Views/Buttons/ArticleShortcutButtons.swift index 79ee831de..7e4660473 100644 --- a/Views/Buttons/ArticleShortcutButtons.swift +++ b/Views/Buttons/ArticleShortcutButtons.swift @@ -40,10 +40,10 @@ struct ArticleShortcutButtons: View { browser.loadMainArticle() dismissSearch() } label: { - Label("Main Article", systemImage: "house") + Label("Main Article".localized, systemImage: "house") } .disabled(zimFiles.isEmpty) - .help("Show main article") + .help("Show main article".localized) #elseif os(iOS) Menu { ForEach(zimFiles) { zimFile in @@ -53,13 +53,13 @@ struct ArticleShortcutButtons: View { } } } label: { - Label("Main Article", systemImage: "house") + Label("Main Article".localized, systemImage: "house") } primaryAction: { browser.loadMainArticle() dismissSearch() } .disabled(zimFiles.isEmpty) - .help("Show main article") + .help("Show main article".localized) #endif } @@ -69,10 +69,10 @@ struct ArticleShortcutButtons: View { browser.loadRandomArticle() dismissSearch() } label: { - Label("Random Article", systemImage: "die.face.5") + Label("Random Article".localized, systemImage: "die.face.5") } .disabled(zimFiles.isEmpty) - .help("Show random article") + .help("Show random article".localized) #elseif os(iOS) Menu { ForEach(zimFiles) { zimFile in @@ -82,13 +82,13 @@ struct ArticleShortcutButtons: View { } } } label: { - Label("Random Page", systemImage: "die.face.5") + Label("Random Page".localized, systemImage: "die.face.5") } primaryAction: { browser.loadRandomArticle() dismissSearch() } .disabled(zimFiles.isEmpty) - .help("Show random article") + .help("Show random article".localized) #endif } } diff --git a/Views/Buttons/BookmarkButton.swift b/Views/Buttons/BookmarkButton.swift index 385084421..96b40565e 100644 --- a/Views/Buttons/BookmarkButton.swift +++ b/Views/Buttons/BookmarkButton.swift @@ -23,7 +23,7 @@ struct BookmarkButton: View { } } label: { Label { - Text(browser.articleBookmarked ? "Remove Bookmark" : "Add Bookmark") + Text(browser.articleBookmarked ? "Remove Bookmark".localized : "Add Bookmark".localized) } icon: { Image(systemName: browser.articleBookmarked ? "star.fill" : "star") .renderingMode(browser.articleBookmarked ? .original : .template) @@ -35,23 +35,23 @@ struct BookmarkButton: View { Button(role: .destructive) { browser.deleteBookmark() } label: { - Label("Remove Bookmark", systemImage: "star.slash.fill") + Label("Remove Bookmark".localized, systemImage: "star.slash.fill") } } else { Button { browser.createBookmark() } label: { - Label("Add Bookmark", systemImage: "star") + Label("Add Bookmark".localized, systemImage: "star") } } Button { isShowingBookmark = true } label: { - Label("Show Bookmarks", systemImage: "list.star") + Label("Show Bookmarks".localized, systemImage: "list.star") } } label: { Label { - Text("Show Bookmarks") + Text("Show Bookmarks".localized) } icon: { Image(systemName: browser.articleBookmarked ? "star.fill" : "star") .renderingMode(browser.articleBookmarked ? .original : .template) @@ -59,7 +59,7 @@ struct BookmarkButton: View { } primaryAction: { isShowingBookmark = true } - .help("Show bookmarks. Long press to bookmark or unbookmark the current article.") + .help("Show bookmarks. Long press to bookmark or unbookmark the current article.".localized) .popover(isPresented: $isShowingBookmark) { NavigationView { Bookmarks().navigationBarTitleDisplayMode(.inline).toolbar { @@ -67,7 +67,7 @@ struct BookmarkButton: View { Button { isShowingBookmark = false } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } diff --git a/Views/Buttons/NavigationButtons.swift b/Views/Buttons/NavigationButtons.swift index e30dec407..8f8f121de 100644 --- a/Views/Buttons/NavigationButtons.swift +++ b/Views/Buttons/NavigationButtons.swift @@ -29,7 +29,7 @@ struct NavigationButtons: View { browser.webView.goBack() dismissSearch() } label: { - Label("Go Back", systemImage: "chevron.left") + Label("Go Back".localized, systemImage: "chevron.left") }.disabled(!browser.canGoBack) } @@ -38,7 +38,7 @@ struct NavigationButtons: View { browser.webView.goForward() dismissSearch() } label: { - Label("Go Forward", systemImage: "chevron.right") + Label("Go Forward".localized, systemImage: "chevron.right") }.disabled(!browser.canGoForward) } } diff --git a/Views/Buttons/OutlineButton.swift b/Views/Buttons/OutlineButton.swift index 671b74d9c..3eb7ef874 100644 --- a/Views/Buttons/OutlineButton.swift +++ b/Views/Buttons/OutlineButton.swift @@ -25,10 +25,10 @@ struct OutlineButton: View { } } } label: { - Label("Outline", systemImage: "list.bullet") + Label("Outline".localized, systemImage: "list.bullet") } .disabled(browser.outlineItems.isEmpty) - .help("Show article outline") + .help("Show article outline".localized) #elseif os(iOS) Button { isShowingOutline = true @@ -36,12 +36,12 @@ struct OutlineButton: View { Image(systemName: "list.bullet") } .disabled(browser.outlineItems.isEmpty) - .help("Show article outline") + .help("Show article outline".localized) .popover(isPresented: $isShowingOutline) { NavigationView { Group { if browser.outlineItemTree.isEmpty { - Message(text: "No outline available") + Message(text: "No outline available".localized) } else { List(browser.outlineItemTree) { item in OutlineNode(item: item) { item in @@ -59,7 +59,7 @@ struct OutlineButton: View { Button { isShowingOutline = false } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } diff --git a/Views/Buttons/TabsManagerButton.swift b/Views/Buttons/TabsManagerButton.swift index f8bf203a4..212ce3024 100644 --- a/Views/Buttons/TabsManagerButton.swift +++ b/Views/Buttons/TabsManagerButton.swift @@ -29,18 +29,18 @@ struct TabsManagerButton: View { Button { navigation.createTab() } label: { - Label("New Tab", systemImage: "plus.square") + Label("New Tab".localized, systemImage: "plus.square") } Button(role: .destructive) { guard case .tab(let tabID) = navigation.currentItem else { return } navigation.deleteTab(tabID: tabID) } label: { - Label("Close This Tab", systemImage: "xmark.square") + Label("Close This Tab".localized, systemImage: "xmark.square") } Button(role: .destructive) { navigation.deleteAllTabs() } label: { - Label("Close All Tabs", systemImage: "xmark.square.fill") + Label("Close All Tabs".localized, systemImage: "xmark.square.fill") } } Section { @@ -54,16 +54,16 @@ struct TabsManagerButton: View { Button { presentedSheet = .library } label: { - Label("Library", systemImage: "folder") + Label("Library".localized, systemImage: "folder") } Button { presentedSheet = .settings } label: { - Label("Settings", systemImage: "gear") + Label("Settings".localized, systemImage: "gear") } } } label: { - Label("Tabs Manager", systemImage: "square.stack") + Label("Tabs Manager".localized, systemImage: "square.stack") } primaryAction: { presentedSheet = .tabsManager } @@ -76,7 +76,7 @@ struct TabsManagerButton: View { Button { self.presentedSheet = nil } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } @@ -90,7 +90,7 @@ struct TabsManagerButton: View { Button { self.presentedSheet = nil } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } @@ -129,12 +129,12 @@ struct TabManager: View { Button(role: .destructive) { navigation.deleteTab(tabID: tab.objectID) } label: { - Label("Close Tab", systemImage: "xmark") + Label("Close Tab".localized, systemImage: "xmark") } } } .listStyle(.plain) - .navigationTitle("Tabs") + .navigationTitle("Tabs".localized) .navigationBarTitleDisplayMode(.inline) .toolbar { Menu { @@ -142,15 +142,15 @@ struct TabManager: View { guard case let .tab(tabID) = navigation.currentItem else { return } navigation.deleteTab(tabID: tabID) } label: { - Label("Close This Tab", systemImage: "xmark.square") + Label("Close This Tab".localized, systemImage: "xmark.square") } Button(role: .destructive) { navigation.deleteAllTabs() } label: { - Label("Close All Tabs", systemImage: "xmark.square.fill") + Label("Close All Tabs".localized, systemImage: "xmark.square.fill") } } label: { - Label("New Tab", systemImage: "plus.square") + Label("New Tab".localized, systemImage: "plus.square") } primaryAction: { navigation.createTab() } diff --git a/Views/Commands.swift b/Views/Commands.swift index 7550ec0e4..61672c3df 100644 --- a/Views/Commands.swift +++ b/Views/Commands.swift @@ -54,10 +54,10 @@ struct NavigationCommands: View { @FocusedValue(\.browserViewModel) var browser: BrowserViewModel? var body: some View { - Button("Go Back") { browser?.webView.goBack() } + Button("Go Back".localized) { browser?.webView.goBack() } .keyboardShortcut("[") .disabled(canGoBack != true) - Button("Go Forward") { browser?.webView.goForward() } + Button("Go Forward".localized) { browser?.webView.goForward() } .keyboardShortcut("]") .disabled(canGoForward != true) } @@ -68,13 +68,13 @@ struct PageZoomCommands: View { @FocusedValue(\.browserViewModel) var browser: BrowserViewModel? var body: some View { - Button("Actual Size") { webViewPageZoom = 1 } + Button("Actual Size".localized) { webViewPageZoom = 1 } .keyboardShortcut("0") .disabled(webViewPageZoom == 1 || browser?.url == nil) - Button("Zoom In") { webViewPageZoom += 0.1 } + Button("Zoom In".localized) { webViewPageZoom += 0.1 } .keyboardShortcut("+") .disabled(webViewPageZoom >= 2 || browser?.url == nil) - Button("Zoom Out") { webViewPageZoom -= 0.1 } + Button("Zoom Out".localized) { webViewPageZoom -= 0.1 } .keyboardShortcut("-") .disabled(webViewPageZoom <= 0.5 || browser?.url == nil) } diff --git a/Views/Library/Library.swift b/Views/Library/Library.swift index 06d9770cd..b2786227e 100644 --- a/Views/Library/Library.swift +++ b/Views/Library/Library.swift @@ -32,7 +32,7 @@ struct Library: View { List(Category.allCases) { category in NavigationLink { ZimFilesCategory(category: .constant(category)) - .navigationTitle(category.name) + .navigationTitle(category.name.localized) .navigationBarTitleDisplayMode(.inline) } label: { HStack { @@ -42,7 +42,7 @@ struct Library: View { } } .listStyle(.plain) - .navigationTitle(NavigationItem.categories.name) + .navigationTitle(NavigationItem.categories.name.localized) case .downloads: ZimFilesDownloads() case .new: @@ -87,7 +87,7 @@ struct LibraryZimFileDetailSidePanel: ViewModifier { if let zimFile = viewModel.selectedZimFile { ZimFileDetail(zimFile: zimFile) } else { - Message(text: "Select a zim file to see detail").background(.thickMaterial) + Message(text: "Select a zim file to see detail".localized).background(.thickMaterial) } }.frame(width: 275).background(.ultraThinMaterial) } @@ -132,13 +132,13 @@ struct LibraryZimFileContext: ViewModifier { guard let url = ZimFileService.shared.getMainPageURL(zimFileID: zimFile.fileID) else { return } NotificationCenter.openURL(url, inNewTab: true) } label: { - Label("Main Page", systemImage: "house") + Label("Main Page".localized, systemImage: "house") } Button { guard let url = ZimFileService.shared.getRandomPageURL(zimFileID: zimFile.fileID) else { return } NotificationCenter.openURL(url, inNewTab: true) } label: { - Label("Random Page", systemImage: "die.face.5") + Label("Random Page".localized, systemImage: "die.face.5") } } @@ -153,7 +153,7 @@ struct LibraryZimFileContext: ViewModifier { UIPasteboard.general.setValue(downloadURL.absoluteString, forPasteboardType: UTType.url.identifier) #endif } label: { - Label("Copy URL", systemImage: "doc.on.doc") + Label("Copy URL".localized, systemImage: "doc.on.doc") } } Button { @@ -164,7 +164,7 @@ struct LibraryZimFileContext: ViewModifier { UIPasteboard.general.setValue(zimFile.fileID.uuidString, forPasteboardType: UTType.plainText.identifier) #endif } label: { - Label("Copy ID", systemImage: "barcode.viewfinder") + Label("Copy ID".localized, systemImage: "barcode.viewfinder") } } } diff --git a/Views/Library/ZimFileDetail.swift b/Views/Library/ZimFileDetail.swift index 72b0d9482..5ee91f1a1 100644 --- a/Views/Library/ZimFileDetail.swift +++ b/Views/Library/ZimFileDetail.swift @@ -25,10 +25,10 @@ struct ZimFileDetail: View { var body: some View { #if os(macOS) List { - Section("Name") { Text(zimFile.name).lineLimit(nil) }.collapsible(false) - Section("Description") { Text(zimFile.fileDescription).lineLimit(nil) }.collapsible(false) - Section("Actions") { actions }.collapsible(false) - Section("Info") { + Section("Name".localized) { Text(zimFile.name).lineLimit(nil) }.collapsible(false) + Section("Description".localized) { Text(zimFile.fileDescription).lineLimit(nil) }.collapsible(false) + Section("Actions".localized) { actions }.collapsible(false) + Section("Info".localized) { basicInfo boolInfo counts @@ -81,15 +81,15 @@ struct ZimFileDetail: View { if let downloadTask = zimFile.downloadTask { // zim file is being downloaded DownloadTaskDetail(downloadTask: downloadTask) } else if zimFile.isMissing { // zim file was opened, but is now missing - Action(title: "Locate") { isPresentingFileLocator = true } + Action(title: "Locate".localized) { isPresentingFileLocator = true } unlinkAction } else if zimFile.fileURLBookmark != nil { // zim file is opened - Action(title: "Open Main Page") { + Action(title: "Open Main Page".localized) { guard let url = ZimFileService.shared.getMainPageURL(zimFileID: zimFile.fileID) else { return } NotificationCenter.openURL(url, inNewTab: true) } #if os(macOS) - Action(title: "Reveal in Finder") { + Action(title: "Reveal in Finder".localized) { guard let url = ZimFileService.shared.getFileURL(zimFileID: zimFile.id) else { return } NSWorkspace.shared.activateFileViewerSelecting([url]) } @@ -105,23 +105,23 @@ struct ZimFileDetail: View { #endif } else if zimFile.downloadURL != nil { // zim file can be downloaded #if os(iOS) - Toggle("Download using cellular", isOn: $downloadUsingCellular) + Toggle("Download using cellular".localized, isOn: $downloadUsingCellular) #endif downloadAction } } var unlinkAction: some View { - Action(title: "Unlink", isDestructive: true) { + Action(title: "Unlink".localized, isDestructive: true) { isPresentingUnlinkAlert = true }.alert(isPresented: $isPresentingUnlinkAlert) { Alert( - title: Text("Unlink \(zimFile.name)"), + title: Text("Unlink %@".localizedWithFormat(withArgs: zimFile.name)), message: Text(""" All bookmarked articles linked to this zim file will be deleted, \ but the original file will remain in place. """), - primaryButton: .destructive(Text("Unlink")) { + primaryButton: .destructive(Text("Unlink".localized)) { LibraryOperations.unlink(zimFileID: zimFile.fileID) #if os(iOS) presentationMode.wrappedValue.dismiss() @@ -137,9 +137,9 @@ struct ZimFileDetail: View { isPresentingDeleteAlert = true }.alert(isPresented: $isPresentingDeleteAlert) { Alert( - title: Text("Delete \(zimFile.name)"), - message: Text("The zim file and all bookmarked articles linked to this zim file will be deleted."), - primaryButton: .destructive(Text("Delete")) { + title: Text("Delete %@".localizedWithFormat(withArgs: zimFile.name)), + message: Text("The zim file and all bookmarked articles linked to this zim file will be deleted.".localized), + primaryButton: .destructive(Text("Delete".localized)) { LibraryOperations.delete(zimFileID: zimFile.fileID) #if os(iOS) presentationMode.wrappedValue.dismiss() @@ -151,7 +151,7 @@ struct ZimFileDetail: View { } var downloadAction: some View { - Action(title: "Download") { + Action(title: "Download".localized) { if let freeSpace = freeSpace, zimFile.size >= freeSpace - 10^9 { isPresentingDownloadAlert = true } else { @@ -159,15 +159,15 @@ struct ZimFileDetail: View { } }.alert(isPresented: $isPresentingDownloadAlert) { Alert( - title: Text("Space Warning"), + title: Text("Space Warning".localized), message: Text({ if let freeSpace = freeSpace, zimFile.size > freeSpace { - return "There might not be enough space on your device for this zim file." + return "There might not be enough space on your device for this zim file.".localized } else { - return "There would be less than 1GB space left after the zim file is downloaded." + return "There would be less than 1GB space left after the zim file is downloaded.".localized } }()), - primaryButton: .default(Text("Download Anyway")) { + primaryButton: .default(Text("Download Anyway".localized)) { DownloadService.shared.start(zimFileID: zimFile.id, allowsCellularAccess: false) }, secondaryButton: .cancel() @@ -177,37 +177,37 @@ struct ZimFileDetail: View { @ViewBuilder var basicInfo: some View { - Attribute(title: "Language", detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) - Attribute(title: "Category", detail: Category(rawValue: zimFile.category)?.description) - Attribute(title: "Size", detail: Formatter.size.string(fromByteCount: zimFile.size)) - Attribute(title: "Created", detail: Formatter.dateMedium.string(from: zimFile.created)) + Attribute(title: "Language".localized, detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) + Attribute(title: "Category".localized, detail: Category(rawValue: zimFile.category)?.description) + Attribute(title: "Size".localized, detail: Formatter.size.string(fromByteCount: zimFile.size)) + Attribute(title: "Created".localized, detail: Formatter.dateMedium.string(from: zimFile.created)) } @ViewBuilder var boolInfo: some View { - AttributeBool(title: "Pictures", detail: zimFile.hasPictures) - AttributeBool(title: "Videos", detail: zimFile.hasVideos) - AttributeBool(title: "Details", detail: zimFile.hasDetails) + AttributeBool(title: "Pictures".localized, detail: zimFile.hasPictures) + AttributeBool(title: "Videos".localized, detail: zimFile.hasVideos) + AttributeBool(title: "Details".localized, detail: zimFile.hasDetails) if zimFile.requiresServiceWorkers { - AttributeBool(title: "Requires Service Workers", detail: zimFile.requiresServiceWorkers) + AttributeBool(title: "Requires Service Workers".localized, detail: zimFile.requiresServiceWorkers) } } @ViewBuilder var counts: some View { Attribute( - title: "Article Count", + title: "Article Count".localized, detail: Formatter.number.string(from: NSNumber(value: zimFile.articleCount)) ) Attribute( - title: "Media Count", + title: "Media Count".localized, detail: Formatter.number.string(from: NSNumber(value: zimFile.mediaCount)) ) } @ViewBuilder var id: some View { - Attribute(title: "ID", detail: String(zimFile.fileID.uuidString.prefix(8))) + Attribute(title: "ID".localized, detail: String(zimFile.fileID.uuidString.prefix(8))) } private var freeSpace: Int64? { @@ -237,27 +237,27 @@ private struct DownloadTaskDetail: View { @ObservedObject var downloadTask: DownloadTask var body: some View { - Action(title: "Cancel", isDestructive: true) { + Action(title: "Cancel".localized, isDestructive: true) { DownloadService.shared.cancel(zimFileID: downloadTask.fileID) } if let error = downloadTask.error { if downloadTask.resumeData != nil { - Action(title: "Try to Recover") { + Action(title: "Try to Recover".localized) { DownloadService.shared.resume(zimFileID: downloadTask.fileID) } } - Attribute(title: "Failed", detail: detail) + Attribute(title: "Failed".localized, detail: detail) Text(error) } else if downloadTask.resumeData == nil { - Action(title: "Pause") { + Action(title: "Pause".localized) { DownloadService.shared.pause(zimFileID: downloadTask.fileID) } - Attribute(title: "Downloading...", detail: detail) + Attribute(title: "Downloading...".localized, detail: detail) } else { - Action(title: "Resume") { + Action(title: "Resume".localized) { DownloadService.shared.resume(zimFileID: downloadTask.fileID) } - Attribute(title: "Paused", detail: detail) + Attribute(title: "Paused".localized, detail: detail) } } @@ -313,7 +313,7 @@ private struct Action: View { private struct ServiceWorkerWarning: View { var body: some View { Label { - Text("Zim files requiring service workers are not supported.") + Text("Zim files requiring service workers are not supported.".localized) } icon: { Image(systemName: "exclamationmark.triangle.fill").renderingMode(.original) } @@ -329,7 +329,7 @@ struct ZimFileDetail_Previews: PreviewProvider { zimFile.created = Date() zimFile.downloadURL = URL(string: "https://www.example.com") zimFile.fileID = UUID() - zimFile.fileDescription = "A very long description" + zimFile.fileDescription = "A very long description".localized zimFile.flavor = "max" zimFile.hasDetails = true zimFile.hasPictures = false diff --git a/Views/Library/ZimFilesCategories.swift b/Views/Library/ZimFilesCategories.swift index b58277097..f08c6298b 100644 --- a/Views/Library/ZimFilesCategories.swift +++ b/Views/Library/ZimFilesCategories.swift @@ -17,7 +17,7 @@ struct ZimFilesCategories: View { var body: some View { ZimFilesCategory(category: $selected) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.categories.name) + .navigationTitle(NavigationItem.categories.name.localized) .toolbar { #if os(iOS) ToolbarItem(placement: .navigationBarLeading) { @@ -25,13 +25,13 @@ struct ZimFilesCategories: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } #endif ToolbarItem { - Picker("Category", selection: $selected) { + Picker("Category".localized, selection: $selected) { ForEach(Category.allCases) { Text($0.name).tag($0) } @@ -91,7 +91,7 @@ private struct CategoryGrid: View { var body: some View { Group { if sections.isEmpty { - Message(text: "No zim file under this category.") + Message(text: "No zim file under this category.".localized) } else { LazyVGrid(columns: ([gridItem]), alignment: .leading, spacing: 12) { ForEach(sections) { section in @@ -187,7 +187,7 @@ private struct CategoryList: View { var body: some View { Group { if zimFiles.isEmpty { - Message(text: "No zim file under this category.") + Message(text: "No zim file under this category.".localized) } else { List(zimFiles, id: \.self, selection: $viewModel.selectedZimFile) { zimFile in ZimFileRow(zimFile) diff --git a/Views/Library/ZimFilesDownloads.swift b/Views/Library/ZimFilesDownloads.swift index 54c3f678d..ea83b8c0d 100644 --- a/Views/Library/ZimFilesDownloads.swift +++ b/Views/Library/ZimFilesDownloads.swift @@ -31,10 +31,10 @@ struct ZimFilesDownloads: View { } .modifier(GridCommon()) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.downloads.name) + .navigationTitle(NavigationItem.downloads.name.localized) .overlay { if downloadTasks.isEmpty { - Message(text: "No download tasks") + Message(text: "No download tasks".localized) } } .toolbar { @@ -44,7 +44,7 @@ struct ZimFilesDownloads: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } diff --git a/Views/Library/ZimFilesNew.swift b/Views/Library/ZimFilesNew.swift index 3ae5c3aeb..772db2764 100644 --- a/Views/Library/ZimFilesNew.swift +++ b/Views/Library/ZimFilesNew.swift @@ -39,7 +39,7 @@ struct ZimFilesNew: View { } .modifier(GridCommon()) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.new.name) + .navigationTitle(NavigationItem.new.name.localized) .searchable(text: $searchText) .onAppear { viewModel.start(isUserInitiated: false) @@ -52,7 +52,7 @@ struct ZimFilesNew: View { } .overlay { if zimFiles.isEmpty { - Message(text: "No new zim file") + Message(text: "No new zim file".localized) } } .toolbar { @@ -62,7 +62,7 @@ struct ZimFilesNew: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } @@ -77,7 +77,7 @@ struct ZimFilesNew: View { Button { viewModel.start(isUserInitiated: true) } label: { - Label("Refresh", systemImage: "arrow.triangle.2.circlepath.circle") + Label("Refresh".localized, systemImage: "arrow.triangle.2.circlepath.circle") } } } diff --git a/Views/Library/ZimFilesOpened.swift b/Views/Library/ZimFilesOpened.swift index 0b19b0159..dfdf3f70e 100644 --- a/Views/Library/ZimFilesOpened.swift +++ b/Views/Library/ZimFilesOpened.swift @@ -31,10 +31,10 @@ struct ZimFilesOpened: View { } .modifier(GridCommon(edges: .all)) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.opened.name) + .navigationTitle(NavigationItem.opened.name.localized) .overlay { if zimFiles.isEmpty { - Message(text: "No opened zim file") + Message(text: "No opened zim file".localized) } } // not using OpenFileButton here, because it does not work on iOS/iPadOS 15 when this view is in a modal @@ -53,7 +53,7 @@ struct ZimFilesOpened: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } @@ -68,8 +68,8 @@ struct ZimFilesOpened: View { } isFileImporterPresented = true } label: { - Label("Open...", systemImage: "plus") - }.help("Open a zim file") + Label("Open...".localized, systemImage: "plus") + }.help("Open a zim file".localized) } } } diff --git a/Views/SearchResults.swift b/Views/SearchResults.swift index c1192cf1f..a617a1fed 100644 --- a/Views/SearchResults.swift +++ b/Views/SearchResults.swift @@ -28,7 +28,7 @@ struct SearchResults: View { var body: some View { Group { if zimFiles.isEmpty { - Message(text: "No opened zim file") + Message(text: "No opened zim file".localized) } else if horizontalSizeClass == .regular { HStack(spacing: 0) { #if os(macOS) @@ -66,7 +66,7 @@ struct SearchResults: View { Spacer() } } else if viewModel.results.isEmpty { - Message(text: "No result") + Message(text: "No result".localized) } else { ScrollView { LazyVGrid(columns: [GridItem(.flexible(minimum: 300, maximum: 700), alignment: .center)]) { @@ -98,7 +98,7 @@ struct SearchResults: View { viewModel.searchText = searchText } }.swipeActions { - Button("Remove", role: .destructive) { + Button("Remove".localized, role: .destructive) { recentSearchTexts.removeAll { $0 == searchText } } } @@ -126,39 +126,39 @@ struct SearchResults: View { private var recentSearchHeader: some View { HStack { - Text("Recent Search") + Text("Recent Search".localized) Spacer() Button { isClearSearchConfirmationPresented = true } label: { - Text("Clear").font(.caption).fontWeight(.medium) - }.confirmationDialog("Clear Recent Searches", isPresented: $isClearSearchConfirmationPresented) { - Button("Clear All", role: .destructive) { + Text("Clear".localized).font(.caption).fontWeight(.medium) + }.confirmationDialog("Clear Recent Searches".localized, isPresented: $isClearSearchConfirmationPresented) { + Button("Clear All".localized, role: .destructive) { recentSearchTexts.removeAll() } } message: { - Text("All recent search history will be removed.") + Text("All recent search history will be removed.".localized) } } } private var searchFilterHeader: some View { HStack { - Text("Included in Search") + Text("Included in Search".localized) Spacer() if zimFiles.count == zimFiles.filter({ $0.includedInSearch }).count { Button { zimFiles.forEach { $0.includedInSearch = false } try? managedObjectContext.save() } label: { - Text("None").font(.caption).fontWeight(.medium) + Text("None".localized).font(.caption).fontWeight(.medium) } } else { Button { zimFiles.forEach { $0.includedInSearch = true } try? managedObjectContext.save() } label: { - Text("All").font(.caption).fontWeight(.medium) + Text("All".localized).font(.caption).fontWeight(.medium) } } } diff --git a/Views/Settings/About.swift b/Views/Settings/About.swift index 09ea7bf0f..c9a7407c6 100644 --- a/Views/Settings/About.swift +++ b/Views/Settings/About.swift @@ -17,27 +17,27 @@ struct About: View { var body: some View { #if os(macOS) VStack(spacing: 16) { - SettingSection(name: "About") { + SettingSection(name: "About".localized) { about ourWebsite } - SettingSection(name: "Release") { + SettingSection(name: "Release".localized) { release HStack { source license } } - SettingSection(name: "Dependencies", alignment: .top) { + SettingSection(name: "Dependencies".localized, alignment: .top) { Table(dependencies) { - TableColumn("Name", value: \.name) - TableColumn("License") { dependency in Text(dependency.license ?? "") } - TableColumn("Version", value: \.version) + TableColumn("Name".localized, value: \.name) + TableColumn("License".localized) { dependency in Text(dependency.license ?? "") } + TableColumn("Version".localized, value: \.version) }.tableStyle(.bordered(alternatesRowBackgrounds: true)) } } .padding() - .tabItem { Label("About", systemImage: "info.circle") } + .tabItem { Label("About".localized, systemImage: "info.circle") } .task { await getDependencies() } .onChange(of: externalLinkURL) { url in guard let url = url else { return } @@ -49,14 +49,14 @@ struct About: View { about ourWebsite } - Section("Release") { + Section("Release".localized) { release appVersion buildNumber source license } - Section("Dependencies") { + Section("Dependencies".localized) { ForEach(dependencies) { dependency in HStack { Text(dependency.name) @@ -70,7 +70,7 @@ struct About: View { } } } - .navigationTitle("About") + .navigationTitle("About".localized) .navigationBarTitleDisplayMode(.inline) .sheet(item: $externalLinkURL) { SafariView(url: $0) } .task { await getDependencies() } @@ -83,36 +83,36 @@ struct About: View { Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ It makes knowledge available to people with no or limited internet access. \ The software as well as the content is free to use for anyone. - """ + """.localized ) } private var release: some View { - Text("This app is released under the terms of the GNU General Public License version 3.") + Text("This app is released under the terms of the GNU General Public License version 3.".localized) } private var appVersion: some View { - Attribute(title: "Version", detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) + Attribute(title: "Version".localized, detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) } private var buildNumber: some View { - Attribute(title: "Build", detail: Bundle.main.infoDictionary?["CFBundleVersion"] as? String) + Attribute(title: "Build".localized, detail: Bundle.main.infoDictionary?["CFBundleVersion"] as? String) } private var ourWebsite: some View { - Button("Our Website") { + Button("Our Website".localized) { externalLinkURL = URL(string: "https://www.kiwix.org") } } private var source: some View { - Button("Source") { + Button("Source".localized) { externalLinkURL = URL(string: "https://github.com/kiwix/apple") } } private var license: some View { - Button("GNU General Public License v3") { + Button("GNU General Public License v3".localized) { externalLinkURL = URL(string: "https://www.gnu.org/licenses/gpl-3.0.en.html") } } diff --git a/Views/Settings/LanguageSelector.swift b/Views/Settings/LanguageSelector.swift index 8dcaf3870..d03751244 100644 --- a/Views/Settings/LanguageSelector.swift +++ b/Views/Settings/LanguageSelector.swift @@ -30,8 +30,8 @@ struct LanguageSelector: View { } }) }.width(14) - TableColumn("Name", value: \.name) - TableColumn("Count", value: \.count) { language in Text(language.count.formatted()) } + TableColumn("Name".localized, value: \.name) + TableColumn("Count".localized, value: \.count) { language in Text(language.count.formatted()) } } .tableStyle(.bordered(alternatesRowBackgrounds: true)) .onChange(of: sortOrder) { languages.sort(using: $0) } @@ -51,29 +51,29 @@ struct LanguageSelector: View { List { Section { if showing.isEmpty { - Text("No language").foregroundColor(.secondary) + Text("No language".localized).foregroundColor(.secondary) } else { ForEach(showing) { language in Button { hide(language) } label: { LanguageLabel(language: language) } } } - } header: { Text("Showing") } + } header: { Text("Showing".localized) } Section { ForEach(hiding) { language in Button { show(language) } label: { LanguageLabel(language: language) } } - } header: { Text("Hiding") } + } header: { Text("Hiding".localized) } } .listStyle(.insetGrouped) - .navigationTitle("Languages") + .navigationTitle("Languages".localized) .navigationBarTitleDisplayMode(.inline) .toolbar { Picker(selection: $sortingMode) { ForEach(LibraryLanguageSortingMode.allCases) { sortingMode in - Text(sortingMode.name).tag(sortingMode) + Text(sortingMode.name.localized).tag(sortingMode) } } label: { - Label("Sorting", systemImage: "arrow.up.arrow.down") + Label("Sorting".localized, systemImage: "arrow.up.arrow.down") }.pickerStyle(.menu) } .onAppear { diff --git a/Views/Settings/Settings.swift b/Views/Settings/Settings.swift index 71f652a37..bee1ddc50 100644 --- a/Views/Settings/Settings.swift +++ b/Views/Settings/Settings.swift @@ -18,30 +18,30 @@ struct ReadingSettings: View { var body: some View { VStack(spacing: 16) { - SettingSection(name: "Page zoom") { + SettingSection(name: "Page zoom".localized) { HStack { Stepper(webViewPageZoom.formatted(.percent), value: $webViewPageZoom, in: 0.5...2, step: 0.05) Spacer() - Button("Reset") { webViewPageZoom = 1 }.disabled(webViewPageZoom == 1) + Button("Reset".localized) { webViewPageZoom = 1 }.disabled(webViewPageZoom == 1) } } - SettingSection(name: "External link") { + SettingSection(name: "External link".localized) { Picker(selection: $externalLinkLoadingPolicy) { ForEach(ExternalLinkLoadingPolicy.allCases) { loadingPolicy in - Text(loadingPolicy.name).tag(loadingPolicy) + Text(loadingPolicy.name.localized).tag(loadingPolicy) } } label: { } } - SettingSection(name: "Search snippet") { + SettingSection(name: "Search snippet".localized) { Picker(selection: $searchResultSnippetMode) { ForEach(SearchResultSnippetMode.allCases) { snippetMode in - Text(snippetMode.name).tag(snippetMode) + Text(snippetMode.name.localized).tag(snippetMode) } } label: { } } } .padding() - .tabItem { Label("Reading", systemImage: "book") } + .tabItem { Label("Reading".localized, systemImage: "book") } } } @@ -51,30 +51,30 @@ struct LibrarySettings: View { var body: some View { VStack(spacing: 16) { - SettingSection(name: "Catalog") { + SettingSection(name: "Catalog".localized) { HStack(spacing: 6) { - Button("Refresh Now") { + Button("Refresh Now".localized) { library.start(isUserInitiated: true) }.disabled(library.isInProgress) if library.isInProgress { ProgressView().progressViewStyle(.circular).scaleEffect(0.5).frame(height: 1) } Spacer() - Text("Last refresh:").foregroundColor(.secondary) + Text("Last refresh".localized + ":").foregroundColor(.secondary) LibraryLastRefreshTime().foregroundColor(.secondary) } VStack(alignment: .leading) { - Toggle("Auto refresh", isOn: $libraryAutoRefresh) - Text("When enabled, the library catalog will be refreshed automatically when outdated.") + Toggle("Auto refresh".localized, isOn: $libraryAutoRefresh) + Text("When enabled, the library catalog will be refreshed automatically when outdated.".localized) .foregroundColor(.secondary) } } - SettingSection(name: "Languages", alignment: .top) { + SettingSection(name: "Languages".localized, alignment: .top) { LanguageSelector() } } .padding() - .tabItem { Label("Library", systemImage: "folder.badge.gearshape") } + .tabItem { Label("Library".localized, systemImage: "folder.badge.gearshape") } } } @@ -126,22 +126,22 @@ struct Settings: View { miscellaneous } .modifier(ToolbarRoleBrowser()) - .navigationTitle("Settings") + .navigationTitle("Settings".localized) } var readingSettings: some View { - Section("Reading") { + Section("Reading".localized) { Stepper(value: $webViewPageZoom, in: 0.5...2, step: 0.05) { - Text("Page zoom: \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") + Text("Page zoom".localized + ": \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") } - Picker("External link", selection: $externalLinkLoadingPolicy) { + Picker("External link".localized, selection: $externalLinkLoadingPolicy) { ForEach(ExternalLinkLoadingPolicy.allCases) { loadingPolicy in - Text(loadingPolicy.name).tag(loadingPolicy) + Text(loadingPolicy.name.localized).tag(loadingPolicy) } } - Picker("Search snippet", selection: $searchResultSnippetMode) { + Picker("Search snippet".localized, selection: $searchResultSnippetMode) { ForEach(SearchResultSnippetMode.allCases) { snippetMode in - Text(snippetMode.name).tag(snippetMode) + Text(snippetMode.name.localized).tag(snippetMode) } } } @@ -154,58 +154,58 @@ struct Settings: View { } label: { SelectedLanaguageLabel() } - Toggle("Download using cellular", isOn: $downloadUsingCellular) + Toggle("Download using cellular".localized, isOn: $downloadUsingCellular) } header: { - Text("Library") + Text("Library".localized) } footer: { - Text("Change will only apply to new download tasks.") + Text("Change will only apply to new download tasks.".localized) } } var catalogSettings: some View { Section { HStack { - Text("Last refresh") + Text("Last refresh".localized) Spacer() LibraryLastRefreshTime().foregroundColor(.secondary) } if library.isInProgress { HStack { - Text("Refreshing...").foregroundColor(.secondary) + Text("Refreshing...".localized).foregroundColor(.secondary) Spacer() ProgressView().progressViewStyle(.circular) } } else { - Button("Refresh Now") { + Button("Refresh Now".localized) { library.start(isUserInitiated: true) } } - Toggle("Auto refresh", isOn: $libraryAutoRefresh) + Toggle("Auto refresh".localized, isOn: $libraryAutoRefresh) } header: { - Text("Catalog") + Text("Catalog".localized) } footer: { - Text("When enabled, the library catalog will be refreshed automatically when outdated.") + Text("When enabled, the library catalog will be refreshed automatically when outdated.".localized) }.onChange(of: libraryAutoRefresh) { LibraryOperations.applyLibraryAutoRefreshSetting(isEnabled: $0) } } var backupSettings: some View { Section { - Toggle("Include zim files in backup", isOn: $backupDocumentDirectory) + Toggle("Include zim files in backup".localized, isOn: $backupDocumentDirectory) } header: { - Text("Backup") + Text("Backup".localized) } footer: { - Text("Does not apply to files opened in place.") + Text("Does not apply to files opened in place.".localized) }.onChange(of: backupDocumentDirectory) { LibraryOperations.applyFileBackupSetting(isEnabled: $0) } } var miscellaneous: some View { - Section("Misc") { - Button("Feedback") { UIApplication.shared.open(URL(string: "mailto:feedback@kiwix.org")!) } - Button("Rate the App") { + Section("Misc".lowercased) { + Button("Feedback".localized) { UIApplication.shared.open(URL(string: "mailto:feedback@kiwix.org")!) } + Button("Rate the App".localized) { let url = URL(string: "itms-apps://itunes.apple.com/us/app/kiwix/id997079563?action=write-review")! UIApplication.shared.open(url) } - NavigationLink("About") { About() } + NavigationLink("About".localized) { About() } } } } @@ -215,7 +215,7 @@ private struct SelectedLanaguageLabel: View { var body: some View { HStack { - Text("Languages") + Text("Languages".localized) Spacer() if languageCodes.count == 1, let languageCode = languageCodes.first, diff --git a/Views/ViewModifiers/AlertHandler.swift b/Views/ViewModifiers/AlertHandler.swift index 940a4ecc5..b305b0550 100644 --- a/Views/ViewModifiers/AlertHandler.swift +++ b/Views/ViewModifiers/AlertHandler.swift @@ -21,7 +21,7 @@ struct AlertHandler: ViewModifier { .alert(item: $activeAlert) { alert in switch alert { case .articleFailedToLoad: - return Alert(title: Text("Unable to load the article requested.")) + return Alert(title: Text("Unable to load the article requested.".localized)) } } } diff --git a/Views/ViewModifiers/BookmarkContextMenu.swift b/Views/ViewModifiers/BookmarkContextMenu.swift index 235e984dd..5620f604f 100644 --- a/Views/ViewModifiers/BookmarkContextMenu.swift +++ b/Views/ViewModifiers/BookmarkContextMenu.swift @@ -18,13 +18,13 @@ struct BookmarkContextMenu: ViewModifier { Button { NotificationCenter.openURL(bookmark.articleURL) } label: { - Label("View", systemImage: "doc.richtext") + Label("View".localized, systemImage: "doc.richtext") } Button(role: .destructive) { managedObjectContext.delete(bookmark) try? managedObjectContext.save() } label: { - Label("Remove", systemImage: "star.slash.fill") + Label("Remove".localized, systemImage: "star.slash.fill") } } } diff --git a/Views/ViewModifiers/ExternalLinkHandler.swift b/Views/ViewModifiers/ExternalLinkHandler.swift index a3a1d0cb4..aff6a6613 100644 --- a/Views/ViewModifiers/ExternalLinkHandler.swift +++ b/Views/ViewModifiers/ExternalLinkHandler.swift @@ -41,19 +41,19 @@ struct ExternalLinkHandler: ViewModifier { activeAlert = .notLoading } } - .alert("External Link", isPresented: $isAlertPresented, presenting: activeAlert) { alert in + .alert("External Link".localized, isPresented: $isAlertPresented, presenting: activeAlert) { alert in if case .ask(let url) = alert { - Button("Load the link") { + Button("Load the link".localized) { load(url: url) } - Button("Cancel", role: .cancel) { } + Button("Cancel".localized, role: .cancel) { } } } message: { alert in switch alert { case .ask: - Text("An external link is tapped, do you wish to load the link?") + Text("An external link is tapped, do you wish to load the link?".localized) case .notLoading: - Text("An external link is tapped. However, your current setting does not allow it to be loaded.") + Text("An external link is tapped. However, your current setting does not allow it to be loaded.".localized) } } #if os(iOS) diff --git a/Views/ViewModifiers/FileImport.swift b/Views/ViewModifiers/FileImport.swift index da40c6ea5..020d7cc6c 100644 --- a/Views/ViewModifiers/FileImport.swift +++ b/Views/ViewModifiers/FileImport.swift @@ -41,7 +41,7 @@ struct OpenFileButton: View { guard case let .success(urls) = result else { return } NotificationCenter.openFiles(urls, context: context) } - .help("Open a zim file") + .help("Open a zim file".localized) .keyboardShortcut("o") } } @@ -98,11 +98,11 @@ struct OpenFileHandler: ViewModifier { isAlertPresented = true activeAlert = .unableToOpen(filenames: invalidURLs.map({ $0.lastPathComponent })) } - }.alert("Unable to open file", isPresented: $isAlertPresented, presenting: activeAlert) { _ in + }.alert("Unable to open file".localized, isPresented: $isAlertPresented, presenting: activeAlert) { _ in } message: { alert in switch alert { case .unableToOpen(let filenames): - Text("\(ListFormatter.localizedString(byJoining: filenames)) cannot be opened.") + Text("%@ cannot be opened.".localizedWithFormat(withArgs: ListFormatter.localizedString(byJoining: filenames))) } } } diff --git a/Views/Welcome.swift b/Views/Welcome.swift index 3c079fe44..377c58e6f 100644 --- a/Views/Welcome.swift +++ b/Views/Welcome.swift @@ -67,7 +67,7 @@ struct Welcome: View { alignment: .leading, spacing: 12 ) { - GridSection(title: "Main Page") { + GridSection(title: "Main Page".localized) { ForEach(zimFiles) { zimFile in Button { guard let url = ZimFileService.shared.getMainPageURL(zimFileID: zimFile.fileID) else { return } @@ -78,7 +78,7 @@ struct Welcome: View { } } if !bookmarks.isEmpty { - GridSection(title: "Bookmarks") { + GridSection(title: "Bookmarks".localized) { ForEach(bookmarks.prefix(6)) { bookmark in Button { browser.load(url: bookmark.articleURL) @@ -112,7 +112,7 @@ struct Welcome: View { OpenFileButton(context: .onBoarding) { HStack { Spacer() - Text("Open File") + Text("Open File".localized) Spacer() }.padding(6) } @@ -123,15 +123,15 @@ struct Welcome: View { Spacer() if library.isInProgress { #if os(macOS) - Text("Fetching...") + Text("Fetching...".localized) #elseif os(iOS) HStack(spacing: 6) { ProgressView().frame(maxHeight: 10) - Text("Fetching...") + Text("Fetching...".localized) } #endif } else { - Text("Fetch Catalog") + Text("Fetch Catalog".localized) } Spacer() }.padding(6) diff --git a/WikiMed/es.lproj/LaunchScreen.strings b/WikiMed/es.lproj/LaunchScreen.strings new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/WikiMed/es.lproj/LaunchScreen.strings @@ -0,0 +1 @@ + diff --git a/WikiMed/es.lproj/Main.strings b/WikiMed/es.lproj/Main.strings new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/WikiMed/es.lproj/Main.strings @@ -0,0 +1 @@ + From 9fa13ea29e33fa519282144debd748a9e02860b9 Mon Sep 17 00:00:00 2001 From: tvision106 Date: Tue, 14 Nov 2023 17:12:29 -0500 Subject: [PATCH 07/17] fix codefactor issue --- Model/Utilities/String+Extension.swift | 9 ++++----- Support/en.lproj/Localizable.strings | 8 ++++++-- ViewModel/BrowserViewModel.swift | 3 ++- Views/BuildingBlocks/ZimFileRow.swift | 3 ++- Views/Library/ZimFileDetail.swift | 8 ++++++-- Views/Settings/About.swift | 3 ++- Views/Settings/Settings.swift | 3 ++- Views/ViewModifiers/ExternalLinkHandler.swift | 5 ++++- Views/ViewModifiers/FileImport.swift | 3 ++- 9 files changed, 30 insertions(+), 15 deletions(-) diff --git a/Model/Utilities/String+Extension.swift b/Model/Utilities/String+Extension.swift index 9f1ad1364..e782c0d14 100644 --- a/Model/Utilities/String+Extension.swift +++ b/Model/Utilities/String+Extension.swift @@ -14,7 +14,7 @@ extension String { return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") } - func localized(withComment:String) -> String { + func localized(withComment: String) -> String { return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: withComment) } @@ -22,11 +22,10 @@ extension String { let format = NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") switch withArgs.count { - case 1: return String.localizedStringWithFormat(format, withArgs[0]) - case 2: return String.localizedStringWithFormat(format, withArgs[0], withArgs[1]) - default: return String.localizedStringWithFormat(format, withArgs) + case 1: return String.localizedStringWithFormat(format, withArgs[0]) + case 2: return String.localizedStringWithFormat(format, withArgs[0], withArgs[1]) + default: return String.localizedStringWithFormat(format, withArgs) } } } - diff --git a/Support/en.lproj/Localizable.strings b/Support/en.lproj/Localizable.strings index 632378726..fd8353f44 100644 --- a/Support/en.lproj/Localizable.strings +++ b/Support/en.lproj/Localizable.strings @@ -45,7 +45,9 @@ "Load the link" = "Load the link"; "Cancel" = "Cancel"; "An external link is tapped, do you wish to load the link?" = "An external link is tapped, do you wish to load the link?"; -"An external link is tapped. However, your current setting does not allow it to be loaded." = "An external link is tapped. However, your current setting does not allow it to be loaded."; +"An external link is tapped. \ +However, your current setting does not allow it to be loaded." = "An external link is tapped. \ +However, your current setting does not allow it to be loaded."; "Open a zim file" = "Open a zim file"; "Unable to open file" = "Unable to open file"; "%@ cannot be opened." = "%@ cannot be opened."; @@ -97,7 +99,9 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; but the original file will remain in place." = "All bookmarked articles linked to this zim file will be deleted, \ but the original file will remain in place."; "Delete %@" = "Delete %@"; -"The zim file and all bookmarked articles linked to this zim file will be deleted." = "The zim file and all bookmarked articles linked to this zim file will be deleted."; +"The zim file and all bookmarked articles \ +linked to this zim file will be deleted." = "The zim file and all bookmarked articles \ +linked to this zim file will be deleted."; "Download" = "Download"; "Space Warning" = "Space Warning"; "There might not be enough space on your device for this zim file." = "There might not be enough space on your device for this zim file."; diff --git a/ViewModel/BrowserViewModel.swift b/ViewModel/BrowserViewModel.swift index 7eaccd1e6..9de806141 100644 --- a/ViewModel/BrowserViewModel.swift +++ b/ViewModel/BrowserViewModel.swift @@ -266,7 +266,8 @@ class BrowserViewModel: NSObject, ObservableObject, let predicate = NSPredicate(format: "articleURL == %@", url as CVarArg) let request = Bookmark.fetchRequest(predicate: predicate) if let bookmarks = try? context.fetch(request), !bookmarks.isEmpty { - return UIAction(title: "Remove Bookmark".localized, image: UIImage(systemName: "star.slash.fill")) { _ in + return UIAction(title: "Remove Bookmark".localized, + image: UIImage(systemName: "star.slash.fill")) { _ in self.deleteBookmark(url: url) } } else { diff --git a/Views/BuildingBlocks/ZimFileRow.swift b/Views/BuildingBlocks/ZimFileRow.swift index 73510e563..df40aa9db 100644 --- a/Views/BuildingBlocks/ZimFileRow.swift +++ b/Views/BuildingBlocks/ZimFileRow.swift @@ -30,7 +30,8 @@ struct ZimFileRow: View { Formatter.size.string(fromByteCount: zimFile.size), { if #available(iOS 15.0, *) { - return "\(zimFile.articleCount.formatted(.number.notation(.compactName)))" + "articles".localized + return "\(zimFile.articleCount.formatted(.number.notation(.compactName)))" + + "articles".localized } else { return Formatter.largeNumber(zimFile.articleCount) } diff --git a/Views/Library/ZimFileDetail.swift b/Views/Library/ZimFileDetail.swift index 5ee91f1a1..4b58afaa7 100644 --- a/Views/Library/ZimFileDetail.swift +++ b/Views/Library/ZimFileDetail.swift @@ -138,7 +138,10 @@ struct ZimFileDetail: View { }.alert(isPresented: $isPresentingDeleteAlert) { Alert( title: Text("Delete %@".localizedWithFormat(withArgs: zimFile.name)), - message: Text("The zim file and all bookmarked articles linked to this zim file will be deleted.".localized), + message: Text(""" + The zim file and all bookmarked articles \ + linked to this zim file will be deleted. + """.localized), primaryButton: .destructive(Text("Delete".localized)) { LibraryOperations.delete(zimFileID: zimFile.fileID) #if os(iOS) @@ -177,7 +180,8 @@ struct ZimFileDetail: View { @ViewBuilder var basicInfo: some View { - Attribute(title: "Language".localized, detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) + Attribute(title: "Language".localized, + detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) Attribute(title: "Category".localized, detail: Category(rawValue: zimFile.category)?.description) Attribute(title: "Size".localized, detail: Formatter.size.string(fromByteCount: zimFile.size)) Attribute(title: "Created".localized, detail: Formatter.dateMedium.string(from: zimFile.created)) diff --git a/Views/Settings/About.swift b/Views/Settings/About.swift index c9a7407c6..72d5bed70 100644 --- a/Views/Settings/About.swift +++ b/Views/Settings/About.swift @@ -92,7 +92,8 @@ struct About: View { } private var appVersion: some View { - Attribute(title: "Version".localized, detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) + Attribute(title: "Version".localized, + detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) } private var buildNumber: some View { diff --git a/Views/Settings/Settings.swift b/Views/Settings/Settings.swift index bee1ddc50..cf6bbd853 100644 --- a/Views/Settings/Settings.swift +++ b/Views/Settings/Settings.swift @@ -132,7 +132,8 @@ struct Settings: View { var readingSettings: some View { Section("Reading".localized) { Stepper(value: $webViewPageZoom, in: 0.5...2, step: 0.05) { - Text("Page zoom".localized + ": \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") + Text("Page zoom".localized + + ": \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") } Picker("External link".localized, selection: $externalLinkLoadingPolicy) { ForEach(ExternalLinkLoadingPolicy.allCases) { loadingPolicy in diff --git a/Views/ViewModifiers/ExternalLinkHandler.swift b/Views/ViewModifiers/ExternalLinkHandler.swift index aff6a6613..f48bde1a4 100644 --- a/Views/ViewModifiers/ExternalLinkHandler.swift +++ b/Views/ViewModifiers/ExternalLinkHandler.swift @@ -53,7 +53,10 @@ struct ExternalLinkHandler: ViewModifier { case .ask: Text("An external link is tapped, do you wish to load the link?".localized) case .notLoading: - Text("An external link is tapped. However, your current setting does not allow it to be loaded.".localized) + Text(""" + An external link is tapped. \ + However, your current setting does not allow it to be loaded. + """.localized) } } #if os(iOS) diff --git a/Views/ViewModifiers/FileImport.swift b/Views/ViewModifiers/FileImport.swift index 020d7cc6c..c3dd86c45 100644 --- a/Views/ViewModifiers/FileImport.swift +++ b/Views/ViewModifiers/FileImport.swift @@ -102,7 +102,8 @@ struct OpenFileHandler: ViewModifier { } message: { alert in switch alert { case .unableToOpen(let filenames): - Text("%@ cannot be opened.".localizedWithFormat(withArgs: ListFormatter.localizedString(byJoining: filenames))) + Text("%@ cannot be opened.".localizedWithFormat(withArgs: + ListFormatter.localizedString(byJoining: filenames))) } } } From 93de4e469e12ebfdf70418a0a367370a8cf84f64 Mon Sep 17 00:00:00 2001 From: tvision106 Date: Tue, 14 Nov 2023 17:15:21 -0500 Subject: [PATCH 08/17] fix codefactor issue --- Views/ViewModifiers/FileImport.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Views/ViewModifiers/FileImport.swift b/Views/ViewModifiers/FileImport.swift index c3dd86c45..edd5f4263 100644 --- a/Views/ViewModifiers/FileImport.swift +++ b/Views/ViewModifiers/FileImport.swift @@ -102,8 +102,8 @@ struct OpenFileHandler: ViewModifier { } message: { alert in switch alert { case .unableToOpen(let filenames): - Text("%@ cannot be opened.".localizedWithFormat(withArgs: - ListFormatter.localizedString(byJoining: filenames))) + let name = ListFormatter.localizedString(byJoining: filenames) + Text("%@ cannot be opened.".localizedWithFormat(withArgs: name)) } } } From 9a41a6d949fabbdc85c51e460f19084ec450082c Mon Sep 17 00:00:00 2001 From: tvision106 Date: Wed, 15 Nov 2023 13:24:44 -0500 Subject: [PATCH 09/17] fix some issues for long strings --- Support/en.lproj/Localizable.strings | 21 ++++++++----------- Views/BuildingBlocks/ArticleCell.swift | 7 +------ Views/BuildingBlocks/SearchResultRow.swift | 7 +------ Views/Library/ZimFileDetail.swift | 10 ++------- Views/Settings/About.swift | 8 +------ Views/ViewModifiers/ExternalLinkHandler.swift | 5 +---- 6 files changed, 15 insertions(+), 43 deletions(-) diff --git a/Support/en.lproj/Localizable.strings b/Support/en.lproj/Localizable.strings index fd8353f44..fbcd8a241 100644 --- a/Support/en.lproj/Localizable.strings +++ b/Support/en.lproj/Localizable.strings @@ -45,16 +45,14 @@ "Load the link" = "Load the link"; "Cancel" = "Cancel"; "An external link is tapped, do you wish to load the link?" = "An external link is tapped, do you wish to load the link?"; -"An external link is tapped. \ -However, your current setting does not allow it to be loaded." = "An external link is tapped. \ +"loc-extenral-alert" = "An external link is tapped. \ However, your current setting does not allow it to be loaded."; "Open a zim file" = "Open a zim file"; "Unable to open file" = "Unable to open file"; "%@ cannot be opened." = "%@ cannot be opened."; "No snippet" = "No snippet"; "Article Title" = "Article Title"; -"Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ -sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ +"loc-article-cell-template" = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; "Unknown" = "Unknown"; "Yes" = "Yes"; @@ -95,12 +93,10 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; "Download using cellular" = "Download using cellular"; "Unlink" = "Unlink"; "Unlink %@" = "Unlink %@"; -"All bookmarked articles linked to this zim file will be deleted, \ -but the original file will remain in place." = "All bookmarked articles linked to this zim file will be deleted, \ +"loc-ZimFileDetail-Alert-unlink" = "All bookmarked articles linked to this zim file will be deleted, \ but the original file will remain in place."; "Delete %@" = "Delete %@"; -"The zim file and all bookmarked articles \ -linked to this zim file will be deleted." = "The zim file and all bookmarked articles \ +"loc-ZimFileDetail-Alert-Delete" = "The zim file and all bookmarked articles \ linked to this zim file will be deleted."; "Download" = "Download"; "Space Warning" = "Space Warning"; @@ -152,9 +148,7 @@ linked to this zim file will be deleted."; "Dependencies" = "Dependencies"; "License" = "License"; "Version" = "Version"; -"Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ -It makes knowledge available to people with no or limited internet access. \ -The software as well as the content is free to use for anyone." = "Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ +"loc-About-description" = "Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ It makes knowledge available to people with no or limited internet access. \ The software as well as the content is free to use for anyone."; "This app is released under the terms of the GNU General Public License version 3." = "This app is released under the terms of the GNU General Public License version 3."; @@ -203,4 +197,7 @@ The software as well as the content is free to use for anyone."; "Vikidia" = "Vikidia"; "StackExchange" = "StackExchange"; "Other" = "Other"; - +"Disabled" = "Disabled"; +"First Paragraph" = "First Paragraph"; +"First Sentence" = "First Sentence"; +"Matches" = "Matches"; diff --git a/Views/BuildingBlocks/ArticleCell.swift b/Views/BuildingBlocks/ArticleCell.swift index fa8069c89..f947e7ddd 100644 --- a/Views/BuildingBlocks/ArticleCell.swift +++ b/Views/BuildingBlocks/ArticleCell.swift @@ -65,12 +65,7 @@ struct ArticleCell: View { struct ArticleCell_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """.localized - ) + result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) return result }() diff --git a/Views/BuildingBlocks/SearchResultRow.swift b/Views/BuildingBlocks/SearchResultRow.swift index 57edb5f07..249f72cd4 100644 --- a/Views/BuildingBlocks/SearchResultRow.swift +++ b/Views/BuildingBlocks/SearchResultRow.swift @@ -39,12 +39,7 @@ struct SearchResultRow: View { struct SearchResultRow_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """.localized - ) + result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) return result }() diff --git a/Views/Library/ZimFileDetail.swift b/Views/Library/ZimFileDetail.swift index 4b58afaa7..7bfafb0a0 100644 --- a/Views/Library/ZimFileDetail.swift +++ b/Views/Library/ZimFileDetail.swift @@ -117,10 +117,7 @@ struct ZimFileDetail: View { }.alert(isPresented: $isPresentingUnlinkAlert) { Alert( title: Text("Unlink %@".localizedWithFormat(withArgs: zimFile.name)), - message: Text(""" - All bookmarked articles linked to this zim file will be deleted, \ - but the original file will remain in place. - """), + message: Text("loc-ZimFileDetail-Alert-unlink".localized), primaryButton: .destructive(Text("Unlink".localized)) { LibraryOperations.unlink(zimFileID: zimFile.fileID) #if os(iOS) @@ -138,10 +135,7 @@ struct ZimFileDetail: View { }.alert(isPresented: $isPresentingDeleteAlert) { Alert( title: Text("Delete %@".localizedWithFormat(withArgs: zimFile.name)), - message: Text(""" - The zim file and all bookmarked articles \ - linked to this zim file will be deleted. - """.localized), + message: Text("loc-ZimFileDetail-Alert-Delete".localized), primaryButton: .destructive(Text("Delete".localized)) { LibraryOperations.delete(zimFileID: zimFile.fileID) #if os(iOS) diff --git a/Views/Settings/About.swift b/Views/Settings/About.swift index 72d5bed70..3db016d6c 100644 --- a/Views/Settings/About.swift +++ b/Views/Settings/About.swift @@ -78,13 +78,7 @@ struct About: View { } private var about: some View { - Text( - """ - Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ - It makes knowledge available to people with no or limited internet access. \ - The software as well as the content is free to use for anyone. - """.localized - ) + Text("loc-About-description".localized) } private var release: some View { diff --git a/Views/ViewModifiers/ExternalLinkHandler.swift b/Views/ViewModifiers/ExternalLinkHandler.swift index f48bde1a4..4d3860d46 100644 --- a/Views/ViewModifiers/ExternalLinkHandler.swift +++ b/Views/ViewModifiers/ExternalLinkHandler.swift @@ -53,10 +53,7 @@ struct ExternalLinkHandler: ViewModifier { case .ask: Text("An external link is tapped, do you wish to load the link?".localized) case .notLoading: - Text(""" - An external link is tapped. \ - However, your current setting does not allow it to be loaded. - """.localized) + Text("loc-extenral-alert".localized) } } #if os(iOS) From b3ee97f07d1510a2da81895089492d6605260c8c Mon Sep 17 00:00:00 2001 From: tvision106 Date: Wed, 15 Nov 2023 14:50:45 -0500 Subject: [PATCH 10/17] fix codefactor issue --- Views/BuildingBlocks/ArticleCell.swift | 2 +- Views/BuildingBlocks/SearchResultRow.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Views/BuildingBlocks/ArticleCell.swift b/Views/BuildingBlocks/ArticleCell.swift index f947e7ddd..a3a1b3f72 100644 --- a/Views/BuildingBlocks/ArticleCell.swift +++ b/Views/BuildingBlocks/ArticleCell.swift @@ -65,7 +65,7 @@ struct ArticleCell: View { struct ArticleCell_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) + result.snippet = NSAttributedString(string: "loc-article-cell-template".localized) return result }() diff --git a/Views/BuildingBlocks/SearchResultRow.swift b/Views/BuildingBlocks/SearchResultRow.swift index 249f72cd4..690a37328 100644 --- a/Views/BuildingBlocks/SearchResultRow.swift +++ b/Views/BuildingBlocks/SearchResultRow.swift @@ -39,7 +39,7 @@ struct SearchResultRow: View { struct SearchResultRow_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) + result.snippet = NSAttributedString(string: "loc-article-cell-template".localized) return result }() From 3857568cbdd2fe92bff218146195598a467ab99b Mon Sep 17 00:00:00 2001 From: tvision106 Date: Tue, 14 Nov 2023 16:20:36 -0500 Subject: [PATCH 11/17] Update the localization --- App/App_macOS.swift | 12 +- App/SidebarViewController.swift | 10 +- App/SplitViewController.swift | 2 +- Kiwix.xcodeproj/project.pbxproj | 33 ++- Model/DownloadService.swift | 4 +- Model/Entities/Errors.swift | 6 +- Model/Utilities/String+Extension.swift | 32 +++ Support/en.lproj/Localizable.strings | 202 ++++++++++++++++++ Support/es.lproj/Localizable.strings | 8 + ViewModel/BrowserViewModel.swift | 8 +- Views/Bookmarks.swift | 6 +- Views/BrowserTab.swift | 2 +- Views/BuildingBlocks/ArticleCell.swift | 6 +- Views/BuildingBlocks/Attribute.swift | 4 +- Views/BuildingBlocks/DownloadTaskCell.swift | 4 +- Views/BuildingBlocks/FlavorTag.swift | 6 +- Views/BuildingBlocks/GridSection.swift | 4 +- .../LibraryLastRefreshTime.swift | 4 +- Views/BuildingBlocks/Message.swift | 2 +- Views/BuildingBlocks/SearchResultRow.swift | 4 +- Views/BuildingBlocks/SheetContent.swift | 2 +- Views/BuildingBlocks/TabLabel.swift | 4 +- .../ZimFileMissingIndicator.swift | 2 +- Views/BuildingBlocks/ZimFileRow.swift | 2 +- Views/Buttons/ArticleShortcutButtons.swift | 16 +- Views/Buttons/BookmarkButton.swift | 14 +- Views/Buttons/NavigationButtons.swift | 4 +- Views/Buttons/OutlineButton.swift | 10 +- Views/Buttons/TabsManagerButton.swift | 26 +-- Views/Commands.swift | 10 +- Views/Library/Library.swift | 14 +- Views/Library/ZimFileDetail.swift | 78 +++---- Views/Library/ZimFilesCategories.swift | 10 +- Views/Library/ZimFilesDownloads.swift | 6 +- Views/Library/ZimFilesNew.swift | 8 +- Views/Library/ZimFilesOpened.swift | 10 +- Views/SearchResults.swift | 22 +- Views/Settings/About.swift | 34 +-- Views/Settings/LanguageSelector.swift | 16 +- Views/Settings/Settings.swift | 76 +++---- Views/ViewModifiers/AlertHandler.swift | 2 +- Views/ViewModifiers/BookmarkContextMenu.swift | 4 +- Views/ViewModifiers/ExternalLinkHandler.swift | 10 +- Views/ViewModifiers/FileImport.swift | 6 +- Views/Welcome.swift | 12 +- WikiMed/es.lproj/LaunchScreen.strings | 1 + WikiMed/es.lproj/Main.strings | 1 + 47 files changed, 515 insertions(+), 244 deletions(-) create mode 100644 Model/Utilities/String+Extension.swift create mode 100644 Support/en.lproj/Localizable.strings create mode 100644 Support/es.lproj/Localizable.strings create mode 100644 WikiMed/es.lproj/LaunchScreen.strings create mode 100644 WikiMed/es.lproj/Main.strings diff --git a/App/App_macOS.swift b/App/App_macOS.swift index ce6efe2cf..2bbdcbf7e 100644 --- a/App/App_macOS.swift +++ b/App/App_macOS.swift @@ -32,10 +32,10 @@ struct Kiwix: App { }.commands { SidebarCommands() CommandGroup(replacing: .importExport) { - OpenFileButton(context: .command) { Text("Open...") } + OpenFileButton(context: .command) { Text("Open...".localized) } } CommandGroup(replacing: .newItem) { - Button("New Tab") { + Button("New Tab".localized) { guard let currentWindow = NSApp.keyWindow, let controller = currentWindow.windowController else { return } controller.newWindowForTab(nil) @@ -91,11 +91,11 @@ struct RootView: View { NavigationView { List(selection: $navigation.currentItem) { ForEach(primaryItems, id: \.self) { navigationItem in - Label(navigationItem.name, systemImage: navigationItem.icon) + Label(navigationItem.name.localized, systemImage: navigationItem.icon) } - Section("Library") { + Section("Library".localized) { ForEach(libraryItems, id: \.self) { navigationItem in - Label(navigationItem.name, systemImage: navigationItem.icon) + Label(navigationItem.name.localized, systemImage: navigationItem.icon) } } } @@ -106,7 +106,7 @@ struct RootView: View { responder.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil) } label: { Image(systemName: "sidebar.leading") - }.help("Show sidebar") + }.help("Show sidebar".localized) } switch navigation.currentItem { case .reading: diff --git a/App/SidebarViewController.swift b/App/SidebarViewController.swift index 836850f3a..a29afb156 100644 --- a/App/SidebarViewController.swift +++ b/App/SidebarViewController.swift @@ -86,7 +86,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl }, menu: UIMenu(children: [ UIAction( - title: "Close This Tab", + title: "Close This Tab".localized, image: UIImage(systemName: "xmark.square"), attributes: .destructive ) { [unowned self] _ in @@ -95,7 +95,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl splitViewController.navigationViewModel.deleteTab(tabID: tabID) }, UIAction( - title: "Close All Tabs", + title: "Close All Tabs".localized, image: UIImage(systemName: "xmark.square.fill"), attributes: .destructive ) { [unowned self] _ in @@ -188,11 +188,11 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl switch section { case .tabs: var config = UIListContentConfiguration.sidebarHeader() - config.text = "Tabs" + config.text = "Tabs".localized headerView.contentConfiguration = config case .library: var config = UIListContentConfiguration.sidebarHeader() - config.text = "Library" + config.text = "Library".localized headerView.contentConfiguration = config default: headerView.contentConfiguration = nil @@ -203,7 +203,7 @@ class SidebarViewController: UICollectionViewController, NSFetchedResultsControl guard let splitViewController = splitViewController as? SplitViewController, let item = dataSource.itemIdentifier(for: indexPath), case let .tab(tabID) = item else { return nil } - let action = UIContextualAction(style: .destructive, title: "Close") { _, _, _ in + let action = UIContextualAction(style: .destructive, title: "Close".localized) { _, _, _ in splitViewController.navigationViewModel.deleteTab(tabID: tabID) } action.image = UIImage(systemName: "xmark") diff --git a/App/SplitViewController.swift b/App/SplitViewController.swift index 1e8300320..45a733c40 100644 --- a/App/SplitViewController.swift +++ b/App/SplitViewController.swift @@ -128,7 +128,7 @@ class SplitViewController: UISplitViewController { let controller = UIHostingController(rootView: Settings()) setViewController(UINavigationController(rootViewController: controller), for: .secondary) default: - let controller = UIHostingController(rootView: Text("Not yet implemented")) + let controller = UIHostingController(rootView: Text("Not yet implemented".localized)) setViewController(UINavigationController(rootViewController: controller), for: .secondary) } } diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index 2e2bc8ae8..2aa1a10ab 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 8E4396462B02E455007F0BC4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8E4396492B02E455007F0BC4 /* Localizable.strings */; }; + 8E4396472B02E455007F0BC4 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8E4396492B02E455007F0BC4 /* Localizable.strings */; }; + 8E43964C2B02E4C6007F0BC4 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */; }; + 8E43964D2B02E4C6007F0BC4 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */; }; 97008ABD2974A5BF0076E60C /* OPDSParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97008ABC2974A5BF0076E60C /* OPDSParserTests.swift */; }; 9709C0982A8E4C5700E4564C /* Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9709C0972A8E4C5700E4564C /* Commands.swift */; }; 97121EBE28849F0000371AEB /* ZimFileMissingIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97121EBC28849F0000371AEB /* ZimFileMissingIndicator.swift */; }; @@ -124,6 +128,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 8E4396442B02E443007F0BC4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Main.strings; sourceTree = ""; }; + 8E4396452B02E443007F0BC4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/LaunchScreen.strings; sourceTree = ""; }; + 8E4396482B02E455007F0BC4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + 8E43964A2B02E458007F0BC4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; 97008AB42974A5A70076E60C /* WikiMed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WikiMed.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97008ABA2974A5BF0076E60C /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 97008ABC2974A5BF0076E60C /* OPDSParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPDSParserTests.swift; sourceTree = ""; }; @@ -293,6 +302,7 @@ 9779A7E224567A5A00F6F6FF /* Log.swift */, 97B3BACD2736CE3500A23F49 /* URL.swift */, 9779A73A2456796B00F6F6FF /* WebKitHandler.swift */, + 8E43964B2B02E4C6007F0BC4 /* String+Extension.swift */, ); path = Utilities; sourceTree = ""; @@ -386,8 +396,8 @@ 974E7EE22930201500BDF59C /* ZimFileService */ = { isa = PBXGroup; children = ( - 974E7EE32930201500BDF59C /* ZimFileService.mm */, 974E7EE42930201500BDF59C /* ZimFileService.h */, + 974E7EE32930201500BDF59C /* ZimFileService.mm */, 974E7EE52930201500BDF59C /* ZimFileService.swift */, ); path = ZimFileService; @@ -549,6 +559,7 @@ 97E94B22271EF250005B0295 /* Kiwix.entitlements */, 9779A5D02456796A00F6F6FF /* Kiwix-Bridging-Header.h */, 970885D0271339A300C5795C /* wikipedia_dark.css */, + 8E4396492B02E455007F0BC4 /* Localizable.strings */, ); path = Support; sourceTree = ""; @@ -649,6 +660,7 @@ knownRegions = ( en, Base, + es, ); mainGroup = 97A2AB7F1C1B80FF00052E74; packageReferences = ( @@ -681,6 +693,7 @@ buildActionMask = 2147483647; files = ( 973A0DF72830929C00B41E71 /* Assets.xcassets in Resources */, + 8E4396462B02E455007F0BC4 /* Localizable.strings in Resources */, 979D3A7C284159BF00E396B8 /* injection.js in Resources */, 97DE2BAD283B133700C63D9B /* wikipedia_dark.css in Resources */, ); @@ -691,6 +704,7 @@ buildActionMask = 2147483647; files = ( 97B448A1210FBC2E0004B056 /* LaunchScreen.storyboard in Resources */, + 8E4396472B02E455007F0BC4 /* Localizable.strings in Resources */, 97B4489E210FBC2E0004B056 /* Assets.xcassets in Resources */, 97B4489C210FBC2C0004B056 /* Main.storyboard in Resources */, ); @@ -753,6 +767,7 @@ buildActionMask = 2147483647; files = ( 972DE4BB2814A5A4004FD9B9 /* Errors.swift in Sources */, + 8E43964C2B02E4C6007F0BC4 /* String+Extension.swift in Sources */, 9790CA5A28A05EBB00D39FC6 /* ZimFilesCategories.swift in Sources */, 97486D08284A42B90096E4DD /* SearchResultRow.swift in Sources */, 9753D949285B55F100A626CC /* DefaultKeys.swift in Sources */, @@ -836,6 +851,7 @@ buildActionMask = 2147483647; files = ( 97B44899210FBC2C0004B056 /* ViewController.swift in Sources */, + 8E43964D2B02E4C6007F0BC4 /* String+Extension.swift in Sources */, 97B44897210FBC2C0004B056 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -851,10 +867,20 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + 8E4396492B02E455007F0BC4 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 8E4396482B02E455007F0BC4 /* en */, + 8E43964A2B02E458007F0BC4 /* es */, + ); + name = Localizable.strings; + sourceTree = ""; + }; 97B4489A210FBC2C0004B056 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 97B4489B210FBC2C0004B056 /* Base */, + 8E4396442B02E443007F0BC4 /* es */, ); name = Main.storyboard; sourceTree = ""; @@ -863,6 +889,7 @@ isa = PBXVariantGroup; children = ( 97B448A0210FBC2E0004B056 /* Base */, + 8E4396452B02E443007F0BC4 /* es */, ); name = LaunchScreen.storyboard; sourceTree = ""; @@ -943,7 +970,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 119; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = L7HWM3SP3L; + DEVELOPMENT_TEAM = ""; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -989,7 +1016,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 119; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = L7HWM3SP3L; + DEVELOPMENT_TEAM = ""; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; diff --git a/Model/DownloadService.swift b/Model/DownloadService.swift index 39761c773..a51b0e1d9 100644 --- a/Model/DownloadService.swift +++ b/Model/DownloadService.swift @@ -191,10 +191,10 @@ class DownloadService: NSObject, URLSessionDelegate, URLSessionTaskDelegate, URL Database.shared.container.performBackgroundTask { context in // configure notification content let content = UNMutableNotificationContent() - content.title = "Download Completed" + content.title = "Download Completed".localized content.sound = .default if let zimFile = try? context.fetch(ZimFile.fetchRequest(fileID: zimFileID)).first { - content.body = "\(zimFile.name) has been downloaded successfully." + content.body = "%@ has been downloaded successfully.".localizedWithFormat(withArgs: zimFile.name) } // schedule notification diff --git a/Model/Entities/Errors.swift b/Model/Entities/Errors.swift index 45c9a26b1..46ce9d552 100644 --- a/Model/Entities/Errors.swift +++ b/Model/Entities/Errors.swift @@ -16,12 +16,12 @@ public enum LibraryRefreshError: LocalizedError { public var errorDescription: String? { switch self { case .retrieve(let description): - let prefix = NSLocalizedString("Error retrieving library data.", comment: "Library Refresh Error") + let prefix = "Error retrieving library data.".localized(withComment: "Library Refresh Error") return [prefix, description].compactMap({ $0 }).joined(separator: " ") case .parse: - return NSLocalizedString("Error parsing library data.", comment: "Library Refresh Error") + return "Error parsing library data.".localized(withComment: "Library Refresh Error") case .process: - return NSLocalizedString("Error processing library data.", comment: "Library Refresh Error") + return "Error processing library data.".localized(withComment: "Library Refresh Error") } } } diff --git a/Model/Utilities/String+Extension.swift b/Model/Utilities/String+Extension.swift new file mode 100644 index 000000000..9f1ad1364 --- /dev/null +++ b/Model/Utilities/String+Extension.swift @@ -0,0 +1,32 @@ +// +// String+Extension.swift +// Kiwix +// +// Created by tvision251 on 11/13/23. +// Copyright © 2023 Chris Li. All rights reserved. +// + +import Foundation + +extension String { + + var localized: String { + return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") + } + + func localized(withComment:String) -> String { + return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: withComment) + } + + func localizedWithFormat(withArgs: CVarArg...) -> String { + let format = NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") + + switch withArgs.count { + case 1: return String.localizedStringWithFormat(format, withArgs[0]) + case 2: return String.localizedStringWithFormat(format, withArgs[0], withArgs[1]) + default: return String.localizedStringWithFormat(format, withArgs) + } + } + +} + diff --git a/Support/en.lproj/Localizable.strings b/Support/en.lproj/Localizable.strings new file mode 100644 index 000000000..632378726 --- /dev/null +++ b/Support/en.lproj/Localizable.strings @@ -0,0 +1,202 @@ +/* + Localizable.strings + Kiwix + + Created by tvision251 on 11/13/23. + Copyright © 2023 Chris Li. All rights reserved. +*/ + +/* Library Refresh Error */ +"Error retrieving library data." = "Error retrieving library data."; +/* Library Refresh Error */ +"Error parsing library data." = "Error parsing library data."; +/* Library Refresh Error */ +"Error processing library data." = "Error processing library data."; + +"Download Completed" = "Download Completed"; +"%@ has been downloaded successfully." = "%@ has been downloaded successfully."; +"Done" = "Done"; +"Go Back" = "Go Back"; +"Go Forward" = "Go Forward"; +"Outline" = "Outline"; +"Show article outline" = "Show article outline"; +"No outline available" = "No outline available"; +"Remove Bookmark" = "Remove Bookmark"; +"Add Bookmark" = "Add Bookmark"; +"Show Bookmarks" = "Show Bookmarks"; +"Show bookmarks. Long press to bookmark or unbookmark the current article." = "Show bookmarks. Long press to bookmark or unbookmark the current article."; +"Main Article" = "Main Article"; +"Show main article" = "Show main article"; +"Random Article" = "Random Article"; +"Show random article" = "Show random article"; +"Random Page" = "Random Page"; +"Tabs" = "Tabs"; +"New Tab" = "New Tab"; +"Close This Tab" = "Close This Tab"; +"Close All Tabs" = "Close All Tabs"; +"Close Tab" = "Close Tab"; +"Library" = "Library"; +"Settings" = "Settings"; +"Tabs Manager" = "Tabs Manager"; +"Unable to load the article requested." = "Unable to load the article requested."; +"View" = "View"; +"Remove" = "Remove"; +"External Link" = "External Link"; +"Load the link" = "Load the link"; +"Cancel" = "Cancel"; +"An external link is tapped, do you wish to load the link?" = "An external link is tapped, do you wish to load the link?"; +"An external link is tapped. However, your current setting does not allow it to be loaded." = "An external link is tapped. However, your current setting does not allow it to be loaded."; +"Open a zim file" = "Open a zim file"; +"Unable to open file" = "Unable to open file"; +"%@ cannot be opened." = "%@ cannot be opened."; +"No snippet" = "No snippet"; +"Article Title" = "Article Title"; +"Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ +sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ +sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; +"Unknown" = "Unknown"; +"Yes" = "Yes"; +"No" = "No"; +"Failed" = "Failed"; +"Downloading..." = "Downloading..."; +"Paused" = "Paused"; +"everything except large media files like video/audio" = "everything except large media files like video/audio"; +"most pictures have been removed" = "most pictures have been removed"; +"only a subset of the text is available, probably the first section" = "only a subset of the text is available, probably the first section"; +"Header Text" = "Header Text"; +"Content" = "Content"; +"Just Now" = "Just Now"; +"Never" = "Never"; +"There is nothing to see" = "There is nothing to see"; +"Article Title" = "Article Title"; +"Zim file is missing." = "Zim file is missing."; +"articles" = "articles"; +"Select a zim file to see detail" = "Select a zim file to see detail"; +"Main Page" = "Main Page"; +"Copy URL" = "Copy URL"; +"Copy ID" = "Copy ID"; +"No opened zim file" = "No opened zim file"; +"Show Sidebar" = "Show Sidebar"; +"Open..." = "Open..."; +"Category" = "Category"; +"No zim file under this category." = "No zim file under this category."; +"No download tasks" = "No download tasks"; +"No new zim file" = "No new zim file"; +"Refresh" = "Refresh"; +"Name" = "Name"; +"Description" = "Description"; +"Actions" = "Actions"; +"Info" = "Info"; +"Locate" = "Locate"; +"Open Main Page" = "Open Main Page"; +"Reveal in Finder" = "Reveal in Finder"; +"Download using cellular" = "Download using cellular"; +"Unlink" = "Unlink"; +"Unlink %@" = "Unlink %@"; +"All bookmarked articles linked to this zim file will be deleted, \ +but the original file will remain in place." = "All bookmarked articles linked to this zim file will be deleted, \ +but the original file will remain in place."; +"Delete %@" = "Delete %@"; +"The zim file and all bookmarked articles linked to this zim file will be deleted." = "The zim file and all bookmarked articles linked to this zim file will be deleted."; +"Download" = "Download"; +"Space Warning" = "Space Warning"; +"There might not be enough space on your device for this zim file." = "There might not be enough space on your device for this zim file."; +"There would be less than 1GB space left after the zim file is downloaded." = "There would be less than 1GB space left after the zim file is downloaded."; +"Download Anyway" = "Download Anyway"; +"Language" = "Language"; +"Size" = "Size"; +"Created" = "Created"; +"Pictures" = "Pictures"; +"Videos" = "Videos"; +"Details" = "Details"; +"Requires Service Workers" = "Requires Service Workers"; +"Article Count" = "Article Count"; +"Media Count" = "Media Count"; +"ID" = "ID"; +"Try to Recover" = "Try to Recover"; +"Pause" = "Pause"; +"Resume" = "Resume"; +"Zim files requiring service workers are not supported." = "Zim files requiring service workers are not supported."; +"A very long description" = "A very long description"; +"Page zoom" = "Page zoom"; +"Reset" = "Reset"; +"External link" = "External link"; +"Search snippet" = "Search snippet"; +"Reading" = "Reading"; +"Catalog" = "Catalog"; +"Refresh Now" = "Refresh Now"; +"Last refresh" = "Last refresh"; +"Auto refresh" = "Auto refresh"; +"When enabled, the library catalog will be refreshed automatically when outdated." = "When enabled, the library catalog will be refreshed automatically when outdated."; +"Languages" = "Languages"; +"Library" = "Library"; +"Change will only apply to new download tasks." = "Change will only apply to new download tasks."; +"Refreshing..." = "Refreshing..."; +"Include zim files in backup" = "Include zim files in backup"; +"Backup" = "Backup"; +"Does not apply to files opened in place." = "Does not apply to files opened in place."; +"Misc" = "Misc"; +"Feedback" = "Feedback"; +"Rate the App" = "Rate the App"; +"About" = "About"; +"Count" = "Count"; +"No language" = "No language"; +"Showing" = "Showing"; +"Hiding" = "Hiding"; +"Sorting" = "Sorting"; +"Release" = "Release"; +"Dependencies" = "Dependencies"; +"License" = "License"; +"Version" = "Version"; +"Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ +It makes knowledge available to people with no or limited internet access. \ +The software as well as the content is free to use for anyone." = "Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ +It makes knowledge available to people with no or limited internet access. \ +The software as well as the content is free to use for anyone."; +"This app is released under the terms of the GNU General Public License version 3." = "This app is released under the terms of the GNU General Public License version 3."; +"Build" = "Build"; +"Our Website" = "Our Website"; +"Source" = "Source"; +"GNU General Public License v3" = "GNU General Public License v3"; +"Bookmarks" = "Bookmarks"; +"No bookmarks" = "No bookmarks"; +"No result" = "No result"; +"Recent Search" = "Recent Search"; +"Clear" = "Clear"; +"Clear Recent Searches" = "Clear Recent Searches"; +"Clear All" = "Clear All"; +"All recent search history will be removed." = "All recent search history will be removed."; +"Included in Search" = "Included in Search"; +"None" = "None"; +"All" = "All"; +"Open File" = "Open File"; +"Fetching..." = "Fetching..."; +"Fetch Catalog" = "Fetch Catalog"; +"Actual Size" = "Actual Size"; +"Zoom In" = "Zoom In"; +"Zoom Out" = "Zoom Out"; +"Not yet implemented" = "Not yet implemented"; +"Close" = "Close"; +"Map" = "Map"; +"Opened" = "Opened"; +"Categories" = "Categories"; +"New" = "New"; +"Downloads" = "Downloads"; +"A-Z" = "A-Z"; +"By Count" = "By Count"; +"Always Ask" = "Always Ask"; +"Always Load" = "Always Load"; +"Never Load" = "Never Load"; +"Wikipedia" = "Wikipedia"; +"Wikibooks" = "Wikibooks"; +"Wikinews" = "Wikinews"; +"Wikiquote" = "Wikiquote"; +"Wikisource" = "Wikisource"; +"Wikiversity" = "Wikiversity"; +"Wikivoyage" = "Wikivoyage"; +"Wiktionary" = "Wiktionary"; +"TED" = "TED"; +"Vikidia" = "Vikidia"; +"StackExchange" = "StackExchange"; +"Other" = "Other"; + diff --git a/Support/es.lproj/Localizable.strings b/Support/es.lproj/Localizable.strings new file mode 100644 index 000000000..e83d7f5c0 --- /dev/null +++ b/Support/es.lproj/Localizable.strings @@ -0,0 +1,8 @@ +/* + Localizable.strings + Kiwix + + Created by tvision251 on 11/13/23. + Copyright © 2023 Chris Li. All rights reserved. +*/ + diff --git a/ViewModel/BrowserViewModel.swift b/ViewModel/BrowserViewModel.swift index 41fa872d3..b77ce98ec 100644 --- a/ViewModel/BrowserViewModel.swift +++ b/ViewModel/BrowserViewModel.swift @@ -251,12 +251,12 @@ class BrowserViewModel: NSObject, ObservableObject, // open url actions.append( - UIAction(title: "Open", image: UIImage(systemName: "doc.text")) { _ in + UIAction(title: "Open".localized, image: UIImage(systemName: "doc.text")) { _ in webView.load(URLRequest(url: url)) } ) actions.append( - UIAction(title: "Open in New Tab", image: UIImage(systemName: "doc.badge.plus")) { _ in + UIAction(title: "Open in New Tab".localized, image: UIImage(systemName: "doc.badge.plus")) { _ in NotificationCenter.openURL(url, inNewTab: true) } ) @@ -267,11 +267,11 @@ class BrowserViewModel: NSObject, ObservableObject, let predicate = NSPredicate(format: "articleURL == %@", url as CVarArg) let request = Bookmark.fetchRequest(predicate: predicate) if let bookmarks = try? context.fetch(request), !bookmarks.isEmpty { - return UIAction(title: "Remove Bookmark", image: UIImage(systemName: "star.slash.fill")) { _ in + return UIAction(title: "Remove Bookmark".localized, image: UIImage(systemName: "star.slash.fill")) { _ in self.deleteBookmark(url: url) } } else { - return UIAction(title: "Bookmark", image: UIImage(systemName: "star")) { _ in + return UIAction(title: "Bookmark".localized, image: UIImage(systemName: "star")) { _ in self.createBookmark(url: url) } } diff --git a/Views/Bookmarks.swift b/Views/Bookmarks.swift index f24ab25d5..988023e86 100644 --- a/Views/Bookmarks.swift +++ b/Views/Bookmarks.swift @@ -36,14 +36,14 @@ struct Bookmarks: View { } .modifier(GridCommon()) .modifier(ToolbarRoleBrowser()) - .navigationTitle("Bookmarks") + .navigationTitle("Bookmarks".localized) .searchable(text: $searchText) .onChange(of: searchText) { searchText in bookmarks.nsPredicate = Bookmarks.buildPredicate(searchText: searchText) } .overlay { if bookmarks.isEmpty { - Message(text: "No bookmarks") + Message(text: "No bookmarks".localized) } } .toolbar { @@ -53,7 +53,7 @@ struct Bookmarks: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } diff --git a/Views/BrowserTab.swift b/Views/BrowserTab.swift index 9a080449e..aec014f54 100644 --- a/Views/BrowserTab.swift +++ b/Views/BrowserTab.swift @@ -22,7 +22,7 @@ struct BrowserTab: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } NavigationButtons() diff --git a/Views/BuildingBlocks/ArticleCell.swift b/Views/BuildingBlocks/ArticleCell.swift index f5681f328..fa8069c89 100644 --- a/Views/BuildingBlocks/ArticleCell.swift +++ b/Views/BuildingBlocks/ArticleCell.swift @@ -44,7 +44,7 @@ struct ArticleCell: View { if let snippet = snippet { Text(AttributedString(snippet)).lineLimit(4) } else if alwaysShowSnippet { - Text("No snippet").foregroundColor(.secondary) + Text("No snippet".localized).foregroundColor(.secondary) } }.font(.caption).multilineTextAlignment(.leading) Spacer(minLength: 0) @@ -64,12 +64,12 @@ struct ArticleCell: View { struct ArticleCell_Previews: PreviewProvider { static let result: SearchResult = { - let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title")! + let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! result.snippet = NSAttributedString(string: """ Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """ + """.localized ) return result }() diff --git a/Views/BuildingBlocks/Attribute.swift b/Views/BuildingBlocks/Attribute.swift index 038f9202d..98ff221b7 100644 --- a/Views/BuildingBlocks/Attribute.swift +++ b/Views/BuildingBlocks/Attribute.swift @@ -16,7 +16,7 @@ struct Attribute: View { HStack { Text(title) Spacer() - Text(detail ?? "Unknown").foregroundColor(.secondary) + Text(detail ?? "Unknown".localized).foregroundColor(.secondary) } } } @@ -30,7 +30,7 @@ struct AttributeBool: View { Text(title) Spacer() #if os(macOS) - Text(detail ? "Yes" : "No").foregroundColor(.secondary) + Text(detail ? "Yes".localized : "No".localized).foregroundColor(.secondary) #elseif os(iOS) if detail { Image(systemName: "checkmark.circle.fill").foregroundColor(.green) diff --git a/Views/BuildingBlocks/DownloadTaskCell.swift b/Views/BuildingBlocks/DownloadTaskCell.swift index f1fb7f1a0..530cd7ad2 100644 --- a/Views/BuildingBlocks/DownloadTaskCell.swift +++ b/Views/BuildingBlocks/DownloadTaskCell.swift @@ -41,9 +41,9 @@ struct DownloadTaskCell: View { } VStack(alignment: .leading, spacing: 4) { if downloadTask.error != nil { - Text("Failed") + Text("Failed".localized) } else if downloadTask.resumeData == nil { - Text("Downloading...") + Text("Downloading...".localized) } else { Text("Paused") } diff --git a/Views/BuildingBlocks/FlavorTag.swift b/Views/BuildingBlocks/FlavorTag.swift index ffe22e3b8..17e3c6827 100644 --- a/Views/BuildingBlocks/FlavorTag.swift +++ b/Views/BuildingBlocks/FlavorTag.swift @@ -41,11 +41,11 @@ struct FlavorTag: View { var help: String { switch flavor { case .max: - return "everything except large media files like video/audio" + return "everything except large media files like video/audio".localized case .noPic: - return "most pictures have been removed" + return "most pictures have been removed".localized case .mini: - return "only a subset of the text is available, probably the first section" + return "only a subset of the text is available, probably the first section".localized } } } diff --git a/Views/BuildingBlocks/GridSection.swift b/Views/BuildingBlocks/GridSection.swift index 6dd622d5d..d820a9151 100644 --- a/Views/BuildingBlocks/GridSection.swift +++ b/Views/BuildingBlocks/GridSection.swift @@ -29,8 +29,8 @@ struct GridSection: View { struct GridSection_Previews: PreviewProvider { static var previews: some View { - GridSection(title: "Header Text") { - Text("Content") + GridSection(title: "Header Text".localized) { + Text("Content".localized) } } } diff --git a/Views/BuildingBlocks/LibraryLastRefreshTime.swift b/Views/BuildingBlocks/LibraryLastRefreshTime.swift index 47156b964..9fbd4aa9d 100644 --- a/Views/BuildingBlocks/LibraryLastRefreshTime.swift +++ b/Views/BuildingBlocks/LibraryLastRefreshTime.swift @@ -16,12 +16,12 @@ struct LibraryLastRefreshTime: View { var body: some View { if let lastRefresh = lastRefresh { if Date().timeIntervalSince(lastRefresh) < 120 { - Text("Just Now") + Text("Just Now".localized) } else { Text(RelativeDateTimeFormatter().localizedString(for: lastRefresh, relativeTo: Date())) } } else { - Text("Never") + Text("Never".localized) } } } diff --git a/Views/BuildingBlocks/Message.swift b/Views/BuildingBlocks/Message.swift index 4992e79de..2b0e5a88d 100644 --- a/Views/BuildingBlocks/Message.swift +++ b/Views/BuildingBlocks/Message.swift @@ -26,7 +26,7 @@ struct Message: View { struct Message_Previews: PreviewProvider { static var previews: some View { - Message(text: "There is nothing to see") + Message(text: "There is nothing to see".localized) .frame(width: 250, height: 200) } } diff --git a/Views/BuildingBlocks/SearchResultRow.swift b/Views/BuildingBlocks/SearchResultRow.swift index 55b33dc7a..57edb5f07 100644 --- a/Views/BuildingBlocks/SearchResultRow.swift +++ b/Views/BuildingBlocks/SearchResultRow.swift @@ -38,12 +38,12 @@ struct SearchResultRow: View { struct SearchResultRow_Previews: PreviewProvider { static let result: SearchResult = { - let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title")! + let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! result.snippet = NSAttributedString(string: """ Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """ + """.localized ) return result }() diff --git a/Views/BuildingBlocks/SheetContent.swift b/Views/BuildingBlocks/SheetContent.swift index f7f3fa061..022ea301e 100644 --- a/Views/BuildingBlocks/SheetContent.swift +++ b/Views/BuildingBlocks/SheetContent.swift @@ -27,7 +27,7 @@ struct SheetContent: View { Button { dismiss() } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } diff --git a/Views/BuildingBlocks/TabLabel.swift b/Views/BuildingBlocks/TabLabel.swift index ebd8fa598..e31847ded 100644 --- a/Views/BuildingBlocks/TabLabel.swift +++ b/Views/BuildingBlocks/TabLabel.swift @@ -15,12 +15,12 @@ struct TabLabel: View { var body: some View { if let zimFile = tab.zimFile, let category = Category(rawValue: zimFile.category) { Label { - Text(tab.title ?? "New Tab").lineLimit(1) + Text(tab.title ?? "New Tab".localized).lineLimit(1) } icon: { Favicon(category: category, imageData: zimFile.faviconData).frame(width: 22, height: 22) } } else { - Label(tab.title ?? "New Tab", systemImage: "square") + Label(tab.title ?? "New Tab".localized, systemImage: "square") } } } diff --git a/Views/BuildingBlocks/ZimFileMissingIndicator.swift b/Views/BuildingBlocks/ZimFileMissingIndicator.swift index da7c3b3c1..2e0a23a8d 100644 --- a/Views/BuildingBlocks/ZimFileMissingIndicator.swift +++ b/Views/BuildingBlocks/ZimFileMissingIndicator.swift @@ -12,6 +12,6 @@ struct ZimFileMissingIndicator: View { var body: some View { Image(systemName: "exclamationmark.triangle.fill") .renderingMode(.original) - .help("Zim file is missing.") + .help("Zim file is missing.".localized) } } diff --git a/Views/BuildingBlocks/ZimFileRow.swift b/Views/BuildingBlocks/ZimFileRow.swift index 0fb4d3878..73510e563 100644 --- a/Views/BuildingBlocks/ZimFileRow.swift +++ b/Views/BuildingBlocks/ZimFileRow.swift @@ -30,7 +30,7 @@ struct ZimFileRow: View { Formatter.size.string(fromByteCount: zimFile.size), { if #available(iOS 15.0, *) { - return "\(zimFile.articleCount.formatted(.number.notation(.compactName))) articles" + return "\(zimFile.articleCount.formatted(.number.notation(.compactName)))" + "articles".localized } else { return Formatter.largeNumber(zimFile.articleCount) } diff --git a/Views/Buttons/ArticleShortcutButtons.swift b/Views/Buttons/ArticleShortcutButtons.swift index 79ee831de..7e4660473 100644 --- a/Views/Buttons/ArticleShortcutButtons.swift +++ b/Views/Buttons/ArticleShortcutButtons.swift @@ -40,10 +40,10 @@ struct ArticleShortcutButtons: View { browser.loadMainArticle() dismissSearch() } label: { - Label("Main Article", systemImage: "house") + Label("Main Article".localized, systemImage: "house") } .disabled(zimFiles.isEmpty) - .help("Show main article") + .help("Show main article".localized) #elseif os(iOS) Menu { ForEach(zimFiles) { zimFile in @@ -53,13 +53,13 @@ struct ArticleShortcutButtons: View { } } } label: { - Label("Main Article", systemImage: "house") + Label("Main Article".localized, systemImage: "house") } primaryAction: { browser.loadMainArticle() dismissSearch() } .disabled(zimFiles.isEmpty) - .help("Show main article") + .help("Show main article".localized) #endif } @@ -69,10 +69,10 @@ struct ArticleShortcutButtons: View { browser.loadRandomArticle() dismissSearch() } label: { - Label("Random Article", systemImage: "die.face.5") + Label("Random Article".localized, systemImage: "die.face.5") } .disabled(zimFiles.isEmpty) - .help("Show random article") + .help("Show random article".localized) #elseif os(iOS) Menu { ForEach(zimFiles) { zimFile in @@ -82,13 +82,13 @@ struct ArticleShortcutButtons: View { } } } label: { - Label("Random Page", systemImage: "die.face.5") + Label("Random Page".localized, systemImage: "die.face.5") } primaryAction: { browser.loadRandomArticle() dismissSearch() } .disabled(zimFiles.isEmpty) - .help("Show random article") + .help("Show random article".localized) #endif } } diff --git a/Views/Buttons/BookmarkButton.swift b/Views/Buttons/BookmarkButton.swift index 385084421..96b40565e 100644 --- a/Views/Buttons/BookmarkButton.swift +++ b/Views/Buttons/BookmarkButton.swift @@ -23,7 +23,7 @@ struct BookmarkButton: View { } } label: { Label { - Text(browser.articleBookmarked ? "Remove Bookmark" : "Add Bookmark") + Text(browser.articleBookmarked ? "Remove Bookmark".localized : "Add Bookmark".localized) } icon: { Image(systemName: browser.articleBookmarked ? "star.fill" : "star") .renderingMode(browser.articleBookmarked ? .original : .template) @@ -35,23 +35,23 @@ struct BookmarkButton: View { Button(role: .destructive) { browser.deleteBookmark() } label: { - Label("Remove Bookmark", systemImage: "star.slash.fill") + Label("Remove Bookmark".localized, systemImage: "star.slash.fill") } } else { Button { browser.createBookmark() } label: { - Label("Add Bookmark", systemImage: "star") + Label("Add Bookmark".localized, systemImage: "star") } } Button { isShowingBookmark = true } label: { - Label("Show Bookmarks", systemImage: "list.star") + Label("Show Bookmarks".localized, systemImage: "list.star") } } label: { Label { - Text("Show Bookmarks") + Text("Show Bookmarks".localized) } icon: { Image(systemName: browser.articleBookmarked ? "star.fill" : "star") .renderingMode(browser.articleBookmarked ? .original : .template) @@ -59,7 +59,7 @@ struct BookmarkButton: View { } primaryAction: { isShowingBookmark = true } - .help("Show bookmarks. Long press to bookmark or unbookmark the current article.") + .help("Show bookmarks. Long press to bookmark or unbookmark the current article.".localized) .popover(isPresented: $isShowingBookmark) { NavigationView { Bookmarks().navigationBarTitleDisplayMode(.inline).toolbar { @@ -67,7 +67,7 @@ struct BookmarkButton: View { Button { isShowingBookmark = false } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } diff --git a/Views/Buttons/NavigationButtons.swift b/Views/Buttons/NavigationButtons.swift index e30dec407..8f8f121de 100644 --- a/Views/Buttons/NavigationButtons.swift +++ b/Views/Buttons/NavigationButtons.swift @@ -29,7 +29,7 @@ struct NavigationButtons: View { browser.webView.goBack() dismissSearch() } label: { - Label("Go Back", systemImage: "chevron.left") + Label("Go Back".localized, systemImage: "chevron.left") }.disabled(!browser.canGoBack) } @@ -38,7 +38,7 @@ struct NavigationButtons: View { browser.webView.goForward() dismissSearch() } label: { - Label("Go Forward", systemImage: "chevron.right") + Label("Go Forward".localized, systemImage: "chevron.right") }.disabled(!browser.canGoForward) } } diff --git a/Views/Buttons/OutlineButton.swift b/Views/Buttons/OutlineButton.swift index 671b74d9c..3eb7ef874 100644 --- a/Views/Buttons/OutlineButton.swift +++ b/Views/Buttons/OutlineButton.swift @@ -25,10 +25,10 @@ struct OutlineButton: View { } } } label: { - Label("Outline", systemImage: "list.bullet") + Label("Outline".localized, systemImage: "list.bullet") } .disabled(browser.outlineItems.isEmpty) - .help("Show article outline") + .help("Show article outline".localized) #elseif os(iOS) Button { isShowingOutline = true @@ -36,12 +36,12 @@ struct OutlineButton: View { Image(systemName: "list.bullet") } .disabled(browser.outlineItems.isEmpty) - .help("Show article outline") + .help("Show article outline".localized) .popover(isPresented: $isShowingOutline) { NavigationView { Group { if browser.outlineItemTree.isEmpty { - Message(text: "No outline available") + Message(text: "No outline available".localized) } else { List(browser.outlineItemTree) { item in OutlineNode(item: item) { item in @@ -59,7 +59,7 @@ struct OutlineButton: View { Button { isShowingOutline = false } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } diff --git a/Views/Buttons/TabsManagerButton.swift b/Views/Buttons/TabsManagerButton.swift index f8bf203a4..212ce3024 100644 --- a/Views/Buttons/TabsManagerButton.swift +++ b/Views/Buttons/TabsManagerButton.swift @@ -29,18 +29,18 @@ struct TabsManagerButton: View { Button { navigation.createTab() } label: { - Label("New Tab", systemImage: "plus.square") + Label("New Tab".localized, systemImage: "plus.square") } Button(role: .destructive) { guard case .tab(let tabID) = navigation.currentItem else { return } navigation.deleteTab(tabID: tabID) } label: { - Label("Close This Tab", systemImage: "xmark.square") + Label("Close This Tab".localized, systemImage: "xmark.square") } Button(role: .destructive) { navigation.deleteAllTabs() } label: { - Label("Close All Tabs", systemImage: "xmark.square.fill") + Label("Close All Tabs".localized, systemImage: "xmark.square.fill") } } Section { @@ -54,16 +54,16 @@ struct TabsManagerButton: View { Button { presentedSheet = .library } label: { - Label("Library", systemImage: "folder") + Label("Library".localized, systemImage: "folder") } Button { presentedSheet = .settings } label: { - Label("Settings", systemImage: "gear") + Label("Settings".localized, systemImage: "gear") } } } label: { - Label("Tabs Manager", systemImage: "square.stack") + Label("Tabs Manager".localized, systemImage: "square.stack") } primaryAction: { presentedSheet = .tabsManager } @@ -76,7 +76,7 @@ struct TabsManagerButton: View { Button { self.presentedSheet = nil } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } @@ -90,7 +90,7 @@ struct TabsManagerButton: View { Button { self.presentedSheet = nil } label: { - Text("Done").fontWeight(.semibold) + Text("Done".localized).fontWeight(.semibold) } } } @@ -129,12 +129,12 @@ struct TabManager: View { Button(role: .destructive) { navigation.deleteTab(tabID: tab.objectID) } label: { - Label("Close Tab", systemImage: "xmark") + Label("Close Tab".localized, systemImage: "xmark") } } } .listStyle(.plain) - .navigationTitle("Tabs") + .navigationTitle("Tabs".localized) .navigationBarTitleDisplayMode(.inline) .toolbar { Menu { @@ -142,15 +142,15 @@ struct TabManager: View { guard case let .tab(tabID) = navigation.currentItem else { return } navigation.deleteTab(tabID: tabID) } label: { - Label("Close This Tab", systemImage: "xmark.square") + Label("Close This Tab".localized, systemImage: "xmark.square") } Button(role: .destructive) { navigation.deleteAllTabs() } label: { - Label("Close All Tabs", systemImage: "xmark.square.fill") + Label("Close All Tabs".localized, systemImage: "xmark.square.fill") } } label: { - Label("New Tab", systemImage: "plus.square") + Label("New Tab".localized, systemImage: "plus.square") } primaryAction: { navigation.createTab() } diff --git a/Views/Commands.swift b/Views/Commands.swift index 7550ec0e4..61672c3df 100644 --- a/Views/Commands.swift +++ b/Views/Commands.swift @@ -54,10 +54,10 @@ struct NavigationCommands: View { @FocusedValue(\.browserViewModel) var browser: BrowserViewModel? var body: some View { - Button("Go Back") { browser?.webView.goBack() } + Button("Go Back".localized) { browser?.webView.goBack() } .keyboardShortcut("[") .disabled(canGoBack != true) - Button("Go Forward") { browser?.webView.goForward() } + Button("Go Forward".localized) { browser?.webView.goForward() } .keyboardShortcut("]") .disabled(canGoForward != true) } @@ -68,13 +68,13 @@ struct PageZoomCommands: View { @FocusedValue(\.browserViewModel) var browser: BrowserViewModel? var body: some View { - Button("Actual Size") { webViewPageZoom = 1 } + Button("Actual Size".localized) { webViewPageZoom = 1 } .keyboardShortcut("0") .disabled(webViewPageZoom == 1 || browser?.url == nil) - Button("Zoom In") { webViewPageZoom += 0.1 } + Button("Zoom In".localized) { webViewPageZoom += 0.1 } .keyboardShortcut("+") .disabled(webViewPageZoom >= 2 || browser?.url == nil) - Button("Zoom Out") { webViewPageZoom -= 0.1 } + Button("Zoom Out".localized) { webViewPageZoom -= 0.1 } .keyboardShortcut("-") .disabled(webViewPageZoom <= 0.5 || browser?.url == nil) } diff --git a/Views/Library/Library.swift b/Views/Library/Library.swift index 06d9770cd..b2786227e 100644 --- a/Views/Library/Library.swift +++ b/Views/Library/Library.swift @@ -32,7 +32,7 @@ struct Library: View { List(Category.allCases) { category in NavigationLink { ZimFilesCategory(category: .constant(category)) - .navigationTitle(category.name) + .navigationTitle(category.name.localized) .navigationBarTitleDisplayMode(.inline) } label: { HStack { @@ -42,7 +42,7 @@ struct Library: View { } } .listStyle(.plain) - .navigationTitle(NavigationItem.categories.name) + .navigationTitle(NavigationItem.categories.name.localized) case .downloads: ZimFilesDownloads() case .new: @@ -87,7 +87,7 @@ struct LibraryZimFileDetailSidePanel: ViewModifier { if let zimFile = viewModel.selectedZimFile { ZimFileDetail(zimFile: zimFile) } else { - Message(text: "Select a zim file to see detail").background(.thickMaterial) + Message(text: "Select a zim file to see detail".localized).background(.thickMaterial) } }.frame(width: 275).background(.ultraThinMaterial) } @@ -132,13 +132,13 @@ struct LibraryZimFileContext: ViewModifier { guard let url = ZimFileService.shared.getMainPageURL(zimFileID: zimFile.fileID) else { return } NotificationCenter.openURL(url, inNewTab: true) } label: { - Label("Main Page", systemImage: "house") + Label("Main Page".localized, systemImage: "house") } Button { guard let url = ZimFileService.shared.getRandomPageURL(zimFileID: zimFile.fileID) else { return } NotificationCenter.openURL(url, inNewTab: true) } label: { - Label("Random Page", systemImage: "die.face.5") + Label("Random Page".localized, systemImage: "die.face.5") } } @@ -153,7 +153,7 @@ struct LibraryZimFileContext: ViewModifier { UIPasteboard.general.setValue(downloadURL.absoluteString, forPasteboardType: UTType.url.identifier) #endif } label: { - Label("Copy URL", systemImage: "doc.on.doc") + Label("Copy URL".localized, systemImage: "doc.on.doc") } } Button { @@ -164,7 +164,7 @@ struct LibraryZimFileContext: ViewModifier { UIPasteboard.general.setValue(zimFile.fileID.uuidString, forPasteboardType: UTType.plainText.identifier) #endif } label: { - Label("Copy ID", systemImage: "barcode.viewfinder") + Label("Copy ID".localized, systemImage: "barcode.viewfinder") } } } diff --git a/Views/Library/ZimFileDetail.swift b/Views/Library/ZimFileDetail.swift index 72b0d9482..5ee91f1a1 100644 --- a/Views/Library/ZimFileDetail.swift +++ b/Views/Library/ZimFileDetail.swift @@ -25,10 +25,10 @@ struct ZimFileDetail: View { var body: some View { #if os(macOS) List { - Section("Name") { Text(zimFile.name).lineLimit(nil) }.collapsible(false) - Section("Description") { Text(zimFile.fileDescription).lineLimit(nil) }.collapsible(false) - Section("Actions") { actions }.collapsible(false) - Section("Info") { + Section("Name".localized) { Text(zimFile.name).lineLimit(nil) }.collapsible(false) + Section("Description".localized) { Text(zimFile.fileDescription).lineLimit(nil) }.collapsible(false) + Section("Actions".localized) { actions }.collapsible(false) + Section("Info".localized) { basicInfo boolInfo counts @@ -81,15 +81,15 @@ struct ZimFileDetail: View { if let downloadTask = zimFile.downloadTask { // zim file is being downloaded DownloadTaskDetail(downloadTask: downloadTask) } else if zimFile.isMissing { // zim file was opened, but is now missing - Action(title: "Locate") { isPresentingFileLocator = true } + Action(title: "Locate".localized) { isPresentingFileLocator = true } unlinkAction } else if zimFile.fileURLBookmark != nil { // zim file is opened - Action(title: "Open Main Page") { + Action(title: "Open Main Page".localized) { guard let url = ZimFileService.shared.getMainPageURL(zimFileID: zimFile.fileID) else { return } NotificationCenter.openURL(url, inNewTab: true) } #if os(macOS) - Action(title: "Reveal in Finder") { + Action(title: "Reveal in Finder".localized) { guard let url = ZimFileService.shared.getFileURL(zimFileID: zimFile.id) else { return } NSWorkspace.shared.activateFileViewerSelecting([url]) } @@ -105,23 +105,23 @@ struct ZimFileDetail: View { #endif } else if zimFile.downloadURL != nil { // zim file can be downloaded #if os(iOS) - Toggle("Download using cellular", isOn: $downloadUsingCellular) + Toggle("Download using cellular".localized, isOn: $downloadUsingCellular) #endif downloadAction } } var unlinkAction: some View { - Action(title: "Unlink", isDestructive: true) { + Action(title: "Unlink".localized, isDestructive: true) { isPresentingUnlinkAlert = true }.alert(isPresented: $isPresentingUnlinkAlert) { Alert( - title: Text("Unlink \(zimFile.name)"), + title: Text("Unlink %@".localizedWithFormat(withArgs: zimFile.name)), message: Text(""" All bookmarked articles linked to this zim file will be deleted, \ but the original file will remain in place. """), - primaryButton: .destructive(Text("Unlink")) { + primaryButton: .destructive(Text("Unlink".localized)) { LibraryOperations.unlink(zimFileID: zimFile.fileID) #if os(iOS) presentationMode.wrappedValue.dismiss() @@ -137,9 +137,9 @@ struct ZimFileDetail: View { isPresentingDeleteAlert = true }.alert(isPresented: $isPresentingDeleteAlert) { Alert( - title: Text("Delete \(zimFile.name)"), - message: Text("The zim file and all bookmarked articles linked to this zim file will be deleted."), - primaryButton: .destructive(Text("Delete")) { + title: Text("Delete %@".localizedWithFormat(withArgs: zimFile.name)), + message: Text("The zim file and all bookmarked articles linked to this zim file will be deleted.".localized), + primaryButton: .destructive(Text("Delete".localized)) { LibraryOperations.delete(zimFileID: zimFile.fileID) #if os(iOS) presentationMode.wrappedValue.dismiss() @@ -151,7 +151,7 @@ struct ZimFileDetail: View { } var downloadAction: some View { - Action(title: "Download") { + Action(title: "Download".localized) { if let freeSpace = freeSpace, zimFile.size >= freeSpace - 10^9 { isPresentingDownloadAlert = true } else { @@ -159,15 +159,15 @@ struct ZimFileDetail: View { } }.alert(isPresented: $isPresentingDownloadAlert) { Alert( - title: Text("Space Warning"), + title: Text("Space Warning".localized), message: Text({ if let freeSpace = freeSpace, zimFile.size > freeSpace { - return "There might not be enough space on your device for this zim file." + return "There might not be enough space on your device for this zim file.".localized } else { - return "There would be less than 1GB space left after the zim file is downloaded." + return "There would be less than 1GB space left after the zim file is downloaded.".localized } }()), - primaryButton: .default(Text("Download Anyway")) { + primaryButton: .default(Text("Download Anyway".localized)) { DownloadService.shared.start(zimFileID: zimFile.id, allowsCellularAccess: false) }, secondaryButton: .cancel() @@ -177,37 +177,37 @@ struct ZimFileDetail: View { @ViewBuilder var basicInfo: some View { - Attribute(title: "Language", detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) - Attribute(title: "Category", detail: Category(rawValue: zimFile.category)?.description) - Attribute(title: "Size", detail: Formatter.size.string(fromByteCount: zimFile.size)) - Attribute(title: "Created", detail: Formatter.dateMedium.string(from: zimFile.created)) + Attribute(title: "Language".localized, detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) + Attribute(title: "Category".localized, detail: Category(rawValue: zimFile.category)?.description) + Attribute(title: "Size".localized, detail: Formatter.size.string(fromByteCount: zimFile.size)) + Attribute(title: "Created".localized, detail: Formatter.dateMedium.string(from: zimFile.created)) } @ViewBuilder var boolInfo: some View { - AttributeBool(title: "Pictures", detail: zimFile.hasPictures) - AttributeBool(title: "Videos", detail: zimFile.hasVideos) - AttributeBool(title: "Details", detail: zimFile.hasDetails) + AttributeBool(title: "Pictures".localized, detail: zimFile.hasPictures) + AttributeBool(title: "Videos".localized, detail: zimFile.hasVideos) + AttributeBool(title: "Details".localized, detail: zimFile.hasDetails) if zimFile.requiresServiceWorkers { - AttributeBool(title: "Requires Service Workers", detail: zimFile.requiresServiceWorkers) + AttributeBool(title: "Requires Service Workers".localized, detail: zimFile.requiresServiceWorkers) } } @ViewBuilder var counts: some View { Attribute( - title: "Article Count", + title: "Article Count".localized, detail: Formatter.number.string(from: NSNumber(value: zimFile.articleCount)) ) Attribute( - title: "Media Count", + title: "Media Count".localized, detail: Formatter.number.string(from: NSNumber(value: zimFile.mediaCount)) ) } @ViewBuilder var id: some View { - Attribute(title: "ID", detail: String(zimFile.fileID.uuidString.prefix(8))) + Attribute(title: "ID".localized, detail: String(zimFile.fileID.uuidString.prefix(8))) } private var freeSpace: Int64? { @@ -237,27 +237,27 @@ private struct DownloadTaskDetail: View { @ObservedObject var downloadTask: DownloadTask var body: some View { - Action(title: "Cancel", isDestructive: true) { + Action(title: "Cancel".localized, isDestructive: true) { DownloadService.shared.cancel(zimFileID: downloadTask.fileID) } if let error = downloadTask.error { if downloadTask.resumeData != nil { - Action(title: "Try to Recover") { + Action(title: "Try to Recover".localized) { DownloadService.shared.resume(zimFileID: downloadTask.fileID) } } - Attribute(title: "Failed", detail: detail) + Attribute(title: "Failed".localized, detail: detail) Text(error) } else if downloadTask.resumeData == nil { - Action(title: "Pause") { + Action(title: "Pause".localized) { DownloadService.shared.pause(zimFileID: downloadTask.fileID) } - Attribute(title: "Downloading...", detail: detail) + Attribute(title: "Downloading...".localized, detail: detail) } else { - Action(title: "Resume") { + Action(title: "Resume".localized) { DownloadService.shared.resume(zimFileID: downloadTask.fileID) } - Attribute(title: "Paused", detail: detail) + Attribute(title: "Paused".localized, detail: detail) } } @@ -313,7 +313,7 @@ private struct Action: View { private struct ServiceWorkerWarning: View { var body: some View { Label { - Text("Zim files requiring service workers are not supported.") + Text("Zim files requiring service workers are not supported.".localized) } icon: { Image(systemName: "exclamationmark.triangle.fill").renderingMode(.original) } @@ -329,7 +329,7 @@ struct ZimFileDetail_Previews: PreviewProvider { zimFile.created = Date() zimFile.downloadURL = URL(string: "https://www.example.com") zimFile.fileID = UUID() - zimFile.fileDescription = "A very long description" + zimFile.fileDescription = "A very long description".localized zimFile.flavor = "max" zimFile.hasDetails = true zimFile.hasPictures = false diff --git a/Views/Library/ZimFilesCategories.swift b/Views/Library/ZimFilesCategories.swift index b58277097..f08c6298b 100644 --- a/Views/Library/ZimFilesCategories.swift +++ b/Views/Library/ZimFilesCategories.swift @@ -17,7 +17,7 @@ struct ZimFilesCategories: View { var body: some View { ZimFilesCategory(category: $selected) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.categories.name) + .navigationTitle(NavigationItem.categories.name.localized) .toolbar { #if os(iOS) ToolbarItem(placement: .navigationBarLeading) { @@ -25,13 +25,13 @@ struct ZimFilesCategories: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } #endif ToolbarItem { - Picker("Category", selection: $selected) { + Picker("Category".localized, selection: $selected) { ForEach(Category.allCases) { Text($0.name).tag($0) } @@ -91,7 +91,7 @@ private struct CategoryGrid: View { var body: some View { Group { if sections.isEmpty { - Message(text: "No zim file under this category.") + Message(text: "No zim file under this category.".localized) } else { LazyVGrid(columns: ([gridItem]), alignment: .leading, spacing: 12) { ForEach(sections) { section in @@ -187,7 +187,7 @@ private struct CategoryList: View { var body: some View { Group { if zimFiles.isEmpty { - Message(text: "No zim file under this category.") + Message(text: "No zim file under this category.".localized) } else { List(zimFiles, id: \.self, selection: $viewModel.selectedZimFile) { zimFile in ZimFileRow(zimFile) diff --git a/Views/Library/ZimFilesDownloads.swift b/Views/Library/ZimFilesDownloads.swift index 54c3f678d..ea83b8c0d 100644 --- a/Views/Library/ZimFilesDownloads.swift +++ b/Views/Library/ZimFilesDownloads.swift @@ -31,10 +31,10 @@ struct ZimFilesDownloads: View { } .modifier(GridCommon()) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.downloads.name) + .navigationTitle(NavigationItem.downloads.name.localized) .overlay { if downloadTasks.isEmpty { - Message(text: "No download tasks") + Message(text: "No download tasks".localized) } } .toolbar { @@ -44,7 +44,7 @@ struct ZimFilesDownloads: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } diff --git a/Views/Library/ZimFilesNew.swift b/Views/Library/ZimFilesNew.swift index 3ae5c3aeb..772db2764 100644 --- a/Views/Library/ZimFilesNew.swift +++ b/Views/Library/ZimFilesNew.swift @@ -39,7 +39,7 @@ struct ZimFilesNew: View { } .modifier(GridCommon()) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.new.name) + .navigationTitle(NavigationItem.new.name.localized) .searchable(text: $searchText) .onAppear { viewModel.start(isUserInitiated: false) @@ -52,7 +52,7 @@ struct ZimFilesNew: View { } .overlay { if zimFiles.isEmpty { - Message(text: "No new zim file") + Message(text: "No new zim file".localized) } } .toolbar { @@ -62,7 +62,7 @@ struct ZimFilesNew: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } @@ -77,7 +77,7 @@ struct ZimFilesNew: View { Button { viewModel.start(isUserInitiated: true) } label: { - Label("Refresh", systemImage: "arrow.triangle.2.circlepath.circle") + Label("Refresh".localized, systemImage: "arrow.triangle.2.circlepath.circle") } } } diff --git a/Views/Library/ZimFilesOpened.swift b/Views/Library/ZimFilesOpened.swift index 0b19b0159..dfdf3f70e 100644 --- a/Views/Library/ZimFilesOpened.swift +++ b/Views/Library/ZimFilesOpened.swift @@ -31,10 +31,10 @@ struct ZimFilesOpened: View { } .modifier(GridCommon(edges: .all)) .modifier(ToolbarRoleBrowser()) - .navigationTitle(NavigationItem.opened.name) + .navigationTitle(NavigationItem.opened.name.localized) .overlay { if zimFiles.isEmpty { - Message(text: "No opened zim file") + Message(text: "No opened zim file".localized) } } // not using OpenFileButton here, because it does not work on iOS/iPadOS 15 when this view is in a modal @@ -53,7 +53,7 @@ struct ZimFilesOpened: View { Button { NotificationCenter.toggleSidebar() } label: { - Label("Show Sidebar", systemImage: "sidebar.left") + Label("Show Sidebar".localized, systemImage: "sidebar.left") } } } @@ -68,8 +68,8 @@ struct ZimFilesOpened: View { } isFileImporterPresented = true } label: { - Label("Open...", systemImage: "plus") - }.help("Open a zim file") + Label("Open...".localized, systemImage: "plus") + }.help("Open a zim file".localized) } } } diff --git a/Views/SearchResults.swift b/Views/SearchResults.swift index c1192cf1f..a617a1fed 100644 --- a/Views/SearchResults.swift +++ b/Views/SearchResults.swift @@ -28,7 +28,7 @@ struct SearchResults: View { var body: some View { Group { if zimFiles.isEmpty { - Message(text: "No opened zim file") + Message(text: "No opened zim file".localized) } else if horizontalSizeClass == .regular { HStack(spacing: 0) { #if os(macOS) @@ -66,7 +66,7 @@ struct SearchResults: View { Spacer() } } else if viewModel.results.isEmpty { - Message(text: "No result") + Message(text: "No result".localized) } else { ScrollView { LazyVGrid(columns: [GridItem(.flexible(minimum: 300, maximum: 700), alignment: .center)]) { @@ -98,7 +98,7 @@ struct SearchResults: View { viewModel.searchText = searchText } }.swipeActions { - Button("Remove", role: .destructive) { + Button("Remove".localized, role: .destructive) { recentSearchTexts.removeAll { $0 == searchText } } } @@ -126,39 +126,39 @@ struct SearchResults: View { private var recentSearchHeader: some View { HStack { - Text("Recent Search") + Text("Recent Search".localized) Spacer() Button { isClearSearchConfirmationPresented = true } label: { - Text("Clear").font(.caption).fontWeight(.medium) - }.confirmationDialog("Clear Recent Searches", isPresented: $isClearSearchConfirmationPresented) { - Button("Clear All", role: .destructive) { + Text("Clear".localized).font(.caption).fontWeight(.medium) + }.confirmationDialog("Clear Recent Searches".localized, isPresented: $isClearSearchConfirmationPresented) { + Button("Clear All".localized, role: .destructive) { recentSearchTexts.removeAll() } } message: { - Text("All recent search history will be removed.") + Text("All recent search history will be removed.".localized) } } } private var searchFilterHeader: some View { HStack { - Text("Included in Search") + Text("Included in Search".localized) Spacer() if zimFiles.count == zimFiles.filter({ $0.includedInSearch }).count { Button { zimFiles.forEach { $0.includedInSearch = false } try? managedObjectContext.save() } label: { - Text("None").font(.caption).fontWeight(.medium) + Text("None".localized).font(.caption).fontWeight(.medium) } } else { Button { zimFiles.forEach { $0.includedInSearch = true } try? managedObjectContext.save() } label: { - Text("All").font(.caption).fontWeight(.medium) + Text("All".localized).font(.caption).fontWeight(.medium) } } } diff --git a/Views/Settings/About.swift b/Views/Settings/About.swift index 09ea7bf0f..c9a7407c6 100644 --- a/Views/Settings/About.swift +++ b/Views/Settings/About.swift @@ -17,27 +17,27 @@ struct About: View { var body: some View { #if os(macOS) VStack(spacing: 16) { - SettingSection(name: "About") { + SettingSection(name: "About".localized) { about ourWebsite } - SettingSection(name: "Release") { + SettingSection(name: "Release".localized) { release HStack { source license } } - SettingSection(name: "Dependencies", alignment: .top) { + SettingSection(name: "Dependencies".localized, alignment: .top) { Table(dependencies) { - TableColumn("Name", value: \.name) - TableColumn("License") { dependency in Text(dependency.license ?? "") } - TableColumn("Version", value: \.version) + TableColumn("Name".localized, value: \.name) + TableColumn("License".localized) { dependency in Text(dependency.license ?? "") } + TableColumn("Version".localized, value: \.version) }.tableStyle(.bordered(alternatesRowBackgrounds: true)) } } .padding() - .tabItem { Label("About", systemImage: "info.circle") } + .tabItem { Label("About".localized, systemImage: "info.circle") } .task { await getDependencies() } .onChange(of: externalLinkURL) { url in guard let url = url else { return } @@ -49,14 +49,14 @@ struct About: View { about ourWebsite } - Section("Release") { + Section("Release".localized) { release appVersion buildNumber source license } - Section("Dependencies") { + Section("Dependencies".localized) { ForEach(dependencies) { dependency in HStack { Text(dependency.name) @@ -70,7 +70,7 @@ struct About: View { } } } - .navigationTitle("About") + .navigationTitle("About".localized) .navigationBarTitleDisplayMode(.inline) .sheet(item: $externalLinkURL) { SafariView(url: $0) } .task { await getDependencies() } @@ -83,36 +83,36 @@ struct About: View { Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ It makes knowledge available to people with no or limited internet access. \ The software as well as the content is free to use for anyone. - """ + """.localized ) } private var release: some View { - Text("This app is released under the terms of the GNU General Public License version 3.") + Text("This app is released under the terms of the GNU General Public License version 3.".localized) } private var appVersion: some View { - Attribute(title: "Version", detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) + Attribute(title: "Version".localized, detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) } private var buildNumber: some View { - Attribute(title: "Build", detail: Bundle.main.infoDictionary?["CFBundleVersion"] as? String) + Attribute(title: "Build".localized, detail: Bundle.main.infoDictionary?["CFBundleVersion"] as? String) } private var ourWebsite: some View { - Button("Our Website") { + Button("Our Website".localized) { externalLinkURL = URL(string: "https://www.kiwix.org") } } private var source: some View { - Button("Source") { + Button("Source".localized) { externalLinkURL = URL(string: "https://github.com/kiwix/apple") } } private var license: some View { - Button("GNU General Public License v3") { + Button("GNU General Public License v3".localized) { externalLinkURL = URL(string: "https://www.gnu.org/licenses/gpl-3.0.en.html") } } diff --git a/Views/Settings/LanguageSelector.swift b/Views/Settings/LanguageSelector.swift index 8dcaf3870..d03751244 100644 --- a/Views/Settings/LanguageSelector.swift +++ b/Views/Settings/LanguageSelector.swift @@ -30,8 +30,8 @@ struct LanguageSelector: View { } }) }.width(14) - TableColumn("Name", value: \.name) - TableColumn("Count", value: \.count) { language in Text(language.count.formatted()) } + TableColumn("Name".localized, value: \.name) + TableColumn("Count".localized, value: \.count) { language in Text(language.count.formatted()) } } .tableStyle(.bordered(alternatesRowBackgrounds: true)) .onChange(of: sortOrder) { languages.sort(using: $0) } @@ -51,29 +51,29 @@ struct LanguageSelector: View { List { Section { if showing.isEmpty { - Text("No language").foregroundColor(.secondary) + Text("No language".localized).foregroundColor(.secondary) } else { ForEach(showing) { language in Button { hide(language) } label: { LanguageLabel(language: language) } } } - } header: { Text("Showing") } + } header: { Text("Showing".localized) } Section { ForEach(hiding) { language in Button { show(language) } label: { LanguageLabel(language: language) } } - } header: { Text("Hiding") } + } header: { Text("Hiding".localized) } } .listStyle(.insetGrouped) - .navigationTitle("Languages") + .navigationTitle("Languages".localized) .navigationBarTitleDisplayMode(.inline) .toolbar { Picker(selection: $sortingMode) { ForEach(LibraryLanguageSortingMode.allCases) { sortingMode in - Text(sortingMode.name).tag(sortingMode) + Text(sortingMode.name.localized).tag(sortingMode) } } label: { - Label("Sorting", systemImage: "arrow.up.arrow.down") + Label("Sorting".localized, systemImage: "arrow.up.arrow.down") }.pickerStyle(.menu) } .onAppear { diff --git a/Views/Settings/Settings.swift b/Views/Settings/Settings.swift index 71f652a37..bee1ddc50 100644 --- a/Views/Settings/Settings.swift +++ b/Views/Settings/Settings.swift @@ -18,30 +18,30 @@ struct ReadingSettings: View { var body: some View { VStack(spacing: 16) { - SettingSection(name: "Page zoom") { + SettingSection(name: "Page zoom".localized) { HStack { Stepper(webViewPageZoom.formatted(.percent), value: $webViewPageZoom, in: 0.5...2, step: 0.05) Spacer() - Button("Reset") { webViewPageZoom = 1 }.disabled(webViewPageZoom == 1) + Button("Reset".localized) { webViewPageZoom = 1 }.disabled(webViewPageZoom == 1) } } - SettingSection(name: "External link") { + SettingSection(name: "External link".localized) { Picker(selection: $externalLinkLoadingPolicy) { ForEach(ExternalLinkLoadingPolicy.allCases) { loadingPolicy in - Text(loadingPolicy.name).tag(loadingPolicy) + Text(loadingPolicy.name.localized).tag(loadingPolicy) } } label: { } } - SettingSection(name: "Search snippet") { + SettingSection(name: "Search snippet".localized) { Picker(selection: $searchResultSnippetMode) { ForEach(SearchResultSnippetMode.allCases) { snippetMode in - Text(snippetMode.name).tag(snippetMode) + Text(snippetMode.name.localized).tag(snippetMode) } } label: { } } } .padding() - .tabItem { Label("Reading", systemImage: "book") } + .tabItem { Label("Reading".localized, systemImage: "book") } } } @@ -51,30 +51,30 @@ struct LibrarySettings: View { var body: some View { VStack(spacing: 16) { - SettingSection(name: "Catalog") { + SettingSection(name: "Catalog".localized) { HStack(spacing: 6) { - Button("Refresh Now") { + Button("Refresh Now".localized) { library.start(isUserInitiated: true) }.disabled(library.isInProgress) if library.isInProgress { ProgressView().progressViewStyle(.circular).scaleEffect(0.5).frame(height: 1) } Spacer() - Text("Last refresh:").foregroundColor(.secondary) + Text("Last refresh".localized + ":").foregroundColor(.secondary) LibraryLastRefreshTime().foregroundColor(.secondary) } VStack(alignment: .leading) { - Toggle("Auto refresh", isOn: $libraryAutoRefresh) - Text("When enabled, the library catalog will be refreshed automatically when outdated.") + Toggle("Auto refresh".localized, isOn: $libraryAutoRefresh) + Text("When enabled, the library catalog will be refreshed automatically when outdated.".localized) .foregroundColor(.secondary) } } - SettingSection(name: "Languages", alignment: .top) { + SettingSection(name: "Languages".localized, alignment: .top) { LanguageSelector() } } .padding() - .tabItem { Label("Library", systemImage: "folder.badge.gearshape") } + .tabItem { Label("Library".localized, systemImage: "folder.badge.gearshape") } } } @@ -126,22 +126,22 @@ struct Settings: View { miscellaneous } .modifier(ToolbarRoleBrowser()) - .navigationTitle("Settings") + .navigationTitle("Settings".localized) } var readingSettings: some View { - Section("Reading") { + Section("Reading".localized) { Stepper(value: $webViewPageZoom, in: 0.5...2, step: 0.05) { - Text("Page zoom: \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") + Text("Page zoom".localized + ": \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") } - Picker("External link", selection: $externalLinkLoadingPolicy) { + Picker("External link".localized, selection: $externalLinkLoadingPolicy) { ForEach(ExternalLinkLoadingPolicy.allCases) { loadingPolicy in - Text(loadingPolicy.name).tag(loadingPolicy) + Text(loadingPolicy.name.localized).tag(loadingPolicy) } } - Picker("Search snippet", selection: $searchResultSnippetMode) { + Picker("Search snippet".localized, selection: $searchResultSnippetMode) { ForEach(SearchResultSnippetMode.allCases) { snippetMode in - Text(snippetMode.name).tag(snippetMode) + Text(snippetMode.name.localized).tag(snippetMode) } } } @@ -154,58 +154,58 @@ struct Settings: View { } label: { SelectedLanaguageLabel() } - Toggle("Download using cellular", isOn: $downloadUsingCellular) + Toggle("Download using cellular".localized, isOn: $downloadUsingCellular) } header: { - Text("Library") + Text("Library".localized) } footer: { - Text("Change will only apply to new download tasks.") + Text("Change will only apply to new download tasks.".localized) } } var catalogSettings: some View { Section { HStack { - Text("Last refresh") + Text("Last refresh".localized) Spacer() LibraryLastRefreshTime().foregroundColor(.secondary) } if library.isInProgress { HStack { - Text("Refreshing...").foregroundColor(.secondary) + Text("Refreshing...".localized).foregroundColor(.secondary) Spacer() ProgressView().progressViewStyle(.circular) } } else { - Button("Refresh Now") { + Button("Refresh Now".localized) { library.start(isUserInitiated: true) } } - Toggle("Auto refresh", isOn: $libraryAutoRefresh) + Toggle("Auto refresh".localized, isOn: $libraryAutoRefresh) } header: { - Text("Catalog") + Text("Catalog".localized) } footer: { - Text("When enabled, the library catalog will be refreshed automatically when outdated.") + Text("When enabled, the library catalog will be refreshed automatically when outdated.".localized) }.onChange(of: libraryAutoRefresh) { LibraryOperations.applyLibraryAutoRefreshSetting(isEnabled: $0) } } var backupSettings: some View { Section { - Toggle("Include zim files in backup", isOn: $backupDocumentDirectory) + Toggle("Include zim files in backup".localized, isOn: $backupDocumentDirectory) } header: { - Text("Backup") + Text("Backup".localized) } footer: { - Text("Does not apply to files opened in place.") + Text("Does not apply to files opened in place.".localized) }.onChange(of: backupDocumentDirectory) { LibraryOperations.applyFileBackupSetting(isEnabled: $0) } } var miscellaneous: some View { - Section("Misc") { - Button("Feedback") { UIApplication.shared.open(URL(string: "mailto:feedback@kiwix.org")!) } - Button("Rate the App") { + Section("Misc".lowercased) { + Button("Feedback".localized) { UIApplication.shared.open(URL(string: "mailto:feedback@kiwix.org")!) } + Button("Rate the App".localized) { let url = URL(string: "itms-apps://itunes.apple.com/us/app/kiwix/id997079563?action=write-review")! UIApplication.shared.open(url) } - NavigationLink("About") { About() } + NavigationLink("About".localized) { About() } } } } @@ -215,7 +215,7 @@ private struct SelectedLanaguageLabel: View { var body: some View { HStack { - Text("Languages") + Text("Languages".localized) Spacer() if languageCodes.count == 1, let languageCode = languageCodes.first, diff --git a/Views/ViewModifiers/AlertHandler.swift b/Views/ViewModifiers/AlertHandler.swift index 940a4ecc5..b305b0550 100644 --- a/Views/ViewModifiers/AlertHandler.swift +++ b/Views/ViewModifiers/AlertHandler.swift @@ -21,7 +21,7 @@ struct AlertHandler: ViewModifier { .alert(item: $activeAlert) { alert in switch alert { case .articleFailedToLoad: - return Alert(title: Text("Unable to load the article requested.")) + return Alert(title: Text("Unable to load the article requested.".localized)) } } } diff --git a/Views/ViewModifiers/BookmarkContextMenu.swift b/Views/ViewModifiers/BookmarkContextMenu.swift index 235e984dd..5620f604f 100644 --- a/Views/ViewModifiers/BookmarkContextMenu.swift +++ b/Views/ViewModifiers/BookmarkContextMenu.swift @@ -18,13 +18,13 @@ struct BookmarkContextMenu: ViewModifier { Button { NotificationCenter.openURL(bookmark.articleURL) } label: { - Label("View", systemImage: "doc.richtext") + Label("View".localized, systemImage: "doc.richtext") } Button(role: .destructive) { managedObjectContext.delete(bookmark) try? managedObjectContext.save() } label: { - Label("Remove", systemImage: "star.slash.fill") + Label("Remove".localized, systemImage: "star.slash.fill") } } } diff --git a/Views/ViewModifiers/ExternalLinkHandler.swift b/Views/ViewModifiers/ExternalLinkHandler.swift index 0fea5c71d..3bb7c4938 100644 --- a/Views/ViewModifiers/ExternalLinkHandler.swift +++ b/Views/ViewModifiers/ExternalLinkHandler.swift @@ -41,19 +41,19 @@ struct ExternalLinkHandler: ViewModifier { activeAlert = .notLoading } } - .alert("External Link", isPresented: $isAlertPresented, presenting: activeAlert) { alert in + .alert("External Link".localized, isPresented: $isAlertPresented, presenting: activeAlert) { alert in if case .ask(let url) = alert { - Button("Load the link") { + Button("Load the link".localized) { load(url: url) } - Button("Cancel", role: .cancel) { } + Button("Cancel".localized, role: .cancel) { } } } message: { alert in switch alert { case .ask: - Text("An external link is tapped, do you wish to load the link?") + Text("An external link is tapped, do you wish to load the link?".localized) case .notLoading: - Text("An external link is tapped. However, your current setting does not allow it to be loaded.") + Text("An external link is tapped. However, your current setting does not allow it to be loaded.".localized) } } #if os(iOS) diff --git a/Views/ViewModifiers/FileImport.swift b/Views/ViewModifiers/FileImport.swift index da40c6ea5..020d7cc6c 100644 --- a/Views/ViewModifiers/FileImport.swift +++ b/Views/ViewModifiers/FileImport.swift @@ -41,7 +41,7 @@ struct OpenFileButton: View { guard case let .success(urls) = result else { return } NotificationCenter.openFiles(urls, context: context) } - .help("Open a zim file") + .help("Open a zim file".localized) .keyboardShortcut("o") } } @@ -98,11 +98,11 @@ struct OpenFileHandler: ViewModifier { isAlertPresented = true activeAlert = .unableToOpen(filenames: invalidURLs.map({ $0.lastPathComponent })) } - }.alert("Unable to open file", isPresented: $isAlertPresented, presenting: activeAlert) { _ in + }.alert("Unable to open file".localized, isPresented: $isAlertPresented, presenting: activeAlert) { _ in } message: { alert in switch alert { case .unableToOpen(let filenames): - Text("\(ListFormatter.localizedString(byJoining: filenames)) cannot be opened.") + Text("%@ cannot be opened.".localizedWithFormat(withArgs: ListFormatter.localizedString(byJoining: filenames))) } } } diff --git a/Views/Welcome.swift b/Views/Welcome.swift index 3c079fe44..377c58e6f 100644 --- a/Views/Welcome.swift +++ b/Views/Welcome.swift @@ -67,7 +67,7 @@ struct Welcome: View { alignment: .leading, spacing: 12 ) { - GridSection(title: "Main Page") { + GridSection(title: "Main Page".localized) { ForEach(zimFiles) { zimFile in Button { guard let url = ZimFileService.shared.getMainPageURL(zimFileID: zimFile.fileID) else { return } @@ -78,7 +78,7 @@ struct Welcome: View { } } if !bookmarks.isEmpty { - GridSection(title: "Bookmarks") { + GridSection(title: "Bookmarks".localized) { ForEach(bookmarks.prefix(6)) { bookmark in Button { browser.load(url: bookmark.articleURL) @@ -112,7 +112,7 @@ struct Welcome: View { OpenFileButton(context: .onBoarding) { HStack { Spacer() - Text("Open File") + Text("Open File".localized) Spacer() }.padding(6) } @@ -123,15 +123,15 @@ struct Welcome: View { Spacer() if library.isInProgress { #if os(macOS) - Text("Fetching...") + Text("Fetching...".localized) #elseif os(iOS) HStack(spacing: 6) { ProgressView().frame(maxHeight: 10) - Text("Fetching...") + Text("Fetching...".localized) } #endif } else { - Text("Fetch Catalog") + Text("Fetch Catalog".localized) } Spacer() }.padding(6) diff --git a/WikiMed/es.lproj/LaunchScreen.strings b/WikiMed/es.lproj/LaunchScreen.strings new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/WikiMed/es.lproj/LaunchScreen.strings @@ -0,0 +1 @@ + diff --git a/WikiMed/es.lproj/Main.strings b/WikiMed/es.lproj/Main.strings new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/WikiMed/es.lproj/Main.strings @@ -0,0 +1 @@ + From df10ab25e2b3fcbdea4db60d23c5363b63b8a66b Mon Sep 17 00:00:00 2001 From: tvision106 Date: Tue, 14 Nov 2023 17:12:29 -0500 Subject: [PATCH 12/17] fix codefactor issue --- Model/Utilities/String+Extension.swift | 9 ++++----- Support/en.lproj/Localizable.strings | 8 ++++++-- ViewModel/BrowserViewModel.swift | 3 ++- Views/BuildingBlocks/ZimFileRow.swift | 3 ++- Views/Library/ZimFileDetail.swift | 8 ++++++-- Views/Settings/About.swift | 3 ++- Views/Settings/Settings.swift | 3 ++- Views/ViewModifiers/ExternalLinkHandler.swift | 5 ++++- Views/ViewModifiers/FileImport.swift | 3 ++- 9 files changed, 30 insertions(+), 15 deletions(-) diff --git a/Model/Utilities/String+Extension.swift b/Model/Utilities/String+Extension.swift index 9f1ad1364..e782c0d14 100644 --- a/Model/Utilities/String+Extension.swift +++ b/Model/Utilities/String+Extension.swift @@ -14,7 +14,7 @@ extension String { return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") } - func localized(withComment:String) -> String { + func localized(withComment: String) -> String { return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: withComment) } @@ -22,11 +22,10 @@ extension String { let format = NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") switch withArgs.count { - case 1: return String.localizedStringWithFormat(format, withArgs[0]) - case 2: return String.localizedStringWithFormat(format, withArgs[0], withArgs[1]) - default: return String.localizedStringWithFormat(format, withArgs) + case 1: return String.localizedStringWithFormat(format, withArgs[0]) + case 2: return String.localizedStringWithFormat(format, withArgs[0], withArgs[1]) + default: return String.localizedStringWithFormat(format, withArgs) } } } - diff --git a/Support/en.lproj/Localizable.strings b/Support/en.lproj/Localizable.strings index 632378726..fd8353f44 100644 --- a/Support/en.lproj/Localizable.strings +++ b/Support/en.lproj/Localizable.strings @@ -45,7 +45,9 @@ "Load the link" = "Load the link"; "Cancel" = "Cancel"; "An external link is tapped, do you wish to load the link?" = "An external link is tapped, do you wish to load the link?"; -"An external link is tapped. However, your current setting does not allow it to be loaded." = "An external link is tapped. However, your current setting does not allow it to be loaded."; +"An external link is tapped. \ +However, your current setting does not allow it to be loaded." = "An external link is tapped. \ +However, your current setting does not allow it to be loaded."; "Open a zim file" = "Open a zim file"; "Unable to open file" = "Unable to open file"; "%@ cannot be opened." = "%@ cannot be opened."; @@ -97,7 +99,9 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; but the original file will remain in place." = "All bookmarked articles linked to this zim file will be deleted, \ but the original file will remain in place."; "Delete %@" = "Delete %@"; -"The zim file and all bookmarked articles linked to this zim file will be deleted." = "The zim file and all bookmarked articles linked to this zim file will be deleted."; +"The zim file and all bookmarked articles \ +linked to this zim file will be deleted." = "The zim file and all bookmarked articles \ +linked to this zim file will be deleted."; "Download" = "Download"; "Space Warning" = "Space Warning"; "There might not be enough space on your device for this zim file." = "There might not be enough space on your device for this zim file."; diff --git a/ViewModel/BrowserViewModel.swift b/ViewModel/BrowserViewModel.swift index b77ce98ec..e27399daf 100644 --- a/ViewModel/BrowserViewModel.swift +++ b/ViewModel/BrowserViewModel.swift @@ -267,7 +267,8 @@ class BrowserViewModel: NSObject, ObservableObject, let predicate = NSPredicate(format: "articleURL == %@", url as CVarArg) let request = Bookmark.fetchRequest(predicate: predicate) if let bookmarks = try? context.fetch(request), !bookmarks.isEmpty { - return UIAction(title: "Remove Bookmark".localized, image: UIImage(systemName: "star.slash.fill")) { _ in + return UIAction(title: "Remove Bookmark".localized, + image: UIImage(systemName: "star.slash.fill")) { _ in self.deleteBookmark(url: url) } } else { diff --git a/Views/BuildingBlocks/ZimFileRow.swift b/Views/BuildingBlocks/ZimFileRow.swift index 73510e563..df40aa9db 100644 --- a/Views/BuildingBlocks/ZimFileRow.swift +++ b/Views/BuildingBlocks/ZimFileRow.swift @@ -30,7 +30,8 @@ struct ZimFileRow: View { Formatter.size.string(fromByteCount: zimFile.size), { if #available(iOS 15.0, *) { - return "\(zimFile.articleCount.formatted(.number.notation(.compactName)))" + "articles".localized + return "\(zimFile.articleCount.formatted(.number.notation(.compactName)))" + + "articles".localized } else { return Formatter.largeNumber(zimFile.articleCount) } diff --git a/Views/Library/ZimFileDetail.swift b/Views/Library/ZimFileDetail.swift index 5ee91f1a1..4b58afaa7 100644 --- a/Views/Library/ZimFileDetail.swift +++ b/Views/Library/ZimFileDetail.swift @@ -138,7 +138,10 @@ struct ZimFileDetail: View { }.alert(isPresented: $isPresentingDeleteAlert) { Alert( title: Text("Delete %@".localizedWithFormat(withArgs: zimFile.name)), - message: Text("The zim file and all bookmarked articles linked to this zim file will be deleted.".localized), + message: Text(""" + The zim file and all bookmarked articles \ + linked to this zim file will be deleted. + """.localized), primaryButton: .destructive(Text("Delete".localized)) { LibraryOperations.delete(zimFileID: zimFile.fileID) #if os(iOS) @@ -177,7 +180,8 @@ struct ZimFileDetail: View { @ViewBuilder var basicInfo: some View { - Attribute(title: "Language".localized, detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) + Attribute(title: "Language".localized, + detail: Locale.current.localizedString(forLanguageCode: zimFile.languageCode)) Attribute(title: "Category".localized, detail: Category(rawValue: zimFile.category)?.description) Attribute(title: "Size".localized, detail: Formatter.size.string(fromByteCount: zimFile.size)) Attribute(title: "Created".localized, detail: Formatter.dateMedium.string(from: zimFile.created)) diff --git a/Views/Settings/About.swift b/Views/Settings/About.swift index c9a7407c6..72d5bed70 100644 --- a/Views/Settings/About.swift +++ b/Views/Settings/About.swift @@ -92,7 +92,8 @@ struct About: View { } private var appVersion: some View { - Attribute(title: "Version".localized, detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) + Attribute(title: "Version".localized, + detail: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) } private var buildNumber: some View { diff --git a/Views/Settings/Settings.swift b/Views/Settings/Settings.swift index bee1ddc50..cf6bbd853 100644 --- a/Views/Settings/Settings.swift +++ b/Views/Settings/Settings.swift @@ -132,7 +132,8 @@ struct Settings: View { var readingSettings: some View { Section("Reading".localized) { Stepper(value: $webViewPageZoom, in: 0.5...2, step: 0.05) { - Text("Page zoom".localized + ": \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") + Text("Page zoom".localized + + ": \(Formatter.percent.string(from: NSNumber(value: webViewPageZoom)) ?? "")") } Picker("External link".localized, selection: $externalLinkLoadingPolicy) { ForEach(ExternalLinkLoadingPolicy.allCases) { loadingPolicy in diff --git a/Views/ViewModifiers/ExternalLinkHandler.swift b/Views/ViewModifiers/ExternalLinkHandler.swift index 3bb7c4938..22a35142b 100644 --- a/Views/ViewModifiers/ExternalLinkHandler.swift +++ b/Views/ViewModifiers/ExternalLinkHandler.swift @@ -53,7 +53,10 @@ struct ExternalLinkHandler: ViewModifier { case .ask: Text("An external link is tapped, do you wish to load the link?".localized) case .notLoading: - Text("An external link is tapped. However, your current setting does not allow it to be loaded.".localized) + Text(""" + An external link is tapped. \ + However, your current setting does not allow it to be loaded. + """.localized) } } #if os(iOS) diff --git a/Views/ViewModifiers/FileImport.swift b/Views/ViewModifiers/FileImport.swift index 020d7cc6c..c3dd86c45 100644 --- a/Views/ViewModifiers/FileImport.swift +++ b/Views/ViewModifiers/FileImport.swift @@ -102,7 +102,8 @@ struct OpenFileHandler: ViewModifier { } message: { alert in switch alert { case .unableToOpen(let filenames): - Text("%@ cannot be opened.".localizedWithFormat(withArgs: ListFormatter.localizedString(byJoining: filenames))) + Text("%@ cannot be opened.".localizedWithFormat(withArgs: + ListFormatter.localizedString(byJoining: filenames))) } } } From 25c8ef322c185c7c7e4315eccaba3e7ae05dbaac Mon Sep 17 00:00:00 2001 From: tvision106 Date: Tue, 14 Nov 2023 17:15:21 -0500 Subject: [PATCH 13/17] fix codefactor issue --- Views/ViewModifiers/FileImport.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Views/ViewModifiers/FileImport.swift b/Views/ViewModifiers/FileImport.swift index c3dd86c45..edd5f4263 100644 --- a/Views/ViewModifiers/FileImport.swift +++ b/Views/ViewModifiers/FileImport.swift @@ -102,8 +102,8 @@ struct OpenFileHandler: ViewModifier { } message: { alert in switch alert { case .unableToOpen(let filenames): - Text("%@ cannot be opened.".localizedWithFormat(withArgs: - ListFormatter.localizedString(byJoining: filenames))) + let name = ListFormatter.localizedString(byJoining: filenames) + Text("%@ cannot be opened.".localizedWithFormat(withArgs: name)) } } } From 22e555dd39fa34b4b77710398c9d28de206f7675 Mon Sep 17 00:00:00 2001 From: tvision106 Date: Wed, 15 Nov 2023 13:24:44 -0500 Subject: [PATCH 14/17] fix some issues for long strings --- Support/en.lproj/Localizable.strings | 21 ++++++++----------- Views/BuildingBlocks/ArticleCell.swift | 7 +------ Views/BuildingBlocks/SearchResultRow.swift | 7 +------ Views/Library/ZimFileDetail.swift | 10 ++------- Views/Settings/About.swift | 8 +------ Views/ViewModifiers/ExternalLinkHandler.swift | 5 +---- 6 files changed, 15 insertions(+), 43 deletions(-) diff --git a/Support/en.lproj/Localizable.strings b/Support/en.lproj/Localizable.strings index fd8353f44..fbcd8a241 100644 --- a/Support/en.lproj/Localizable.strings +++ b/Support/en.lproj/Localizable.strings @@ -45,16 +45,14 @@ "Load the link" = "Load the link"; "Cancel" = "Cancel"; "An external link is tapped, do you wish to load the link?" = "An external link is tapped, do you wish to load the link?"; -"An external link is tapped. \ -However, your current setting does not allow it to be loaded." = "An external link is tapped. \ +"loc-extenral-alert" = "An external link is tapped. \ However, your current setting does not allow it to be loaded."; "Open a zim file" = "Open a zim file"; "Unable to open file" = "Unable to open file"; "%@ cannot be opened." = "%@ cannot be opened."; "No snippet" = "No snippet"; "Article Title" = "Article Title"; -"Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ -sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ +"loc-article-cell-template" = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; "Unknown" = "Unknown"; "Yes" = "Yes"; @@ -95,12 +93,10 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; "Download using cellular" = "Download using cellular"; "Unlink" = "Unlink"; "Unlink %@" = "Unlink %@"; -"All bookmarked articles linked to this zim file will be deleted, \ -but the original file will remain in place." = "All bookmarked articles linked to this zim file will be deleted, \ +"loc-ZimFileDetail-Alert-unlink" = "All bookmarked articles linked to this zim file will be deleted, \ but the original file will remain in place."; "Delete %@" = "Delete %@"; -"The zim file and all bookmarked articles \ -linked to this zim file will be deleted." = "The zim file and all bookmarked articles \ +"loc-ZimFileDetail-Alert-Delete" = "The zim file and all bookmarked articles \ linked to this zim file will be deleted."; "Download" = "Download"; "Space Warning" = "Space Warning"; @@ -152,9 +148,7 @@ linked to this zim file will be deleted."; "Dependencies" = "Dependencies"; "License" = "License"; "Version" = "Version"; -"Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ -It makes knowledge available to people with no or limited internet access. \ -The software as well as the content is free to use for anyone." = "Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ +"loc-About-description" = "Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ It makes knowledge available to people with no or limited internet access. \ The software as well as the content is free to use for anyone."; "This app is released under the terms of the GNU General Public License version 3." = "This app is released under the terms of the GNU General Public License version 3."; @@ -203,4 +197,7 @@ The software as well as the content is free to use for anyone."; "Vikidia" = "Vikidia"; "StackExchange" = "StackExchange"; "Other" = "Other"; - +"Disabled" = "Disabled"; +"First Paragraph" = "First Paragraph"; +"First Sentence" = "First Sentence"; +"Matches" = "Matches"; diff --git a/Views/BuildingBlocks/ArticleCell.swift b/Views/BuildingBlocks/ArticleCell.swift index fa8069c89..f947e7ddd 100644 --- a/Views/BuildingBlocks/ArticleCell.swift +++ b/Views/BuildingBlocks/ArticleCell.swift @@ -65,12 +65,7 @@ struct ArticleCell: View { struct ArticleCell_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """.localized - ) + result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) return result }() diff --git a/Views/BuildingBlocks/SearchResultRow.swift b/Views/BuildingBlocks/SearchResultRow.swift index 57edb5f07..249f72cd4 100644 --- a/Views/BuildingBlocks/SearchResultRow.swift +++ b/Views/BuildingBlocks/SearchResultRow.swift @@ -39,12 +39,7 @@ struct SearchResultRow: View { struct SearchResultRow_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string: - """ - Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - """.localized - ) + result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) return result }() diff --git a/Views/Library/ZimFileDetail.swift b/Views/Library/ZimFileDetail.swift index 4b58afaa7..7bfafb0a0 100644 --- a/Views/Library/ZimFileDetail.swift +++ b/Views/Library/ZimFileDetail.swift @@ -117,10 +117,7 @@ struct ZimFileDetail: View { }.alert(isPresented: $isPresentingUnlinkAlert) { Alert( title: Text("Unlink %@".localizedWithFormat(withArgs: zimFile.name)), - message: Text(""" - All bookmarked articles linked to this zim file will be deleted, \ - but the original file will remain in place. - """), + message: Text("loc-ZimFileDetail-Alert-unlink".localized), primaryButton: .destructive(Text("Unlink".localized)) { LibraryOperations.unlink(zimFileID: zimFile.fileID) #if os(iOS) @@ -138,10 +135,7 @@ struct ZimFileDetail: View { }.alert(isPresented: $isPresentingDeleteAlert) { Alert( title: Text("Delete %@".localizedWithFormat(withArgs: zimFile.name)), - message: Text(""" - The zim file and all bookmarked articles \ - linked to this zim file will be deleted. - """.localized), + message: Text("loc-ZimFileDetail-Alert-Delete".localized), primaryButton: .destructive(Text("Delete".localized)) { LibraryOperations.delete(zimFileID: zimFile.fileID) #if os(iOS) diff --git a/Views/Settings/About.swift b/Views/Settings/About.swift index 72d5bed70..3db016d6c 100644 --- a/Views/Settings/About.swift +++ b/Views/Settings/About.swift @@ -78,13 +78,7 @@ struct About: View { } private var about: some View { - Text( - """ - Kiwix is an offline reader for online content like Wikipedia, Project Gutenberg, or TED Talks. \ - It makes knowledge available to people with no or limited internet access. \ - The software as well as the content is free to use for anyone. - """.localized - ) + Text("loc-About-description".localized) } private var release: some View { diff --git a/Views/ViewModifiers/ExternalLinkHandler.swift b/Views/ViewModifiers/ExternalLinkHandler.swift index 22a35142b..d7db201af 100644 --- a/Views/ViewModifiers/ExternalLinkHandler.swift +++ b/Views/ViewModifiers/ExternalLinkHandler.swift @@ -53,10 +53,7 @@ struct ExternalLinkHandler: ViewModifier { case .ask: Text("An external link is tapped, do you wish to load the link?".localized) case .notLoading: - Text(""" - An external link is tapped. \ - However, your current setting does not allow it to be loaded. - """.localized) + Text("loc-extenral-alert".localized) } } #if os(iOS) From 095a6baac4cdccdd63e6f85ec1d1f3fd98cc8e88 Mon Sep 17 00:00:00 2001 From: tvision106 Date: Wed, 15 Nov 2023 14:50:45 -0500 Subject: [PATCH 15/17] fix codefactor issue --- Views/BuildingBlocks/ArticleCell.swift | 2 +- Views/BuildingBlocks/SearchResultRow.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Views/BuildingBlocks/ArticleCell.swift b/Views/BuildingBlocks/ArticleCell.swift index f947e7ddd..a3a1b3f72 100644 --- a/Views/BuildingBlocks/ArticleCell.swift +++ b/Views/BuildingBlocks/ArticleCell.swift @@ -65,7 +65,7 @@ struct ArticleCell: View { struct ArticleCell_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) + result.snippet = NSAttributedString(string: "loc-article-cell-template".localized) return result }() diff --git a/Views/BuildingBlocks/SearchResultRow.swift b/Views/BuildingBlocks/SearchResultRow.swift index 249f72cd4..690a37328 100644 --- a/Views/BuildingBlocks/SearchResultRow.swift +++ b/Views/BuildingBlocks/SearchResultRow.swift @@ -39,7 +39,7 @@ struct SearchResultRow: View { struct SearchResultRow_Previews: PreviewProvider { static let result: SearchResult = { let result = SearchResult(zimFileID: UUID(), path: "", title: "Article Title".localized)! - result.snippet = NSAttributedString(string:"loc-article-cell-template".localized) + result.snippet = NSAttributedString(string: "loc-article-cell-template".localized) return result }() From d0e310180043216ba7cd29c855ab845a3e544ebc Mon Sep 17 00:00:00 2001 From: tvision106 Date: Mon, 20 Nov 2023 10:13:15 -0500 Subject: [PATCH 16/17] fix typo issue --- Support/en.lproj/Localizable.strings | 2 +- Views/ViewModifiers/ExternalLinkHandler.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Support/en.lproj/Localizable.strings b/Support/en.lproj/Localizable.strings index fbcd8a241..b386eb7f7 100644 --- a/Support/en.lproj/Localizable.strings +++ b/Support/en.lproj/Localizable.strings @@ -45,7 +45,7 @@ "Load the link" = "Load the link"; "Cancel" = "Cancel"; "An external link is tapped, do you wish to load the link?" = "An external link is tapped, do you wish to load the link?"; -"loc-extenral-alert" = "An external link is tapped. \ +"loc-external-alert" = "An external link is tapped. \ However, your current setting does not allow it to be loaded."; "Open a zim file" = "Open a zim file"; "Unable to open file" = "Unable to open file"; diff --git a/Views/ViewModifiers/ExternalLinkHandler.swift b/Views/ViewModifiers/ExternalLinkHandler.swift index d7db201af..68d426eeb 100644 --- a/Views/ViewModifiers/ExternalLinkHandler.swift +++ b/Views/ViewModifiers/ExternalLinkHandler.swift @@ -53,7 +53,7 @@ struct ExternalLinkHandler: ViewModifier { case .ask: Text("An external link is tapped, do you wish to load the link?".localized) case .notLoading: - Text("loc-extenral-alert".localized) + Text("loc-external-alert".localized) } } #if os(iOS) From 2c9f8d8cd56b10e74140ce51d91cc0c69eb8b2aa Mon Sep 17 00:00:00 2001 From: tvision106 Date: Tue, 21 Nov 2023 17:45:02 -0500 Subject: [PATCH 17/17] CI/CD - apple developer team --- Kiwix.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Kiwix.xcodeproj/project.pbxproj b/Kiwix.xcodeproj/project.pbxproj index 2aa1a10ab..a84aa6882 100644 --- a/Kiwix.xcodeproj/project.pbxproj +++ b/Kiwix.xcodeproj/project.pbxproj @@ -970,7 +970,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 119; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = L7HWM3SP3L; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -1016,7 +1016,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 119; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = L7HWM3SP3L; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES;