Reverts to existing localization strategy

Existing `strings` tables removed. `tableName`s replaced with `comment`s.
This commit is contained in:
Stuart Breckenridge
2022-12-21 19:45:39 +08:00
parent 46be5a8768
commit 65dfa91ee1
30 changed files with 185 additions and 389 deletions

View File

@@ -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 = "<group>"; };
DF28B454294FE74A00C4D8CA /* ExtensionSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionSectionHeader.swift; sourceTree = "<group>"; };
DF28B4562950163F00C4D8CA /* EnableExtensionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionViewModel.swift; sourceTree = "<group>"; };
DF32ABEA29494CF0008E3A12 /* Settings.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Settings.strings; sourceTree = "<group>"; };
DF3630EA2936183D00326FB8 /* OPMLDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLDocument.swift; sourceTree = "<group>"; };
DF3630EE293618A900326FB8 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
DF394EFF29357A180081EB6E /* NewArticleNotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewArticleNotificationsView.swift; sourceTree = "<group>"; };
@@ -1614,15 +1607,11 @@
DF84E562295122BA0045C334 /* TimelineCustomizerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineCustomizerView.swift; sourceTree = "<group>"; };
DFB34979294A962D00BC81AD /* AddAccountListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountListView.swift; sourceTree = "<group>"; };
DFB3497F294B085100BC81AD /* AccountInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountInspectorView.swift; sourceTree = "<group>"; };
DFB34981294B0B9B00BC81AD /* Inspector.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Inspector.strings; sourceTree = "<group>"; };
DFB34983294B3AFF00BC81AD /* Buttons.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Buttons.strings; sourceTree = "<group>"; };
DFB34987294B447F00BC81AD /* InjectedNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedNavigationView.swift; sourceTree = "<group>"; };
DFB34989294B45AC00BC81AD /* ExtensionInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionInspectorView.swift; sourceTree = "<group>"; };
DFB3498B294B4CA700BC81AD /* WebFeedInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebFeedInspectorView.swift; sourceTree = "<group>"; };
DFB34990294C0B2200BC81AD /* ReaderAPIAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderAPIAddAccountView.swift; sourceTree = "<group>"; };
DFB34992294C0B7400BC81AD /* Account.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Account.strings; sourceTree = "<group>"; };
DFB34995294C4DCB00BC81AD /* LocalizedNetNewsWireError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedNetNewsWireError.swift; sourceTree = "<group>"; };
DFB34998294C4F1D00BC81AD /* Errors.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Errors.strings; sourceTree = "<group>"; };
DFB3499D294C5D5000BC81AD /* CloudKitAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitAddAccountView.swift; sourceTree = "<group>"; };
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>"; };
@@ -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 */,

View File

