diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index c03c7b408..f8f02a349 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -836,7 +836,6 @@ DF28B453294FE6C600C4D8CA /* EnableExtensionPointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF28B452294FE6C600C4D8CA /* EnableExtensionPointView.swift */; }; DF28B455294FE74A00C4D8CA /* ExtensionSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF28B454294FE74A00C4D8CA /* ExtensionSectionHeader.swift */; }; DF28B4572950163F00C4D8CA /* EnableExtensionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF28B4562950163F00C4D8CA /* EnableExtensionViewModel.swift */; }; - DF32ABEB29494CF1008E3A12 /* Settings.strings in Resources */ = {isa = PBXBuildFile; fileRef = DF32ABEA29494CF0008E3A12 /* Settings.strings */; }; DF3630EB2936183D00326FB8 /* OPMLDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3630EA2936183D00326FB8 /* OPMLDocument.swift */; }; DF3630EC2936183D00326FB8 /* OPMLDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3630EA2936183D00326FB8 /* OPMLDocument.swift */; }; DF3630ED2936183D00326FB8 /* OPMLDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3630EA2936183D00326FB8 /* OPMLDocument.swift */; }; @@ -851,17 +850,12 @@ DF84E563295122BA0045C334 /* TimelineCustomizerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF84E562295122BA0045C334 /* TimelineCustomizerView.swift */; }; DFB3497A294A962D00BC81AD /* AddAccountListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34979294A962D00BC81AD /* AddAccountListView.swift */; }; DFB34980294B085100BC81AD /* AccountInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3497F294B085100BC81AD /* AccountInspectorView.swift */; }; - DFB34982294B0B9B00BC81AD /* Inspector.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB34981294B0B9B00BC81AD /* Inspector.strings */; }; - DFB34984294B3AFF00BC81AD /* Buttons.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB34983294B3AFF00BC81AD /* Buttons.strings */; }; DFB34988294B447F00BC81AD /* InjectedNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34987294B447F00BC81AD /* InjectedNavigationView.swift */; }; DFB3498A294B45AC00BC81AD /* ExtensionInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34989294B45AC00BC81AD /* ExtensionInspectorView.swift */; }; DFB3498C294B4CA700BC81AD /* WebFeedInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3498B294B4CA700BC81AD /* WebFeedInspectorView.swift */; }; - DFB34993294C0B7400BC81AD /* Account.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB34992294C0B7400BC81AD /* Account.strings */; }; DFB34994294C0E3900BC81AD /* ReaderAPIAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34990294C0B2200BC81AD /* ReaderAPIAddAccountView.swift */; }; DFB34996294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34995294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift */; }; DFB34997294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB34995294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift */; }; - DFB34999294C4F1D00BC81AD /* Errors.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB34998294C4F1D00BC81AD /* Errors.strings */; }; - DFB3499A294C4F1D00BC81AD /* Errors.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFB34998294C4F1D00BC81AD /* Errors.strings */; }; DFB3499E294C5D5000BC81AD /* CloudKitAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3499D294C5D5000BC81AD /* CloudKitAddAccountView.swift */; }; DFB349A0294E87B700BC81AD /* LocalAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB3499F294E87B700BC81AD /* LocalAddAccountView.swift */; }; DFB349A2294E90B500BC81AD /* FeedbinAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB349A1294E90B500BC81AD /* FeedbinAddAccountView.swift */; }; @@ -1602,7 +1596,6 @@ DF28B452294FE6C600C4D8CA /* EnableExtensionPointView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionPointView.swift; sourceTree = ""; }; DF28B454294FE74A00C4D8CA /* ExtensionSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionSectionHeader.swift; sourceTree = ""; }; DF28B4562950163F00C4D8CA /* EnableExtensionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionViewModel.swift; sourceTree = ""; }; - DF32ABEA29494CF0008E3A12 /* Settings.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Settings.strings; sourceTree = ""; }; DF3630EA2936183D00326FB8 /* OPMLDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLDocument.swift; sourceTree = ""; }; DF3630EE293618A900326FB8 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = ""; }; DF394EFF29357A180081EB6E /* NewArticleNotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewArticleNotificationsView.swift; sourceTree = ""; }; @@ -1614,15 +1607,11 @@ DF84E562295122BA0045C334 /* TimelineCustomizerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineCustomizerView.swift; sourceTree = ""; }; DFB34979294A962D00BC81AD /* AddAccountListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountListView.swift; sourceTree = ""; }; DFB3497F294B085100BC81AD /* AccountInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountInspectorView.swift; sourceTree = ""; }; - DFB34981294B0B9B00BC81AD /* Inspector.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Inspector.strings; sourceTree = ""; }; - DFB34983294B3AFF00BC81AD /* Buttons.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Buttons.strings; sourceTree = ""; }; DFB34987294B447F00BC81AD /* InjectedNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedNavigationView.swift; sourceTree = ""; }; DFB34989294B45AC00BC81AD /* ExtensionInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionInspectorView.swift; sourceTree = ""; }; DFB3498B294B4CA700BC81AD /* WebFeedInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebFeedInspectorView.swift; sourceTree = ""; }; DFB34990294C0B2200BC81AD /* ReaderAPIAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderAPIAddAccountView.swift; sourceTree = ""; }; - DFB34992294C0B7400BC81AD /* Account.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Account.strings; sourceTree = ""; }; DFB34995294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedNetNewsWireError.swift; sourceTree = ""; }; - DFB34998294C4F1D00BC81AD /* Errors.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Errors.strings; sourceTree = ""; }; DFB3499D294C5D5000BC81AD /* CloudKitAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitAddAccountView.swift; sourceTree = ""; }; DFB3499F294E87B700BC81AD /* LocalAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAddAccountView.swift; sourceTree = ""; }; DFB349A1294E90B500BC81AD /* FeedbinAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAddAccountView.swift; sourceTree = ""; }; @@ -2969,11 +2958,6 @@ DFB34985294B3B0800BC81AD /* Localizations */ = { isa = PBXGroup; children = ( - DFB34992294C0B7400BC81AD /* Account.strings */, - DFB34983294B3AFF00BC81AD /* Buttons.strings */, - DFB34998294C4F1D00BC81AD /* Errors.strings */, - DFB34981294B0B9B00BC81AD /* Inspector.strings */, - DF32ABEA29494CF0008E3A12 /* Settings.strings */, DFB34995294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift */, ); path = Localizations; @@ -3575,25 +3559,20 @@ 51C452862265093600C03939 /* Add.storyboard in Resources */, 511D43EF231FBDE900FB1562 /* LaunchScreenPad.storyboard in Resources */, 511D43D2231FA62C00FB1562 /* GlobalKeyboardShortcuts.plist in Resources */, - DFB34993294C0B7400BC81AD /* Account.strings in Resources */, 84C9FCA12262A1B300D921D6 /* Main.storyboard in Resources */, 51BB7C312335ACDE008E8144 /* page.html in Resources */, - DFB34982294B0B9B00BC81AD /* Inspector.strings in Resources */, 512392C324E3451400F11704 /* TwitterAdd.storyboard in Resources */, 51077C5A27A86D16000C71DB /* Hyperlegible.nnwtheme in Resources */, DDF9E1D928EDF2FC000BC355 /* notificationSoundBlip.mp3 in Resources */, - DF32ABEB29494CF1008E3A12 /* Settings.strings in Resources */, 51DEE81A26FBFF84006DAA56 /* Promenade.nnwtheme in Resources */, 1768140B2564BB8300D98635 /* NetNewsWire_iOSwidgetextension_target.xcconfig in Resources */, 5103A9B424216A4200410853 /* blank.html in Resources */, - DFB3499A294C4F1D00BC81AD /* Errors.strings in Resources */, 51D0214826ED617100FF2E0F /* core.css in Resources */, 84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */, 511D43D1231FA62800FB1562 /* SidebarKeyboardShortcuts.plist in Resources */, 51C452AB22650DC600C03939 /* template.html in Resources */, 84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */, B27EEBFB244D15F3000932E6 /* stylesheet.css in Resources */, - DFB34984294B3AFF00BC81AD /* Buttons.strings in Resources */, 511D43CF231FA62200FB1562 /* DetailKeyboardShortcuts.plist in Resources */, 49F40DF92335B71000552BF4 /* newsfoot.js in Resources */, 512392C024E33A3C00F11704 /* RedditAdd.storyboard in Resources */, @@ -3628,7 +3607,6 @@ DFCE4F9128EF26F100405869 /* About.plist in Resources */, 84C9FC8C22629E8F00D921D6 /* KeyboardShortcuts.html in Resources */, B27EEBF9244D15F3000932E6 /* stylesheet.css in Resources */, - DFB34999294C4F1D00BC81AD /* Errors.strings in Resources */, 5144EA3B227A379E00D19003 /* ImportOPMLSheet.xib in Resources */, 844B5B691FEA20DF00C7C76A /* SidebarKeyboardShortcuts.plist in Resources */, DDF9E1D728EDF2FC000BC355 /* notificationSoundBlip.mp3 in Resources */, diff --git a/Shared/Localizations/Account.strings b/Shared/Localizations/Account.strings deleted file mode 100644 index 385293f21..000000000 --- a/Shared/Localizations/Account.strings +++ /dev/null @@ -1,29 +0,0 @@ -/* - Account.strings - NetNewsWire - - Created by Stuart Breckenridge on 16/12/2022. - Copyright © 2022 Ranchero Software. All rights reserved. -*/ - -/* Account Section */ -"ACCOUNT_NAME" = "Name"; -"CLOUDKIT" = "iCloud"; -"LOCAL_ACCOUNT_NAME_PHONE" = "On My iPhone"; -"LOCAL_ACCOUNT_NAME_PAD" = "On My iPad"; -"ACCOUNT_EMAIL_ADDRESS_PROMPT" = "Email Address"; -"ACCOUNT_USERNAME_OR_EMAIL_PROMPT" = "Username or Email"; -"ACCOUNT_PASSWORD_PROMPT" = "Password"; - - -/* Explainers */ -"LOCAL_FOOTER_EXPLAINER" = "Local accounts do not sync your feeds across devices"; -"BAZQUX_FOOTER_EXPLAINER" = "Sign in to your BazQux account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a BazQux account? [Sign Up Here](https://bazqux.com)"; -"INOREADER_FOOTER_EXPLAINER" = "Sign in to your InoReader account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have an InoReader account? [Sign Up Here](https://www.inoreader.com)"; -"OLDREADER_FOOTER_EXPLAINER" = "Sign in to your The Old Reader account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a The Old Reader account? [Sign Up Here](https://theoldreader.com)"; -"FRESHRSS_FOOTER_EXPLAINER" = "Sign in to your FreshRSS instance and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have an FreshRSS instance? [Sign Up Here](https://freshrss.org)"; -"CLOUDKIT_FOOTER_EXPLAINER" = "NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices."; -"FEEDBIN_FOOTER_EXPLAINER" = "Sign in to your Feedbin account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a Feedbin account? [Sign Up Here](https://feedbin.com/signup)"; -"NEWSBLUR_FOOTER_EXPLAINER" = "Sign in to your NewsBlur account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a NewsBlur account? [Sign Up Here](https://newsblur.com)"; - - diff --git a/Shared/Localizations/Buttons.strings b/Shared/Localizations/Buttons.strings deleted file mode 100644 index 27b69d9fa..000000000 --- a/Shared/Localizations/Buttons.strings +++ /dev/null @@ -1,22 +0,0 @@ -/* - Buttons.strings - NetNewsWire - - Created by Stuart Breckenridge on 15/12/2022. - Copyright © 2022 Ranchero Software. All rights reserved. -*/ - -"CANCEL_BUTTON_TITLE" = "Cancel"; -"DISMISS_BUTTON_TITLE" = "Dismiss"; -"DONE_BUTTON_TITLE" = "Done"; -"REMOVE_ACCOUNT_BUTTON_TITLE" = "Remove Account"; -"DEACTIVATE_BUTTON_TITLE" = "Deactivate"; -"DEACTIVATE_EXTENSION_BUTTON_TITLE" = "Deactivate Extension"; -"CREDENTIALS_BUTTON_TITLE" = "Credentials"; -"ADD_ACCOUNT_BUTTON_TITLE" = "Add Account"; -"ENABLE_EXTENSION_BUTTON_TITLE" = "Enable Extension"; -"UPDATE_CREDENTIALS_BUTTON_TITLE" = "Update Credentials"; -"USE_CLOUDKIT_BUTTON_TITLE" = "Use iCloud"; -"DELETE_THEME_BUTTON_TITLE" = "Delete Theme"; -"IMPORT_THEME_BUTTON_TITLE" = "Import Theme"; -"IMPORT_AND_OVERWRITE_THEME_BUTTON_TITLE" = "Overwrite Existing Theme"; diff --git a/Shared/Localizations/Errors.strings b/Shared/Localizations/Errors.strings deleted file mode 100644 index 40d454828..000000000 --- a/Shared/Localizations/Errors.strings +++ /dev/null @@ -1,15 +0,0 @@ -/* - Errors.strings - NetNewsWire - - Created by Stuart Breckenridge on 16/12/2022. - Copyright © 2022 Ranchero Software. All rights reserved. -*/ - -"ERROR_TITLE" = "Error"; -"DUPLICATE_ACCOUNT_ERROR" = "There is already an account of that type with that username created."; -"CLOUDKIT_NOT_ENABLED_ERROR" = "Unable to add iCloud Account. Please make sure you have iCloud and iCloud Drive enabled in System Settings."; -"USERNAME_AND_PASSWORD_REQUIRED" = "Username and password required."; -"INVALID_USERNAME_PASSWORD" = "Invalid email or password combination."; -"KEYCHAIN_ERROR" = "Keychain error while storing credentials."; -"DUPLICATE_DEFAULT_THEME" = "You cannot import a theme that shares the same name as a provided theme."; diff --git a/Shared/Localizations/Inspector.strings b/Shared/Localizations/Inspector.strings deleted file mode 100644 index 94addd18a..000000000 --- a/Shared/Localizations/Inspector.strings +++ /dev/null @@ -1,22 +0,0 @@ -/* - Inspector.strings - NetNewsWire - - Created by Stuart Breckenridge on 15/12/2022. - Copyright © 2022 Ranchero Software. All rights reserved. -*/ - -/* Account Inspector */ -"ACCOUNT_NAME_FIELD" = "Account Name"; -"ACTIVE" = "Active"; -"CLOUDKIT_LIMITATIONS_LINK" = "[iCloud Syncing Limitations & Solutions](https://netnewswire.com/help/iCloud)"; -"REMOVE_ACCOUNT_TITLE" = "Remove Account"; -"REMOVE_FEEDLY_MESSAGE" = "Are you sure you want to remove this account? NetNewsWire will no longer be able to access articles and feeds unless the account is added again."; -"REMOVE_ACCOUNT_MESSAGE" = "Are you sure you want to remove this account? This cannot be undone."; -"REMOVE_ACCOUNT_TITLE" = "Remove Account"; - - -"NOTIFY_ABOUT_NEW_ARTICLES" = "Notify About New Articles"; -"ALWAYS_SHOW_READER_VIEW" = "Always Show Reader View"; -"HOME_PAGE" = "Home Page"; -"FEED_URL" = "Feed URL"; diff --git a/Shared/Localizations/LocalizedNetNewsWireError.swift b/Shared/Localizations/LocalizedNetNewsWireError.swift index 18a03ab34..46ff38945 100644 --- a/Shared/Localizations/LocalizedNetNewsWireError.swift +++ b/Shared/Localizations/LocalizedNetNewsWireError.swift @@ -30,17 +30,17 @@ public enum LocalizedNetNewsWireError: LocalizedError { public var errorDescription: String? { switch self { case .duplicateAccount: - return Bundle.main.localizedString(forKey: "DUPLICATE_ACCOUNT_ERROR", value: nil, table: "Errors") + return String(localized: "There is already an account of that type with that username created.", comment: "Error message: duplicate account with same username.") case .iCloudDriveMissing: - return Bundle.main.localizedString(forKey: "CLOUDKIT_NOT_ENABLED_ERROR", value: nil, table: "Errors") + 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.") case .userNameAndPasswordRequired: - return Bundle.main.localizedString(forKey: "USERNAME_AND_PASSWORD_REQUIRED", value: nil, table: "Errors") + return String(localized: "Username and password required", comment: "Error message: The user must provide a username and password.") case .invalidUsernameOrPassword: - return Bundle.main.localizedString(forKey: "INVALID_USERNAME_PASSWORD", value: nil, table: "Errors") + return String(localized: "Invalid username or password", comment: "Error message: The user provided an invalid username or password.") case .keychainError: - return Bundle.main.localizedString(forKey: "KEYCHAIN_ERROR", value: nil, table: "Errors") + return String(localized: "Keychain error while storing credentials.", comment: "Error message: Unable to save due a Keychain error.") case .duplicateDefaultTheme: - return Bundle.main.localizedString(forKey: "DUPLICATE_DEFAULT_THEME", value: nil, table: "Errors") + 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.") } } } diff --git a/Shared/Localizations/Settings.strings b/Shared/Localizations/Settings.strings deleted file mode 100644 index 03978d47e..000000000 --- a/Shared/Localizations/Settings.strings +++ /dev/null @@ -1,109 +0,0 @@ -/* - Settings.strings - NetNewsWire - - Created by Stuart Breckenridge on 14/12/2022. - Copyright © 2022 Ranchero Software. All rights reserved. -*/ - -/* Settings */ - -"SETTINGS_TITLE" = "Settings"; -"DEVICE_PERMISSIONS_HEADER" = "Device Permissions"; -"DEVICE_PERMISSIONS_FOOTER" = "Configure NetNewsWire's access to Siri, background app refresh, mobile data, and more."; -"ACCOUNTS_EXTENSIONS_HEADER" = "Accounts & Extensions"; -"ACCOUNTS_EXTENSIONS_FOOTER" = "Add, delete, enable, or disable accounts and extensions."; -"APPEARANCE_HEADER" = "Appearance"; -"APPEARANCE_FOOTER" = "Manage the look, feel, and behavior of NetNewsWire."; -"DISPLAY_BEHAVIORS_HEADER" = "Display & Behaviors"; - -/* Settings Rows */ - -"OPEN_SYSTEM_SETTINGS" = "Open System Settings"; -"NEW_ARTICLE_NOTIFICATIONS" = "New Article Notifications"; -"MANAGE_ACCOUNTS" = "Manage Accounts"; -"MANAGE_EXTENSIONS" = "Manage Extensions"; -"IMPORT_SUBSCRIPTIONS" = "Import Subscriptions"; -"EXPORT_SUBSCRIPTIONS" = "Export Subscriptions"; -"ABOUT" = "About NetNewsWire"; -"NETNEWSWIRE_HELP" = "NetNewsWire Help Guide"; -"NETNEWSWIRE_WEBSITE" = "NewNewsWire Website"; - -/* Display & Behaviors */ - -"APPLICATION_HEADER" = "Application"; -"TIMELINE_HEADER" = "Timeline"; -"ARTICLE_HEADER" = "Article"; -"ALWAYS_DARK_MODE" = "Always Dark"; -"ALWAYS_LIGHT_MODE" = "Always Light"; -"USE_SYSTEM_DISPLAY_MODE" = "Use System"; -"SORT_OLDEST_NEWEST" = "Sort Oldest to Newest"; -"GROUP_BY_FEED" = "Group by Feed"; -"REFRESH_TO_CLEAR_READ_ARTICLES" = "Refresh to Clear Articles"; -"TIMELINE_LAYOUT" = "Timeline Layout"; -"ICON_SIZE" = "Icon Size"; -"NUMBER_OF_LINES" = "Number of Lines"; -"INSTALLED_THEMES" = "Installed Themes"; -"ARTICLE_THEMES_TITLE" = "Article Themes"; -"ARTICLE_THEME" = "Article Theme"; -"ARTICLE_THEME_CREATOR_%@" = "by %@"; -"IMPORT_THEME_CONFIRMATION_TITLE" = "Import Theme"; -"IMPORT_THEME_CONFIRMATION_MESSAGE_%@_%@" = "Are you sure you want to import “%@” by %@?"; -"IMPORT_AND_OVERWRITE_THEME_CONFIRMATION_MESSAGE_%@" = "The theme “%@” already exists. Do you want to overwrite it?"; -"IMPORT_THEME_SUCCESS_TITLE" = "Import Successful"; -"IMPORT_THEME_SUCCESS_MESSAGE_%@" = "The theme “%@” has been imported."; -"DELETE_THEME_ALERT_TITLE_%@" = "Are you sure you want to delete “%@”?"; -"DELETE_THEME_ALERT_MESSAGE" = "Are you sure you want to delete this theme? This action is not reversible."; -"CONFIRM_MARK_ALL_AS_READ" = "Confirm Mark All as Read"; -"OPEN_LINKS_IN_APP" = "Open Links in NetNewsWire"; -"SMALL_ICON_SIZE" = "Small"; -"MEDIUM_ICON_SIZE" = "Medium"; -"LARGE_ICON_SIZE" = "Large"; - -/* Account Management */ -"ADD_ACCOUNT" = "Add Account"; -"NO_INACTIVE_ACCOUNT_FOOTER" = "There are no inactive accounts."; -"ACCOUNT_REMOVE %@" = "Are you sure you want to remove “%@”?"; -"REMOVE_FEEDLY_CONFIRMATION" = "Are you sure you want to remove this account? NetNewsWire will no longer be able to access articles and feeds unless the account is added again."; -"REMOVE_ACCOUNT_CONFIRMATION" = "Are you sure you want to remove this account? This cannot be undone."; -"ACTIVE_ACCOUNTS_HEADER" = "Active Accounts"; -"INACTIVE_ACCOUNTS_HEADER" = "Inactive Accounts"; - -/* Extension Management */ -"DEACTIVATE" = "Deactivate"; -"DEACTIVATE_EXTENSION_TITLE" = "Deactivate Extension"; -"DEACTIVATE_EXTENSION %@" = "Are you sure you want to deactivate the “%@” extension?"; -"ACTIVE_EXTENSIONS" = "Active Extensions"; -"FEED_PROVIDER_HEADER" = "Feed Provider"; -"FEED_PROVIDER_FOOTER" = "Feed Providers allow you to subscribe to some pages as if they were RSS Feeds."; -"ADD_EXTENSIONS_TITLE" = "Add Extension"; - -/* New Article Notifications */ -"NEW_ARTICLE_NOTIFICATIONS_TITLE" = "New Article Notifications"; - -/* About */ -"ABOUT_TITLE" = "About"; -"PRIMARY_CONTRIBUTORS" = "Primary Contributors"; -"ADDITIONAL_CONTRIBUTORS" = "Additional Contributors"; -"THANKS" = "Thanks"; -"BYLINE" = "By Brent Simmons and the Ranchero Software team."; - -/* Add Account */ -"ADD_LOCAL_ACCOUNT_HEADER" = "Local"; -"ADD_LOCAL_ACCOUNT_FOOTER" = "Local accounts do not sync your feeds across devices"; -"ADD_CLOUDKIT_ACCOUNT_HEADER" = "iCloud"; -"ADD_CLOUDKIT_ACCOUNT_FOOTER" = "Your iCloud account syncs your feeds across your Mac and iOS devices"; -"ADD_WEB_ACCOUNT_HEADER" = "Web"; -"ADD_WEB_ACCOUNT_FOOTER" = "Web accounts sync your feeds across all your devices"; -"ADD_SELFHOSTED_ACCOUNT_HEADER" = "Self-hosted"; -"ADD_SELFHOSTED_ACCOUNT_FOOTER" = "Self-hosted accounts sync your feeds across all your devices"; - - -/* Alerts */ -"IMPORT_OPML_CONFIRMATION" = "Choose an account to receive the imported feeds and folders"; -"IMPORT_OPML_SUCCESS_TITLE" = "Imported Successfully"; -"IMPORT_OPML_SUCCESS_MESSAGE %@" = "Subscriptions have been imported to your %@ account."; -"EXPORT_OPML_CONFIRMATION" = "Choose an account with the subscriptions to export"; -"EXPORT_OPML_SUCCESS_TITLE" = "Exported Successfully"; -"EXPORT_OPML_SUCCESS_MESSAGE" = "Your OPML file has been successfully exported."; -"ERROR_TITLE" = "Error"; diff --git a/iOS/Account/CloudKitAddAccountView.swift b/iOS/Account/CloudKitAddAccountView.swift index 5b4873286..e06cbc591 100644 --- a/iOS/Account/CloudKitAddAccountView.swift +++ b/iOS/Account/CloudKitAddAccountView.swift @@ -21,15 +21,15 @@ struct CloudKitAddAccountView: View { Section { createCloudKitAccount } Section(footer: cloudKitExplainer) {} } - .navigationTitle(Text("CLOUDKIT", tableName: "Account")) + .navigationTitle(Text(verbatim: "iCloud")) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") }) + Button(action: { dismiss() }, label: { Text("Cancel", comment: "Button title") }) } } - .alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountError.1) { - Button(action: {}, label: { Text("DISMISS_BUTTON_TITLE", tableName: "Buttons") }) + .alert(Text("Error", comment: "Alert title: Error"), isPresented: $accountError.1) { + Button(action: {}, label: { Text("Dismiss", comment: "Button title") }) } message: { Text(accountError.0?.localizedDescription ?? "Unknown Error") } @@ -48,7 +48,7 @@ struct CloudKitAddAccountView: View { } label: { HStack { Spacer() - Text("USE_CLOUDKIT_BUTTON_TITLE", tableName: "Buttons") + Text("Use iCloud", comment: "Button title") Spacer() } } @@ -58,9 +58,9 @@ struct CloudKitAddAccountView: View { VStack(spacing: 4) { if !AccountManager.shared.accounts.contains(where: { $0.type == .cloudKit }) { // The explainer is only shown when a CloudKit account doesn't exist. - Text("CLOUDKIT_FOOTER_EXPLAINER", tableName: "Account") + Text("NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices.", comment: "iCloud account explanatory text") } - Text("CLOUDKIT_LIMITATIONS_TITLE", tableName: "Inspector") + Text("[iCloud Syncing Limitations & Solutions](https://netnewswire.com/help/iCloud)", comment: "Link which opens webpage describing iCloud syncing limitations.") }.multilineTextAlignment(.center) } diff --git a/iOS/Account/FeedbinAddAccountView.swift b/iOS/Account/FeedbinAddAccountView.swift index 581bd44ae..0fac486cb 100644 --- a/iOS/Account/FeedbinAddAccountView.swift +++ b/iOS/Account/FeedbinAddAccountView.swift @@ -36,23 +36,23 @@ struct FeedbinAddAccountView: View { } .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") }) + Button(action: { dismiss() }, label: { Text("Cancel", comment: "Button title") }) .disabled(showProgressIndicator) } ToolbarItem(placement: .navigationBarTrailing) { if showProgressIndicator { ProgressView() } } } - .alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountError.1) { + .alert(Text("Error", comment: "Alert title: Error"), isPresented: $accountError.1) { Button(role: .cancel) { // } label: { - Text("DISMISS_BUTTON_TITLE", tableName: "Buttons") + Text("Dismiss", comment: "Button title") } } message: { Text(accountError.0?.localizedDescription ?? "Error") } - .navigationTitle(Text(account?.type.localizedAccountName() ?? "Feedbin")) + .navigationTitle(Text(verbatim: account?.type.localizedAccountName() ?? "Feedbin")) .navigationBarTitleDisplayMode(.inline) .interactiveDismissDisabled(showProgressIndicator) .dismissOnExternalContextLaunch() @@ -62,11 +62,11 @@ struct FeedbinAddAccountView: View { var accountDetails: some View { Section { - TextField("Email", text: $accountEmail, prompt: Text("ACCOUNT_EMAIL_ADDRESS_PROMPT", tableName: "Account")) + TextField("Email", text: $accountEmail, prompt: Text("Email Address", comment: "Textfield for the user to enter their account email address.")) .autocorrectionDisabled() .autocapitalization(.none) .textContentType(.username) - SecureField("Password", text: $accountPassword, prompt: Text("ACCOUNT_PASSWORD_PROMPT", tableName: "Account")) + SecureField("Password", text: $accountPassword, prompt: Text("Password", comment: "Textfield for the user to enter their account password.")) .textContentType(.password) } } @@ -92,9 +92,9 @@ struct FeedbinAddAccountView: View { HStack{ Spacer() if account == nil { - Text("ADD_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") + Text("Add Account", comment: "Button title") } else { - Text("UPDATE_CREDENTIALS_BUTTON_TITLE", tableName: "Buttons") + Text("Update Credentials", comment: "Button title") } Spacer() } @@ -103,7 +103,11 @@ struct FeedbinAddAccountView: View { } var feedbinAccountExplainer: some View { - Text(account == nil ? "FEEDBIN_FOOTER_EXPLAINER" : "", tableName: "Account").multilineTextAlignment(.center) + if account == nil { + return Text("Sign in to your Feedbin account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a Feedbin account? [Sign Up Here](https://feedbin.com/signup)", comment: "Explanatory text describing the Feedbin account.") + .multilineTextAlignment(.center) + } + return Text("").multilineTextAlignment(.center) } private func retrieveCredentials() { diff --git a/iOS/Account/LocalAddAccountView.swift b/iOS/Account/LocalAddAccountView.swift index b40269a05..4ca855080 100644 --- a/iOS/Account/LocalAddAccountView.swift +++ b/iOS/Account/LocalAddAccountView.swift @@ -24,7 +24,7 @@ struct LocalAddAccountView: View { } .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") }) + Button(action: { dismiss() }, label: { Text("Cancel", comment: "Button title") }) } } .navigationTitle(deviceAccountName()) @@ -37,7 +37,7 @@ struct LocalAddAccountView: View { var accountNameSection: some View { TextField("Name", text: $accountName, - prompt: Text("ACCOUNT_NAME", tableName: "Account")) + prompt: Text("Name", comment: "Textfield placeholder for the name of the account.")) .autocorrectionDisabled() .autocapitalization(.none) } @@ -49,7 +49,7 @@ struct LocalAddAccountView: View { } label: { HStack { Spacer() - Text("ADD_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") + Text("Add Account", comment: "Button title") Spacer() } } @@ -58,7 +58,7 @@ struct LocalAddAccountView: View { var accountFooterView: some View { HStack { Spacer() - Text("LOCAL_FOOTER_EXPLAINER", tableName: "Account") + Text("Local accounts do not sync your feeds across devices.", comment: "Explanatory text describing the local account.") .multilineTextAlignment(.center) Spacer() } @@ -73,9 +73,9 @@ struct LocalAddAccountView: View { private func deviceAccountName() -> Text { if UIDevice.current.userInterfaceIdiom == .pad { - return Text("LOCAL_ACCOUNT_NAME_PAD", tableName: "Account") + return Text("On My iPad", comment: "Account name for iPad") } - return Text("LOCAL_ACCOUNT_NAME_PHONE", tableName: "Account") + return Text("On My iPhone", comment: "Account name for iPhone") } } diff --git a/iOS/Account/NewsBlurAddAccountView.swift b/iOS/Account/NewsBlurAddAccountView.swift index ce84df509..37a414d2a 100644 --- a/iOS/Account/NewsBlurAddAccountView.swift +++ b/iOS/Account/NewsBlurAddAccountView.swift @@ -31,7 +31,7 @@ struct NewsBlurAddAccountView: View, Logging { } .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") }) + Button(action: { dismiss() }, label: { Text("Cancel", comment: "Button title") }) .disabled(showProgressIndicator) } ToolbarItem(placement: .navigationBarTrailing) { @@ -43,11 +43,11 @@ struct NewsBlurAddAccountView: View, Logging { .task { retreiveCredentials() } - .alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountError.1) { + .alert(Text("Error", comment: "Alert title: Error"), isPresented: $accountError.1) { Button(role: .cancel) { // } label: { - Text("DISMISS_BUTTON_TITLE", tableName: "Buttons") + Text("Dismiss", comment: "Button title") } } message: { Text(accountError.0?.localizedDescription ?? "") @@ -79,11 +79,11 @@ struct NewsBlurAddAccountView: View, Logging { var accountDetails: some View { Section { - TextField("Email", text: $accountUserName, prompt: Text("ACCOUNT_USERNAME_OR_EMAIL_PROMPT", tableName: "Account")) + TextField("Email", text: $accountUserName, prompt: Text("Username or Email", comment: "Textfield for the user to enter their account username or email.")) .autocorrectionDisabled() .autocapitalization(.none) .textContentType(.username) - SecureField("Password", text: $accountPassword, prompt: Text("ACCOUNT_PASSWORD_PROMPT", tableName: "Account")) + SecureField("Password", text: $accountPassword, prompt: Text("Password", comment: "Textfield for the user to enter their account password.")) .textContentType(.password) } } @@ -109,9 +109,9 @@ struct NewsBlurAddAccountView: View, Logging { HStack{ Spacer() if account == nil { - Text("ADD_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") + Text("Add Account", comment: "Button title") } else { - Text("UPDATE_CREDENTIALS_BUTTON_TITLE", tableName: "Buttons") + Text("Update Credentials", comment: "Button title") } Spacer() } @@ -120,7 +120,11 @@ struct NewsBlurAddAccountView: View, Logging { } var newsBlurAccountExplainer: some View { - Text(account == nil ? "NEWSBLUR_FOOTER_EXPLAINER" : "", tableName: "Account").multilineTextAlignment(.center) + if account == nil { + return Text("Sign in to your NewsBlur account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a NewsBlur account? [Sign Up Here](https://newsblur.com)", comment: "Explanatory text describing the NewsBlur account") + .multilineTextAlignment(.center) + } + return Text("").multilineTextAlignment(.center) } private func executeAccountCredentials() async throws { diff --git a/iOS/Account/ReaderAPIAddAccountView.swift b/iOS/Account/ReaderAPIAddAccountView.swift index 6be436511..c0842d14f 100644 --- a/iOS/Account/ReaderAPIAddAccountView.swift +++ b/iOS/Account/ReaderAPIAddAccountView.swift @@ -43,18 +43,18 @@ struct ReaderAPIAddAccountView: View { } .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") }) + Button(action: { dismiss() }, label: { Text("Cancel", comment: "Button title") }) .disabled(showProgressIndicator) } ToolbarItem(placement: .navigationBarTrailing) { if showProgressIndicator { ProgressView() } } } - .alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountError.1) { + .alert(Text("Error", comment: "Alert title: Error"), isPresented: $accountError.1) { Button(role: .cancel) { // } label: { - Text("DISMISS_BUTTON_TITLE", tableName: "Buttons") + Text("Dismiss", comment: "Button title") } } message: { Text(accountError.0?.localizedDescription ?? "") @@ -69,13 +69,13 @@ struct ReaderAPIAddAccountView: View { if accountType == nil { return Text("").multilineTextAlignment(.center) } switch accountType! { case .bazQux: - return Text("BAZQUX_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center) + return Text("Sign in to your BazQux account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a BazQux account? [Sign Up Here](https://bazqux.com)", comment: "Explanatory text describing the BazQux account").multilineTextAlignment(.center) case .inoreader: - return Text("INOREADER_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center) + return Text("Sign in to your InoReader account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have an InoReader account? [Sign Up Here](https://www.inoreader.com)", comment: "Explanatory text describing the Inoreader account").multilineTextAlignment(.center) case .theOldReader: - return Text("OLDREADER_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center) + return Text("Sign in to your The Old Reader account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a The Old Reader account? [Sign Up Here](https://theoldreader.com)", comment: "Explanatory text describing The Old Reader account").multilineTextAlignment(.center) case .freshRSS: - return Text("FRESHRSS_FOOTER_EXPLAINER", tableName: "Account").multilineTextAlignment(.center) + return Text("Sign in to your FreshRSS instance and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have an FreshRSS instance? [Sign Up Here](https://freshrss.org)", comment: "Explanatory text describing the FreshRSS account").multilineTextAlignment(.center) default: return Text("").multilineTextAlignment(.center) } @@ -120,9 +120,9 @@ struct ReaderAPIAddAccountView: View { HStack { Spacer() if accountCredentials == nil { - Text("ADD_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") + Text("Add Account", comment: "Button title") } else { - Text("UPDATE_CREDENTIALS_BUTTON_TITLE", tableName: "Buttons") + Text("Update Credentials", comment: "Button title") } Spacer() } diff --git a/iOS/Inspector/AccountInspectorView.swift b/iOS/Inspector/AccountInspectorView.swift index 2dd2924ee..261e8c5b6 100644 --- a/iOS/Inspector/AccountInspectorView.swift +++ b/iOS/Inspector/AccountInspectorView.swift @@ -73,13 +73,13 @@ struct AccountInspectorView: View { get: { account.name ?? account.defaultName }, set: { account.name = $0 }), prompt: Text(account.defaultName)) { - Text("ACCOUNT_NAME", tableName: "Inspector") + Text("Name", comment: "Textfield for the user to enter account name.") } Toggle(isOn: Binding(get: { account.isActive }, set: { account.isActive = $0 })) { - Text("ACTIVE", tableName: "Inspector") + Text("Active", comment: "Toggle denoting if the account is active.") } } } @@ -91,7 +91,7 @@ struct AccountInspectorView: View { } label: { HStack { Spacer() - Text("CREDENTIALS_BUTTON_TITLE", tableName: "Buttons") + Text("Credentials", comment: "Button title") Spacer() } } @@ -105,29 +105,29 @@ struct AccountInspectorView: View { } label: { HStack { Spacer() - Text("REMOVE_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") + Text("Remove Account", comment: "Button title") Spacer() } } - .confirmationDialog(Text("REMOVE_ACCOUNT_TITLE", tableName: "Inspector"), isPresented: $showRemoveAccountAlert, titleVisibility: .visible) { + .confirmationDialog(Text("Remove Account", comment: "Remove account alert title"), isPresented: $showRemoveAccountAlert, titleVisibility: .visible) { Button(role: .destructive) { AccountManager.shared.deleteAccount(account) dismiss() } label: { - Text("REMOVE_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") + Text("Remove Account", comment: "Button title") } Button(role: .cancel) { // } label: { - Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") + Text("Cancel", comment: "Button title") } } message: { if account.type == .feedly { - Text("REMOVE_FEEDLY_MESSAGE", tableName: "Inspector") + Text("Are you sure you want to remove this account? NetNewsWire will no longer be able to access articles and feeds unless the account is added again.", comment: "Confirmation of the impacts of deleting the Feedly account.") } else { - Text("REMOVE_ACCOUNT_MESSAGE", tableName: "Inspector") + Text("Are you sure you want to remove this account? This cannot be undone.", comment: "Confirmation of the impacts of deleting the account.") } } } @@ -136,7 +136,7 @@ struct AccountInspectorView: View { var cloudKitLimitations: some View { HStack { Spacer() - Text("CLOUDKIT_LIMITATIONS_LINK", tableName: "Inspector") + Text("[iCloud Syncing Limitations & Solutions](https://netnewswire.com/help/iCloud)", comment: "Link to the NetNewsWire iCloud syncing limitations and soltutions website.") Spacer() } } diff --git a/iOS/Inspector/ExtensionInspectorView.swift b/iOS/Inspector/ExtensionInspectorView.swift index 6a2785673..fa300f71c 100644 --- a/iOS/Inspector/ExtensionInspectorView.swift +++ b/iOS/Inspector/ExtensionInspectorView.swift @@ -26,24 +26,24 @@ struct ExtensionInspectorView: View { Button(role: .destructive) { showDeactivateConfirmation = true } label: { - Text("DEACTIVATE_EXTENSION_BUTTON_TITLE", tableName: "Buttons") + Text("Deactivate Extension", comment: "Button title") } - .confirmationDialog(Text("DEACTIVATE_EXTENSION_TITLE", tableName: "Settings") , isPresented: $showDeactivateConfirmation, titleVisibility: .visible) { + .confirmationDialog(Text("Deactivate Extension", comment: "Deactivate Extension confirmation title") , isPresented: $showDeactivateConfirmation, titleVisibility: .visible) { Button(role: .destructive) { ExtensionPointManager.shared.deactivateExtensionPoint(extensionPoint!.extensionPointID) dismiss() } label: { - Text("DEACTIVATE_EXTENSION_BUTTON_TITLE", tableName: "Buttons") + Text("Deactivate Extension", comment: "Button title") } Button(role: .cancel) { // } label: { - Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") + Text("Cancel", comment: "Button title") } } message: { - Text("DEACTIVATE_EXTENSION \(extensionPoint?.title ?? "")", tableName: "Settings") + Text("Are you sure you want to deactivate the “\(extensionPoint?.title ?? "")” extension?)", comment: "Confirmation text regarding deactivation on an extension.") } Spacer() } diff --git a/iOS/Inspector/WebFeedInspectorView.swift b/iOS/Inspector/WebFeedInspectorView.swift index 568729e2a..ef7a24653 100644 --- a/iOS/Inspector/WebFeedInspectorView.swift +++ b/iOS/Inspector/WebFeedInspectorView.swift @@ -29,19 +29,19 @@ struct WebFeedInspectorView: View { prompt: nil) Toggle(isOn: Binding(get: { webFeed.isNotifyAboutNewArticles ?? false }, set: { webFeed.isNotifyAboutNewArticles = $0 })) { - Text("NOTIFY_ABOUT_NEW_ARTICLES", tableName: "Inspector") + Text("Notify About New Articles", comment: "Toggle denoting whether the user has enabled new article notifications for this feed.") } if webFeed.isFeedProvider == false { Toggle(isOn: Binding( get: { webFeed.isArticleExtractorAlwaysOn ?? false }, set: { webFeed.isArticleExtractorAlwaysOn = $0 })) { - Text("ALWAYS_SHOW_READER_VIEW", tableName: "Inspector") + Text("Always Show Reader View", comment: "Toggle denoting whether the user has enabled Reader view for this feed.") } } } - Section(header: Text("HOME_PAGE", tableName: "Inspector")) { + Section(header: Text("Home Page", comment: "Home Page section header in the Feed inspector.")) { HStack { Text(webFeed.homePageURL?.decodedURLString ?? "") Spacer() @@ -54,7 +54,7 @@ struct WebFeedInspectorView: View { } } - Section(header: Text("FEED_URL", tableName: "Inspector")) { + Section(header: Text("Feed URL", comment: "Feed URL section header in the Feed inspector.")) { Text(webFeed.url.description) } } diff --git a/iOS/Settings/Account and Extensions/Accounts/AccountsManagementView.swift b/iOS/Settings/Account and Extensions/Accounts/AccountsManagementView.swift index c32c7cbcd..18f376411 100644 --- a/iOS/Settings/Account and Extensions/Accounts/AccountsManagementView.swift +++ b/iOS/Settings/Account and Extensions/Accounts/AccountsManagementView.swift @@ -68,19 +68,19 @@ struct AccountsManagementView: View { var body: some View { List { - Section(header: Text("ACTIVE_ACCOUNTS_HEADER", tableName: "Settings")) { + Section(header: Text("Active Accounts", comment: "Active accounts section header")) { ForEach(viewModel.sortedActiveAccounts, id: \.self) { account in accountRow(account) } } - Section(header: Text("INACTIVE_ACCOUNTS_HEADER", tableName: "Settings")) { + Section(header: Text("Inactive Accounts", comment: "Inactive accounts section header")) { ForEach(viewModel.sortedInactiveAccounts, id: \.self) { account in accountRow(account) } } } - .navigationTitle(Text("MANAGE_ACCOUNTS", tableName: "Settings")) + .navigationTitle(Text("Manage Accounts", comment: "Navigation title: Manage Accounts")) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button { @@ -93,18 +93,18 @@ struct AccountsManagementView: View { .sheet(isPresented: $viewModel.showAddAccountSheet) { AddAccountListView() } - .alert(Text("ACCOUNT_REMOVE \(viewModel.accountToDelete?.nameForDisplay ?? "")", tableName: "Settings"), + .alert(Text("Are you sure you want to remove “\(viewModel.accountToDelete?.nameForDisplay ?? "")”?", comment: "Alert title: confirm account removal"), isPresented: $viewModel.showAccountDeletionAlert) { Button(role: .destructive) { AccountManager.shared.deleteAccount(viewModel.accountToDelete!) } label: { - Text("REMOVE_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") + Text("Remove Account", comment: "Button title") } Button(role: .cancel) { viewModel.restoreAccount(viewModel.accountToDelete!) } label: { - Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") + Text("Cancel", comment: "Button title") } } message: { switch viewModel.accountToDelete { @@ -113,9 +113,9 @@ struct AccountsManagementView: View { case .some(let account): switch account.type { case .feedly: - Text("REMOVE_FEEDLY_CONFIRMATION", tableName: "Settings") + Text("Are you sure you want to remove this account? NetNewsWire will no longer be able to access articles and feeds unless the account is added again.", comment: "Alert message: remove Feedly account confirmation") default: - Text("REMOVE_ACCOUNT_CONFIRMATION", tableName: "Settings") + Text("Are you sure you want to remove this account? This cannot be undone.", comment: "Alert message: remove account confirmation") } } } @@ -137,7 +137,7 @@ struct AccountsManagementView: View { viewModel.temporarilyDeleteAccount(account) } label: { Label { - Text("REMOVE_ACCOUNT_BUTTON_TITLE", tableName: "Buttons") + Text("Remove Account", comment: "Button title") } icon: { Image(systemName: "trash") } diff --git a/iOS/Settings/Account and Extensions/Accounts/AddAccountListView.swift b/iOS/Settings/Account and Extensions/Accounts/AddAccountListView.swift index c933136dd..5123ee3e3 100644 --- a/iOS/Settings/Account and Extensions/Accounts/AddAccountListView.swift +++ b/iOS/Settings/Account and Extensions/Accounts/AddAccountListView.swift @@ -74,7 +74,7 @@ struct AddAccountListView: View { webAccountSection selfHostedSection } - .navigationTitle(Text("ADD_ACCOUNT", tableName: "Settings")) + .navigationTitle(Text("Add Account", comment: "Navigation title: Add Account")) .navigationBarTitleDisplayMode(.inline) .listItemTint(.primary) .toolbar { @@ -82,7 +82,7 @@ struct AddAccountListView: View { Button(role: .cancel) { dismiss() } label: { - Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") + Text("Cancel", comment: "Button title") } } } @@ -100,12 +100,12 @@ struct AddAccountListView: View { Text(viewModel.showAddAccountSheet.accountType.localizedAccountName()) } } - .alert(Text("ERROR_TITLE", tableName: "Errors"), + .alert(Text("Error", comment: "Alert title: Error"), isPresented: $viewModel.showAddAccountError.1, actions: { Button { // } label: { - Text("DISMISS_BUTTON_TITLE", tableName: "Buttons") + Text("Dismiss", comment: "Button title") } }, message: { Text("\(viewModel.showAddAccountError.0?.localizedDescription ?? "Unknown Error")") @@ -129,9 +129,9 @@ struct AddAccountListView: View { } } } header: { - Text("ADD_LOCAL_ACCOUNT_HEADER", tableName: "Settings") + Text("Local", comment: "Add Account: Local account section header") } footer: { - Text("ADD_LOCAL_ACCOUNT_FOOTER", tableName: "Settings") + Text("Local accounts do not sync your feeds across devices", comment: "Local account section footer") } } @@ -152,9 +152,9 @@ struct AddAccountListView: View { } .disabled(interactionDisabled(for: .cloudKit)) } header: { - Text("ADD_CLOUDKIT_ACCOUNT_HEADER", tableName: "Settings") + Text("iCloud", comment: "Add Account: iCloud section header") } footer: { - Text("ADD_CLOUDKIT_ACCOUNT_FOOTER", tableName: "Settings") + Text("Your iCloud account syncs your feeds across your Mac and iOS devices", comment: "Add Account: iCloud section footer") } } @@ -184,9 +184,9 @@ struct AddAccountListView: View { } } } header: { - Text("ADD_WEB_ACCOUNT_HEADER", tableName: "Settings") + Text("Web Account", comment: "Add Account: Web Account section header") } footer: { - Text("ADD_WEB_ACCOUNT_FOOTER", tableName: "Settings") + Text("Web accounts sync your feeds across all your devices", comment: "Add Account: Web Account section footer") } } @@ -206,9 +206,9 @@ struct AddAccountListView: View { } } } header: { - Text("ADD_SELFHOSTED_ACCOUNT_HEADER", tableName: "Settings") + Text("Self-Hosted", comment: "Add Accont: Self-hosted section header") } footer: { - Text("ADD_SELFHOSTED_ACCOUNT_FOOTER", tableName: "Settings") + Text("Self-hosted accounts sync your feeds across all your devices", comment: "Add Account: Self-hosted section footer") } } diff --git a/iOS/Settings/Account and Extensions/Extensions/AddExtensionListView.swift b/iOS/Settings/Account and Extensions/Extensions/AddExtensionListView.swift index 5ca059f17..970d5929d 100644 --- a/iOS/Settings/Account and Extensions/Extensions/AddExtensionListView.swift +++ b/iOS/Settings/Account and Extensions/Extensions/AddExtensionListView.swift @@ -18,8 +18,8 @@ struct AddExtensionListView: View { var body: some View { NavigationView { List { - Section(header: Text("FEED_PROVIDER_HEADER", tableName: "Settings"), - footer: Text("FEED_PROVIDER_FOOTER", tableName: "Settings")) { + Section(header: Text("Feed Providers", comment: "Feed Providers section header"), + footer: Text("Feed Providers allow you to subscribe to some pages as if they were RSS feeds.", comment: "Feed Providers section footer.")) { ForEach(0..( @@ -36,7 +36,7 @@ struct DisplayAndBehaviorsView: View { // TODO: Add Reader Mode Defaults here. See #3684. } } - .navigationTitle(Text("Display & Behaviors")) + .navigationTitle(Text("Display & Behaviors", comment: "Navigation title for Display & Behaviours")) .tint(Color(uiColor: AppAssets.primaryAccentColor)) } diff --git a/iOS/Settings/Appearance/TimelineCustomizerView.swift b/iOS/Settings/Appearance/TimelineCustomizerView.swift index 342a9f0b5..1f020e72c 100644 --- a/iOS/Settings/Appearance/TimelineCustomizerView.swift +++ b/iOS/Settings/Appearance/TimelineCustomizerView.swift @@ -15,7 +15,7 @@ struct TimelineCustomizerView: View { var body: some View { List { - Section(header: Text("ICON_SIZE", tableName: "Settings")) { + Section(header: Text("Icon Size", comment: "Timline Customiser: Icon size section header")) { ZStack { TickMarkSliderView(minValue: 1, maxValue: 3, currentValue: Binding(get: { @@ -34,7 +34,7 @@ struct TimelineCustomizerView: View { .listRowBackground(Color.clear) .listRowSeparator(.hidden) - Section(header: Text("NUMBER_OF_LINES", tableName: "Settings")) { + Section(header: Text("Number of Lines", comment: "Timeline customiser: Number of lines section header")) { ZStack { TickMarkSliderView(minValue: 1, maxValue: 5, currentValue: Binding(get: { Float(appDefaults.timelineNumberOfLines) @@ -61,7 +61,7 @@ struct TimelineCustomizerView: View { } } .listStyle(.grouped) - .navigationTitle(Text("TIMELINE_LAYOUT", tableName: "Settings")) + .navigationTitle(Text("Timeline Layout", comment: "Navigation bar title for Timeline Layout")) .onAppear { } @@ -88,11 +88,11 @@ struct TimelineCustomizerView: View { .bold() .lineLimit(appDefaults.timelineNumberOfLines) HStack { - Text("Feed name") + Text("Feed name", comment: "Feed name placeholder used in timeline preview") .foregroundColor(.secondary) .font(.caption) Spacer() - Text("08:51") + Text("08:51", comment: "Sample time used in timeline preview") .foregroundColor(.secondary) .font(.caption) }.padding(0) diff --git a/iOS/Settings/General/SettingsRows.swift b/iOS/Settings/General/SettingsRows.swift index 4ca8b7c40..92d78b572 100644 --- a/iOS/Settings/General/SettingsRows.swift +++ b/iOS/Settings/General/SettingsRows.swift @@ -18,7 +18,7 @@ struct SettingsViewRows { /// This row, when tapped, will open iOS System Settings. static var openSystemSettings: some View { Label { - Text("OPEN_SYSTEM_SETTINGS", tableName: "Settings") + Text("Open System Settings", comment: "Button: opens device Settings app.") } icon: { Image("system.settings") .resizable() @@ -36,7 +36,7 @@ struct SettingsViewRows { static var configureNewArticleNotifications: some View { NavigationLink(destination: NewArticleNotificationsView()) { Label { - Text("NEW_ARTICLE_NOTIFICATIONS", tableName: "Settings") + Text("New Article Notifications", comment: "Button: opens New Article Notifications view") } icon: { Image("notifications.sounds") .resizable() @@ -51,7 +51,7 @@ struct SettingsViewRows { static var addAccount: some View { NavigationLink(destination: AccountsManagementView()) { Label { - Text("MANAGE_ACCOUNTS", tableName: "Settings") + Text("Manage Accounts", comment: "Button: opens Accounts Management view") } icon: { Image("app.account") .resizable() @@ -66,7 +66,7 @@ struct SettingsViewRows { static var manageExtensions: some View { NavigationLink(destination: ExtensionsManagementView()) { Label { - Text("MANAGE_EXTENSIONS", tableName: "Settings") + Text("Manage Extensions", comment: "Button: opens Extensions Management view") } icon: { Image("app.extension") .resizable() @@ -83,7 +83,7 @@ struct SettingsViewRows { showImportActionSheet.wrappedValue.toggle() } label: { Label { - Text("IMPORT_SUBSCRIPTIONS", tableName: "Settings") + Text("Import Subscriptions", comment: "Button: opens import subscriptions view") .foregroundColor(.primary) } icon: { @@ -102,7 +102,7 @@ struct SettingsViewRows { showExportActionSheet.wrappedValue.toggle() } label: { Label { - Text("EXPORT_SUBSCRIPTIONS", tableName: "Settings") + Text("Export Subscriptions", comment: "Button: opens Export Subscriptions view") .foregroundColor(.primary) } icon: { @@ -119,7 +119,7 @@ struct SettingsViewRows { /// - Returns: `Toggle` static func sortOldestToNewest(_ preference: Binding) -> some View { Toggle(isOn: preference) { - Text("SORT_OLDEST_NEWEST", tableName: "Settings") + Text("Sort Oldest to Newest", comment: "Toggle: Sort articles from oldest to newest when enabled.") } } @@ -128,7 +128,7 @@ struct SettingsViewRows { /// - Returns: `Toggle` static func groupByFeed(_ preference: Binding) -> some View { Toggle(isOn: preference) { - Text("GROUP_BY_FEED", tableName: "Settings") + Text("Group by Feed", comment: "Toggle: groups articles by feed when enabled.") } } @@ -137,7 +137,7 @@ struct SettingsViewRows { /// - Returns: `Toggle` static func refreshToClearReadArticles(_ preference: Binding) -> some View { Toggle(isOn: preference) { - Text("REFRESH_TO_CLEAR_READ_ARTICLES", tableName: "Settings") + Text("Refresh to Clear Read Articles", comment: "Toggle: when enabled, articles will be cleared when the timeline is refreshed") } } @@ -147,7 +147,7 @@ struct SettingsViewRows { NavigationLink { TimelineCustomizerView() } label: { - Text("TIMELINE_LAYOUT", tableName: "Settings") + Text("Timeline Layout", comment: "Button: opens the timeline customiser") } } @@ -156,7 +156,7 @@ struct SettingsViewRows { static var themeSelection: some View { NavigationLink(destination: ArticleThemeManagerView()) { HStack { - Text("ARTICLE_THEME", tableName: "Settings") + Text("Article Theme", comment: "Button: opens the Article Theme manager view") Spacer() Text(ArticleThemesManager.shared.currentTheme.name) .font(.callout) @@ -165,15 +165,21 @@ struct SettingsViewRows { } } + /// Returns a `Toggle` which triggers changes to the user's mark all as read preferences. + /// - Parameter preference: `Binding` + /// - Returns: `Toggle` static func confirmMarkAllAsRead(_ preference: Binding) -> some View { Toggle(isOn: preference) { - Text("CONFIRM_MARK_ALL_AS_READ", tableName: "Settings") + Text("Confirm Mark All as Read", comment: "Toggle: when enabled, the app will confirm whether to mark all items as read") } } + /// Returns a `Toggle` which triggers changes to the user's link opening behaviour. + /// - Parameter preference: `Binding` + /// - Returns: `Toggle` static func openLinksInNetNewsWire(_ preference: Binding) -> some View { Toggle(isOn: preference) { - Text("OPEN_LINKS_IN_APP", tableName: "Settings") + Text("Open Links in NetNewsWire", comment: "Toggle: when enabled, links will open in NetNewsWire") } } @@ -184,7 +190,7 @@ struct SettingsViewRows { static func configureAppearance(_ isShown: Binding) -> some View { NavigationLink(destination: DisplayAndBehaviorsView(), isActive: isShown) { Label { - Text("DISPLAY_BEHAVIORS_HEADER", tableName: "Settings") + Text("Display & Behaviours", comment: "Button: opens the Display and Appearance view.") } icon: { Image("app.appearance") .resizable() @@ -222,7 +228,7 @@ struct SettingsViewRows { AboutView() } label: { Label { - Text("ABOUT", tableName: "Settings") + Text("About", comment: "Button: opens the NetNewsWire about view.") } icon: { Image(systemName: "info.circle") .resizable() diff --git a/iOS/Settings/General/SettingsView.swift b/iOS/Settings/General/SettingsView.swift index 79d046156..f2e5f17f4 100644 --- a/iOS/Settings/General/SettingsView.swift +++ b/iOS/Settings/General/SettingsView.swift @@ -23,18 +23,18 @@ struct SettingsView: View { NavigationView { List { // Device Permissions - Section(header: Text("DEVICE_PERMISSIONS_HEADER", tableName: "Settings"), - footer: Text("DEVICE_PERMISSIONS_FOOTER", tableName: "Settings")) { + Section(header: Text("Device Permissions", comment: "Settings: Device Permissions section header."), + footer: Text("Configure NetNewsWire's access to Siri, background app refresh, mobile data, and more.", comment: "Settings: Device Permissions section footer.")) { SettingsViewRows.openSystemSettings } // Account/Extensions/OPML Management - Section(header: Text("ACCOUNTS_EXTENSIONS_HEADER", tableName: "Settings"), - footer: Text("ACCOUNTS_EXTENSIONS_FOOTER", tableName: "Settings")) { + Section(header: Text("Accounts & Extensions", comment: "Settings: Accounts and Extensions section header."), + footer: Text("Add, delete, enable, or disable accounts and extensions.", comment: "Settings: Accounts and Extensions section footer.")) { SettingsViewRows.addAccount SettingsViewRows.manageExtensions SettingsViewRows.importOPML(showImportActionSheet: $viewModel.showImportActionSheet) - .confirmationDialog(Text("IMPORT_OPML_CONFIRMATION", tableName: "Settings"), + .confirmationDialog(Text("Import OPML", comment: "Import OPML confirmation title."), isPresented: $viewModel.showImportActionSheet, titleVisibility: .visible) { ForEach(AccountManager.shared.sortedActiveAccounts, id: \.self) { account in @@ -46,7 +46,7 @@ struct SettingsView: View { } SettingsViewRows.exportOPML(showExportActionSheet: $viewModel.showExportActionSheet) - .confirmationDialog(Text("EXPORT_OPML_CONFIRMATION", tableName: "Settings"), + .confirmationDialog(Text("Export OPML", comment: "Export OPML confirmation title."), isPresented: $viewModel.showExportActionSheet, titleVisibility: .visible) { ForEach(AccountManager.shared.sortedAccounts, id: \.self) { account in @@ -65,8 +65,8 @@ struct SettingsView: View { } // Appearance - Section(header: Text("APPEARANCE_HEADER", tableName: "Settings"), - footer: Text("APPEARANCE_FOOTER", tableName: "Settings")) { + Section(header: Text("Appearance", comment: "Settings: Appearance section header."), + footer: Text("Manage the look, feel, and behavior of NetNewsWire.", comment: "Settings: Appearance section footer.")) { SettingsViewRows.configureAppearance($isConfigureAppearanceShown) if viewModel.notificationPermissions == .authorized { SettingsViewRows.configureNewArticleNotifications @@ -83,11 +83,11 @@ struct SettingsView: View { } .tint(Color(uiColor: AppAssets.primaryAccentColor)) .listStyle(.insetGrouped) - .navigationTitle(Text("SETTINGS_TITLE", tableName: "Settings")) + .navigationTitle(Text("Settings", comment: "Navigation bar title for Settings.")) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading, content: { - Button(action: { dismiss() }, label: { Text("DONE_BUTTON_TITLE", tableName: "Buttons") }) + Button(action: { dismiss() }, label: { Text("Done", comment: "Button title") }) }) } .sheet(isPresented: $viewModel.showAddAccountView) { @@ -144,15 +144,15 @@ struct SettingsView: View { viewModel.showImportExportError = true } }) - .alert(Text("IMPORT_OPML_SUCCESS_TITLE", tableName: "Settings"), + .alert(Text("Imported Successfully", comment: "Alert title: imported OPML file successfully."), isPresented: $viewModel.showImportSuccess, actions: {}, - message: { Text("IMPORT_OPML_SUCCESS_MESSAGE \(viewModel.importAccount?.nameForDisplay ?? "")", tableName: "Settings") }) - .alert(Text("EXPORT_OPML_SUCCESS_TITLE", tableName: "Settings"), + message: { Text("Subscriptions have been imported to your \(viewModel.importAccount?.nameForDisplay ?? "") account.", tableName: "Alert message: imported OPML file successfully.") }) + .alert(Text("Exported Successfully", comment: "Alert title: exported OPML file successfully."), isPresented: $viewModel.showExportSuccess, actions: {}, - message: { Text("EXPORT_OPML_SUCCESS_MESSAGE", tableName: "Settings") }) - .alert(Text("ERROR_TITLE", tableName: "Errors"), + message: { Text("Your OPML file has been successfully exported.", comment: "Alert message: exported OPML file successfully.") }) + .alert(Text("Error", comment: "Alert title: Error"), isPresented: $viewModel.showImportExportError, actions: {}, message: { Text(viewModel.importExportError?.localizedDescription ?? "Import/Export Error") } ) diff --git a/iOS/Settings/Help/AboutView.swift b/iOS/Settings/Help/AboutView.swift index 1190e5366..69b4b3a55 100644 --- a/iOS/Settings/Help/AboutView.swift +++ b/iOS/Settings/Help/AboutView.swift @@ -13,21 +13,21 @@ struct AboutView: View, LoadableAboutData { var body: some View { List { Section(header: aboutHeaderView) {} - Section(header: Text("PRIMARY_CONTRIBUTORS", tableName: "Settings")) { + Section(header: Text("Primary Contributors", comment: "About: Primary Contributors section header")) { ForEach(0..