Localized mac app

This commit is contained in:
Stuart Breckenridge
2023-01-04 12:33:38 +08:00
parent 2bca08195e
commit e670d610e3
61 changed files with 1051 additions and 300 deletions

View File

@@ -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 = {

View File

@@ -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("Authors 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)
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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() {

View File

@@ -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 {

View File

@@ -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")!)

View File

@@ -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")

View File

@@ -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("Cant add this feed because youve 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: "Cant add this feed because youve 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("Cant add this feed because of a download error: “%@", comment: "Feed finder")
let formatString = NSLocalizedString("alert.message.download-error.%@", comment: "Cant 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("Cant 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: "Cant 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() {

View File

@@ -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)..<screenName.endIndex])
}
typeDescriptionLabel.stringValue = NSLocalizedString("Tweets from @\(screenSearch!)", comment: "Home Timeline")
let tweets = NSLocalizedString("label.text.tweets-from.%@", comment: "Tweets from @%@")
typeDescriptionLabel.stringValue = String(format: tweets, screenSearch!)
} else {
typeDescriptionLabel.stringValue = ""
}
screenSearchTextField.placeholderString = NSLocalizedString("@name", comment: "@name")
screenSearchTextField.placeholderString = NSLocalizedString("textfield.placeholder.twitter-username", comment: "@name")
screenSearchTextField.isHidden = false
addButton.isEnabled = !screenSearchTextField.stringValue.isEmpty
@@ -171,12 +172,13 @@ private extension AddTwitterFeedWindowController {
accountPopupButton.isHidden = true
if !screenSearchTextField.stringValue.isEmpty {
typeDescriptionLabel.stringValue = NSLocalizedString("Tweets that contain \(screenSearchTextField.stringValue)", comment: "Home Timeline")
let tweets = NSLocalizedString("label.text.tweets-containing.%@", comment: "Tweets that contain %@")
typeDescriptionLabel.stringValue = String(format: tweets, screenSearchTextField.stringValue)
} else {
typeDescriptionLabel.stringValue = ""
}
screenSearchTextField.placeholderString = NSLocalizedString("Search Term or #hashtag", comment: "Search Term")
screenSearchTextField.placeholderString = NSLocalizedString("textfield.placeholder.search-term-hashtag", comment: "Search Term or #hashtag")
screenSearchTextField.isHidden = false
addButton.isEnabled = !screenSearchTextField.stringValue.isEmpty

View File

@@ -148,7 +148,7 @@ private extension AddRedditFeedWindowController {
animateShowHideFields(collapsed: false) {
self.accountLabel.isHidden = false
self.accountPopupButton.isHidden = false
self.typeDescriptionLabel.stringValue = NSLocalizedString("Your personal Reddit frontpage", comment: "Home")
self.typeDescriptionLabel.stringValue = NSLocalizedString("label.text.reddit-front-page", comment: "Your personal Reddit frontpage")
self.subredditTextField.isHidden = true
self.addButton.isEnabled = true
}
@@ -157,7 +157,7 @@ private extension AddRedditFeedWindowController {
accountLabel.isHidden = true
accountPopupButton.isHidden = true
typeDescriptionLabel.stringValue = NSLocalizedString("The best posts on Reddit for you", comment: "Popular")
typeDescriptionLabel.stringValue = NSLocalizedString("label.text.reddit-best-posts", comment: "The best posts on Reddit for you")
subredditTextField.isHidden = true
addButton.isEnabled = true
animateShowHideFields(collapsed: true)
@@ -166,7 +166,7 @@ private extension AddRedditFeedWindowController {
accountLabel.isHidden = true
accountPopupButton.isHidden = true
typeDescriptionLabel.stringValue = NSLocalizedString("The most active posts", comment: "All")
typeDescriptionLabel.stringValue = NSLocalizedString("label.text.reddit-active-posts", comment: "The most active posts")
subredditTextField.isHidden = true
addButton.isEnabled = true
animateShowHideFields(collapsed: true)
@@ -178,12 +178,13 @@ private extension AddRedditFeedWindowController {
self.accountPopupButton.isHidden = true
if !self.subredditTextField.stringValue.isEmpty {
self.typeDescriptionLabel.stringValue = NSLocalizedString("Posts from r/\(self.subredditTextField.stringValue)", comment: "Subreddit")
let subreddit = NSLocalizedString("label.text.posts-from-subreddit.%@", comment: "Posts from r/%@")
self.typeDescriptionLabel.stringValue = String(format: subreddit, self.subredditTextField.stringValue)
} else {
self.typeDescriptionLabel.stringValue = ""
}
self.subredditTextField.placeholderString = NSLocalizedString("Subreddit", comment: "Search Term")
self.subredditTextField.placeholderString = NSLocalizedString("label.text.subreddit", comment: "Subreddit")
self.subredditTextField.isHidden = false
self.addButton.isEnabled = !self.subredditTextField.stringValue.isEmpty
}

View File

@@ -45,13 +45,13 @@ class ArticleExtractorButton: NSButton {
override func accessibilityLabel() -> 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")
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}

View File

@@ -20,8 +20,8 @@ extension URLPasteboardWriter {
if urlStrings.contains(nil), !AppDefaults.shared.hasSeenNotAllArticlesHaveURLsAlert {
let alert = NSAlert()
alert.messageText = NSLocalizedString("Some articles dont 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 dont 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)

View File

@@ -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.")
}
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}
}
}

View File

@@ -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:

View File

@@ -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("Dont 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: "Dont 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("Dont have an InoReader account?", comment: "No InoReader")
noAccountTextField.stringValue = NSLocalizedString("label.text.no-inoreader", comment: "Dont 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("Dont have a BazQux account?", comment: "No BazQux")
noAccountTextField.stringValue = NSLocalizedString("label.text.no-bazqux", comment: "Dont 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("Dont have a The Old Reader account?", comment: "No OldReader")
noAccountTextField.stringValue = NSLocalizedString("label.text.no-old-reader", comment: "Dont 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
}
}

View File

@@ -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
}))

View File

@@ -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)

View File

@@ -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)

View File

@@ -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))