@@ -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\nDont 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\nDont 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\nDont 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\nDont 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\nDont 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\nDont have a NewsBlur account? [Sign Up Here](https://newsblur.com)";

View File

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

View File

@@ -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.";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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..<availableExtensionPointTypes.count, id: \.self) { i in
Button {
showExtensionPointView = (availableExtensionPointTypes[i], true)
@@ -35,7 +35,7 @@ struct AddExtensionListView: View {
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text("ADD_EXTENSIONS_TITLE", tableName: "Settings"))
.navigationTitle(Text("Add Extensions", comment: "Navigation title: Add Extensions"))
.sheet(isPresented: $showExtensionPointView.1, content: {
if showExtensionPointView.0 != nil {
EnableExtensionPointView(extensionPoint: showExtensionPointView.0!)
@@ -46,7 +46,7 @@ struct AddExtensionListView: View {
Button(role: .cancel) {
dismiss()
} label: {
Text("CANCEL_BUTTON_TITLE", tableName: "Buttons")
Text("Cancel", comment: "Button title")
}
}
}

View File

@@ -21,8 +21,8 @@ struct EnableExtensionPointView: View {
Section(footer: extensionExplainer) {}
Section { enableButton }
}
.alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $extensionError.1, actions: {
Button(action: {}, label: { Text("DISMISS_BUTTON_TITLE", tableName: "Buttons") })
.alert(Text("Error", comment: "Alert title: Error"), isPresented: $extensionError.1, actions: {
Button(action: {}, label: { Text("Dismiss", comment: "Button title") })
}, message: {
Text(extensionError.0?.localizedDescription ?? "Unknown Error")
})
@@ -53,7 +53,7 @@ struct EnableExtensionPointView: View {
} label: {
HStack {
Spacer()
Text("ENABLE_EXTENSION_BUTTON_TITLE", tableName: "Buttons")
Text("Enable Extension", comment: "Button title")
Spacer()
}

View File

@@ -20,7 +20,7 @@ struct ExtensionsManagementView: View {
List {
activeExtensionsSection
}
.navigationTitle(Text("MANAGE_EXTENSIONS", tableName: "Settings"))
.navigationTitle(Text("Manage Extensions", comment: "Navigation title: Manage Extensions"))
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
@@ -33,23 +33,23 @@ struct ExtensionsManagementView: View {
.sheet(isPresented: $showAddExtensionView) {
AddExtensionListView()
}
.alert(Text("DEACTIVATE_EXTENSION_TITLE", tableName: "Settings"),
.alert(Text("Deactivate Extension", comment: "Alert title: confirm deactivate extension"),
isPresented: $showDeactivateAlert) {
Button(role: .destructive) {
ExtensionPointManager.shared.deactivateExtensionPoint(extensionToDeactivate!.value.extensionPointID)
} label: {
Text("DEACTIVATE", tableName: "Settings")
Text("Deactivate", comment: "Button: deactivate extension.")
}
Button(role: .cancel) {
extensionToDeactivate = nil
} label: {
Text("CANCEL_BUTTON_TITLE", tableName: "Buttons")
Text("Cancel", comment: "Button title")
}
} message: {
Text("DEACTIVATE_EXTENSION \(extensionToDeactivate?.value.title ?? "")", tableName: "Settings")
Text("Are you sure you want to deactivate the “\(extensionToDeactivate?.value.title ?? "")” extension?", comment: "Alert message: confirm deactivation of extension.")
}
.onReceive(NotificationCenter.default.publisher(for: .ActiveExtensionPointsDidChange)) { _ in
availableExtensionPointTypes = ExtensionPointManager.shared.availableExtensionPointTypes.sorted(by: { $0.title < $1.title })
@@ -58,7 +58,7 @@ struct ExtensionsManagementView: View {
}
private var activeExtensionsSection: some View {
Section(header: Text("ACTIVE_EXTENSIONS", tableName: "Settings")) {
Section(header: Text("Active Extensions", comment: "Active Extensions section header")) {
ForEach(0..<ExtensionPointManager.shared.activeExtensionPoints.count, id: \.self) { i in
let point = Array(ExtensionPointManager.shared.activeExtensionPoints)[i]
NavigationLink {
@@ -74,7 +74,7 @@ struct ExtensionsManagementView: View {
extensionToDeactivate = point
showDeactivateAlert = true
} label: {
Text("DEACTIVATE", tableName: "Settings")
Text("Deactivate", comment: "Button: deactivates extension")
Image(systemName: "minus.circle")
}.tint(.red)
}

View File

@@ -19,21 +19,21 @@ struct ArticleThemeManagerView: View {
var body: some View {
Form {
Section(header: Text("INSTALLED_THEMES", tableName: "Settings")) {
Section(header: Text("Installed Themes", comment: "Section header for installed themes")) {
articleThemeRow("Default")
ForEach(themeManager.themeNames, id: \.self) {theme in
articleThemeRow(theme)
}
}
}
.navigationTitle(Text("ARTICLE_THEMES_TITLE", tableName: "Settings"))
.navigationTitle(Text("Article Themes", comment: "Navigation bar title for Article Themes"))
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
showImportThemeView = true
} label: {
Label {
Text("IMPORT_THEME_BUTTON_TITLE", tableName: "Buttons")
Text("Import Theme", comment: "Button title")
} icon: {
Image(systemName: "plus")
}
@@ -57,23 +57,23 @@ struct ArticleThemeManagerView: View {
showImportErrorAlert = (failure, true)
}
}
.alert(Text("DELETE_THEME_ALERT_TITLE_\(showDeleteConfirmation.0)", tableName: "Settings"),
.alert(Text("Are you sure you want to delete “\(showDeleteConfirmation.0)”?", comment: "Alert title: confirm theme deletion"),
isPresented: $showDeleteConfirmation.1, actions: {
Button(role: .destructive) {
themeManager.deleteTheme(themeName: showDeleteConfirmation.0)
} label: {
Text("DELETE_THEME_BUTTON_TITLE", tableName: "Buttons")
Text("Delete Theme", comment: "Button title")
}
Button(role: .cancel) {
} label: {
Text("CANCEL_BUTTON_TITLE", tableName: "Buttons")
Text("Cancel", comment: "Button title")
}
}, message: {
Text("DELETE_THEME_ALERT_MESSAGE", tableName: "Settings")
Text("Are you sure you want to delete this theme? This action cannot be undone.", comment: "Alert message: confirm theme deletion")
})
.alert(Text("IMPORT_THEME_CONFIRMATION_TITLE", tableName: "Settings"),
.alert(Text("Import Theme", comment: "Alert title: confirm theme import"),
isPresented: $showImportConfirmationAlert.1,
actions: {
Button {
@@ -95,43 +95,43 @@ struct ArticleThemeManagerView: View {
} label: {
let exists = themeManager.themeExists(filename: showImportConfirmationAlert.0?.path ?? "")
if exists == true {
Text("IMPORT_AND_OVERWRITE_THEME_BUTTON_TITLE", tableName: "Buttons")
Text("Overwrite", comment: "Button title")
} else {
Text("IMPORT_THEME_BUTTON_TITLE", tableName: "Buttons")
Text("Import Theme", comment: "Button title")
}
}
Button(role: .cancel) {
} label: {
Text("CANCEL_BUTTON_TITLE", tableName: "Buttons")
Text("Cancel", comment: "Button title")
}
}, message: {
let exists = themeManager.themeExists(filename: showImportConfirmationAlert.0?.path ?? "")
if exists {
Text("IMPORT_AND_OVERWRITE_THEME_CONFIRMATION_MESSAGE_\(showImportConfirmationAlert.0?.name ?? "")", tableName: "Settings")
Text("The theme “\(showImportConfirmationAlert.0?.name ?? "")” already exists. Do you want to overwrite it?", comment: "Alert message: confirm theme import and overwrite of existing theme")
} else {
Text("IMPORT_THEME_CONFIRMATION_MESSAGE_\(showImportConfirmationAlert.0?.name ?? "")_\(showImportConfirmationAlert.0?.creatorName ?? "")", tableName: "Settings")
Text("Are you sure you want to import “\(showImportConfirmationAlert.0?.name ?? "")” by \(showImportConfirmationAlert.0?.creatorName ?? "")?", comment: "Alert message: confirm theme import")
}
})
.alert(Text("IMPORT_THEME_SUCCESS_TITLE", tableName: "Settings"),
.alert(Text("Imported Successfully", comment: "Alert title: theme imported successfully"),
isPresented: $showImportSuccessAlert,
actions: {
Button(role: .cancel) {
} label: {
Text("DISMISS_BUTTON_TITLE", tableName: "Buttons")
Text("Dismiss", comment: "Button title")
}
}, message: {
Text("IMPORT_THEME_SUCCESS_MESSAGE_\(showImportConfirmationAlert.0?.name ?? "")", tableName: "Settings")
Text("The theme “\(showImportConfirmationAlert.0?.name ?? "")” has been imported.", comment: "Alert message: theme imported successfully")
})
.alert(Text("ERROR_TITLE", tableName: "Errors"),
.alert(Text("Error", comment: "Alert title: Error"),
isPresented: $showImportErrorAlert.1,
actions: {
Button(role: .cancel) {
} label: {
Text("DISMISS_BUTTON_TITLE", tableName: "Buttons")
Text("Dismiss", comment: "Button title")
}
}, message: {
Text("\(showImportErrorAlert.0?.localizedDescription ?? "")")
@@ -147,7 +147,7 @@ struct ArticleThemeManagerView: View {
Text(theme)
.foregroundColor(.primary)
if let articleTheme = try? themeManager.articleThemeWithThemeName(theme) {
Text("ARTICLE_THEME_CREATOR_\(articleTheme.creatorName)", tableName: "Settings")
Text("Created by \(articleTheme.creatorName)", comment: "Article theme creator byline.")
.font(.caption)
.foregroundColor(.secondary)
}
@@ -158,6 +158,7 @@ struct ArticleThemeManagerView: View {
.foregroundColor(Color(uiColor: AppAssets.primaryAccentColor))
}
}
}
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
if theme == themeManager.currentThemeName { }
@@ -166,7 +167,7 @@ struct ArticleThemeManagerView: View {
Button {
showDeleteConfirmation = (theme, true)
} label: {
Text("DELETE_BUTTON_TITLE", tableName: "Buttons")
Text("Delete", comment: "Button title")
Image(systemName: "trash")
}
.tint(.red)

View File

@@ -28,7 +28,7 @@ struct ColorPaletteSelectorView: View {
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40.0, height: 40.0)
Text("ALWAYS_LIGHT_MODE", tableName: "Settings")
Text("Always Light", comment: "Button: always use light display mode")
.font(.subheadline)
if AppDefaults.userInterfaceColorPalette == .light {
Image(systemName: "checkmark.circle.fill")
@@ -47,7 +47,7 @@ struct ColorPaletteSelectorView: View {
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40.0, height: 40.0)
Text("ALWAYS_DARK_MODE", tableName: "Settings")
Text("Always Dark", comment: "Button: always use dark display mode")
.font(.subheadline)
if AppDefaults.userInterfaceColorPalette == .dark {
Image(systemName: "checkmark.circle.fill")
@@ -66,7 +66,7 @@ struct ColorPaletteSelectorView: View {
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40.0, height: 40.0)
Text("USE_SYSTEM_DISPLAY_MODE", tableName: "Settings")
Text("Use Device", comment: "Button: always use device display mode")
.font(.subheadline)
if AppDefaults.userInterfaceColorPalette == .automatic {
Image(systemName: "checkmark.circle.fill")

View File

@@ -14,19 +14,19 @@ struct DisplayAndBehaviorsView: View {
var body: some View {
List {
Section(header: Text("APPLICATION_HEADER", tableName: "Settings")) {
Section(header: Text("Application", comment: "Display & Behaviours: Application section header")) {
ColorPaletteSelectorView()
.listRowBackground(Color.clear)
}
Section(header: Text("TIMELINE_HEADER", tableName: "Settings")) {
Section(header: Text("Timeline", comment: "Display & Behaviours: Timeline section header")) {
SettingsViewRows.sortOldestToNewest($appDefaults.timelineSortDirectionBool)
SettingsViewRows.groupByFeed($appDefaults.timelineGroupByFeed)
SettingsViewRows.refreshToClearReadArticles($appDefaults.refreshClearsReadArticles)
SettingsViewRows.timelineLayout
}
Section(header: Text("ARTICLE_HEADER", tableName: "Settings")) {
Section(header: Text("Artilces", comment: "Display & Behaviours: Articles section header")) {
SettingsViewRows.themeSelection
SettingsViewRows.confirmMarkAllAsRead($appDefaults.confirmMarkAllAsRead)
SettingsViewRows.openLinksInNetNewsWire(Binding<Bool>(
@@ -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))
}

View File

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

View File

@@ -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<Bool>) -> 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<Bool>) -> 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<Bool>) -> 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<Bool>`
/// - Returns: `Toggle`
static func confirmMarkAllAsRead(_ preference: Binding<Bool>) -> 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<Bool>`
/// - Returns: `Toggle`
static func openLinksInNetNewsWire(_ preference: Binding<Bool>) -> 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<Bool>) -> 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()

View File

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

View File

@@ -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..<about.PrimaryContributors.count, id: \.self) { i in
contributorView(about.PrimaryContributors[i])
}
}
Section(header: Text("ADDITIONAL_CONTRIBUTORS", tableName: "Settings")) {
Section(header: Text("Additional Contributors", comment: "About: Additional Contributors section header")) {
ForEach(0..<about.AdditionalContributors.count, id: \.self) { i in
contributorView(about.AdditionalContributors[i])
}
}
Section(header: Text("THANKS", tableName: "Settings"), footer: thanks, content: {})
Section(header: Text("Thanks", comment: "About: Thanks section header"), footer: thanks, content: {})
Section(footer: copyright, content: {})
}
.listStyle(.insetGrouped)
.navigationTitle(Text("ABOUT", tableName: "Settings"))
.navigationTitle(Text("About", comment: "Navigation title: About"))
.navigationBarTitleDisplayMode(.inline)
}
@@ -46,7 +46,7 @@ struct AboutView: View, LoadableAboutData {
.foregroundColor(.secondary)
.font(.callout)
Text("BYLINE", tableName: "Settings")
Text("By Brent Simmons and the Ranchero Software team.", comment: "NetNewsWire byline.")
.font(.subheadline)
Text("[netnewswire.com](https://netnewswire.com)")

View File

@@ -16,9 +16,9 @@ public enum HelpSheet: CustomStringConvertible, CaseIterable {
public var description: String {
switch self {
case .help:
return NSLocalizedString("NETNEWSWIRE_HELP", tableName: "Settings", comment: "NetNewsWire Help")
return String(localized: "NetNewsWire Help", comment: "Button: opens NetNewsWire Help page")
case .website:
return NSLocalizedString("NETNEWSWIRE_WEBSITE", tableName: "Settings", comment: "NetNewsWire Website")
return String(localized: "NetNewsWire Website", comment: "Button: opens NetNewsWire website")
}
}

View File

@@ -23,7 +23,7 @@ struct NewArticleNotificationsView: View, Logging {
.id(feed.webFeedID)
}
}
.navigationTitle(Text("NEW_ARTICLE_NOTIFICATIONS_TITLE", tableName: "Settings"))
.navigationTitle(Text("New Article Notifications", comment: "Navigation title: New Article Notifications"))
.navigationBarTitleDisplayMode(.inline)
}

View File

@@ -21,7 +21,7 @@ struct InjectedNavigationView: View {
Button(role: .cancel) {
dismiss()
} label: {
Text("DONE_BUTTON_TITLE", tableName: "Buttons")
Text("Done", comment: "Button title")
}
}
}