mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Reverts to existing localization strategy
Existing `strings` tables removed. `tableName`s replaced with `comment`s.
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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") } )
|
||||
|
||||
@@ -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)")
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ struct InjectedNavigationView: View {
|
||||
Button(role: .cancel) {
|
||||
dismiss()
|
||||
} label: {
|
||||
Text("DONE_BUTTON_TITLE", tableName: "Buttons")
|
||||
Text("Done", comment: "Button title")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user