View File

@@ -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")!)

View File

@@ -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

View File

@@ -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";
/* Cant add this feed because youve already subscribed to it. */
"alert.message.already-subscribed" = "Cant add this feed because youve already subscribed to it.";
/* Some articles dont have links, so they weren't copied. */
"alert.message.articles-without-links" = "Some articles dont 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?";
/* Cant add this feed because of a download error: “%@” */
"alert.message.download-error.%@" = "Cant 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.";
/* Cant add a feed because no feed was found. */
"alert.message.feed-not-found" = "Cant 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.";
/* Dont have a BazQux account? */
"label.text.no-bazqux" = "Dont have a BazQux account?";
/* Dont have a FreshRSS instance? */
"label.text.no-fresh-rss" = "Dont have a FreshRSS instance?";
/* Dont have an InoReader account? */
"label.text.no-inoreader" = "Dont have an InoReader account?";
/* Dont have a The Old Reader account? */
"label.text.no-old-reader" = "Dont 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";

View File

@@ -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 = "<group>"; };
DFB349A1294E90B500BC81AD /* FeedbinAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAddAccountView.swift; sourceTree = "<group>"; };
DFB349A3294E914D00BC81AD /* AccountSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSectionHeader.swift; sourceTree = "<group>"; };
DFB616A82965300400A359AB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
DFB616AB2965300400A359AB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
DFBB4EAB2951BC0200639228 /* NNWThemeDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNWThemeDocument.swift; sourceTree = "<group>"; };
DFBB4EAF2951BCAC00639228 /* ArticleThemeManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleThemeManagerView.swift; sourceTree = "<group>"; };
DFC14F0E28EA55BD00F6EE86 /* AboutWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutWindowController.swift; sourceTree = "<group>"; };
@@ -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 = "<group>";
};
DFB616A72965300400A359AB /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
DFB616A82965300400A359AB /* en */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
DFB616AA2965300400A359AB /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
DFB616AB2965300400A359AB /* en */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
DFD86798295D553D0070D62D /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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")
}
}

View File

@@ -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?

View File

@@ -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? {

View File

@@ -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 {

View File

@@ -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.")
}
}
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.")
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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")
}
}

View File

@@ -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",

View File

@@ -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.")
}
}
}

View File

@@ -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()
}

View File

@@ -0,0 +1,3 @@
/* Unable to communicate with NetNewsWire. */
"errordescription.localized.communication-failure" = "Unable to communicate with NetNewsWire.";

View File

@@ -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: Authors 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";

View File

@@ -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 })

View File

@@ -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,

View File

@@ -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
}