From e670d610e3ade0b389063c268447ca975911d4e5 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Wed, 4 Jan 2023 12:33:38 +0800 Subject: [PATCH] Localized mac app --- Mac/AppAssets.swift | 2 +- Mac/AppDelegate.swift | 42 +- Mac/Browser.swift | 10 +- ...ltinSmartFeedInspectorViewController.swift | 4 +- .../FolderInspectorViewController.swift | 4 +- .../NothingInspectorViewController.swift | 2 +- .../WebFeedInspectorViewController.swift | 14 +- .../About/AboutNetNewsWireView.swift | 4 +- .../AddFeed/AddFeedController.swift | 14 +- .../AddTwitterFeedWindowController.swift | 14 +- .../AddRedditFeedWindowController.swift | 11 +- Mac/MainWindow/ArticleExtractorButton.swift | 8 +- Mac/MainWindow/Detail/DetailWebView.swift | 2 +- Mac/MainWindow/MainWindowController.swift | 64 +- .../NNW3/NNW3ImportController.swift | 2 +- .../OPML/ExportOPMLWindowController.swift | 8 +- Mac/MainWindow/Sidebar/Cell/SidebarCell.swift | 2 +- .../Renaming/RenameWindowController.swift | 2 +- .../Sidebar/SidebarDeleteItemsAlert.swift | 16 +- ...idebarViewController+ContextualMenus.swift | 26 +- .../Timeline/Cell/TimelineCellData.swift | 2 +- .../Timeline/TimelineTableView.swift | 2 +- ...melineViewController+ContextualMenus.swift | 24 +- .../URLPasteboardWriter+NetNewsWire.swift | 4 +- .../AccountsAddCloudKitWindowController.swift | 2 +- .../AccountsAddLocalWindowController.swift | 2 +- .../AccountsFeedbinWindowController.swift | 20 +- .../AccountsNewsBlurWindowController.swift | 20 +- .../AccountsPreferencesViewController.swift | 22 +- .../AccountsReaderAPIWindowController.swift | 40 +- .../Accounts/AddAccountHelpView.swift | 4 +- .../Accounts/AddAccountsView.swift | 34 +- .../EnableExtensionPointView.swift | 12 +- ...ensionPointPreferencesViewController.swift | 22 +- .../GeneralPrefencesViewController.swift | 10 +- .../PreferencesWindowController.swift | 8 +- Mac/Resources/en.lproj/Localizable.strings | 659 ++++++++++++++++++ NetNewsWire.xcodeproj/project.pbxproj | 26 + Shared/AccountType+Helpers.swift | 6 +- Shared/Activity/ActivityManager.swift | 4 +- .../Article Rendering/ArticleRenderer.swift | 2 +- .../Article Rendering/ArticleTextSize.swift | 10 +- Shared/ArticleStyles/ArticleTheme.swift | 5 +- Shared/Commands/DeleteCommand.swift | 10 +- Shared/Commands/MarkStatusCommand.swift | 8 +- .../ExtensionPointManager.swift | 2 +- .../SendToMarsEditCommand.swift | 4 +- .../SendToMicroBlogCommand.swift | 4 +- .../LocalizedNetNewsWireError.swift | 32 +- Shared/SmartFeeds/SearchFeedDelegate.swift | 2 +- .../SearchTimelineFeedDelegate.swift | 2 +- Shared/Timer/AccountRefreshTimer.swift | 4 +- Shared/Timer/RefreshInterval.swift | 14 +- .../UserNotificationManager.swift | 6 +- iOS/Inspector/AccountInspectorView.swift | 2 +- iOS/Inspector/ExtensionInspectorView.swift | 2 +- .../en.lproj/Localizable.strings | 3 + iOS/Resources/en.lproj/Localizable.strings | 55 +- .../Extensions/ExtensionsManagementView.swift | 4 +- .../Appearance/ArticleThemeManagerView.swift | 2 +- iOS/ShareExtension/ShareViewController.swift | 4 +- 61 files changed, 1051 insertions(+), 300 deletions(-) create mode 100644 Mac/Resources/en.lproj/Localizable.strings create mode 100644 iOS/IntentsExtension/en.lproj/Localizable.strings diff --git a/Mac/AppAssets.swift b/Mac/AppAssets.swift index 21c22c35a..ad9ed9519 100644 --- a/Mac/AppAssets.swift +++ b/Mac/AppAssets.swift @@ -183,7 +183,7 @@ struct AppAssets { }() static var privacyPolicyLink: NSAttributedString = { - return NSAttributedString(linkText: NSLocalizedString("Privacy Policy", comment: "Privacy Policy"), linkURL: URL(string: "https://netnewswire.com/privacypolicy")!) + return NSAttributedString(linkText: NSLocalizedString("label.text.privacy-policy", comment: "Privacy Policy"), linkURL: URL(string: "https://netnewswire.com/privacypolicy")!) }() static var readClosedImage: RSImage = { diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index d155b34a4..92db499cf 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -351,13 +351,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, if let decodingError = error as? DecodingError { switch decodingError { case .typeMismatch(let type, _): - let localizedError = NSLocalizedString("This theme cannot be used because the the type—“%@”—is mismatched in the Info.plist", comment: "Type mismatch") + let localizedError = NSLocalizedString("alert.error.theme-type-mismatch.%@", comment: "This theme cannot be used because the the type—“%@”—is mismatched in the Info.plist") informativeText = NSString.localizedStringWithFormat(localizedError as NSString, type as! CVarArg) as String case .valueNotFound(let value, _): - let localizedError = NSLocalizedString("This theme cannot be used because the the value—“%@”—is not found in the Info.plist.", comment: "Decoding value missing") + let localizedError = NSLocalizedString("alert.error.theme-value-missing.%@", comment: "This theme cannot be used because the the value—“%@”—is not found in the Info.plist.") informativeText = NSString.localizedStringWithFormat(localizedError as NSString, value as! CVarArg) as String case .keyNotFound(let codingKey, _): - let localizedError = NSLocalizedString("This theme cannot be used because the the key—“%@”—is not found in the Info.plist.", comment: "Decoding key missing") + let localizedError = NSLocalizedString("alert.error.theme-key-not-found.%@", comment: "This theme cannot be used because the the key—“%@”—is not found in the Info.plist.") informativeText = NSString.localizedStringWithFormat(localizedError as NSString, codingKey.stringValue) as String case .dataCorrupted(let context): guard let underlyingError = context.underlyingError as NSError?, @@ -365,7 +365,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, informativeText = error.localizedDescription break } - let localizedError = NSLocalizedString("This theme cannot be used because of data corruption in the Info.plist: %@.", comment: "Decoding key missing") + let localizedError = NSLocalizedString("alert.error.theme-data-corruption.%@", comment: "This theme cannot be used because of data corruption in the Info.plist: %@.") informativeText = NSString.localizedStringWithFormat(localizedError as NSString, debugDescription) as String default: @@ -378,9 +378,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, DispatchQueue.main.async { let alert = NSAlert() alert.alertStyle = .warning - alert.messageText = NSLocalizedString("Theme Error", comment: "Theme error") + alert.messageText = NSLocalizedString("alert.title.theme-error", comment: "Theme error") alert.informativeText = informativeText - alert.addButton(withTitle: NSLocalizedString("OK", comment: "OK")) + alert.addButton(withTitle: NSLocalizedString("button.title.ok", comment: "OK")) alert.buttons[0].keyEquivalent = "\r" @@ -633,7 +633,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, @IBAction func showKeyboardShortcutsWindow(_ sender: Any?) { if keyboardShortcutsWindowController == nil { - keyboardShortcutsWindowController = WebViewWindowController(title: NSLocalizedString("Keyboard Shortcuts", comment: "window title")) + keyboardShortcutsWindowController = WebViewWindowController(title: NSLocalizedString("window.title.keyboard-shortcuts", comment: "Keyboard Shortcuts")) let htmlFile = Bundle(for: type(of: self)).path(forResource: "KeyboardShortcuts", ofType: "html")! keyboardShortcutsWindowController?.displayContents(of: htmlFile) @@ -828,10 +828,10 @@ extension AppDelegate { @IBAction func debugClearImageCaches(_ sender: Any?) { let alert = NSAlert() alert.alertStyle = .warning - alert.messageText = NSLocalizedString("Are you sure you want to clear the image caches? This will restart NetNewsWire to begin reloading the remote images.", - comment: "Clear and restart confirmation message.") - alert.addButton(withTitle: NSLocalizedString("Clear & Restart", comment: "Clear & Restart")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel")) + alert.messageText = NSLocalizedString("alert.message.clear-image-cache-confirmation", + comment: "Are you sure you want to clear the image caches? This will restart NetNewsWire to begin reloading the remote images.") + alert.addButton(withTitle: NSLocalizedString("button.title.clear-and-restart", comment: "Clear & Restart")) + alert.addButton(withTitle: NSLocalizedString("button.title.cancel", comment: "Cancel")) let userChoice = alert.runModal() if userChoice == .alertFirstButtonReturn { @@ -925,7 +925,7 @@ internal extension AppDelegate { let alert = NSAlert() alert.alertStyle = .informational - let localizedMessageText = NSLocalizedString("Install theme “%@” by %@?", comment: "Theme message text") + let localizedMessageText = NSLocalizedString("alert.title.install-theme.%@.%@", comment: "Install theme “%@” by %@? — the order of the variables is theme name, author name") alert.messageText = NSString.localizedStringWithFormat(localizedMessageText as NSString, theme.name, theme.creatorName) as String var attrs = [NSAttributedString.Key : Any]() @@ -937,7 +937,7 @@ internal extension AppDelegate { attrs[.paragraphStyle] = titleParagraphStyle let websiteText = NSMutableAttributedString() - websiteText.append(NSAttributedString(string: NSLocalizedString("Author‘s website:", comment: "Author's Website"), attributes: attrs)) + websiteText.append(NSAttributedString(string: NSLocalizedString("alert.title.authors-website", comment: "Author's website:"), attributes: attrs)) websiteText.append(NSAttributedString(string: "\n")) @@ -953,8 +953,8 @@ internal extension AppDelegate { textView.textStorage?.setAttributedString(websiteText) alert.accessoryView = textView - alert.addButton(withTitle: NSLocalizedString("Install Theme", comment: "Install Theme")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel Install Theme")) + alert.addButton(withTitle: NSLocalizedString("button.title.install-theme", comment: "Install Theme")) + alert.addButton(withTitle: NSLocalizedString("button.title.cancel", comment: "Cancel Install Theme")) func importTheme() { do { @@ -973,11 +973,11 @@ internal extension AppDelegate { let alert = NSAlert() alert.alertStyle = .warning - let localizedMessageText = NSLocalizedString("The theme “%@” already exists. Overwrite it?", comment: "Overwrite theme") + let localizedMessageText = NSLocalizedString("alert.message.duplicate-theme.%@", comment: "The theme “%@” already exists. Overwrite it?") alert.messageText = NSString.localizedStringWithFormat(localizedMessageText as NSString, theme.name) as String - alert.addButton(withTitle: NSLocalizedString("Overwrite", comment: "Overwrite")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel Install Theme")) + alert.addButton(withTitle: NSLocalizedString("button.title.overwrite", comment: "Overwrite")) + alert.addButton(withTitle: NSLocalizedString("button.title.cancel", comment: "Cancel Install Theme")) alert.beginSheetModal(for: window) { result in if result == NSApplication.ModalResponse.alertFirstButtonReturn { @@ -999,12 +999,12 @@ internal extension AppDelegate { let alert = NSAlert() alert.alertStyle = .informational - alert.messageText = NSLocalizedString("Theme installed", comment: "Theme installed") + alert.messageText = NSLocalizedString("alert.title.theme-installed", comment: "Theme installed") - let localizedInformativeText = NSLocalizedString("The theme “%@” has been installed.", comment: "Theme installed") + let localizedInformativeText = NSLocalizedString("alert.message.theme-installed.%@", comment: "The theme “%@” has been installed.") alert.informativeText = NSString.localizedStringWithFormat(localizedInformativeText as NSString, themeName) as String - alert.addButton(withTitle: NSLocalizedString("OK", comment: "OK")) + alert.addButton(withTitle: NSLocalizedString("button.title.ok", comment: "OK")) alert.beginSheetModal(for: window) } diff --git a/Mac/Browser.swift b/Mac/Browser.swift index 7b47dad85..6bf3bc982 100644 --- a/Mac/Browser.swift +++ b/Mac/Browser.swift @@ -69,8 +69,8 @@ extension Browser { let openInBackgroundPref = AppDefaults.shared.openInBrowserInBackground return openInBackgroundPref ? - NSLocalizedString("Open in Browser in Foreground", comment: "Open in Browser in Foreground menu item title") : - NSLocalizedString("Open in Browser in Background", comment: "Open in Browser in Background menu item title") + NSLocalizedString("button.title.open-in-foreground", comment: "Open in Browser in Foreground") : + NSLocalizedString("button.title.open-in-background", comment: "Open in Browser in Background") } } @@ -95,11 +95,11 @@ extension Browser { if urlStrings.count > 20 { let alert = NSAlert() - let messageFormat = NSLocalizedString("Are you sure you want to open %ld articles in your browser?", comment: "Open in Browser confirmation alert message format") + let messageFormat = NSLocalizedString("alert.title.open-articles-in-browser.%ld", comment: "Are you sure you want to open %ld articles in your browser?") alert.messageText = String.localizedStringWithFormat(messageFormat, urlStrings.count) - let confirmButtonTitleFormat = NSLocalizedString("Open %ld Articles", comment: "Open URLs in Browser confirm button format") + let confirmButtonTitleFormat = NSLocalizedString("button.title.open-articles.%ld", comment: "Open %ld Articles") alert.addButton(withTitle: String.localizedStringWithFormat(confirmButtonTitleFormat, urlStrings.count)) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel button")) + alert.addButton(withTitle: NSLocalizedString("button.title.cancel", comment: "Cancel button")) if let window { alert.beginSheetModal(for: window) { response in diff --git a/Mac/Inspector/BuiltinSmartFeedInspectorViewController.swift b/Mac/Inspector/BuiltinSmartFeedInspectorViewController.swift index 3c6c7b908..289c953a1 100644 --- a/Mac/Inspector/BuiltinSmartFeedInspectorViewController.swift +++ b/Mac/Inspector/BuiltinSmartFeedInspectorViewController.swift @@ -27,7 +27,7 @@ final class BuiltinSmartFeedInspectorViewController: NSViewController, Inspector updateSmartFeed() } } - var windowTitle: String = NSLocalizedString("Smart Feed Inspector", comment: "Smart Feed Inspector window title") + var windowTitle: String = NSLocalizedString("window.title.smart-feed-inspector", comment: "Smart Feed Inspector") func canInspect(_ objects: [Any]) -> Bool { @@ -63,7 +63,7 @@ private extension BuiltinSmartFeedInspectorViewController { func updateUI() { nameTextField?.stringValue = smartFeed?.nameForDisplay ?? "" - windowTitle = smartFeed?.nameForDisplay ?? NSLocalizedString("Smart Feed Inspector", comment: "Smart Feed Inspector window title") + windowTitle = smartFeed?.nameForDisplay ?? NSLocalizedString("window.title.smart-feed-inspector", comment: "Smart Feed Inspector") smartFeedImageView?.image = smartFeed?.smallIcon?.image } } diff --git a/Mac/Inspector/FolderInspectorViewController.swift b/Mac/Inspector/FolderInspectorViewController.swift index 63f76b0d7..421148f9e 100644 --- a/Mac/Inspector/FolderInspectorViewController.swift +++ b/Mac/Inspector/FolderInspectorViewController.swift @@ -32,7 +32,7 @@ final class FolderInspectorViewController: NSViewController, Inspector { updateFolder() } } - var windowTitle: String = NSLocalizedString("Folder Inspector", comment: "Folder Inspector window title") + var windowTitle: String = NSLocalizedString("window.title.folder-inspector", comment: "Folder Inspector") func canInspect(_ objects: [Any]) -> Bool { @@ -102,7 +102,7 @@ private extension FolderInspectorViewController { if nameTextField.stringValue != name { nameTextField.stringValue = name } - windowTitle = folder?.nameForDisplay ?? NSLocalizedString("Folder Inspector", comment: "Folder Inspector window title") + windowTitle = folder?.nameForDisplay ?? NSLocalizedString("window.title.folder-inspector", comment: "Folder Inspector") } func renameFolderIfNecessary() { diff --git a/Mac/Inspector/NothingInspectorViewController.swift b/Mac/Inspector/NothingInspectorViewController.swift index 376e6a594..f18782a2c 100644 --- a/Mac/Inspector/NothingInspectorViewController.swift +++ b/Mac/Inspector/NothingInspectorViewController.swift @@ -19,7 +19,7 @@ final class NothingInspectorViewController: NSViewController, Inspector { updateTextFields() } } - var windowTitle: String = NSLocalizedString("Inspector", comment: "Inspector window title") + var windowTitle: String = NSLocalizedString("window.title.inspector", comment: "Inspector") func canInspect(_ objects: [Any]) -> Bool { diff --git a/Mac/Inspector/WebFeedInspectorViewController.swift b/Mac/Inspector/WebFeedInspectorViewController.swift index 58c24b254..7944c92b6 100644 --- a/Mac/Inspector/WebFeedInspectorViewController.swift +++ b/Mac/Inspector/WebFeedInspectorViewController.swift @@ -39,7 +39,7 @@ final class WebFeedInspectorViewController: NSViewController, Inspector { updateFeed() } } - var windowTitle: String = NSLocalizedString("Feed Inspector", comment: "Feed Inspector window title") + var windowTitle: String = NSLocalizedString("window.title.feed-inspector", comment: "Feed Inspector") func canInspect(_ objects: [Any]) -> Bool { return objects.count == 1 && objects.first is WebFeed @@ -138,7 +138,7 @@ private extension WebFeedInspectorViewController { updateFeedURL() updateNotifyAboutNewArticles() updateIsReaderViewAlwaysOn() - windowTitle = feed?.nameForDisplay ?? NSLocalizedString("Feed Inspector", comment: "Feed Inspector window title") + windowTitle = feed?.nameForDisplay ?? NSLocalizedString("window.title.feed-inspector", comment: "Feed Inspector") view.needsLayout = true if let webfeed = feed { webfeed.isFeedProvider ? (isReaderViewAlwaysOnCheckBox?.isEnabled = false) : (isReaderViewAlwaysOnCheckBox?.isEnabled = true) @@ -172,7 +172,7 @@ private extension WebFeedInspectorViewController { } func updateNotifyAboutNewArticles() { - isNotifyAboutNewArticlesCheckBox?.title = feed?.notificationDisplayName ?? NSLocalizedString("Show notifications for new articles", comment: "Show notifications for new articles") + isNotifyAboutNewArticlesCheckBox?.title = feed?.notificationDisplayName ?? NSLocalizedString("checkbox.title.show-new-article-notifications", comment: "Show notifications for new articles") isNotifyAboutNewArticlesCheckBox?.state = (feed?.isNotifyAboutNewArticles ?? false) ? .on : .off } @@ -194,10 +194,10 @@ private extension WebFeedInspectorViewController { func showNotificationsDeniedError() { let updateAlert = NSAlert() updateAlert.alertStyle = .informational - updateAlert.messageText = NSLocalizedString("Enable Notifications", comment: "Notifications") - updateAlert.informativeText = NSLocalizedString("To enable notifications, open Notifications in System Preferences, then find NetNewsWire in the list.", comment: "To enable notifications, open Notifications in System Preferences, then find NetNewsWire in the list.") - updateAlert.addButton(withTitle: NSLocalizedString("Open System Preferences", comment: "Open System Preferences")) - updateAlert.addButton(withTitle: NSLocalizedString("Close", comment: "Close")) + updateAlert.messageText = NSLocalizedString("alert.title.enable-notifications", comment: "Enable Notifications") + updateAlert.informativeText = NSLocalizedString("alert.message.enable-notifications-in-system-settings", comment: "To enable notifications, open Notifications in System Settings, then find NetNewsWire in the list.") + updateAlert.addButton(withTitle: NSLocalizedString("button.title.open-system-settings", comment: "Open System Settings")) + updateAlert.addButton(withTitle: NSLocalizedString("button.title.close", comment: "Close")) let modalResponse = updateAlert.runModal() if modalResponse == .alertFirstButtonReturn { NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.notifications")!) diff --git a/Mac/MainWindow/About/AboutNetNewsWireView.swift b/Mac/MainWindow/About/AboutNetNewsWireView.swift index 6ac15504d..371dce693 100644 --- a/Mac/MainWindow/About/AboutNetNewsWireView.swift +++ b/Mac/MainWindow/About/AboutNetNewsWireView.swift @@ -20,14 +20,14 @@ struct AboutNetNewsWireView: View { .resizable() .frame(width: 75, height: 75) - Text("NetNewsWire") + Text(verbatim: "NetNewsWire") .font(.headline) Text("\(Bundle.main.versionNumber) (\(Bundle.main.buildNumber))") .foregroundColor(.secondary) .font(.callout) - Text("By Brent Simmons and the NetNewsWire team.") + Text("label.text.netnewswire-byline", comment: "By Brent Simmons and the NetNewsWire team.") .font(.subheadline) Text("label.markdown.netnewswire-website", comment: "Markdown formatted link to netnewswire.com") diff --git a/Mac/MainWindow/AddFeed/AddFeedController.swift b/Mac/MainWindow/AddFeed/AddFeedController.swift index 6e82d40a9..0530a980a 100644 --- a/Mac/MainWindow/AddFeed/AddFeedController.swift +++ b/Mac/MainWindow/AddFeed/AddFeedController.swift @@ -139,17 +139,17 @@ private extension AddFeedController { func showAlreadySubscribedError(_ urlString: String) { let alert = NSAlert() alert.alertStyle = .informational - alert.messageText = NSLocalizedString("Already subscribed", comment: "Feed finder") - alert.informativeText = NSLocalizedString("Can’t add this feed because you’ve already subscribed to it.", comment: "Feed finder") + alert.messageText = NSLocalizedString("alert.title.already-subscribed", comment: "Already subscribed") + alert.informativeText = NSLocalizedString("alert.message.already-subscribed", comment: "Can’t add this feed because you’ve already subscribed to it.") alert.beginSheetModal(for: hostWindow) } func showInitialDownloadError(_ error: Error) { let alert = NSAlert() alert.alertStyle = .informational - alert.messageText = NSLocalizedString("Download Error", comment: "Feed finder") + alert.messageText = NSLocalizedString("alert.title.download-error", comment: "Download Error") - let formatString = NSLocalizedString("Can’t add this feed because of a download error: “%@”", comment: "Feed finder") + let formatString = NSLocalizedString("alert.message.download-error.%@", comment: "Can’t add this feed because of a download error: “%@”") let errorText = NSString.localizedStringWithFormat(formatString as NSString, error.localizedDescription) alert.informativeText = errorText as String alert.beginSheetModal(for: hostWindow) @@ -158,15 +158,15 @@ private extension AddFeedController { func showNoFeedsErrorMessage() { let alert = NSAlert() alert.alertStyle = .informational - alert.messageText = NSLocalizedString("Feed not found", comment: "Feed finder") - alert.informativeText = NSLocalizedString("Can’t add a feed because no feed was found.", comment: "Feed finder") + alert.messageText = NSLocalizedString("alert.title.feed-not-found", comment: "Feed not found") + alert.informativeText = NSLocalizedString("alert.message.feed-not-found", comment: "Can’t add a feed because no feed was found.") alert.beginSheetModal(for: hostWindow) } // MARK: Progress func beginShowingProgress() { - runIndeterminateProgressWithMessage(NSLocalizedString("Finding feed…", comment:"Feed finder")) + runIndeterminateProgressWithMessage(NSLocalizedString("label.text.finding-feed", comment:"Finding feed...")) } func endShowingProgress() { diff --git a/Mac/MainWindow/AddFeed/AddTwitterFeedWindowController.swift b/Mac/MainWindow/AddFeed/AddTwitterFeedWindowController.swift index 31748d6b0..1d5ed0d5f 100644 --- a/Mac/MainWindow/AddFeed/AddTwitterFeedWindowController.swift +++ b/Mac/MainWindow/AddFeed/AddTwitterFeedWindowController.swift @@ -134,7 +134,7 @@ private extension AddTwitterFeedWindowController { accountLabel.isHidden = false accountPopupButton.isHidden = false - typeDescriptionLabel.stringValue = NSLocalizedString("Tweets from everyone you follow", comment: "Home Timeline") + typeDescriptionLabel.stringValue = NSLocalizedString("label.text.tweets-from-everyone", comment: "Tweets from everyone you follow") screenSearchTextField.isHidden = true addButton.isEnabled = true @@ -142,7 +142,7 @@ private extension AddTwitterFeedWindowController { accountLabel.isHidden = false accountPopupButton.isHidden = false - typeDescriptionLabel.stringValue = NSLocalizedString("Tweets mentioning you", comment: "Mentions") + typeDescriptionLabel.stringValue = NSLocalizedString("label.text.tweets-mentioning-you", comment: "Tweets mentioning you") screenSearchTextField.isHidden = true addButton.isEnabled = true @@ -156,12 +156,13 @@ private extension AddTwitterFeedWindowController { if let screenName = screenSearch, screenName.starts(with: "@") { screenSearch = String(screenName[screenName.index(screenName.startIndex, offsetBy: 1).. String? { switch buttonState { case .error: - return NSLocalizedString("Error - Reader View", comment: "Error - Reader View") + return NSLocalizedString("label.text.error-reader-view", comment: "Error - Reader View") case .animated: - return NSLocalizedString("Processing - Reader View", comment: "Processing - Reader View") + return NSLocalizedString("label.text.processing-reader-view", comment: "Processing - Reader View") case .on: - return NSLocalizedString("Selected - Reader View", comment: "Selected - Reader View") + return NSLocalizedString("label.text.selected-reader-view", comment: "Selected - Reader View") case .off: - return NSLocalizedString("Reader View", comment: "Reader View") + return NSLocalizedString("label.text.reader-view", comment: "Reader View") } } diff --git a/Mac/MainWindow/Detail/DetailWebView.swift b/Mac/MainWindow/Detail/DetailWebView.swift index 56f5d7812..f366e5ebe 100644 --- a/Mac/MainWindow/Detail/DetailWebView.swift +++ b/Mac/MainWindow/Detail/DetailWebView.swift @@ -15,7 +15,7 @@ final class DetailWebView: WKWebView { weak var keyboardDelegate: KeyboardDelegate? override func accessibilityLabel() -> String? { - return NSLocalizedString("Article", comment: "Article") + return NSLocalizedString("label.text.article", comment: "Article") } // MARK: - NSResponder diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index 36b89dddb..e2392b9e2 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -207,7 +207,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { let canCopyArticleURL = canCopyArticleURL() if let item = item as? NSMenuItem { - let format = NSLocalizedString("Copy Article URL", comment: "Copy Article URL"); + let format = NSLocalizedString("button.title.copy-article-url", comment: "Copy Article URL"); item.title = String.localizedStringWithFormat(format, selectedArticles?.count ?? 0) } @@ -290,7 +290,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { let sidebarIsShowing = !splitViewItem.isCollapsed if let menuItem = item as? NSMenuItem { - let title = sidebarIsShowing ? NSLocalizedString("Hide Sidebar", comment: "Menu item") : NSLocalizedString("Show Sidebar", comment: "Menu item") + let title = sidebarIsShowing ? NSLocalizedString("button.title.hide-sidebar", comment: "Hide Sidebar") : NSLocalizedString("button.title.show-sidebar", comment: "Show Sidebar") menuItem.title = title } @@ -581,7 +581,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { let menu = NSMenu() let alwaysUseReaderViewItem = NSMenuItem() - alwaysUseReaderViewItem.title = NSLocalizedString("Always Use Reader View", comment: "Always Use Reader View") + alwaysUseReaderViewItem.title = NSLocalizedString("button.title.always-use-reader-view", comment: "Always Use Reader View") alwaysUseReaderViewItem.target = self alwaysUseReaderViewItem.action = #selector(alwaysUseReaderView) alwaysUseReaderViewItem.state = { @@ -852,57 +852,57 @@ extension MainWindowController: NSToolbarDelegate { switch itemIdentifier { case .sidebarToggle: - let title = NSLocalizedString("Toggle Sidebar", comment: "Toggle Sidebar") + let title = NSLocalizedString("button.title.toggle-sidebar", comment: "Toggle Sidebar") return buildToolbarButton(.toggleSidebar, title, AppAssets.sidebarToggleImage, "toggleTheSidebar:") case .refresh: - let title = NSLocalizedString("Refresh", comment: "Refresh") + let title = NSLocalizedString("button.title.refresh", comment: "Refresh") return buildToolbarButton(.refresh, title, AppAssets.refreshImage, "refreshAll:") case .newSidebarItemMenu: let toolbarItem = NSMenuToolbarItem(itemIdentifier: .newSidebarItemMenu) toolbarItem.image = AppAssets.addNewSidebarItemImage - let description = NSLocalizedString("Add Item", comment: "Add Item") + let description = NSLocalizedString("button.title.add-item", comment: "Add Item") toolbarItem.toolTip = description toolbarItem.label = description toolbarItem.menu = buildNewSidebarItemMenu() return toolbarItem case .markAllAsRead: - let title = NSLocalizedString("Mark All as Read", comment: "Mark All as Read") + let title = NSLocalizedString("button.title.mark-all-as-read.titlecase", comment: "Mark All as Read") return buildToolbarButton(.markAllAsRead, title, AppAssets.markAllAsReadImage, "markAllAsRead:") case .markAboveAsRead: - let title = NSLocalizedString("Mark Above as Read", comment: "Mark Above as Read") + let title = NSLocalizedString("button.title.mark-above-as-read.titlecase", comment: "Mark Above as Read") return buildToolbarButton(.markAboveAsRead, title, AppAssets.markAboveAsReadImage, "markAboveAsRead:") case .markBelowAsRead: - let title = NSLocalizedString("Mark Below as Read", comment: "Mark Below as Read") + let title = NSLocalizedString("button.title.mark-below-as-read.titlecase", comment: "Mark Below as Read") return buildToolbarButton(.markBelowAsRead, title, AppAssets.markBelowAsReadImage, "markBelowAsRead:") case .toggleReadArticlesFilter: - let title = NSLocalizedString("Read Articles Filter", comment: "Read Articles Filter") + let title = NSLocalizedString("button.title.read-articles-filter", comment: "Read Articles Filter") return buildToolbarButton(.toggleReadArticlesFilter, title, AppAssets.filterInactive, "toggleReadArticlesFilter:") case .timelineTrackingSeparator: return NSTrackingSeparatorToolbarItem(identifier: .timelineTrackingSeparator, splitView: splitViewController!.splitView, dividerIndex: 1) case .markRead: - let title = NSLocalizedString("Mark Read", comment: "Mark Read") + let title = NSLocalizedString("button.title.mark-read", comment: "Mark Read") return buildToolbarButton(.markRead, title, AppAssets.readClosedImage, "toggleRead:") case .markStar: - let title = NSLocalizedString("Star", comment: "Star") + let title = NSLocalizedString("button.title.mark-star", comment: "Star") return buildToolbarButton(.markStar, title, AppAssets.starOpenImage, "toggleStarred:") case .nextUnread: - let title = NSLocalizedString("Next Unread", comment: "Next Unread") + let title = NSLocalizedString("button.title.next-read", comment: "Next Unread") return buildToolbarButton(.nextUnread, title, AppAssets.nextUnreadImage, "nextUnread:") case .readerView: let toolbarItem = RSToolbarItem(itemIdentifier: .readerView) toolbarItem.autovalidates = true - let description = NSLocalizedString("Reader View", comment: "Reader View") + let description = NSLocalizedString("button.title.reader-view", comment: "Reader View") toolbarItem.toolTip = description toolbarItem.label = description let button = ArticleExtractorButton() @@ -914,7 +914,7 @@ extension MainWindowController: NSToolbarDelegate { return toolbarItem case .share: - let title = NSLocalizedString("Share", comment: "Share") + let title = NSLocalizedString("button.title.share", comment: "Share") let image = AppAssets.shareImage if #available(macOS 13.0, *) { // `item.view` is required for properly positioning the sharing picker. @@ -929,25 +929,25 @@ extension MainWindowController: NSToolbarDelegate { } case .openInBrowser: - let title = NSLocalizedString("Open in Browser", comment: "Open in Browser") + let title = NSLocalizedString("button.title.open-in-browser", comment: "Open in Browser") return buildToolbarButton(.openInBrowser, title, AppAssets.openInBrowserImage, "openArticleInBrowser:") case .articleThemeMenu: articleThemeMenuToolbarItem.image = AppAssets.articleTheme - let description = NSLocalizedString("Article Theme", comment: "Article Theme") + let description = NSLocalizedString("button.title.article-theme", comment: "Article Theme") articleThemeMenuToolbarItem.toolTip = description articleThemeMenuToolbarItem.label = description return articleThemeMenuToolbarItem case .search: let toolbarItem = NSSearchToolbarItem(itemIdentifier: .search) - let description = NSLocalizedString("Search", comment: "Search") + let description = NSLocalizedString("button.title.search", comment: "Search") toolbarItem.toolTip = description toolbarItem.label = description return toolbarItem case .cleanUp: - let title = NSLocalizedString("Clean Up", comment: "Clean Up") + let title = NSLocalizedString("button.title.clean-up", comment: "Clean Up") return buildToolbarButton(.cleanUp, title, AppAssets.cleanUpImage, "cleanUp:") default: @@ -1162,7 +1162,7 @@ private extension MainWindowController { result = false } - let commandName = markingRead ? NSLocalizedString("Mark as Read", comment: "Command") : NSLocalizedString("Mark as Unread", comment: "Command") + let commandName = markingRead ? NSLocalizedString("button.title.mark-as-read", comment: "Mark as Read") : NSLocalizedString("button.title.mark-as-unread", comment: "Mark as Unread") if let toolbarItem = item as? NSToolbarItem { toolbarItem.toolTip = commandName @@ -1249,7 +1249,7 @@ private extension MainWindowController { result = false } - let commandName = starring ? NSLocalizedString("Mark as Starred", comment: "Command") : NSLocalizedString("Mark as Unstarred", comment: "Command") + let commandName = starring ? NSLocalizedString("button.title.mark-as-starred", comment: "Mark as Starred") : NSLocalizedString("button.title.mark-as-unstarred", comment: "Mark as Unstarred") if let toolbarItem = item as? NSToolbarItem { toolbarItem.toolTip = commandName @@ -1270,15 +1270,15 @@ private extension MainWindowController { func validateToggleReadFeeds(_ item: NSValidatedUserInterfaceItem) -> Bool { guard let menuItem = item as? NSMenuItem else { return false } - let showCommand = NSLocalizedString("Show Read Feeds", comment: "Command") - let hideCommand = NSLocalizedString("Hide Read Feeds", comment: "Command") + let showCommand = NSLocalizedString("button.title.show-read-feeds", comment: "Show Read Feeds") + let hideCommand = NSLocalizedString("button.title.hide-read-feeds", comment: "Hide Read Feeds") menuItem.title = sidebarViewController?.isReadFiltered ?? false ? showCommand : hideCommand return true } func validateToggleReadArticles(_ item: NSValidatedUserInterfaceItem) -> Bool { - let showCommand = NSLocalizedString("Show Read Articles", comment: "Command") - let hideCommand = NSLocalizedString("Hide Read Articles", comment: "Command") + let showCommand = NSLocalizedString("button.title.show-read-articles", comment: "Show Read Articles") + let hideCommand = NSLocalizedString("button.title.hide-read-articles", comment: "Hide Read Articles") guard let isReadFiltered = timelineContainerViewController?.isReadFiltered else { (item as? NSMenuItem)?.title = hideCommand @@ -1330,14 +1330,14 @@ private extension MainWindowController { func updateWindowTitle() { guard timelineSourceMode != .search else { - let localizedLabel = NSLocalizedString("Search: %@", comment: "Search") + let localizedLabel = NSLocalizedString("window.title.search.%@", comment: "Search: %@") window?.title = NSString.localizedStringWithFormat(localizedLabel as NSString, searchString ?? "") as String window?.subtitle = "" return } func setSubtitle(_ count: Int) { - let localizedLabel = NSLocalizedString("%d unread", comment: "Unread") + let localizedLabel = NSLocalizedString("window.subtitle.unread-count.%d", comment: "%d unread") let formattedLabel = NSString.localizedStringWithFormat(localizedLabel as NSString, count) window?.subtitle = formattedLabel as String } @@ -1349,7 +1349,7 @@ private extension MainWindowController { } guard selectedObjects.count == 1 else { - window?.title = NSLocalizedString("Multiple", comment: "Multiple") + window?.title = NSLocalizedString("window.title.multiple", comment: "Multiple") let unreadCount = selectedObjects.reduce(0, { result, selectedObject in if let unreadCountProvider = selectedObject as? UnreadCountProvider { return result + unreadCountProvider.unreadCount @@ -1443,22 +1443,22 @@ private extension MainWindowController { let menu = NSMenu() let newWebFeedItem = NSMenuItem() - newWebFeedItem.title = NSLocalizedString("New Web Feed…", comment: "New Web Feed") + newWebFeedItem.title = NSLocalizedString("button.title.new-web-feed", comment: "New Web Feed...") newWebFeedItem.action = Selector(("showAddWebFeedWindow:")) menu.addItem(newWebFeedItem) let newRedditFeedItem = NSMenuItem() - newRedditFeedItem.title = NSLocalizedString("New Reddit Feed…", comment: "New Reddit Feed") + newRedditFeedItem.title = NSLocalizedString("button.title.new-reddit-feed", comment: "New Reddit Feed...") newRedditFeedItem.action = Selector(("showAddRedditFeedWindow:")) menu.addItem(newRedditFeedItem) let newTwitterFeedItem = NSMenuItem() - newTwitterFeedItem.title = NSLocalizedString("New Twitter Feed…", comment: "New Twitter Feed") + newTwitterFeedItem.title = NSLocalizedString("button.title.new-twitter-feed", comment: "New Twitter Feed...") newTwitterFeedItem.action = Selector(("showAddTwitterFeedWindow:")) menu.addItem(newTwitterFeedItem) let newFolderFeedItem = NSMenuItem() - newFolderFeedItem.title = NSLocalizedString("New Folder…", comment: "New Folder") + newFolderFeedItem.title = NSLocalizedString("button.title.new-folder", comment: "New Folder...") newFolderFeedItem.action = Selector(("showAddFolderWindow:")) menu.addItem(newFolderFeedItem) diff --git a/Mac/MainWindow/NNW3/NNW3ImportController.swift b/Mac/MainWindow/NNW3/NNW3ImportController.swift index 32084490e..24cd71c43 100644 --- a/Mac/MainWindow/NNW3/NNW3ImportController.swift +++ b/Mac/MainWindow/NNW3/NNW3ImportController.swift @@ -73,7 +73,7 @@ private extension NNW3ImportController { panel.allowsOtherFileTypes = false panel.accessoryView = accessoryViewController.view panel.isAccessoryViewDisclosed = true - panel.title = NSLocalizedString("Choose a Subscriptions.plist file:", comment: "NNW3 Import") + panel.title = NSLocalizedString("panel.title.select-opml-file", comment: "Choose a Subscriptions.plist file:") panel.beginSheetModal(for: window) { modalResult in guard modalResult == .OK, let subscriptionsPlistURL = panel.url else { diff --git a/Mac/MainWindow/OPML/ExportOPMLWindowController.swift b/Mac/MainWindow/OPML/ExportOPMLWindowController.swift index 4b2ce20a6..f642a9641 100644 --- a/Mac/MainWindow/OPML/ExportOPMLWindowController.swift +++ b/Mac/MainWindow/OPML/ExportOPMLWindowController.swift @@ -77,10 +77,10 @@ class ExportOPMLWindowController: NSWindowController { let panel = NSSavePanel() panel.allowedFileTypes = ["opml"] panel.allowsOtherFileTypes = false - panel.prompt = NSLocalizedString("Export OPML", comment: "Export OPML") - panel.title = NSLocalizedString("Export OPML", comment: "Export OPML") - panel.nameFieldLabel = NSLocalizedString("Export to:", comment: "Export OPML") - panel.message = NSLocalizedString("Choose a location for the exported OPML file.", comment: "Export OPML") + panel.prompt = NSLocalizedString("panel.prompt.export-opml", comment: "Export OPML") + panel.title = NSLocalizedString("panel.title.export-opml", comment: "Export OPML") + panel.nameFieldLabel = NSLocalizedString("panel.textfield.export-opml-destination", comment: "Export to:") + panel.message = NSLocalizedString("panel.message.export-opml", comment: "Choose a location for the exported OPML file.") panel.isExtensionHidden = false let accountName = account.nameForDisplay.replacingOccurrences(of: " ", with: "").trimmingCharacters(in: .whitespaces) diff --git a/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift b/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift index 52b3804b4..0bd35cf22 100644 --- a/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift +++ b/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift @@ -112,7 +112,7 @@ class SidebarCell : NSTableCellView { override func accessibilityLabel() -> String? { if unreadCount > 0 { - let unreadLabel = NSLocalizedString("unread", comment: "Unread label for accessiblity") + let unreadLabel = NSLocalizedString("label.text.unread", comment: "unread") return "\(name) \(unreadCount) \(unreadLabel)" } else { return name diff --git a/Mac/MainWindow/Sidebar/Renaming/RenameWindowController.swift b/Mac/MainWindow/Sidebar/Renaming/RenameWindowController.swift index 5cb8b6309..dadf22ce0 100644 --- a/Mac/MainWindow/Sidebar/Renaming/RenameWindowController.swift +++ b/Mac/MainWindow/Sidebar/Renaming/RenameWindowController.swift @@ -35,7 +35,7 @@ final class RenameWindowController: NSWindowController { newTitleTextField.stringValue = originalTitle! - let prompt = NSLocalizedString("Rename %@ to:", comment: "Rename sheet") + let prompt = NSLocalizedString("textfield.prompt.rename-to.%@", comment: "Rename %@ to:") let localizedPrompt = NSString.localizedStringWithFormat(prompt as NSString, originalTitle!) renamePrompt.stringValue = localizedPrompt as String diff --git a/Mac/MainWindow/Sidebar/SidebarDeleteItemsAlert.swift b/Mac/MainWindow/Sidebar/SidebarDeleteItemsAlert.swift index 899c29cf1..e11dbc055 100644 --- a/Mac/MainWindow/Sidebar/SidebarDeleteItemsAlert.swift +++ b/Mac/MainWindow/Sidebar/SidebarDeleteItemsAlert.swift @@ -19,22 +19,22 @@ enum SidebarDeleteItemsAlert { if nodes.count == 1 { if let folder = nodes.first?.representedObject as? Folder { - alert.messageText = NSLocalizedString("Delete Folder", comment: "Delete Folder") - let localizedInformativeText = NSLocalizedString("Are you sure you want to delete the “%@” folder?", comment: "Folder delete text") + alert.messageText = NSLocalizedString("alert.title.delete-folder", comment: "Delete Folder") + let localizedInformativeText = NSLocalizedString("alert.message.delete-folder.%@", comment: "Are you sure you want to delete the “%@” folder?") alert.informativeText = NSString.localizedStringWithFormat(localizedInformativeText as NSString, folder.nameForDisplay) as String } else if let feed = nodes.first?.representedObject as? Feed { - alert.messageText = NSLocalizedString("Delete Feed", comment: "Delete Feed") - let localizedInformativeText = NSLocalizedString("Are you sure you want to delete the “%@” feed?", comment: "Feed delete text") + alert.messageText = NSLocalizedString("alert.title.delete-feed", comment: "Delete Feed") + let localizedInformativeText = NSLocalizedString("alert.message.delete-feed.%@", comment: "Are you sure you want to delete the “%@” feed?") alert.informativeText = NSString.localizedStringWithFormat(localizedInformativeText as NSString, feed.nameForDisplay) as String } } else { - alert.messageText = NSLocalizedString("Delete Items", comment: "Delete Items") - let localizedInformativeText = NSLocalizedString("Are you sure you want to delete the %d selected items?", comment: "Items delete text") + alert.messageText = NSLocalizedString("alert.title.delete-items", comment: "Delete Items") + let localizedInformativeText = NSLocalizedString("alert.message.delete-items.%d", comment: "Are you sure you want to delete the %d selected items?") alert.informativeText = NSString.localizedStringWithFormat(localizedInformativeText as NSString, nodes.count) as String } - alert.addButton(withTitle: NSLocalizedString("Delete", comment: "Delete Account")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel Delete Account")) + alert.addButton(withTitle: NSLocalizedString("button.title.delete", comment: "Delete Account")) + alert.addButton(withTitle: NSLocalizedString("button.title.cancel", comment: "Cancel Delete Account")) return alert } diff --git a/Mac/MainWindow/Sidebar/SidebarViewController+ContextualMenus.swift b/Mac/MainWindow/Sidebar/SidebarViewController+ContextualMenus.swift index 4f1819daa..d4ae09c91 100644 --- a/Mac/MainWindow/Sidebar/SidebarViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Sidebar/SidebarViewController+ContextualMenus.swift @@ -156,10 +156,10 @@ extension SidebarViewController { func showNotificationsNotEnabledAlert() { DispatchQueue.main.async { let alert = NSAlert() - alert.messageText = NSLocalizedString("Notifications are not enabled", comment: "Notifications are not enabled.") - alert.informativeText = NSLocalizedString("You can enable NetNewsWire notifications in System Preferences.", comment: "Notifications are not enabled.") - alert.addButton(withTitle: NSLocalizedString("Open System Preferences", comment: "Open System Preferences")) - alert.addButton(withTitle: NSLocalizedString("Dismiss", comment: "Dismiss")) + alert.messageText = NSLocalizedString("alert.title.notifications-not-enabled", comment: "Notifications are not enabled.") + alert.informativeText = NSLocalizedString("alert.message.enable-notifications-in-settings", comment: "You can enable NetNewsWire notifications in System Settings.") + alert.addButton(withTitle: NSLocalizedString("button.title.open-settings", comment: "Open Settings")) + alert.addButton(withTitle: NSLocalizedString("button.title.dismiss", comment: "Dismiss")) let userChoice = alert.runModal() if userChoice == .alertFirstButtonReturn { let config = NSWorkspace.OpenConfiguration() @@ -208,8 +208,8 @@ private extension SidebarViewController { let menu = NSMenu(title: "") - menu.addItem(withTitle: NSLocalizedString("New Feed", comment: "Command"), action: #selector(AppDelegate.showAddWebFeedWindow(_:)), keyEquivalent: "") - menu.addItem(withTitle: NSLocalizedString("New Folder", comment: "Command"), action: #selector(AppDelegate.showAddFolderWindow(_:)), keyEquivalent: "") + menu.addItem(withTitle: NSLocalizedString("button.title.new-feed", comment: "New Feed"), action: #selector(AppDelegate.showAddWebFeedWindow(_:)), keyEquivalent: "") + menu.addItem(withTitle: NSLocalizedString("button.title.new-folder", comment: "New Folder"), action: #selector(AppDelegate.showAddFolderWindow(_:)), keyEquivalent: "") return menu } @@ -224,16 +224,16 @@ private extension SidebarViewController { } if let homePageURL = webFeed.homePageURL, let _ = URL(string: homePageURL) { - let item = menuItem(NSLocalizedString("Open Home Page", comment: "Command"), #selector(openHomePageFromContextualMenu(_:)), homePageURL.decodedURLString ?? homePageURL) + let item = menuItem(NSLocalizedString("button.title.open-home-page", comment: "Open Home Page"), #selector(openHomePageFromContextualMenu(_:)), homePageURL.decodedURLString ?? homePageURL) menu.addItem(item) menu.addItem(NSMenuItem.separator()) } - let copyFeedURLItem = menuItem(NSLocalizedString("Copy Feed URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), webFeed.url.decodedURLString ?? webFeed.url) + let copyFeedURLItem = menuItem(NSLocalizedString("button.title.copy-feed-url", comment: "Copy Feed URL"), #selector(copyURLFromContextualMenu(_:)), webFeed.url.decodedURLString ?? webFeed.url) menu.addItem(copyFeedURLItem) if let homePageURL = webFeed.homePageURL { - let item = menuItem(NSLocalizedString("Copy Home Page URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), homePageURL.decodedURLString ?? homePageURL) + let item = menuItem(NSLocalizedString("button.title.copy-home-page-url", comment: "Copy Home Page URL"), #selector(copyURLFromContextualMenu(_:)), homePageURL.decodedURLString ?? homePageURL) menu.addItem(item) } menu.addItem(NSMenuItem.separator()) @@ -250,7 +250,7 @@ private extension SidebarViewController { if !webFeed.isFeedProvider { - let articleExtractorText = NSLocalizedString("Always Use Reader View", comment: "Always Use Reader View") + let articleExtractorText = NSLocalizedString("button.title.always-use-reader-view", comment: "Always Use Reader View") let articleExtractorMenuItem = menuItem(articleExtractorText, #selector(toggleArticleExtractorFromContextMenu(_:)), webFeed) if webFeed.isArticleExtractorAlwaysOn == nil || webFeed.isArticleExtractorAlwaysOn! == false { @@ -313,17 +313,17 @@ private extension SidebarViewController { func markAllReadMenuItem(_ objects: [Any]) -> NSMenuItem { - return menuItem(NSLocalizedString("Mark All as Read", comment: "Command"), #selector(markObjectsReadFromContextualMenu(_:)), objects) + return menuItem(NSLocalizedString("button.title.mark-all-as-read.titlecase", comment: "Mark All as Read"), #selector(markObjectsReadFromContextualMenu(_:)), objects) } func deleteMenuItem(_ objects: [Any]) -> NSMenuItem { - return menuItem(NSLocalizedString("Delete", comment: "Command"), #selector(deleteFromContextualMenu(_:)), objects) + return menuItem(NSLocalizedString("button.title.delete", comment: "Delete"), #selector(deleteFromContextualMenu(_:)), objects) } func renameMenuItem(_ object: Any) -> NSMenuItem { - return menuItem(NSLocalizedString("Rename", comment: "Command"), #selector(renameFromContextualMenu(_:)), object) + return menuItem(NSLocalizedString("button.title.rename", comment: "Rename"), #selector(renameFromContextualMenu(_:)), object) } func anyObjectInArrayHasNonZeroUnreadCount(_ objects: [Any]) -> Bool { diff --git a/Mac/MainWindow/Timeline/Cell/TimelineCellData.swift b/Mac/MainWindow/Timeline/Cell/TimelineCellData.swift index 53bb79adb..bb7591bdb 100644 --- a/Mac/MainWindow/Timeline/Cell/TimelineCellData.swift +++ b/Mac/MainWindow/Timeline/Cell/TimelineCellData.swift @@ -11,7 +11,7 @@ import Articles struct TimelineCellData { - private static let noText = NSLocalizedString("(No Text)", comment: "No Text") + private static let noText = NSLocalizedString("label.text.no-text", comment: "(No Text)") let title: String let attributedTitle: NSAttributedString diff --git a/Mac/MainWindow/Timeline/TimelineTableView.swift b/Mac/MainWindow/Timeline/TimelineTableView.swift index e70bbed17..67c12fd70 100644 --- a/Mac/MainWindow/Timeline/TimelineTableView.swift +++ b/Mac/MainWindow/Timeline/TimelineTableView.swift @@ -14,7 +14,7 @@ class TimelineTableView: NSTableView { weak var keyboardDelegate: KeyboardDelegate? override func accessibilityLabel() -> String? { - return NSLocalizedString("Timeline", comment: "Timeline") + return NSLocalizedString("label.text.timeline", comment: "Timeline") } // MARK: - NSResponder diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index 88f0ac440..a2c1e423d 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -227,7 +227,7 @@ private extension TimelineViewController { return nil } - let menu = NSMenu(title: NSLocalizedString("Share", comment: "Share menu name")) + let menu = NSMenu(title: NSLocalizedString("button.title.share", comment: "Share menu name")) services.forEach { (service) in service.delegate = sharingServiceDelegate let menuItem = NSMenuItem(title: service.menuItemTitle, action: #selector(performShareServiceFromContextualMenu(_:)), keyEquivalent: "") @@ -242,34 +242,34 @@ private extension TimelineViewController { func markReadMenuItem(_ articles: [Article]) -> NSMenuItem { - return menuItem(NSLocalizedString("Mark as Read", comment: "Command"), #selector(markArticlesReadFromContextualMenu(_:)), articles) + return menuItem(NSLocalizedString("button.title.mark-as-read", comment: "Mark as Read"), #selector(markArticlesReadFromContextualMenu(_:)), articles) } func markUnreadMenuItem(_ articles: [Article]) -> NSMenuItem { - return menuItem(NSLocalizedString("Mark as Unread", comment: "Command"), #selector(markArticlesUnreadFromContextualMenu(_:)), articles) + return menuItem(NSLocalizedString("button.title.mark-as-unread", comment: "Mark as Unread"), #selector(markArticlesUnreadFromContextualMenu(_:)), articles) } func markStarredMenuItem(_ articles: [Article]) -> NSMenuItem { - return menuItem(NSLocalizedString("Mark as Starred", comment: "Command"), #selector(markArticlesStarredFromContextualMenu(_:)), articles) + return menuItem(NSLocalizedString("button.title.mark-as-starred", comment: "Mark as Starred"), #selector(markArticlesStarredFromContextualMenu(_:)), articles) } func markUnstarredMenuItem(_ articles: [Article]) -> NSMenuItem { - return menuItem(NSLocalizedString("Mark as Unstarred", comment: "Command"), #selector(markArticlesUnstarredFromContextualMenu(_:)), articles) + return menuItem(NSLocalizedString("button.title.mark-as-unstarred", comment: "Mark as Unstarred"), #selector(markArticlesUnstarredFromContextualMenu(_:)), articles) } func markAboveReadMenuItem(_ articles: [Article]) -> NSMenuItem { - return menuItem(NSLocalizedString("Mark Above as Read", comment: "Command"), #selector(markAboveArticlesReadFromContextualMenu(_:)), articles) + return menuItem(NSLocalizedString("button.title-mark-above-as-read.titlecase", comment: "Mark Above as Read"), #selector(markAboveArticlesReadFromContextualMenu(_:)), articles) } func markBelowReadMenuItem(_ articles: [Article]) -> NSMenuItem { - return menuItem(NSLocalizedString("Mark Below as Read", comment: "Command"), #selector(markBelowArticlesReadFromContextualMenu(_:)), articles) + return menuItem(NSLocalizedString("button.title-mark-below-as-read.titlecase", comment: "Mark Below as Read"), #selector(markBelowArticlesReadFromContextualMenu(_:)), articles) } func selectFeedInSidebarMenuItem(_ feed: WebFeed) -> NSMenuItem { - let localizedMenuText = NSLocalizedString("Select “%@” in Sidebar", comment: "Command") + let localizedMenuText = NSLocalizedString("button.title.select-in-sidebar.%@", comment: "Select “%@” in Sidebar") let formattedMenuText = NSString.localizedStringWithFormat(localizedMenuText as NSString, feed.nameForDisplay) return menuItem(formattedMenuText as String, #selector(selectFeedInSidebarFromContextualMenu(_:)), feed) } @@ -283,14 +283,14 @@ private extension TimelineViewController { return nil } - let localizedMenuText = NSLocalizedString("Mark All as Read in “%@”", comment: "Command") + let localizedMenuText = NSLocalizedString("button.title.mark-all-as-read.%@", comment: "Mark All as Read in “%@”") let menuText = NSString.localizedStringWithFormat(localizedMenuText as NSString, feed.nameForDisplay) as String return menuItem(menuText, #selector(markAllInFeedAsRead(_:)), articles) } func openInBrowserMenuItem(_ urlStrings: [String]) -> NSMenuItem { - return menuItem(NSLocalizedString("Open in Browser", comment: "Command"), #selector(openInBrowserFromContextualMenu(_:)), urlStrings) + return menuItem(NSLocalizedString("button.title.open-in-browser", comment: "Open in Browser"), #selector(openInBrowserFromContextualMenu(_:)), urlStrings) } func openInBrowserReversedMenuItem(_ urlStrings: [String]) -> NSMenuItem { @@ -301,13 +301,13 @@ private extension TimelineViewController { } func copyArticleURLsMenuItem(_ urlStrings: [String?]) -> NSMenuItem { - let format = NSLocalizedString("Copy Article URL", comment: "Command") + let format = NSLocalizedString("button.title.copy-article-url", comment: "Copy Article URL") let title = String.localizedStringWithFormat(format, urlStrings.count) return menuItem(title, #selector(copyURLFromContextualMenu(_:)), urlStrings) } func copyExternalURLMenuItem(_ urlString: String) -> NSMenuItem { - return menuItem(NSLocalizedString("Copy External URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), urlString) + return menuItem(NSLocalizedString("button.title.copy-external-url", comment: "Copy External URL"), #selector(copyURLFromContextualMenu(_:)), urlString) } diff --git a/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift b/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift index 81c3e3ce4..a0c078089 100644 --- a/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift +++ b/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift @@ -20,8 +20,8 @@ extension URLPasteboardWriter { if urlStrings.contains(nil), !AppDefaults.shared.hasSeenNotAllArticlesHaveURLsAlert { let alert = NSAlert() - alert.messageText = NSLocalizedString("Some articles don’t have links, so they weren't copied.", comment: "\"Some articles have no links\" copy alert message text") - alert.informativeText = NSLocalizedString("You won't see this message again.", comment: "You won't see this message again") + alert.messageText = NSLocalizedString("alert.message.articles-without-links", comment: "Some articles don’t have links, so they weren't copied.") + alert.informativeText = NSLocalizedString("alert.informative.will-not-see-again", comment: "You won't see this message again") if let window { alert.beginSheetModal(for: window) diff --git a/Mac/Preferences/Accounts/AccountsAddCloudKitWindowController.swift b/Mac/Preferences/Accounts/AccountsAddCloudKitWindowController.swift index 6c01d9ef9..cf4aa0f10 100644 --- a/Mac/Preferences/Accounts/AccountsAddCloudKitWindowController.swift +++ b/Mac/Preferences/Accounts/AccountsAddCloudKitWindowController.swift @@ -13,7 +13,7 @@ enum AccountsAddCloudKitWindowControllerError: LocalizedError { case iCloudDriveMissing var errorDescription: String? { - return NSLocalizedString("Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Preferences.", comment: "Unable to add iCloud Account.") + return NSLocalizedString("error.description.cloudkit-unavailable", comment: "Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Settings.") } } diff --git a/Mac/Preferences/Accounts/AccountsAddLocalWindowController.swift b/Mac/Preferences/Accounts/AccountsAddLocalWindowController.swift index 8aedbccd7..7482f309f 100644 --- a/Mac/Preferences/Accounts/AccountsAddLocalWindowController.swift +++ b/Mac/Preferences/Accounts/AccountsAddLocalWindowController.swift @@ -23,7 +23,7 @@ class AccountsAddLocalWindowController: NSWindowController { override func windowDidLoad() { super.windowDidLoad() - localAccountNameTextField.stringValue = NSLocalizedString("Create a local account on your Mac.", comment: "Account Local") + localAccountNameTextField.stringValue = NSLocalizedString("textfield.text.create-a-local-account", comment: "Create a local account on your Mac.") } // MARK: API diff --git a/Mac/Preferences/Accounts/AccountsFeedbinWindowController.swift b/Mac/Preferences/Accounts/AccountsFeedbinWindowController.swift index 6ac802851..f223f50d4 100644 --- a/Mac/Preferences/Accounts/AccountsFeedbinWindowController.swift +++ b/Mac/Preferences/Accounts/AccountsFeedbinWindowController.swift @@ -34,13 +34,13 @@ class AccountsFeedbinWindowController: NSWindowController, Logging { override func windowDidLoad() { if let account = account, let credentials = try? account.retrieveCredentials(type: .basic) { usernameTextField.stringValue = credentials.username - actionButton.title = NSLocalizedString("Update", comment: "Update") - signInTextField.stringValue = NSLocalizedString("Update your Feedbin account credentials.", comment: "SignIn") + actionButton.title = NSLocalizedString("button.title.update", comment: "Update") + signInTextField.stringValue = NSLocalizedString("textfield.text.update-feedbin-credentials", comment: "Update your Feedbin account credentials.") noAccountTextField.isHidden = true createNewAccountButton.isHidden = true } else { - actionButton.title = NSLocalizedString("Create", comment: "Add Account") - signInTextField.stringValue = NSLocalizedString("Sign in to your Feedbin account.", comment: "SignIn") + actionButton.title = NSLocalizedString("button.title.create", comment: "Create") + signInTextField.stringValue = NSLocalizedString("textfield.text.sign-in-feedbin", comment: "Sign in to your Feedbin account.") } enableAutofill() @@ -66,12 +66,13 @@ class AccountsFeedbinWindowController: NSWindowController, Logging { self.errorMessageLabel.stringValue = "" guard !usernameTextField.stringValue.isEmpty && !passwordTextField.stringValue.isEmpty else { - self.errorMessageLabel.stringValue = NSLocalizedString("Username & password required.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.userNameAndPasswordRequired.localizedDescription + return } guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .feedbin, username: usernameTextField.stringValue) else { - self.errorMessageLabel.stringValue = NSLocalizedString("There is already a Feedbin account with that username created.", comment: "Duplicate Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.duplicateAccount.localizedDescription return } @@ -92,7 +93,8 @@ class AccountsFeedbinWindowController: NSWindowController, Logging { case .success(let validatedCredentials): guard let validatedCredentials = validatedCredentials else { - self.errorMessageLabel.stringValue = NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.invalidUsernameOrPassword.localizedDescription + return } @@ -115,13 +117,13 @@ class AccountsFeedbinWindowController: NSWindowController, Logging { self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK) } catch { - self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.keychainError.localizedDescription self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public)") } case .failure: - self.errorMessageLabel.stringValue = NSLocalizedString("Network error. Try again later.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.networkError.localizedDescription } diff --git a/Mac/Preferences/Accounts/AccountsNewsBlurWindowController.swift b/Mac/Preferences/Accounts/AccountsNewsBlurWindowController.swift index 0af42818e..30825b14a 100644 --- a/Mac/Preferences/Accounts/AccountsNewsBlurWindowController.swift +++ b/Mac/Preferences/Accounts/AccountsNewsBlurWindowController.swift @@ -34,13 +34,13 @@ class AccountsNewsBlurWindowController: NSWindowController, Logging { override func windowDidLoad() { if let account = account, let credentials = try? account.retrieveCredentials(type: .newsBlurBasic) { usernameTextField.stringValue = credentials.username - actionButton.title = NSLocalizedString("Update", comment: "Update") - signInTextField.stringValue = NSLocalizedString("Update your NewsBlur account credentials.", comment: "SignIn") + actionButton.title = NSLocalizedString("button.title.update", comment: "Update") + signInTextField.stringValue = NSLocalizedString("textfield.text.update-newsblur-credentials", comment: "Update your NewsBlur account credentials.") noAccountTextField.isHidden = true createNewAccountButton.isHidden = true } else { - actionButton.title = NSLocalizedString("Create", comment: "Create") - signInTextField.stringValue = NSLocalizedString("Sign in to your NewsBlur account.", comment: "SignIn") + actionButton.title = NSLocalizedString("button.title.create", comment: "Create") + signInTextField.stringValue = NSLocalizedString("textfield.text.sign-in-newsblur", comment: "Sign in to your NewsBlur account.") } enableAutofill() usernameTextField.becomeFirstResponder() @@ -63,12 +63,12 @@ class AccountsNewsBlurWindowController: NSWindowController, Logging { self.errorMessageLabel.stringValue = "" guard !usernameTextField.stringValue.isEmpty else { - self.errorMessageLabel.stringValue = NSLocalizedString("Username required.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.userNameRequired.localizedDescription return } guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .newsBlur, username: usernameTextField.stringValue) else { - self.errorMessageLabel.stringValue = NSLocalizedString("There is already a NewsBlur account with that username created.", comment: "Duplicate Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.duplicateAccount.localizedDescription return } @@ -88,7 +88,7 @@ class AccountsNewsBlurWindowController: NSWindowController, Logging { switch result { case .success(let validatedCredentials): guard let validatedCredentials = validatedCredentials else { - self.errorMessageLabel.stringValue = NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.invalidUsernameOrPassword.localizedDescription return } @@ -113,14 +113,12 @@ class AccountsNewsBlurWindowController: NSWindowController, Logging { self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK) } catch { - self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.keychainError.localizedDescription self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public)") } case .failure: - - self.errorMessageLabel.stringValue = NSLocalizedString("Network error. Try again later.", comment: "Credentials Error") - + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.networkError.localizedDescription } } } diff --git a/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift b/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift index 307f5b8b5..4745a3d49 100644 --- a/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift +++ b/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift @@ -66,12 +66,12 @@ final class AccountsPreferencesViewController: NSViewController { let alert = NSAlert() alert.alertStyle = .warning - let deletePrompt = NSLocalizedString("Delete", comment: "Delete") - alert.messageText = "\(deletePrompt) “\(acctName)”?" - alert.informativeText = NSLocalizedString("Are you sure you want to delete the account “\(acctName)”? This cannot be undone.", comment: "Delete text") + let deletePrompt = NSLocalizedString("alert.title.delete.%@", comment: "Delete “%@“") + alert.messageText = String(format: deletePrompt, acctName) + alert.informativeText = NSLocalizedString("alert.message.cannot-undo-action", comment: "Cannot undo action") - alert.addButton(withTitle: NSLocalizedString("Delete", comment: "Delete Account")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel Delete Account")) + alert.addButton(withTitle: NSLocalizedString("button.title.delete", comment: "Delete Account")) + alert.addButton(withTitle: NSLocalizedString("button.title.cancel", comment: "Cancel Delete Account")) alert.beginSheetModal(for: view.window!) { [weak self] result in if result == NSApplication.ModalResponse.alertFirstButtonReturn { @@ -191,13 +191,13 @@ extension AccountsPreferencesViewController: AccountsPreferencesAddAccountDelega private func runAwaitingFeedlyLoginAlertModal(forLifetimeOf operation: OAuthAccountAuthorizationOperation) { let alert = NSAlert() alert.alertStyle = .informational - alert.messageText = NSLocalizedString("Waiting for access to Feedly", + alert.messageText = NSLocalizedString("alert.title.waiting-for-feedly-access", comment: "Alert title when adding a Feedly account and waiting for authorization from the user.") - alert.informativeText = NSLocalizedString("A web browser will open the Feedly login for you to authorize access.", - comment: "Alert informative text when adding a Feedly account and waiting for authorization from the user.") + alert.informativeText = NSLocalizedString("alert.message.feedly-web-browser-information", + comment: "A web browser will open the Feedly login for you to authorize access.") - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel")) + alert.addButton(withTitle: NSLocalizedString("button.title.cancel", comment: "Cancel")) let attachedWindow = self.view.window! @@ -242,9 +242,9 @@ private extension AccountsPreferencesViewController { if tableView.selectedRow == -1 { var helpText = "" if sortedAccounts.count == 0 { - helpText = NSLocalizedString("Add an account by clicking the + button.", comment: "Add Account Explainer") + helpText = NSLocalizedString("label.text.add-account-explainer", comment: "Add an account by clicking the + button.") } else { - helpText = NSLocalizedString("Select an account or add a new account by clicking the + button.", comment: "Add Account Explainer") + helpText = NSLocalizedString("label.text.select-or-add-account-explainer", comment: "Select an account or add a new account by clicking the + button.") } let textHostingController = NSHostingController(rootView: diff --git a/Mac/Preferences/Accounts/AccountsReaderAPIWindowController.swift b/Mac/Preferences/Accounts/AccountsReaderAPIWindowController.swift index b127003db..228395cc9 100644 --- a/Mac/Preferences/Accounts/AccountsReaderAPIWindowController.swift +++ b/Mac/Preferences/Accounts/AccountsReaderAPIWindowController.swift @@ -41,25 +41,25 @@ class AccountsReaderAPIWindowController: NSWindowController, Logging { switch accountType { case .freshRSS: titleImageView.image = AppAssets.accountFreshRSS - titleLabel.stringValue = NSLocalizedString("Sign in to your FreshRSS account.", comment: "FreshRSS") - noAccountTextField.stringValue = NSLocalizedString("Don’t have a FreshRSS instance?", comment: "No FreshRSS") - createAccountButton.title = NSLocalizedString("Find out more", comment: "No FreshRSS Button") - apiURLTextField.placeholderString = NSLocalizedString("fresh.rss.net/api/greader.php", comment: "FreshRSS API Helper") + titleLabel.stringValue = NSLocalizedString("label.text.sign-in-freshrss", comment: "Sign in to your FreshRSS account.") + noAccountTextField.stringValue = NSLocalizedString("label.text.no-fresh-rss", comment: "Don’t have a FreshRSS instance?") + createAccountButton.title = NSLocalizedString("label.text.find-out-more", comment: "Find out more") + apiURLTextField.placeholderString = "fresh.rss.net/api/greader.php" // not localized. case .inoreader: titleImageView.image = AppAssets.accountInoreader - titleLabel.stringValue = NSLocalizedString("Sign in to your InoReader account.", comment: "InoReader") + titleLabel.stringValue = NSLocalizedString("label.text.sign-in-inoreader", comment: "Sign in to your InoReader account.") gridView.row(at: 2).isHidden = true - noAccountTextField.stringValue = NSLocalizedString("Don’t have an InoReader account?", comment: "No InoReader") + noAccountTextField.stringValue = NSLocalizedString("label.text.no-inoreader", comment: "Don’t have an InoReader account?") case .bazQux: titleImageView.image = AppAssets.accountBazQux - titleLabel.stringValue = NSLocalizedString("Sign in to your BazQux account.", comment: "BazQux") + titleLabel.stringValue = NSLocalizedString("label.text.sign-in-bazqux", comment: "Sign in to your BazQux account.") gridView.row(at: 2).isHidden = true - noAccountTextField.stringValue = NSLocalizedString("Don’t have a BazQux account?", comment: "No BazQux") + noAccountTextField.stringValue = NSLocalizedString("label.text.no-bazqux", comment: "Don’t have a BazQux account?") case .theOldReader: titleImageView.image = AppAssets.accountTheOldReader - titleLabel.stringValue = NSLocalizedString("Sign in to your The Old Reader account.", comment: "The Old Reader") + titleLabel.stringValue = NSLocalizedString("label.text.sign-in-old-reader", comment: "Sign in to your The Old Reader account.") gridView.row(at: 2).isHidden = true - noAccountTextField.stringValue = NSLocalizedString("Don’t have a The Old Reader account?", comment: "No OldReader") + noAccountTextField.stringValue = NSLocalizedString("label.text.no-old-reader", comment: "Don’t have a The Old Reader account?") default: break } @@ -68,9 +68,9 @@ class AccountsReaderAPIWindowController: NSWindowController, Logging { if let account = account, let credentials = try? account.retrieveCredentials(type: .readerBasic) { usernameTextField.stringValue = credentials.username apiURLTextField.stringValue = account.endpointURL?.absoluteString ?? "" - actionButton.title = NSLocalizedString("Update", comment: "Update") + actionButton.title = NSLocalizedString("button.title.update", comment: "Update") } else { - actionButton.title = NSLocalizedString("Create", comment: "Create") + actionButton.title = NSLocalizedString("button.title.create", comment: "Create") } enableAutofill() @@ -94,17 +94,17 @@ class AccountsReaderAPIWindowController: NSWindowController, Logging { self.errorMessageLabel.stringValue = "" guard !usernameTextField.stringValue.isEmpty && !passwordTextField.stringValue.isEmpty else { - self.errorMessageLabel.stringValue = NSLocalizedString("Username, password & API URL are required.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.userNameAndPasswordRequired.localizedDescription return } guard let accountType = accountType, !(accountType == .freshRSS && apiURLTextField.stringValue.isEmpty) else { - self.errorMessageLabel.stringValue = NSLocalizedString("Username, password & API URL are required.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.userNamePasswordAndURLRequired.localizedDescription return } guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: accountType, username: usernameTextField.stringValue) else { - self.errorMessageLabel.stringValue = NSLocalizedString("There is already an account of this type with that username created.", comment: "Duplicate Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.duplicateAccount.localizedDescription return } @@ -112,7 +112,7 @@ class AccountsReaderAPIWindowController: NSWindowController, Logging { switch accountType { case .freshRSS: guard let inputURL = URL(string: apiURLTextField.stringValue) else { - self.errorMessageLabel.stringValue = NSLocalizedString("Invalid API URL.", comment: "Invalid API URL") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.invalidURL.localizedDescription return } apiURL = inputURL @@ -123,7 +123,7 @@ class AccountsReaderAPIWindowController: NSWindowController, Logging { case .theOldReader: apiURL = URL(string: ReaderAPIVariant.theOldReader.host)! default: - self.errorMessageLabel.stringValue = NSLocalizedString("Unrecognized account type.", comment: "Bad account type") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.unrecognizedAccount.localizedDescription return } @@ -143,7 +143,7 @@ class AccountsReaderAPIWindowController: NSWindowController, Logging { switch result { case .success(let validatedCredentials): guard let validatedCredentials = validatedCredentials else { - self.errorMessageLabel.stringValue = NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.invalidUsernameOrPassword.localizedDescription return } @@ -170,12 +170,12 @@ class AccountsReaderAPIWindowController: NSWindowController, Logging { self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK) } catch { - self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.keychainError.localizedDescription self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public)") } case .failure: - self.errorMessageLabel.stringValue = NSLocalizedString("Network error. Try again later.", comment: "Credentials Error") + self.errorMessageLabel.stringValue = LocalizedNetNewsWireError.networkError.localizedDescription } } diff --git a/Mac/Preferences/Accounts/AddAccountHelpView.swift b/Mac/Preferences/Accounts/AddAccountHelpView.swift index 4fa1fd54a..7a40b1804 100644 --- a/Mac/Preferences/Accounts/AddAccountHelpView.swift +++ b/Mac/Preferences/Accounts/AddAccountHelpView.swift @@ -43,8 +43,8 @@ struct AddAccountHelpView: View { } .alert(isPresented: $iCloudUnavailableError, content: { - Alert(title: Text(NSLocalizedString("Error", comment: "Error")), - message: Text(NSLocalizedString("You've already set up an iCloud account.", comment: "Error")), + Alert(title: Text(NSLocalizedString("alert.title.error", comment: "Error")), + message: Text(NSLocalizedString("alert.message.cloudkit-already-setup", comment: "You've already set up an iCloud account.")), dismissButton: Alert.Button.cancel({ iCloudUnavailableError = false })) diff --git a/Mac/Preferences/Accounts/AddAccountsView.swift b/Mac/Preferences/Accounts/AddAccountsView.swift index fb8fd6687..8a0b44c30 100644 --- a/Mac/Preferences/Accounts/AddAccountsView.swift +++ b/Mac/Preferences/Accounts/AddAccountsView.swift @@ -20,13 +20,13 @@ enum AddAccountSections: Int, CaseIterable { var sectionHeader: String { switch self { case .local: - return NSLocalizedString("Local", comment: "Local Account") + return NSLocalizedString("label.text.local", comment: "Local") case .icloud: - return NSLocalizedString("iCloud", comment: "iCloud Account") + return NSLocalizedString("label.text.cloudkit", comment: "iCloud") case .web: - return NSLocalizedString("Web", comment: "Web Account") + return NSLocalizedString("label.text.web", comment: "Web") case .selfhosted: - return NSLocalizedString("Self-hosted", comment: "Self hosted Account") + return NSLocalizedString("label.text.self-hosted", comment: "Self-hosted") case .allOrdered: return "" } @@ -35,13 +35,13 @@ enum AddAccountSections: Int, CaseIterable { var sectionFooter: String { switch self { case .local: - return NSLocalizedString("Local accounts do not sync feeds across devices", comment: "Local Account") + return NSLocalizedString("label.text.local-account-explainer", comment: "Local accounts do not sync your feeds across devices") case .icloud: - return NSLocalizedString("Your iCloud account syncs your feeds across your Mac and iOS devices", comment: "iCloud Account") + return NSLocalizedString("label.text.cloudkit-explainer", comment: "Your iCloud account syncs your feeds across your Mac and iOS devices") case .web: - return NSLocalizedString("Web accounts sync your feeds across all your devices", comment: "Web Account") + return NSLocalizedString("label.text.web-account-explainer", comment: "Web accounts sync your feeds across all your devices") case .selfhosted: - return NSLocalizedString("Self-hosted accounts sync your feeds across all your devices", comment: "Self hosted Account") + return NSLocalizedString("label.text.self-hosted-accounts-explainer", comment: "Self-hosted accounts sync your feeds across all your devices") case .allOrdered: return "" } @@ -87,7 +87,7 @@ struct AddAccountsView: View { var body: some View { VStack(alignment: .leading, spacing: 8) { - Text("Choose an account type to add...") + Text("label.text.choose-account-to-add", comment: "Choose an account type to add...") .font(.headline) .padding() @@ -106,20 +106,20 @@ struct AddAccountsView: View { Button(action: { parent?.dismiss(nil) }, label: { - Text("Cancel") + Text("button.title.cancel") .frame(width: 76) }) - .help("Cancel") + .help("label.text.cancel") .keyboardShortcut(.cancelAction) Button(action: { addAccountDelegate?.presentSheetForAccount(selectedAccount) parent?.dismiss(nil) }, label: { - Text("Continue") + Text("button.title.continue", comment: "Continue") .frame(width: 76) }) - .help("Add Account") + .help("label.text.add-account") .keyboardShortcut(.defaultAction) } .padding(.top, 12) @@ -133,7 +133,7 @@ struct AddAccountsView: View { var localAccount: some View { VStack(alignment: .leading) { - Text("Local") + Text("label.text.local", comment: "Local") .font(.headline) .padding(.horizontal) @@ -164,7 +164,7 @@ struct AddAccountsView: View { var icloudAccount: some View { VStack(alignment: .leading) { - Text("iCloud") + Text("label.text.cloudkit", comment: "iCloud") .font(.headline) .padding(.horizontal) .padding(.top, 8) @@ -196,7 +196,7 @@ struct AddAccountsView: View { @ViewBuilder var webAccounts: some View { VStack(alignment: .leading) { - Text("Web") + Text("label.text.web", comment: "Web") .font(.headline) .padding(.horizontal) .padding(.top, 8) @@ -234,7 +234,7 @@ struct AddAccountsView: View { var selfhostedAccounts: some View { VStack(alignment: .leading) { - Text("Self-hosted") + Text("label.text.self-hosted", comment: "Self-hosted") .font(.headline) .padding(.horizontal) .padding(.top, 8) diff --git a/Mac/Preferences/ExtensionPoints/EnableExtensionPointView.swift b/Mac/Preferences/ExtensionPoints/EnableExtensionPointView.swift index 5f8d0c113..a9d799047 100644 --- a/Mac/Preferences/ExtensionPoints/EnableExtensionPointView.swift +++ b/Mac/Preferences/ExtensionPoints/EnableExtensionPointView.swift @@ -25,7 +25,7 @@ struct EnableExtensionPointView: View { var body: some View { VStack(alignment: .leading, spacing: 8) { - Text("Choose an extension to add...") + Text("label.text.choose-extension-to-add", comment: "Choose an extension to add...") .font(.headline) .padding() @@ -37,7 +37,7 @@ struct EnableExtensionPointView: View { Button(action: { parent?.dismiss(nil) }, label: { - Text("Cancel") + Text("button.title.cancel", comment: "Cancel") .frame(width: 80) }) .help("Cancel") @@ -72,7 +72,7 @@ struct EnableExtensionPointView: View { VStack(alignment: .leading) { let extensionPointTypeNames = Self.feedProviderExtensionPointTypes.map { String(describing: $0) } if extensionPointTypeNames.count > 0 { - Text("Feed Provider") + Text("label.text.feed-provider", comment: "Feed Provider") .font(.headline) .padding(.horizontal) @@ -95,7 +95,7 @@ struct EnableExtensionPointView: View { .pickerStyle(RadioGroupPickerStyle()) .offset(x: 7.5, y: 0) - Text("An extension that makes websites appear to provide RSS feeds for their content.") + Text("label.text.feed-provider-explainer", comment: "An extension that makes websites appear to provide RSS feeds for their content.") .foregroundColor(.gray) .font(.caption) .padding(.horizontal) @@ -113,7 +113,7 @@ struct EnableExtensionPointView: View { VStack(alignment: .leading) { let extensionPointTypeNames = Self.sendToCommandExtensionPointTypes.map { String(describing: $0) } if extensionPointTypeNames.count > 0 { - Text("Third-Party Integration") + Text("label.text.third-party-integration", comment: "Third-Party Integration") .font(.headline) .padding(.horizontal) .padding(.top, 8) @@ -137,7 +137,7 @@ struct EnableExtensionPointView: View { .pickerStyle(RadioGroupPickerStyle()) .offset(x: 7.5, y: 0) - Text("An extension that enables a share menu item that passes article data to a third-party application.") + Text("label.text.share-extension-explainer", comment: "An extension that enables a share menu item that passes article data to a third-party application.") .foregroundColor(.gray) .font(.caption) .padding(.horizontal) diff --git a/Mac/Preferences/ExtensionPoints/ExtensionPointPreferencesViewController.swift b/Mac/Preferences/ExtensionPoints/ExtensionPointPreferencesViewController.swift index d79525d30..6f29e6770 100644 --- a/Mac/Preferences/ExtensionPoints/ExtensionPointPreferencesViewController.swift +++ b/Mac/Preferences/ExtensionPoints/ExtensionPointPreferencesViewController.swift @@ -64,13 +64,13 @@ final class ExtensionPointPreferencesViewController: NSViewController { let alert = NSAlert() alert.alertStyle = .warning - let prompt = NSLocalizedString("Deactivate", comment: "Deactivate") - alert.messageText = "\(prompt) “\(extensionPoint.title)”?" + let prompt = NSLocalizedString("alert.title.deactivate-extension.%@", comment: "Deactivate “%@“?") + alert.messageText = String(format: prompt, extensionPoint.title) let extensionPointTypeTitle = extensionPoint.extensionPointID.extensionPointType.title - alert.informativeText = NSLocalizedString("Are you sure you want to deactivate the \(extensionPointTypeTitle) extension “\(extensionPoint.title)”?", comment: "Deactivate text") + alert.informativeText = NSLocalizedString("alert.message.cannot-undo-action", comment: "You can't undo this action.") - alert.addButton(withTitle: NSLocalizedString("Deactivate", comment: "Deactivate Extension")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel Deactivate Extension")) + alert.addButton(withTitle: NSLocalizedString("button.title.deactivate", comment: "Deactivate Extension")) + alert.addButton(withTitle: NSLocalizedString("button.title.cancel", comment: "Cancel Deactivate Extension")) alert.beginSheetModal(for: view.window!) { [weak self] result in if result == NSApplication.ModalResponse.alertFirstButtonReturn { @@ -201,12 +201,12 @@ private extension ExtensionPointPreferencesViewController { if tableView.selectedRow == -1 { var helpText = "" if ExtensionPointManager.shared.availableExtensionPointTypes.count == 0 { - helpText = NSLocalizedString("You've added all available extensions.", comment: "Extension Explainer") + helpText = NSLocalizedString("label.text.added-all-extensions", comment: "You've added all available extensions.") } else if activeExtensionPoints.count == 0 { - helpText = NSLocalizedString("Add an extension by clicking the + button.", comment: "Extension Explainer") + helpText = NSLocalizedString("label.text.add-extension", comment: "Add an extension by clicking the + button.") } else { - helpText = NSLocalizedString("Select an extension or add a new extension by clicking the + button.", comment: "Extension Explainer") + helpText = NSLocalizedString("label.text.select-or-add-extension", comment: "Select an extension or add a new extension by clicking the + button.") } if let controller = children.first { @@ -244,12 +244,12 @@ private extension ExtensionPointPreferencesViewController { if tableView.selectedRow == -1 { var helpText = "" if ExtensionPointManager.shared.availableExtensionPointTypes.count == 0 { - helpText = NSLocalizedString("You've added all available extensions.", comment: "Extension Explainer") + helpText = NSLocalizedString("label.text.added-all-extensions", comment: "You've added all available extensions.") } else if activeExtensionPoints.count == 0 { - helpText = NSLocalizedString("Add an extension by clicking the + button.", comment: "Extension Explainer") + helpText = NSLocalizedString("label.text.add-extension", comment: "Add an extension by clicking the + button.") } else { - helpText = NSLocalizedString("Select an extension or add a new extension by clicking the + button.", comment: "Extension Explainer") + helpText = NSLocalizedString("label.text.select-or-add-extension", comment: "Select an extension or add a new extension by clicking the + button.") } let textHostingController = NSHostingController(rootView: EnableExtensionPointHelpView(helpText: helpText, preferencesController: self)) diff --git a/Mac/Preferences/General/GeneralPrefencesViewController.swift b/Mac/Preferences/General/GeneralPrefencesViewController.swift index 19fa9b7a0..db69f3863 100644 --- a/Mac/Preferences/General/GeneralPrefencesViewController.swift +++ b/Mac/Preferences/General/GeneralPrefencesViewController.swift @@ -109,7 +109,7 @@ private extension GeneralPreferencesViewController { let defaultBrowser = MacWebBrowser.default - let defaultBrowserFormat = NSLocalizedString("System Default (%@)", comment: "Default browser item title format") + let defaultBrowserFormat = NSLocalizedString("button.title.default-browser.%@", comment: "System Default (%@)") let defaultBrowserTitle = String(format: defaultBrowserFormat, defaultBrowser.name!) let item = NSMenuItem(title: defaultBrowserTitle, action: nil, keyEquivalent: "") let icon = defaultBrowser.icon! @@ -148,10 +148,10 @@ private extension GeneralPreferencesViewController { func showNotificationsDeniedError() { let updateAlert = NSAlert() updateAlert.alertStyle = .informational - updateAlert.messageText = NSLocalizedString("Enable Notifications", comment: "Notifications") - updateAlert.informativeText = NSLocalizedString("To enable notifications, open Notifications in System Preferences, then find NetNewsWire in the list.", comment: "To enable notifications, open Notifications in System Preferences, then find NetNewsWire in the list.") - updateAlert.addButton(withTitle: NSLocalizedString("Open System Preferences", comment: "Open System Preferences")) - updateAlert.addButton(withTitle: NSLocalizedString("Close", comment: "Close")) + updateAlert.messageText = NSLocalizedString("alert.title.enable-notifications", comment: "Enable Notifications") + updateAlert.informativeText = NSLocalizedString("alert.message.enable-notifications-in-system-settings", comment: "To enable notifications, open Notifications in System Settings, then find NetNewsWire in the list.") + updateAlert.addButton(withTitle: NSLocalizedString("button.title.open-system-settings", comment: "Open System Settings")) + updateAlert.addButton(withTitle: NSLocalizedString("button.title.close", comment: "Close")) let modalResponse = updateAlert.runModal() if modalResponse == .alertFirstButtonReturn { NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.notifications")!) diff --git a/Mac/Preferences/PreferencesWindowController.swift b/Mac/Preferences/PreferencesWindowController.swift index 738aa5d81..eb9ac233c 100644 --- a/Mac/Preferences/PreferencesWindowController.swift +++ b/Mac/Preferences/PreferencesWindowController.swift @@ -35,13 +35,13 @@ class PreferencesWindowController : NSWindowController, NSToolbarDelegate { private let toolbarItemSpecs: [PreferencesToolbarItemSpec] = { var specs = [PreferencesToolbarItemSpec]() specs += [PreferencesToolbarItemSpec(identifierRawValue: ToolbarItemIdentifier.General, - name: NSLocalizedString("General", comment: "Preferences"), + name: NSLocalizedString("button.title.general", comment: "General"), image: AppAssets.preferencesToolbarGeneralImage)] specs += [PreferencesToolbarItemSpec(identifierRawValue: ToolbarItemIdentifier.Accounts, - name: NSLocalizedString("Accounts", comment: "Preferences"), + name: NSLocalizedString("button.title.accounts", comment: "Account"), image: AppAssets.preferencesToolbarAccountsImage)] specs += [PreferencesToolbarItemSpec(identifierRawValue: ToolbarItemIdentifier.Extensions, - name: NSLocalizedString("Extensions", comment: "Preferences"), + name: NSLocalizedString("button.title.extensions", comment: "Extensions"), image: AppAssets.preferencesToolbarExtensionsImage)] // Omit the Advanced Preferences for now because the Software Update related functionality is @@ -50,7 +50,7 @@ class PreferencesWindowController : NSWindowController, NSToolbarDelegate { // of the content in this tab. #if !MAC_APP_STORE specs += [PreferencesToolbarItemSpec(identifierRawValue: ToolbarItemIdentifier.Advanced, - name: NSLocalizedString("Advanced", comment: "Preferences"), + name: NSLocalizedString("button.title.advanced", comment: "Advanced"), image: AppAssets.preferencesToolbarAdvancedImage)] #endif return specs diff --git a/Mac/Resources/en.lproj/Localizable.strings b/Mac/Resources/en.lproj/Localizable.strings new file mode 100644 index 000000000..bdf931502 --- /dev/null +++ b/Mac/Resources/en.lproj/Localizable.strings @@ -0,0 +1,659 @@ +/* On My iPad */ +"account.name.ipad" = "On My iPad"; + +/* On My iPhone */ +"account.name.iphone" = "On My iPhone"; + +/* On My Mac */ +"account.name.mac" = "On My Mac"; + +/* See articles in “%@” */ +"activity.title.see-article-in.folder.%@" = "See articles in “%@”"; + +/* See first unread article */ +"activity.title.see-first-unread-article" = "See first unread article"; + +/* Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Settings. */ +"alert.error.cloudkit-missing" = "Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Settings."; + +/* There is already an account of that type with that username created. */ +"alert.error.duplicate-account-username" = "There is already an account of that type with that username created."; + +/* Invalid API URL. */ +"alert.error.invalid-api-url" = "Invalid API URL."; + +/* Error message: The user provided an invalid username or password. */ +"alert.error.invalid-username-or-password" = "A username or password is required."; + +/* Error message: Unable to save due a Keychain error. */ +"alert.error.keychain-error" = "Unable to save account credentials due to a Keychain error."; + +/* Network error. Please try later. */ +"alert.error.network-error" = "A network error has occurred. Please try later."; + +/* This theme cannot be used because of data corruption in the Info.plist: %@. */ +"alert.error.theme-data-corruption.%@" = "This theme cannot be used because of data corruption in the Info.plist: %@."; + +/* Error message: This theme shares the same name as a provided theme and cannot be imported. */ +"alert.error.theme-duplicate-of-provided" = "This theme shares the same name as a provided theme and cannot be imported."; + +/* This theme cannot be used because the the key—“%@”—is not found in the Info.plist. */ +"alert.error.theme-key-not-found.%@" = "This theme cannot be used because the the key—“%@”—is not found in the Info.plist."; + +/* This theme cannot be used because the the type—“%@”—is mismatched in the Info.plist */ +"alert.error.theme-type-mismatch.%@" = "This theme cannot be used because the the type—“%@”—is mismatched in the Info.plist"; + +/* This theme cannot be used because the the value—“%@”—is not found in the Info.plist. */ +"alert.error.theme-value-missing.%@" = "This theme cannot be used because the the value—“%@”—is not found in the Info.plist."; + +/* The account type in invalid. */ +"alert.error.unrecognized-account" = "The account type is not recognized."; + +/* Error message: The user must provide a username and password. */ +"alert.error.username-and-password-required" = "A username and password are required."; + +/* The user must provide a username, password, and URL. */ +"alert.error.username-password-url-required" = "A username, password, and API URL are required."; + +/* Username required. */ +"alert.error.username-required" = "A username is required."; + +/* You won't see this message again */ +"alert.informative.will-not-see-again" = "You won't see this message again"; + +/* Can’t add this feed because you’ve already subscribed to it. */ +"alert.message.already-subscribed" = "Can’t add this feed because you’ve already subscribed to it."; + +/* Some articles don’t have links, so they weren't copied. */ +"alert.message.articles-without-links" = "Some articles don’t have links, so they weren't copied."; + +/* Cannot undo action + You can't undo this action. */ +"alert.message.cannot-undo-action" = "You can't undo this action."; + +/* Are you sure you want to clear the image caches? This will restart NetNewsWire to begin reloading the remote images. */ +"alert.message.clear-image-cache-confirmation" = "Are you sure you want to clear the image caches? This will restart NetNewsWire to begin reloading the remote images."; + +/* You've already set up an iCloud account. */ +"alert.message.cloudkit-already-setup" = "You've already set up an iCloud account."; + +/* Are you sure you want to delete the “%@” feed? */ +"alert.message.delete-feed.%@" = "Are you sure you want to delete the “%@” feed?"; + +/* Are you sure you want to delete the “%@” folder? */ +"alert.message.delete-folder.%@" = "Are you sure you want to delete the “%@” folder?"; + +/* Are you sure you want to delete the %d selected items? */ +"alert.message.delete-items.%d" = "Are you sure you want to delete the %d selected items?"; + +/* Can’t add this feed because of a download error: “%@” */ +"alert.message.download-error.%@" = "Can’t add this feed because of a download error: “%@”"; + +/* The theme “%@” already exists. Overwrite it? */ +"alert.message.duplicate-theme.%@" = "The theme “%@” already exists. Overwrite it?"; + +/* You can enable NetNewsWire notifications in System Settings. */ +"alert.message.enable-notifications-in-settings" = "You can enable NetNewsWire notifications in System Settings."; + +/* To enable notifications, open Notifications in System Settings, then find NetNewsWire in the list. */ +"alert.message.enable-notifications-in-system-settings" = "To enable notifications, open Notifications in System Settings, then find NetNewsWire in the list."; + +/* Can’t add a feed because no feed was found. */ +"alert.message.feed-not-found" = "Can’t add a feed because no feed was found."; + +/* A web browser will open the Feedly login for you to authorize access. */ +"alert.message.feedly-web-browser-information" = "A web browser will open the Feedly login for you to authorize access."; + +/* The theme “%@” has been installed. */ +"alert.message.theme-installed.%@" = "The theme “%@” has been installed."; + +/* Already subscribed */ +"alert.title.already-subscribed" = "Already subscribed"; + +/* Author's website: */ +"alert.title.authors-website" = "Author's website:"; + +/* Deactivate “%@“? */ +"alert.title.deactivate-extension.%@" = "Deactivate “%@“?"; + +/* Delete Feed */ +"alert.title.delete-feed" = "Delete Feed"; + +/* Delete Folder */ +"alert.title.delete-folder" = "Delete Folder"; + +/* Delete Items */ +"alert.title.delete-items" = "Delete Items"; + +/* Delete “%@“ */ +"alert.title.delete.%@" = "Delete “%@“"; + +/* Download Error */ +"alert.title.download-error" = "Download Error"; + +/* Enable Notifications */ +"alert.title.enable-notifications" = "Enable Notifications"; + +/* Error */ +"alert.title.error" = "Error"; + +/* Feed not found */ +"alert.title.feed-not-found" = "Feed not found"; + +/* Install theme “%@” by %@? — the order of the variables is theme name, author name */ +"alert.title.install-theme.%@.%@" = "Install theme “%@” by %@?"; + +/* Notifications are not enabled. */ +"alert.title.notifications-not-enabled" = "Notifications are not enabled."; + +/* Are you sure you want to open %ld articles in your browser? */ +"alert.title.open-articles-in-browser.%ld" = "Are you sure you want to open %ld articles in your browser?"; + +/* Theme error */ +"alert.title.theme-error" = "Theme error"; + +/* Theme installed */ +"alert.title.theme-installed" = "Theme installed"; + +/* Alert title when adding a Feedly account and waiting for authorization from the user. */ +"alert.title.waiting-for-feedly-access" = "Waiting for Feedly access..."; + +/* Mark Above as Read */ +"button.title-mark-above-as-read.titlecase" = "Mark Above as Read"; + +/* Mark Below as Read */ +"button.title-mark-below-as-read.titlecase" = "Mark Below as Read"; + +/* Every 2 Hours */ +"button.title.2-hours" = "Every 2 Hours"; + +/* Every 4 Hours */ +"button.title.4-hours" = "Every 4 Hours"; + +/* Every 8 Hours */ +"button.title.8-hours" = "Every 8 Hours"; + +/* Every 10 Minutes */ +"button.title.10-minutes" = "Every 10 Minutes"; + +/* Every 30 Minutes */ +"button.title.30-minutes" = "Every 30 Minutes"; + +/* Account */ +"button.title.accounts" = "Account"; + +/* Add Item */ +"button.title.add-item" = "Add Item"; + +/* Advanced */ +"button.title.advanced" = "Advanced"; + +/* Always Use Reader View */ +"button.title.always-use-reader-view" = "Always User Reader View"; + +/* Article Theme */ +"button.title.article-theme" = "Article Theme"; + +/* Cancel + Cancel button + Cancel Deactivate Extension + Cancel Delete Account + Cancel Install Theme */ +"button.title.cancel" = "Cancel"; + +/* Clean Up */ +"button.title.clean-up" = "Clean Up"; + +/* Clear & Restart */ +"button.title.clear-and-restart" = "Clear & Restart"; + +/* Close */ +"button.title.close" = "Close"; + +/* Continue */ +"button.title.continue" = "Continue"; + +/* Copy Article URL */ +"button.title.copy-article-url" = "Copy Article URL"; + +/* Copy External URL */ +"button.title.copy-external-url" = "Copy External URL"; + +/* Copy Feed URL */ +"button.title.copy-feed-url" = "Copy Feed URL"; + +/* Copy Home Page URL */ +"button.title.copy-home-page-url" = "Copy Home Page URL"; + +/* Create */ +"button.title.create" = "Create"; + +/* Deactivate Extension */ +"button.title.deactivate" = "Deactivate"; + +/* System Default (%@) */ +"button.title.default-browser.%@" = "System Default (%@)"; + +/* Delete + Delete Account */ +"button.title.delete" = "Delete"; + +/* Delete Feed */ +"button.title.delete-feed" = "Delete Feed"; + +/* Delete Feeds */ +"button.title.delete-feeds" = "Delete Feeds"; + +/* Delete Feeds and Folders */ +"button.title.delete-feeds-and-folders" = "Delete Feeds and Folders"; + +/* Delete Folder */ +"button.title.delete-folder" = "Delete Folder"; + +/* Delete Folders */ +"button.title.delete-folders" = "Delete Folders"; + +/* Dismiss */ +"button.title.dismiss" = "Dismiss"; + +/* Every Hour */ +"button.title.every-hour" = "Every Hour"; + +/* Extensions */ +"button.title.extensions" = "Extensions"; + +/* General */ +"button.title.general" = "General"; + +/* Hide Read Articles */ +"button.title.hide-read-articles" = "Hide Read Articles"; + +/* Hide Read Feeds */ +"button.title.hide-read-feeds" = "Hide Read Feeds"; + +/* Hide Sidebar */ +"button.title.hide-sidebar" = "Hide Sidebar"; + +/* Install Theme */ +"button.title.install-theme" = "Install Theme"; + +/* Mark Above as Read */ +"button.title.mark-above-as-read.titlecase" = "Mark Above as Read"; + +/* Mark All as Read in “%@” */ +"button.title.mark-all-as-read.%@" = "Mark All as Read in “%@”"; + +/* Mark All as Read */ +"button.title.mark-all-as-read.titlecase" = "Mark All as Read"; + +/* Mark as Read */ +"button.title.mark-as-read" = "Mark as Read"; + +/* Mark as Starred */ +"button.title.mark-as-starred" = "Mark as Starred"; + +/* Mark as Unread */ +"button.title.mark-as-unread" = "Mark as Unread"; + +/* Mark as Unstarred */ +"button.title.mark-as-unstarred" = "Mask as Unstarred"; + +/* Mark Below as Read */ +"button.title.mark-below-as-read.titlecase" = "Mark Below as Read"; + +/* Mark Read */ +"button.title.mark-read" = "Mark Read"; + +/* Star */ +"button.title.mark-star" = "Mark Star"; + +/* Mark Starred */ +"button.title.mark-starred" = "Mark Starred"; + +/* Mark Unread */ +"button.title.mark-unread" = "Mark Unread"; + +/* Mark Unstarred */ +"button.title.mark-unstarred" = "Mark Unstarred"; + +/* New Feed */ +"button.title.new-feed" = "New Feed"; + +/* New Folder + New Folder... */ +"button.title.new-folder" = "New Folder"; + +/* New Reddit Feed... */ +"button.title.new-reddit-feed" = "New Reddit Feed..."; + +/* New Twitter Feed... */ +"button.title.new-twitter-feed" = "New Twitter Feed..."; + +/* New Web Feed... */ +"button.title.new-web-feed" = "New Web Feed..."; + +/* Next Unread */ +"button.title.next-read" = "Next Unread"; + +/* OK */ +"button.title.ok" = "OK"; + +/* Open */ +"button.title.open" = "Open"; + +/* Open %ld Articles */ +"button.title.open-articles.%ld" = "Open %ld Articles"; + +/* Open Home Page */ +"button.title.open-home-page" = "Open Home Page"; + +/* Open in Browser in Background */ +"button.title.open-in-background" = "Open in Browser in Background"; + +/* Open in Browser */ +"button.title.open-in-browser" = "Open in Browser"; + +/* Open in Browser in Foreground */ +"button.title.open-in-foreground" = "Open in Browser in Foreground"; + +/* Open Settings */ +"button.title.open-settings" = "Open Settings"; + +/* Open System Settings */ +"button.title.open-system-settings" = "Open System Settings"; + +/* Overwrite */ +"button.title.overwrite" = "Overwrite"; + +/* Read Articles Filter */ +"button.title.read-articles-filter" = "Read Articles Filter"; + +/* Reader View */ +"button.title.reader-view" = "Reader View"; + +/* Refresh */ +"button.title.refresh" = "Refresh"; + +/* Rename */ +"button.title.rename" = "Rename"; + +/* Search */ +"button.title.search" = "Search"; + +/* Select “%@” in Sidebar */ +"button.title.select-in-sidebar.%@" = "Select “%@” in Sidebar"; + +/* Share + Share menu name */ +"button.title.share" = "Share"; + +/* Show Read Articles */ +"button.title.show-read-articles" = "Show Read Articles"; + +/* Show Read Feeds */ +"button.title.show-read-feeds" = "Show Read Feeds"; + +/* Show Sidebar */ +"button.title.show-sidebar" = "Show Sidebar"; + +/* Toggle Sidebar */ +"button.title.toggle-sidebar" = "Toggle Sidebar"; + +/* Update */ +"button.title.update" = "Update"; + +/* Show notifications for new articles */ +"checkbox.title.show-new-article-notifications" = "Show notifications for new articles"; + +/* Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Settings. */ +"error.description.cloudkit-unavailable" = "Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Settings."; + +/* Unable to create extension. */ +"error.message.unable-to-create-extension" = "Unable to create extension."; + +/* Markdown formatted link to netnewswire.com */ +"label.markdown.netnewswire-website" = "[netnewswire.com](https://netnewswire.com)"; + +/* Add an account by clicking the + button. */ +"label.text.add-account-explainer" = "Add an account by clicking the + button."; + +/* Add an extension by clicking the + button. */ +"label.text.add-extension" = "Add an extension by clicking the + button."; + +/* You've added all available extensions. */ +"label.text.added-all-extensions" = "You've added all available extensions."; + +/* Article */ +"label.text.article" = "Article"; + +/* Choose an account type to add... */ +"label.text.choose-account-to-add" = "Choose an account type to add..."; + +/* Choose an extension to add... */ +"label.text.choose-extension-to-add" = "Choose an extension to add..."; + +/* iCloud */ +"label.text.cloudkit" = "iCloud"; + +/* Your iCloud account syncs your feeds across your Mac and iOS devices */ +"label.text.cloudkit-explainer" = "Your iCloud account syncs your feeds across your Mac and iOS devices"; + +/* Error - Reader View */ +"label.text.error-reader-view" = "Error - Reader View"; + +/* XX-Large */ +"label.text.extra-extra-large" = "XX-Large"; + +/* X-Large */ +"label.text.extra-large" = "X-Large"; + +/* Feed Provider */ +"label.text.feed-provider" = "Feed Provider"; + +/* An extension that makes websites appear to provide RSS feeds for their content. */ +"label.text.feed-provider-explainer" = "An extension that makes websites appear to provide RSS feeds for their content."; + +/* Find out more */ +"label.text.find-out-more" = "Find out more"; + +/* Finding feed... */ +"label.text.finding-feed" = "Finding feed..."; + +/* Large */ +"label.text.large" = "Large"; + +/* Link: */ +"label.text.link" = "Link:"; + +/* Local */ +"label.text.local" = "Local"; + +/* Local accounts do not sync your feeds across devices */ +"label.text.local-account-explainer" = "Local accounts do not sync your feeds across devices"; + +/* This extension enables share menu functionality to send selected article text to MarsEdit. You need the MarsEdit application for this to work. */ +"label.text.marsedit.explainer" = "This extension enables share menu functionality to send selected article text to MarsEdit. You need the MarsEdit application for this to work."; + +/* Medium */ +"label.text.medium" = "Medium"; + +/* This extension enables share menu functionality to send selected article text to Micro.blog. You need the Micro.blog application for this to work. */ +"label.text.micro-blog-expaliner" = "This extension enables share menu functionality to send selected article text to Micro.blog. You need the Micro.blog application for this to work."; + +/* By Brent Simmons and the NetNewsWire team. */ +"label.text.netnewswire-byline" = "By Brent Simmons and the NetNewsWire team."; + +/* Don’t have a BazQux account? */ +"label.text.no-bazqux" = "Don’t have a BazQux account?"; + +/* Don’t have a FreshRSS instance? */ +"label.text.no-fresh-rss" = "Don’t have a FreshRSS instance?"; + +/* Don’t have an InoReader account? */ +"label.text.no-inoreader" = "Don’t have an InoReader account?"; + +/* Don’t have a The Old Reader account? */ +"label.text.no-old-reader" = "Don’t have a The Old Reader account?"; + +/* (No Text) */ +"label.text.no-text" = "(No Text)"; + +/* Posts from r/%@ */ +"label.text.posts-from-subreddit.%@" = "Posts from r/%@"; + +/* Privacy Policy */ +"label.text.privacy-policy" = "Privacy Policy"; + +/* Processing - Reader View */ +"label.text.processing-reader-view" = "Processing - Reader View"; + +/* Reader View */ +"label.text.reader-view" = "Reader View"; + +/* The most active posts */ +"label.text.reddit-active-posts" = "The most active posts"; + +/* The best posts on Reddit for you */ +"label.text.reddit-best-posts" = "The best posts on Reddit for you"; + +/* Your personal Reddit frontpage */ +"label.text.reddit-front-page" = "Your personal Reddit frontpage"; + +/* Select an account or add a new account by clicking the + button. */ +"label.text.select-or-add-account-explainer" = "Select an account or add a new account by clicking the + button."; + +/* Select an extension or add a new extension by clicking the + button. */ +"label.text.select-or-add-extension" = "Select an extension or add a new extension by clicking the + button."; + +/* Selected - Reader View */ +"label.text.selected-reader-view" = "Selected - Reader View"; + +/* Self-hosted */ +"label.text.self-hosted" = "Self-hosted"; + +/* Self-hosted accounts sync your feeds across all your devices */ +"label.text.self-hosted-accounts-explainer" = "Self-hosted accounts sync your feeds across all your devices"; + +/* An extension that enables a share menu item that passes article data to a third-party application. */ +"label.text.share-extension-explainer" = "An extension that enables a share menu item that passes article data to a third-party application."; + +/* Sign in to your BazQux account. */ +"label.text.sign-in-bazqux" = "Sign in to your BazQux account."; + +/* Sign in to your FreshRSS account. */ +"label.text.sign-in-freshrss" = "Sign in to your FreshRSS account."; + +/* Sign in to your InoReader account. */ +"label.text.sign-in-inoreader" = "Sign in to your InoReader account."; + +/* Sign in to your The Old Reader account. */ +"label.text.sign-in-old-reader" = "Sign in to your The Old Reader account."; + +/* Small */ +"label.text.small" = "Small"; + +/* Subreddit */ +"label.text.subreddit" = "Subreddit"; + +/* Third-Party Integration */ +"label.text.third-party-integration" = "Third-Party Integration"; + +/* Timeline */ +"label.text.timeline" = "Timeline"; + +/* Tweets that contain %@ */ +"label.text.tweets-containing.%@" = "Tweets that contain %@"; + +/* Tweets from everyone you follow */ +"label.text.tweets-from-everyone" = "Tweets from everyone you follow"; + +/* Tweets from @%@ */ +"label.text.tweets-from.%@" = "Tweets from @%@"; + +/* Tweets mentioning you */ +"label.text.tweets-mentioning-you" = "Tweets mentioning you"; + +/* unread */ +"label.text.unread" = "unread"; + +/* Web */ +"label.text.web" = "Web"; + +/* Web accounts sync your feeds across all your devices */ +"label.text.web-account-explainer" = "Web accounts sync your feeds across all your devices"; + +/* Choose a location for the exported OPML file. */ +"panel.message.export-opml" = "Choose a location for the exported OPML file."; + +/* Export OPML */ +"panel.prompt.export-opml" = "Export OPML"; + +/* Export to: */ +"panel.textfield.export-opml-destination" = "Export to:"; + +/* Export OPML */ +"panel.title.export-opml" = "Export OPML"; + +/* Choose a Subscriptions.plist file: */ +"panel.title.select-opml-file" = "Choose a Subscriptions.plist file:"; + +/* All Unread pseudo-feed title */ +"smartfeed.title.allunread" = "All Unread"; + +/* Starred pseudo-feed title */ +"smartfeed.title.starred" = "Starred"; + +/* Today pseudo-feed title */ +"smartfeed.title.today" = "Today"; + +/* Smart Feeds group title */ +"smartfeeds.title" = "Smart Feeds"; + +/* Search Term or #hashtag */ +"textfield.placeholder.search-term-hashtag" = "Search Term or #hashtag"; + +/* @name */ +"textfield.placeholder.twitter-username" = "@name"; + +/* Rename %@ to: */ +"textfield.prompt.rename-to.%@" = "Rename %@ to:"; + +/* Create a local account on your Mac. */ +"textfield.text.create-a-local-account" = "Create a local account on your Mac."; + +/* Sign in to your Feedbin account. */ +"textfield.text.sign-in-feedbin" = "Sign in to your Feedbin account."; + +/* Sign in to your NewsBlur account. */ +"textfield.text.sign-in-newsblur" = "Sign in to your NewsBlur account."; + +/* Update your Feedbin account credentials. */ +"textfield.text.update-feedbin-credentials" = "Update your Feedbin account credentials."; + +/* Update your NewsBlur account credentials. */ +"textfield.text.update-newsblur-credentials" = "Update your NewsBlur account credentials."; + +/* %d unread */ +"window.subtitle.unread-count.%d" = "%d unread"; + +/* Feed Inspector */ +"window.title.feed-inspector" = "Feed Inspector"; + +/* Folder Inspector */ +"window.title.folder-inspector" = "Folder Inspector"; + +/* Inspector */ +"window.title.inspector" = "Inspector"; + +/* Keyboard Shortcuts */ +"window.title.keyboard-shortcuts" = "Keyboard Shortcuts"; + +/* Multiple */ +"window.title.multiple" = "Multiple"; + +/* Search: %@ */ +"window.title.search.%@" = "Search: %@"; + +/* Smart Feed Inspector */ +"window.title.smart-feed-inspector" = "Smart Feed Inspector"; + diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 72e56894a..941a85dcc 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -860,6 +860,9 @@ DFB349A0294E87B700BC81AD /* LocalAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3499F294E87B700BC81AD /* LocalAddAccountView.swift */; }; DFB349A2294E90B500BC81AD /* FeedbinAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB349A1294E90B500BC81AD /* FeedbinAddAccountView.swift */; }; DFB349A4294E914D00BC81AD /* AccountSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB349A3294E914D00BC81AD /* AccountSectionHeader.swift */; }; + DFB616A92965300400A359AB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB616A72965300400A359AB /* Localizable.strings */; }; + DFB616AC2965300400A359AB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB616AA2965300400A359AB /* Localizable.strings */; }; + DFB616AD2965300400A359AB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB616AA2965300400A359AB /* Localizable.strings */; }; DFBB4EAC2951BC0200639228 /* NNWThemeDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFBB4EAB2951BC0200639228 /* NNWThemeDocument.swift */; }; DFBB4EAD2951BC0200639228 /* NNWThemeDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFBB4EAB2951BC0200639228 /* NNWThemeDocument.swift */; }; DFBB4EAE2951BC0200639228 /* NNWThemeDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFBB4EAB2951BC0200639228 /* NNWThemeDocument.swift */; }; @@ -1621,6 +1624,8 @@ DFB3499F294E87B700BC81AD /* LocalAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAddAccountView.swift; sourceTree = ""; }; DFB349A1294E90B500BC81AD /* FeedbinAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAddAccountView.swift; sourceTree = ""; }; DFB349A3294E914D00BC81AD /* AccountSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSectionHeader.swift; sourceTree = ""; }; + DFB616A82965300400A359AB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + DFB616AB2965300400A359AB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; DFBB4EAB2951BC0200639228 /* NNWThemeDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNWThemeDocument.swift; sourceTree = ""; }; DFBB4EAF2951BCAC00639228 /* ArticleThemeManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleThemeManagerView.swift; sourceTree = ""; }; DFC14F0E28EA55BD00F6EE86 /* AboutWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutWindowController.swift; sourceTree = ""; }; @@ -1953,6 +1958,7 @@ children = ( 51314666235A7E4600387FDC /* IntentHandler.swift */, 51314665235A7E4600387FDC /* Info.plist */, + DFB616A72965300400A359AB /* Localizable.strings */, 51314684235A7EB900387FDC /* NetNewsWire_iOS_IntentsExtension.entitlements */, ); path = IntentsExtension; @@ -2701,6 +2707,7 @@ 84C9FC9022629ECB00D921D6 /* NetNewsWire.entitlements */, 51F805D32428499E0022C792 /* NetNewsWire-dev.entitlements */, 84C9FC9122629F2200D921D6 /* Info.plist */, + DFB616AA2965300400A359AB /* Localizable.strings */, 65ED409F235DEFF00081F399 /* container-migration.plist */, 17192AE12567B3FE00AAEACA /* org.sparkle-project.Downloader.xpc */, 17192AE22567B3FE00AAEACA /* org.sparkle-project.InstallerConnection.xpc */, @@ -3453,6 +3460,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + DFB616A92965300400A359AB /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3539,6 +3547,7 @@ 65ED406B235DEF6C0081F399 /* CrashReporterWindow.xib in Resources */, 65ED406C235DEF6C0081F399 /* Credits.rtf in Resources */, 65ED406D235DEF6C0081F399 /* Inspector.storyboard in Resources */, + DFB616AD2965300400A359AB /* Localizable.strings in Resources */, 65ED406E235DEF6C0081F399 /* AddWebFeedSheet.xib in Resources */, 51077C5927A86D16000C71DB /* Hyperlegible.nnwtheme in Resources */, 51DEE81326FB9233006DAA56 /* Appanoose.nnwtheme in Resources */, @@ -3609,6 +3618,7 @@ 84C9FC7D22629E1200D921D6 /* AccountsDetail.xib in Resources */, 5137C2E426F3F52D009EFEDB /* Sepia.nnwtheme in Resources */, 517630042336215100E15FFF /* main.js in Resources */, + DFB616AC2965300400A359AB /* Localizable.strings in Resources */, 65ED40A0235DEFF00081F399 /* container-migration.plist in Resources */, 5144EA362279FC3D00D19003 /* AccountsAddLocal.xib in Resources */, 51D0214626ED617100FF2E0F /* core.css in Resources */, @@ -4749,6 +4759,22 @@ name = LaunchScreenPhone.storyboard; sourceTree = ""; }; + DFB616A72965300400A359AB /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + DFB616A82965300400A359AB /* en */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + DFB616AA2965300400A359AB /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + DFB616AB2965300400A359AB /* en */, + ); + name = Localizable.strings; + sourceTree = ""; + }; DFD86798295D553D0070D62D /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( diff --git a/Shared/AccountType+Helpers.swift b/Shared/AccountType+Helpers.swift index a9016a98e..80d8bae78 100644 --- a/Shared/AccountType+Helpers.swift +++ b/Shared/AccountType+Helpers.swift @@ -25,12 +25,12 @@ extension AccountType { case .onMyMac: let defaultName: String #if os(macOS) - defaultName = NSLocalizedString("On My Mac", comment: "Account name") + defaultName = NSLocalizedString("account.name.mac", comment: "On My Mac") #else if UIDevice.current.userInterfaceIdiom == .pad { - defaultName = NSLocalizedString("On My iPad", comment: "Account name") + defaultName = NSLocalizedString("account.name.ipad", comment: "On My iPad") } else { - defaultName = NSLocalizedString("On My iPhone", comment: "Account name") + defaultName = NSLocalizedString("account.name.iphone", comment: "On My iPhone") } #endif return defaultName diff --git a/Shared/Activity/ActivityManager.swift b/Shared/Activity/ActivityManager.swift index b0c5d0784..d3108b261 100644 --- a/Shared/Activity/ActivityManager.swift +++ b/Shared/Activity/ActivityManager.swift @@ -70,7 +70,7 @@ class ActivityManager { guard nextUnreadActivity == nil else { return } nextUnreadActivity = NSUserActivity(activityType: ActivityType.nextUnread.rawValue) - nextUnreadActivity!.title = NSLocalizedString("See first unread article", comment: "First Unread") + nextUnreadActivity!.title = NSLocalizedString("activity.title.see-first-unread-article", comment: "See first unread article") #if os(iOS) nextUnreadActivity!.suggestedInvocationPhrase = nextUnreadActivity!.title @@ -165,7 +165,7 @@ private extension ActivityManager { func makeSelectFeedActivity(feed: Feed) -> NSUserActivity { let activity = NSUserActivity(activityType: ActivityType.selectFeed.rawValue) - let localizedText = NSLocalizedString("See articles in “%@”", comment: "See articles in Folder") + let localizedText = NSLocalizedString("activity.title.see-article-in.folder.%@", comment: "See articles in “%@”") let title = NSString.localizedStringWithFormat(localizedText as NSString, feed.nameForDisplay) as String activity.title = title diff --git a/Shared/Article Rendering/ArticleRenderer.swift b/Shared/Article Rendering/ArticleRenderer.swift index 9ecd6a7b7..b44bb2374 100644 --- a/Shared/Article Rendering/ArticleRenderer.swift +++ b/Shared/Article Rendering/ArticleRenderer.swift @@ -210,7 +210,7 @@ private extension ArticleRenderer { d["preferred_link"] = article.preferredLink ?? "" if let externalLink = article.externalLink, externalLink != article.preferredLink { - d["external_link_label"] = NSLocalizedString("Link:", comment: "Link") + d["external_link_label"] = NSLocalizedString("label.text.link", comment: "Link: ") d["external_link_stripped"] = externalLink.strippingHTTPOrHTTPSScheme d["external_link"] = externalLink } else { diff --git a/Shared/Article Rendering/ArticleTextSize.swift b/Shared/Article Rendering/ArticleTextSize.swift index bbc433974..defc24eb8 100644 --- a/Shared/Article Rendering/ArticleTextSize.swift +++ b/Shared/Article Rendering/ArticleTextSize.swift @@ -35,15 +35,15 @@ enum ArticleTextSize: Int, CaseIterable, Identifiable { func description() -> String { switch self { case .small: - return NSLocalizedString("Small", comment: "Small") + return NSLocalizedString("label.text.small", comment: "Small") case .medium: - return NSLocalizedString("Medium", comment: "Medium") + return NSLocalizedString("label.text.medium", comment: "Medium") case .large: - return NSLocalizedString("Large", comment: "Large") + return NSLocalizedString("label.text.large", comment: "Large") case .xlarge: - return NSLocalizedString("Extra Large", comment: "X-Large") + return NSLocalizedString("label.text.extra-large", comment: "X-Large") case .xxlarge: - return NSLocalizedString("Extra Extra Large", comment: "XX-Large") + return NSLocalizedString("label.text.extra-extra-large", comment: "XX-Large") } } diff --git a/Shared/ArticleStyles/ArticleTheme.swift b/Shared/ArticleStyles/ArticleTheme.swift index f10219f46..c99d863a2 100644 --- a/Shared/ArticleStyles/ArticleTheme.swift +++ b/Shared/ArticleStyles/ArticleTheme.swift @@ -20,8 +20,9 @@ struct ArticleTheme: Equatable { static let defaultTheme = ArticleTheme() static let nnwThemeSuffix = ".nnwtheme" - private static let defaultThemeName = NSLocalizedString("Default", comment: "Default") - private static let unknownValue = NSLocalizedString("Unknown", comment: "Unknown Value") + // Don't localize the theme names. + private static let defaultThemeName = "Default" + private static let unknownValue = "Unknown" let path: String? let template: String? diff --git a/Shared/Commands/DeleteCommand.swift b/Shared/Commands/DeleteCommand.swift index 991b7b430..7ec5fb393 100644 --- a/Shared/Commands/DeleteCommand.swift +++ b/Shared/Commands/DeleteCommand.swift @@ -259,11 +259,11 @@ private extension Node { private struct DeleteActionName { - private static let deleteFeed = NSLocalizedString("Delete Feed", comment: "command") - private static let deleteFeeds = NSLocalizedString("Delete Feeds", comment: "command") - private static let deleteFolder = NSLocalizedString("Delete Folder", comment: "command") - private static let deleteFolders = NSLocalizedString("Delete Folders", comment: "command") - private static let deleteFeedsAndFolders = NSLocalizedString("Delete Feeds and Folders", comment: "command") + private static let deleteFeed = NSLocalizedString("button.title.delete-feed", comment: "Delete Feed") + private static let deleteFeeds = NSLocalizedString("button.title.delete-feeds", comment: "Delete Feeds") + private static let deleteFolder = NSLocalizedString("button.title.delete-folder", comment: "Delete Folder") + private static let deleteFolders = NSLocalizedString("button.title.delete-folders", comment: "Delete Folders") + private static let deleteFeedsAndFolders = NSLocalizedString("button.title.delete-feeds-and-folders", comment: "Delete Feeds and Folders") static func name(for nodes: [Node]) -> String? { diff --git a/Shared/Commands/MarkStatusCommand.swift b/Shared/Commands/MarkStatusCommand.swift index aa3e39079..dc7fe75f1 100644 --- a/Shared/Commands/MarkStatusCommand.swift +++ b/Shared/Commands/MarkStatusCommand.swift @@ -111,10 +111,10 @@ private extension MarkStatusCommand { Account.UserInfoKey.statusFlag: flag]) } - static private let markReadActionName = NSLocalizedString("Mark Read", comment: "command") - static private let markUnreadActionName = NSLocalizedString("Mark Unread", comment: "command") - static private let markStarredActionName = NSLocalizedString("Mark Starred", comment: "command") - static private let markUnstarredActionName = NSLocalizedString("Mark Unstarred", comment: "command") + static private let markReadActionName = NSLocalizedString("button.title.mark-read", comment: "Mark Read") + static private let markUnreadActionName = NSLocalizedString("button.title.mark-unread", comment: "Mark Unread") + static private let markStarredActionName = NSLocalizedString("button.title.mark-starred", comment: "Mark Starred") + static private let markUnstarredActionName = NSLocalizedString("button.title.mark-unstarred", comment: "Mark Unstarred") static func actionName(_ statusKey: ArticleStatus.Key, _ flag: Bool) -> String { diff --git a/Shared/ExtensionPoints/ExtensionPointManager.swift b/Shared/ExtensionPoints/ExtensionPointManager.swift index bf8cb0402..58914716f 100644 --- a/Shared/ExtensionPoints/ExtensionPointManager.swift +++ b/Shared/ExtensionPoints/ExtensionPointManager.swift @@ -21,7 +21,7 @@ public enum ExtensionPointManagerError: LocalizedError { public var localizedDescription: String { switch self { case .unableToCreate: - return NSLocalizedString("Unable to create extension.", comment: "Unable to create extension") + return NSLocalizedString("error.message.unable-to-create-extension", comment: "Unable to create extension.") } } } diff --git a/Shared/ExtensionPoints/SendToMarsEditCommand.swift b/Shared/ExtensionPoints/SendToMarsEditCommand.swift index c921d71c3..2f24bf82f 100644 --- a/Shared/ExtensionPoints/SendToMarsEditCommand.swift +++ b/Shared/ExtensionPoints/SendToMarsEditCommand.swift @@ -14,10 +14,10 @@ final class SendToMarsEditCommand: ExtensionPoint, SendToCommand { static var isSinglton = true static var isDeveloperBuildRestricted = false - static var title = NSLocalizedString("MarsEdit", comment: "MarsEdit") + static var title = "MarsEdit" // not localized static var image = AppAssets.extensionPointMarsEdit static var description: NSAttributedString = { - let attrString = SendToMarsEditCommand.makeAttrString("This extension enables share menu functionality to send selected article text to MarsEdit. You need the MarsEdit application for this to work.") + let attrString = SendToMarsEditCommand.makeAttrString(NSLocalizedString("label.text.marsedit.explainer", comment: "This extension enables share menu functionality to send selected article text to MarsEdit. You need the MarsEdit application for this to work.")) let range = NSRange(location: 81, length: 8) attrString.beginEditing() attrString.addAttribute(NSAttributedString.Key.link, value: "https://red-sweater.com/marsedit/", range: range) diff --git a/Shared/ExtensionPoints/SendToMicroBlogCommand.swift b/Shared/ExtensionPoints/SendToMicroBlogCommand.swift index 1c8bc031b..c87314bda 100644 --- a/Shared/ExtensionPoints/SendToMicroBlogCommand.swift +++ b/Shared/ExtensionPoints/SendToMicroBlogCommand.swift @@ -16,10 +16,10 @@ final class SendToMicroBlogCommand: ExtensionPoint, SendToCommand { static var isSinglton = true static var isDeveloperBuildRestricted = false - static var title: String = NSLocalizedString("Micro.blog", comment: "Micro.blog") + static var title: String = "Micro.blog" // not localized static var image = AppAssets.extensionPointMicroblog static var description: NSAttributedString = { - let attrString = SendToMicroBlogCommand.makeAttrString("This extension enables share menu functionality to send selected article text to Micro.blog. You need the Micro.blog application for this to work.") + let attrString = SendToMicroBlogCommand.makeAttrString(NSLocalizedString("label.text.micro-blog-expaliner", comment: "This extension enables share menu functionality to send selected article text to Micro.blog. You need the Micro.blog application for this to work.")) let range = NSRange(location: 81, length: 10) attrString.beginEditing() attrString.addAttribute(NSAttributedString.Key.link, value: "https://micro.blog", range: range) diff --git a/Shared/Localizations/LocalizedNetNewsWireError.swift b/Shared/Localizations/LocalizedNetNewsWireError.swift index 46ff38945..5cee252d0 100644 --- a/Shared/Localizations/LocalizedNetNewsWireError.swift +++ b/Shared/Localizations/LocalizedNetNewsWireError.swift @@ -21,26 +21,46 @@ public enum LocalizedNetNewsWireError: LocalizedError { case userNameAndPasswordRequired + case userNamePasswordAndURLRequired + + case userNameRequired + case invalidUsernameOrPassword + case invalidURL + case keychainError case duplicateDefaultTheme + + case networkError + + case unrecognizedAccount public var errorDescription: String? { switch self { case .duplicateAccount: - return String(localized: "There is already an account of that type with that username created.", comment: "Error message: duplicate account with same username.") + return NSLocalizedString("alert.error.duplicate-account-username", comment: "There is already an account of that type with that username created.") case .iCloudDriveMissing: - return String(localized: "Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Settings.", comment: "Error message: The user cannot enable the iCloud account becasue iCloud or iCloud Drive isn't enabled in Settings.") + return NSLocalizedString("alert.error.cloudkit-missing", comment: "Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Settings.") case .userNameAndPasswordRequired: - return String(localized: "Username and password required", comment: "Error message: The user must provide a username and password.") + return NSLocalizedString("alert.error.username-and-password-required", comment: "Error message: The user must provide a username and password.") + case .userNamePasswordAndURLRequired: + return NSLocalizedString("alert.error.username-password-url-required", comment: "The user must provide a username, password, and URL.") + case .userNameRequired: + return NSLocalizedString("alert.error.username-required", comment: "Username required.") case .invalidUsernameOrPassword: - return String(localized: "Invalid username or password", comment: "Error message: The user provided an invalid username or password.") + return NSLocalizedString("alert.error.invalid-username-or-password", comment: "Error message: The user provided an invalid username or password.") + case .invalidURL: + return NSLocalizedString("alert.error.invalid-api-url", comment: "Invalid API URL.") case .keychainError: - return String(localized: "Keychain error while storing credentials.", comment: "Error message: Unable to save due a Keychain error.") + return NSLocalizedString("alert.error.keychain-error", comment: "Error message: Unable to save due a Keychain error.") case .duplicateDefaultTheme: - return String(localized: "You cannot import a theme that shares the same name as a provided theme.", comment: "Error message: cannot import theme as this is a duplicate of a provided theme.") + return NSLocalizedString("alert.error.theme-duplicate-of-provided", comment: "Error message: This theme shares the same name as a provided theme and cannot be imported.") + case .networkError: + return NSLocalizedString("alert.error.network-error", comment: "Network error. Please try later.") + case .unrecognizedAccount: + return NSLocalizedString("alert.error.unrecognized-account", comment: "The account type in invalid.") } } } diff --git a/Shared/SmartFeeds/SearchFeedDelegate.swift b/Shared/SmartFeeds/SearchFeedDelegate.swift index 246ccca75..23f14ffdf 100644 --- a/Shared/SmartFeeds/SearchFeedDelegate.swift +++ b/Shared/SmartFeeds/SearchFeedDelegate.swift @@ -22,7 +22,7 @@ struct SearchFeedDelegate: SmartFeedDelegate { return nameForDisplayPrefix + searchString } - let nameForDisplayPrefix = NSLocalizedString("Search: ", comment: "Search smart feed title prefix") + let nameForDisplayPrefix = NSLocalizedString("textfield.placeholder.search", comment: "Search: ") let searchString: String let fetchType: FetchType var smallIcon: IconImage? = AppAssets.searchFeedImage diff --git a/Shared/SmartFeeds/SearchTimelineFeedDelegate.swift b/Shared/SmartFeeds/SearchTimelineFeedDelegate.swift index c186fa838..a1ca0bf53 100644 --- a/Shared/SmartFeeds/SearchTimelineFeedDelegate.swift +++ b/Shared/SmartFeeds/SearchTimelineFeedDelegate.swift @@ -22,7 +22,7 @@ struct SearchTimelineFeedDelegate: SmartFeedDelegate { return nameForDisplayPrefix + searchString } - let nameForDisplayPrefix = NSLocalizedString("Search: ", comment: "Search smart feed title prefix") + let nameForDisplayPrefix = NSLocalizedString("textfield.placeholder.search", comment: "Search: ") let searchString: String let fetchType: FetchType var smallIcon: IconImage? = AppAssets.searchFeedImage diff --git a/Shared/Timer/AccountRefreshTimer.swift b/Shared/Timer/AccountRefreshTimer.swift index 04c944a5a..c60e9eade 100644 --- a/Shared/Timer/AccountRefreshTimer.swift +++ b/Shared/Timer/AccountRefreshTimer.swift @@ -73,8 +73,8 @@ class AccountRefreshTimer { lastTimedRefresh = Date() update() - //AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log) - AccountManager.shared.refreshAll(completion: nil) + + AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log, completion: nil) } } diff --git a/Shared/Timer/RefreshInterval.swift b/Shared/Timer/RefreshInterval.swift index 603ed2cac..178462d9d 100644 --- a/Shared/Timer/RefreshInterval.swift +++ b/Shared/Timer/RefreshInterval.swift @@ -41,19 +41,19 @@ enum RefreshInterval: Int, CaseIterable, Identifiable { func description() -> String { switch self { case .manually: - return NSLocalizedString("Manually", comment: "Manually") + return NSLocalizedString("button.title.manually", comment: "Manually") case .every10Minutes: - return NSLocalizedString("Every 10 Minutes", comment: "Every 10 Minutes") + return NSLocalizedString("button.title.10-minutes", comment: "Every 10 Minutes") case .every30Minutes: - return NSLocalizedString("Every 30 Minutes", comment: "Every 30 Minutes") + return NSLocalizedString("button.title.30-minutes", comment: "Every 30 Minutes") case .everyHour: - return NSLocalizedString("Every Hour", comment: "Every Hour") + return NSLocalizedString("button.title.every-hour", comment: "Every Hour") case .every2Hours: - return NSLocalizedString("Every 2 Hours", comment: "Every 2 Hours") + return NSLocalizedString("button.title.2-hours", comment: "Every 2 Hours") case .every4Hours: - return NSLocalizedString("Every 4 Hours", comment: "Every 4 Hours") + return NSLocalizedString("button.title.4-hours", comment: "Every 4 Hours") case .every8Hours: - return NSLocalizedString("Every 8 Hours", comment: "Every 8 Hours") + return NSLocalizedString("button.title.8-hours", comment: "Every 8 Hours") } } diff --git a/Shared/UserNotifications/UserNotificationManager.swift b/Shared/UserNotifications/UserNotificationManager.swift index 2a20f693b..231641caa 100644 --- a/Shared/UserNotifications/UserNotificationManager.swift +++ b/Shared/UserNotifications/UserNotificationManager.swift @@ -88,9 +88,9 @@ private extension UserNotificationManager { } func registerCategoriesAndActions() { - let readAction = UNNotificationAction(identifier: "MARK_AS_READ", title: NSLocalizedString("Mark as Read", comment: "Mark as Read"), options: []) - let starredAction = UNNotificationAction(identifier: "MARK_AS_STARRED", title: NSLocalizedString("Mark as Starred", comment: "Mark as Starred"), options: []) - let openAction = UNNotificationAction(identifier: "OPEN_ARTICLE", title: NSLocalizedString("Open", comment: "Open"), options: [.foreground]) + let readAction = UNNotificationAction(identifier: "MARK_AS_READ", title: NSLocalizedString("button.title.mark-as-read", comment: "Mark as Read"), options: []) + let starredAction = UNNotificationAction(identifier: "MARK_AS_STARRED", title: NSLocalizedString("button.title.mark-as-starred", comment: "Mark as Starred"), options: []) + let openAction = UNNotificationAction(identifier: "OPEN_ARTICLE", title: NSLocalizedString("button.title.open", comment: "Open"), options: [.foreground]) let newArticleCategory = UNNotificationCategory(identifier: "NEW_ARTICLE_NOTIFICATION_CATEGORY", diff --git a/iOS/Inspector/AccountInspectorView.swift b/iOS/Inspector/AccountInspectorView.swift index c0bfa9a1a..c67450946 100644 --- a/iOS/Inspector/AccountInspectorView.swift +++ b/iOS/Inspector/AccountInspectorView.swift @@ -124,7 +124,7 @@ struct AccountInspectorView: View { } } message: { - Text("alert.message.cannot-undo-action", comment: "This action cannot be undone.") + Text("alert.message.cannot-undo-action", comment: "You can't undo this action.") } } } diff --git a/iOS/Inspector/ExtensionInspectorView.swift b/iOS/Inspector/ExtensionInspectorView.swift index 5a2b169b0..15e85ce3c 100644 --- a/iOS/Inspector/ExtensionInspectorView.swift +++ b/iOS/Inspector/ExtensionInspectorView.swift @@ -43,7 +43,7 @@ struct ExtensionInspectorView: View { Text("button.title.cancel", comment: "Cancel") } } message: { - Text("alert.message.cannot-undo-action", comment: "This action cannot be undone.") + Text("alert.message.cannot-undo-action", comment: "You can't undo this action.") } Spacer() } diff --git a/iOS/IntentsExtension/en.lproj/Localizable.strings b/iOS/IntentsExtension/en.lproj/Localizable.strings new file mode 100644 index 000000000..e1ecae87c --- /dev/null +++ b/iOS/IntentsExtension/en.lproj/Localizable.strings @@ -0,0 +1,3 @@ +/* Unable to communicate with NetNewsWire. */ +"errordescription.localized.communication-failure" = "Unable to communicate with NetNewsWire."; + diff --git a/iOS/Resources/en.lproj/Localizable.strings b/iOS/Resources/en.lproj/Localizable.strings index 00f54f09d..adae7015c 100644 --- a/iOS/Resources/en.lproj/Localizable.strings +++ b/iOS/Resources/en.lproj/Localizable.strings @@ -22,12 +22,45 @@ /* Open in Browser */ "activity.title.open-in-browser" = "Open in Browser"; +/* Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Settings. */ +"alert.error.cloudkit-missing" = "Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Settings."; + +/* There is already an account of that type with that username created. */ +"alert.error.duplicate-account-username" = "There is already an account of that type with that username created."; + +/* Invalid API URL. */ +"alert.error.invalid-api-url" = "Invalid API URL."; + +/* Error message: The user provided an invalid username or password. */ +"alert.error.invalid-username-or-password" = "A username or password is required."; + +/* Error message: Unable to save due a Keychain error. */ +"alert.error.keychain-error" = "Unable to save account credentials due to a Keychain error."; + +/* Network error. Please try later. */ +"alert.error.network-error" = "A network error has occurred. Please try later."; + +/* Error message: This theme shares the same name as a provided theme and cannot be imported. */ +"alert.error.theme-duplicate-of-provided" = "This theme shares the same name as a provided theme and cannot be imported."; + +/* The account type in invalid. */ +"alert.error.unrecognized-account" = "The account type is not recognized."; + +/* Error message: The user must provide a username and password. */ +"alert.error.username-and-password-required" = "A username and password are required."; + +/* The user must provide a username, password, and URL. */ +"alert.error.username-password-url-required" = "A username, password, and API URL are required."; + +/* Username required. */ +"alert.error.username-required" = "A username is required."; + /* The variable is the author's home page. In English, the alert message is: Author‘s website:\n%@ */ "alert.message.author-website.%@" = "Author's website:\n%@"; /* The action cannot be undone. - This action cannot be undone. */ -"alert.message.cannot-undo-action" = "This action cannot be undone."; + You can't undo this action. */ +"alert.message.cannot-undo-action" = "You can't undo this action."; /* Asks the user for confirmation that they wish to delete a folder. The variable provided is the folder name. In English, the message is: Are you sure you want to delete the “%@” feed? */ "alert.message.delete-feed.%@" = "Are you sure you want to delete the “%@” feed?"; @@ -78,6 +111,9 @@ /* Are you sure you want to deactivate “%@“? */ "alert.title.deactivate-extension.%@" = "Are you sure you want to deactivate “%@“?"; +/* Are you sure you want to deactivate the %@ extension “%@“? Note: the ordering of the variables is extension type, extension name. */ +"alert.title.deactive-extension.%@.%@" = "Are you sure you want to deactivate the %@ extension “%@“? "; + /* Delete folder */ "alert.title.delete-folder" = "Delete Folder"; @@ -200,12 +236,12 @@ /* Deactivate */ "button.title.deactivate" = "Deactivate"; -/* Deactivate Extension */ -"button.title.deactivate-extension" = "Deactivate Extension"; - /* Deactivate Account */ "button.title.deactivate-account" = "Deactivate Account"; +/* Deactivate Extension */ +"button.title.deactivate-extension" = "Deactivate Extension"; + /* Button title: Default */ "button.title.default" = "Default"; @@ -451,15 +487,15 @@ /* Toggle Read Articles Filter */ "keyboard.command.toggle-read-articles-filter" = "Toggle Read Articles Filter"; -/* Toggle Reader View */ -"keyboard.command.toggle-reader-view" = "Toggle Reader View"; - /* Toggle Read Feeds Filter */ "keyboard.command.toggle-read-feeds-filter" = "Toggle Read Feeds Filter"; /* Toggle Read Status */ "keyboard.command.toggle-read-status" = "Toggle Read Status"; +/* Toggle Reader View */ +"keyboard.command.toggle-reader-view" = "Toggle Reader View"; + /* Toggle Sidebar */ "keyboard.command.toggle-sidebar" = "Toggle Sidebar"; @@ -671,6 +707,9 @@ /* Enter Name */ "navigation.title.enter-name" = "Enter Name"; +/* Enter Search */ +"navigation.title.enter-search" = "Enter Search"; + /* Manage Accounts */ "navigation.title.manage-accounts" = "Manage Accounts"; diff --git a/iOS/Settings/Account and Extensions/Extensions/ExtensionsManagementView.swift b/iOS/Settings/Account and Extensions/Extensions/ExtensionsManagementView.swift index 136b54a73..92b0c9b71 100644 --- a/iOS/Settings/Account and Extensions/Extensions/ExtensionsManagementView.swift +++ b/iOS/Settings/Account and Extensions/Extensions/ExtensionsManagementView.swift @@ -33,7 +33,7 @@ struct ExtensionsManagementView: View { .sheet(isPresented: $showAddExtensionView) { AddExtensionListView() } - .alert(Text("alert.title.deactive-extension.\(extensionToDeactivate?.value.extensionPointID.extensionPointType.title ?? "").\(extensionToDeactivate?.value.title ?? "")", comment: "Are you sure you want to deactivate the %@ extension “%@“? Note: the ordering of the variables is "), + .alert(Text("alert.title.deactive-extension.\(extensionToDeactivate?.value.extensionPointID.extensionPointType.title ?? "").\(extensionToDeactivate?.value.title ?? "")", comment: "Are you sure you want to deactivate the %@ extension “%@“? Note: the ordering of the variables is extension type, extension name."), isPresented: $showDeactivateAlert) { Button(role: .destructive) { @@ -49,7 +49,7 @@ struct ExtensionsManagementView: View { } } message: { - Text("alert.message.cannot-undo-action", comment: "This action cannot be undone.") + Text("alert.message.cannot-undo-action", comment: "You can't undo this action.") } .onReceive(NotificationCenter.default.publisher(for: .ActiveExtensionPointsDidChange)) { _ in availableExtensionPointTypes = ExtensionPointManager.shared.availableExtensionPointTypes.sorted(by: { $0.title < $1.title }) diff --git a/iOS/Settings/Appearance/ArticleThemeManagerView.swift b/iOS/Settings/Appearance/ArticleThemeManagerView.swift index 9cafb9a0c..b7d7cbea5 100644 --- a/iOS/Settings/Appearance/ArticleThemeManagerView.swift +++ b/iOS/Settings/Appearance/ArticleThemeManagerView.swift @@ -83,7 +83,7 @@ struct ArticleThemeManagerView: View { Text("button.title.cancel", comment: "Cancel") } }, message: { - Text("alert.message.cannot-undo-action", comment: "This action cannot be undone.") + Text("alert.message.cannot-undo-action", comment: "You can't undo this action.") }) .alert(Text("alert.title.import-theme", comment: "Import Theme"), isPresented: $showImportConfirmationAlert.1, diff --git a/iOS/ShareExtension/ShareViewController.swift b/iOS/ShareExtension/ShareViewController.swift index 51829d4ee..2e94279fa 100644 --- a/iOS/ShareExtension/ShareViewController.swift +++ b/iOS/ShareExtension/ShareViewController.swift @@ -31,9 +31,9 @@ class ShareViewController: SLComposeServiceViewController, ShareFolderPickerCont } title = "NetNewsWire" - placeholder = "Feed Name (Optional)" + placeholder = NSLocalizedString("textfield.placeholder.feed-name", comment: "Feed Name (Optional)") if let button = navigationController?.navigationBar.topItem?.rightBarButtonItem { - button.title = "Add Feed" + button.title = NSLocalizedString("button.title.add-feed", comment: "Add Feed") button.isEnabled = true }