From 2bca08195e3a229a61daeebc30e0ccf0b534976c Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Fri, 30 Dec 2022 21:53:07 +0800 Subject: [PATCH] Localization work --- .../About/AboutNetNewsWireView.swift | 2 +- NetNewsWire.xcodeproj/project.pbxproj | 18 + Shared/AccountType+Helpers.swift | 19 +- .../RedditFeedProvider-Extensions.swift | 2 +- .../TwitterFeedProvider-Extensions.swift | 2 +- iOS/Account/CloudKitAddAccountView.swift | 14 +- iOS/Account/FeedbinAddAccountView.swift | 21 +- iOS/Account/LocalAddAccountView.swift | 13 +- iOS/Account/NewsBlurAddAccountView.swift | 21 +- iOS/Account/ReaderAPIAddAccountView.swift | 32 +- iOS/Add/AddFeedViewController.swift | 4 +- ...RedditEnterDetailTableViewController.swift | 2 +- ...witterEnterDetailTableViewController.swift | 10 +- iOS/AppDelegate.swift | 4 +- iOS/Article/ArticleExtractorButton.swift | 8 +- iOS/Article/ArticleSearchBar.swift | 10 +- iOS/Article/ArticleViewController.swift | 18 +- iOS/Article/FindInArticleActivity.swift | 2 +- iOS/Article/OpenInSafariActivity.swift | 2 +- iOS/Article/WebViewController.swift | 18 +- iOS/Inspector/AccountInspectorView.swift | 20 +- iOS/Inspector/ExtensionInspectorView.swift | 10 +- iOS/Inspector/WebFeedInspectorView.swift | 8 +- iOS/Intents/AddWebFeedIntentHandler.swift | 2 +- iOS/KeyboardManager.swift | 48 +- .../Cell/MasterFeedTableViewCell.swift | 4 +- .../MasterFeedTableViewSectionHeader.swift | 4 +- iOS/MasterFeed/MasterFeedViewController.swift | 46 +- .../Cell/MasterTimelineCellData.swift | 2 +- .../Cell/MasterTimelineTableViewCell.swift | 1 + .../MarkAsReadAlertController.swift | 2 +- .../MasterTimelineTitleView.swift | 3 +- .../MasterTimelineViewController.swift | 42 +- iOS/Resources/en.lproj/Localizable.strings | 754 ++++++++++++++++++ .../Accounts/AccountsManagementView.swift | 38 +- .../Accounts/AddAccountListView.swift | 26 +- .../Extensions/AddExtensionListView.swift | 8 +- .../Extensions/EnableExtensionPointView.swift | 10 +- .../Extensions/ExtensionsManagementView.swift | 12 +- .../Appearance/ArticleThemeManagerView.swift | 51 +- .../Appearance/ColorPaletteSelectorView.swift | 6 +- .../Appearance/DisplayAndBehaviorsView.swift | 8 +- .../Appearance/TimelineCustomizerView.swift | 17 +- iOS/Settings/ArticleThemeImporter.swift | 24 +- iOS/Settings/General/SettingsRows.swift | 30 +- iOS/Settings/General/SettingsView.swift | 32 +- iOS/Settings/Help/AboutView.swift | 12 +- iOS/Settings/Help/SettingsHelpSheets.swift | 4 +- .../NewArticleNotificationsView.swift | 2 +- iOS/ShareExtension/ShareViewController.swift | 2 +- .../UIViewController-Extensions.swift | 18 +- iOS/en.lproj/LaunchScreenPad.strings | 6 + iOS/en.lproj/LaunchScreenPhone.strings | 6 + iOS/en.lproj/Main.strings | 42 + 54 files changed, 1172 insertions(+), 350 deletions(-) create mode 100644 iOS/Resources/en.lproj/Localizable.strings create mode 100644 iOS/en.lproj/LaunchScreenPad.strings create mode 100644 iOS/en.lproj/LaunchScreenPhone.strings create mode 100644 iOS/en.lproj/Main.strings diff --git a/Mac/MainWindow/About/AboutNetNewsWireView.swift b/Mac/MainWindow/About/AboutNetNewsWireView.swift index 94937cc85..6ac15504d 100644 --- a/Mac/MainWindow/About/AboutNetNewsWireView.swift +++ b/Mac/MainWindow/About/AboutNetNewsWireView.swift @@ -30,7 +30,7 @@ struct AboutNetNewsWireView: View { Text("By Brent Simmons and the NetNewsWire team.") .font(.subheadline) - Text("[netnewswire.com](https://netnewswire.com)") + Text("label.markdown.netnewswire-website", comment: "Markdown formatted link to netnewswire.com") .font(.callout) Spacer() diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index d5f1eadf2..72e56894a 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -878,6 +878,7 @@ DFD406FA291FB5E400C02962 /* SettingsRows.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD406F9291FB5E400C02962 /* SettingsRows.swift */; }; DFD406FC291FB63B00C02962 /* SettingsHelpSheets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD406FB291FB63B00C02962 /* SettingsHelpSheets.swift */; }; DFD406FF291FDC0C00C02962 /* DisplayAndBehaviorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD406FE291FDC0C00C02962 /* DisplayAndBehaviorsView.swift */; }; + DFD86796295D553D0070D62D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DFD86798295D553D0070D62D /* Localizable.strings */; }; DFE522A32953DEF400376B77 /* CustomInsetGroupedRowStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFE522A22953DEF400376B77 /* CustomInsetGroupedRowStyle.swift */; }; DFFB8FC2279B75E300AC21D7 /* Account in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 51BC2F4A24D343A500E90810 /* Account */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; DFFC4E7428E95C01006B82AF /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFFC4E7328E95C01006B82AF /* AboutView.swift */; }; @@ -1597,6 +1598,9 @@ DF28B452294FE6C600C4D8CA /* EnableExtensionPointView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionPointView.swift; sourceTree = ""; }; DF28B454294FE74A00C4D8CA /* ExtensionSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionSectionHeader.swift; sourceTree = ""; }; DF28B4562950163F00C4D8CA /* EnableExtensionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionViewModel.swift; sourceTree = ""; }; + DF332714295BBBB900BFD911 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = ""; }; + DF332716295BBBBF00BFD911 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LaunchScreenPad.strings; sourceTree = ""; }; + DF332718295BBBC200BFD911 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LaunchScreenPhone.strings; sourceTree = ""; }; DF3630EA2936183D00326FB8 /* OPMLDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLDocument.swift; sourceTree = ""; }; DF3630EE293618A900326FB8 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = ""; }; DF394EFF29357A180081EB6E /* NewArticleNotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewArticleNotificationsView.swift; sourceTree = ""; }; @@ -1630,6 +1634,7 @@ DFD406FB291FB63B00C02962 /* SettingsHelpSheets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsHelpSheets.swift; sourceTree = ""; }; DFD406FE291FDC0C00C02962 /* DisplayAndBehaviorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayAndBehaviorsView.swift; sourceTree = ""; }; DFD6AACB27ADE80900463FAD /* NewsFax.nnwtheme */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = NewsFax.nnwtheme; sourceTree = ""; }; + DFD86797295D553D0070D62D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; DFE522A22953DEF400376B77 /* CustomInsetGroupedRowStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomInsetGroupedRowStyle.swift; sourceTree = ""; }; DFFC4E7328E95C01006B82AF /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; FF3ABF09232599450074C542 /* ArticleSorterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorterTests.swift; sourceTree = ""; }; @@ -2751,6 +2756,7 @@ 84C9FC9A2262A1A900D921D6 /* Resources */ = { isa = PBXGroup; children = ( + DFD86798295D553D0070D62D /* Localizable.strings */, 5103A9B324216A4200410853 /* blank.html */, 51BB7C302335ACDE008E8144 /* page.html */, 514219572353C28900E07E2C /* main_ios.js */, @@ -3567,6 +3573,7 @@ 512392C324E3451400F11704 /* TwitterAdd.storyboard in Resources */, 51077C5A27A86D16000C71DB /* Hyperlegible.nnwtheme in Resources */, DDF9E1D928EDF2FC000BC355 /* notificationSoundBlip.mp3 in Resources */, + DFD86796295D553D0070D62D /* Localizable.strings in Resources */, 51DEE81A26FBFF84006DAA56 /* Promenade.nnwtheme in Resources */, 1768140B2564BB8300D98635 /* NetNewsWire_iOSwidgetextension_target.xcconfig in Resources */, 5103A9B424216A4200410853 /* blank.html in Resources */, @@ -4630,6 +4637,7 @@ isa = PBXVariantGroup; children = ( 511D43EE231FBDE800FB1562 /* Base */, + DF332716295BBBBF00BFD911 /* en */, ); name = LaunchScreenPad.storyboard; sourceTree = ""; @@ -4727,6 +4735,7 @@ isa = PBXVariantGroup; children = ( 84C9FCA02262A1B300D921D6 /* Base */, + DF332714295BBBB900BFD911 /* en */, ); name = Main.storyboard; sourceTree = ""; @@ -4735,10 +4744,19 @@ isa = PBXVariantGroup; children = ( 84C9FCA32262A1B800D921D6 /* Base */, + DF332718295BBBC200BFD911 /* en */, ); name = LaunchScreenPhone.storyboard; sourceTree = ""; }; + DFD86798295D553D0070D62D /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + DFD86797295D553D0070D62D /* en */, + ); + name = Localizable.strings; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ diff --git a/Shared/AccountType+Helpers.swift b/Shared/AccountType+Helpers.swift index dd5d04083..a9016a98e 100644 --- a/Shared/AccountType+Helpers.swift +++ b/Shared/AccountType+Helpers.swift @@ -34,22 +34,25 @@ extension AccountType { } #endif return defaultName + + /* The below account names are not localized as they are product names. */ + case .bazQux: - return NSLocalizedString("BazQux", comment: "Account name") + return "BazQux" case .cloudKit: - return NSLocalizedString("iCloud", comment: "Account name") + return "iCloud" case .feedbin: - return NSLocalizedString("Feedbin", comment: "Account name") + return "Feedbin" case .feedly: - return NSLocalizedString("Feedly", comment: "Account name") + return "Feedly" case .freshRSS: - return NSLocalizedString("FreshRSS", comment: "Account name") + return "FreshRSS" case .inoreader: - return NSLocalizedString("Inoreader", comment: "Account name") + return "Inoreader" case .newsBlur: - return NSLocalizedString("NewsBlur", comment: "Account name") + return "NewsBlur" case .theOldReader: - return NSLocalizedString("The Old Reader", comment: "Account name") + return "The Old Reader" } } diff --git a/Shared/ExtensionPoints/RedditFeedProvider-Extensions.swift b/Shared/ExtensionPoints/RedditFeedProvider-Extensions.swift index 0f4731e01..de21775fe 100644 --- a/Shared/ExtensionPoints/RedditFeedProvider-Extensions.swift +++ b/Shared/ExtensionPoints/RedditFeedProvider-Extensions.swift @@ -13,7 +13,7 @@ extension RedditFeedProvider: ExtensionPoint { static var isSinglton = false static var isDeveloperBuildRestricted = true - static var title = NSLocalizedString("Reddit", comment: "Reddit") + static var title = "Reddit" // not localized - product name static var image = AppAssets.extensionPointReddit static var description: NSAttributedString = { return RedditFeedProvider.makeAttrString("This extension enables you to subscribe to Reddit URLs as if they were RSS feeds. It only works with \(Account.defaultLocalAccountName) or iCloud accounts.") diff --git a/Shared/ExtensionPoints/TwitterFeedProvider-Extensions.swift b/Shared/ExtensionPoints/TwitterFeedProvider-Extensions.swift index 0e538066d..89b170936 100644 --- a/Shared/ExtensionPoints/TwitterFeedProvider-Extensions.swift +++ b/Shared/ExtensionPoints/TwitterFeedProvider-Extensions.swift @@ -13,7 +13,7 @@ extension TwitterFeedProvider: ExtensionPoint { static var isSinglton = false static var isDeveloperBuildRestricted = true - static var title = NSLocalizedString("Twitter", comment: "Twitter") + static var title = "Twitter" // not localized - product name static var image = AppAssets.extensionPointTwitter static var description: NSAttributedString = { return TwitterFeedProvider.makeAttrString("This extension enables you to subscribe to Twitter URLs as if they were RSS feeds. It only works with \(Account.defaultLocalAccountName) or iCloud accounts.") diff --git a/iOS/Account/CloudKitAddAccountView.swift b/iOS/Account/CloudKitAddAccountView.swift index e06cbc591..7de98f92e 100644 --- a/iOS/Account/CloudKitAddAccountView.swift +++ b/iOS/Account/CloudKitAddAccountView.swift @@ -25,13 +25,13 @@ struct CloudKitAddAccountView: View { .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { dismiss() }, label: { Text("Cancel", comment: "Button title") }) + Button(action: { dismiss() }, label: { Text("button.title.cancel", comment: "Cancel") }) } } - .alert(Text("Error", comment: "Alert title: Error"), isPresented: $accountError.1) { - Button(action: {}, label: { Text("Dismiss", comment: "Button title") }) + .alert(Text("alert.title.error", comment: "Error"), isPresented: $accountError.1) { + } message: { - Text(accountError.0?.localizedDescription ?? "Unknown Error") + Text(verbatim: accountError.0?.localizedDescription ?? "Unknown Error") } .dismissOnExternalContextLaunch() .dismissOnAccountAdd() @@ -48,7 +48,7 @@ struct CloudKitAddAccountView: View { } label: { HStack { Spacer() - Text("Use iCloud", comment: "Button title") + Text("button.title.use-cloudkit", comment: "Use iCloud") 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("NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices.", comment: "iCloud account explanatory text") + Text("label.text.cloudkit-explainer", comment: "NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices.") } - Text("[iCloud Syncing Limitations & Solutions](https://netnewswire.com/help/iCloud)", comment: "Link which opens webpage describing iCloud syncing limitations.") + Text("link.markdown.icloud-limitations", comment: "Link which opens webpage describing iCloud syncing limitations.") }.multilineTextAlignment(.center) } diff --git a/iOS/Account/FeedbinAddAccountView.swift b/iOS/Account/FeedbinAddAccountView.swift index 51db90293..23e4a0580 100644 --- a/iOS/Account/FeedbinAddAccountView.swift +++ b/iOS/Account/FeedbinAddAccountView.swift @@ -36,21 +36,16 @@ struct FeedbinAddAccountView: View { } .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { dismiss() }, label: { Text("Cancel", comment: "Button title") }) + Button(action: { dismiss() }, label: { Text("button.title.cancel", comment: "Cancel") }) .disabled(showProgressIndicator) } ToolbarItem(placement: .navigationBarTrailing) { if showProgressIndicator { ProgressView() } } } - .alert(Text("Error", comment: "Alert title: Error"), isPresented: $accountError.1) { - Button(role: .cancel) { - // - } label: { - Text("Dismiss", comment: "Button title") - } + .alert(Text("alert.title.error", comment: "Error"), isPresented: $accountError.1) { } message: { - Text(accountError.0?.localizedDescription ?? "Error") + Text(verbatim: accountError.0?.localizedDescription ?? "Error") } .navigationTitle(Text(verbatim: account?.type.localizedAccountName() ?? "Feedbin")) .navigationBarTitleDisplayMode(.inline) @@ -62,11 +57,11 @@ struct FeedbinAddAccountView: View { var accountDetails: some View { Section { - TextField("Email", text: $accountEmail, prompt: Text("Email Address", comment: "Textfield for the user to enter their account email address.")) + TextField("Email", text: $accountEmail, prompt: Text("textfield.placeholder.email-address", comment: "Email Address")) .autocorrectionDisabled() .autocapitalization(.none) .textContentType(.username) - SecureField("Password", text: $accountPassword, prompt: Text("Password", comment: "Textfield for the user to enter their account password.")) + SecureField("Password", text: $accountPassword, prompt: Text("textfield.placeholder.password", comment: "Password")) .textContentType(.password) } } @@ -92,9 +87,9 @@ struct FeedbinAddAccountView: View { HStack{ Spacer() if account == nil { - Text("Add Account", comment: "Button title") + Text("button.title.add-account", comment: "Add Account") } else { - Text("Update Credentials", comment: "Button title") + Text("button.title.update-credentials", comment: "Update Credentials") } Spacer() } @@ -105,7 +100,7 @@ struct FeedbinAddAccountView: View { var feedbinAccountExplainer: some View { 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.") + return Text("label.text.feedbin-explainer", comment: "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)") .multilineTextAlignment(.center) } return Text("").multilineTextAlignment(.center) diff --git a/iOS/Account/LocalAddAccountView.swift b/iOS/Account/LocalAddAccountView.swift index 4ca855080..c823296d7 100644 --- a/iOS/Account/LocalAddAccountView.swift +++ b/iOS/Account/LocalAddAccountView.swift @@ -24,7 +24,7 @@ struct LocalAddAccountView: View { } .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { dismiss() }, label: { Text("Cancel", comment: "Button title") }) + Button(action: { dismiss() }, label: { Text("button.title.cancel", comment: "Cancel") }) } } .navigationTitle(deviceAccountName()) @@ -37,7 +37,7 @@ struct LocalAddAccountView: View { var accountNameSection: some View { TextField("Name", text: $accountName, - prompt: Text("Name", comment: "Textfield placeholder for the name of the account.")) + prompt: Text("textfield.placeholder.name", comment: "Name")) .autocorrectionDisabled() .autocapitalization(.none) } @@ -49,7 +49,7 @@ struct LocalAddAccountView: View { } label: { HStack { Spacer() - Text("Add Account", comment: "Button title") + Text("button.title.add-account", comment: "Add Account") Spacer() } } @@ -58,7 +58,7 @@ struct LocalAddAccountView: View { var accountFooterView: some View { HStack { Spacer() - Text("Local accounts do not sync your feeds across devices.", comment: "Explanatory text describing the local account.") + Text("label.text.local-account-explainer", comment: "Local accounts do not sync your feeds across devices") .multilineTextAlignment(.center) Spacer() } @@ -72,10 +72,7 @@ struct LocalAddAccountView: View { } private func deviceAccountName() -> Text { - if UIDevice.current.userInterfaceIdiom == .pad { - return Text("On My iPad", comment: "Account name for iPad") - } - return Text("On My iPhone", comment: "Account name for iPhone") + Text(verbatim: AccountType.onMyMac.localizedAccountName()) } } diff --git a/iOS/Account/NewsBlurAddAccountView.swift b/iOS/Account/NewsBlurAddAccountView.swift index 5a47bad44..db83bcede 100644 --- a/iOS/Account/NewsBlurAddAccountView.swift +++ b/iOS/Account/NewsBlurAddAccountView.swift @@ -31,7 +31,7 @@ struct NewsBlurAddAccountView: View, Logging { } .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { dismiss() }, label: { Text("Cancel", comment: "Button title") }) + Button(action: { dismiss() }, label: { Text("button.title.cancel", comment: "Cancel") }) .disabled(showProgressIndicator) } ToolbarItem(placement: .navigationBarTrailing) { @@ -43,14 +43,9 @@ struct NewsBlurAddAccountView: View, Logging { .task { retreiveCredentials() } - .alert(Text("Error", comment: "Alert title: Error"), isPresented: $accountError.1) { - Button(role: .cancel) { - // - } label: { - Text("Dismiss", comment: "Button title") - } + .alert(Text("alert.title.error", comment: "Error"), isPresented: $accountError.1) { } message: { - Text(accountError.0?.localizedDescription ?? "") + Text(verbatim: accountError.0?.localizedDescription ?? "") } .interactiveDismissDisabled(showProgressIndicator) .dismissOnExternalContextLaunch() @@ -70,11 +65,11 @@ struct NewsBlurAddAccountView: View, Logging { var accountDetails: some View { Section { - TextField("Email", text: $accountUserName, prompt: Text("Username or Email", comment: "Textfield for the user to enter their account username or email.")) + TextField("Email", text: $accountUserName, prompt: Text("textfield.placeholder.username-or-email", comment: "Username or Email")) .autocorrectionDisabled() .autocapitalization(.none) .textContentType(.username) - SecureField("Password", text: $accountPassword, prompt: Text("Password", comment: "Textfield for the user to enter their account password.")) + SecureField("Password", text: $accountPassword, prompt: Text("textfield.placeholder.password", comment: "Password")) .textContentType(.password) } } @@ -100,9 +95,9 @@ struct NewsBlurAddAccountView: View, Logging { HStack{ Spacer() if account == nil { - Text("Add Account", comment: "Button title") + Text("button.title.add-account", comment: "Add Account") } else { - Text("Update Credentials", comment: "Button title") + Text("button.title.update-credentials", comment: "Update Credentials") } Spacer() } @@ -113,7 +108,7 @@ struct NewsBlurAddAccountView: View, Logging { var newsBlurAccountExplainer: some View { 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") + return Text("label.text.newsblur-explainer", comment: "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)") .multilineTextAlignment(.center) } return Text("").multilineTextAlignment(.center) diff --git a/iOS/Account/ReaderAPIAddAccountView.swift b/iOS/Account/ReaderAPIAddAccountView.swift index c0842d14f..0474c40c8 100644 --- a/iOS/Account/ReaderAPIAddAccountView.swift +++ b/iOS/Account/ReaderAPIAddAccountView.swift @@ -43,21 +43,17 @@ struct ReaderAPIAddAccountView: View { } .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(action: { dismiss() }, label: { Text("Cancel", comment: "Button title") }) + Button(action: { dismiss() }, label: { Text("button.title.cancel", comment: "Cancel") }) .disabled(showProgressIndicator) } ToolbarItem(placement: .navigationBarTrailing) { if showProgressIndicator { ProgressView() } } } - .alert(Text("Error", comment: "Alert title: Error"), isPresented: $accountError.1) { - Button(role: .cancel) { - // - } label: { - Text("Dismiss", comment: "Button title") - } + .alert(Text("alert.title.error", comment: "Error"), isPresented: $accountError.1) { + } message: { - Text(accountError.0?.localizedDescription ?? "") + Text(verbatim: accountError.0?.localizedDescription ?? "") } .interactiveDismissDisabled(showProgressIndicator) .dismissOnExternalContextLaunch() @@ -69,15 +65,15 @@ struct ReaderAPIAddAccountView: View { if accountType == nil { return Text("").multilineTextAlignment(.center) } switch accountType! { case .bazQux: - 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) + return Text("label.text.bazqux-explainer", comment: "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)").multilineTextAlignment(.center) case .inoreader: - 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) + return Text("label.text.inoreader-explainer", comment: "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)").multilineTextAlignment(.center) case .theOldReader: - 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) + return Text("label.text.theoldreader-explainer", comment: "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)").multilineTextAlignment(.center) case .freshRSS: - 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) + return Text("label.text.freshrss-explainer", comment: "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)").multilineTextAlignment(.center) default: - return Text("").multilineTextAlignment(.center) + return Text(verbatim: "").multilineTextAlignment(.center) } } @@ -85,14 +81,14 @@ struct ReaderAPIAddAccountView: View { var accountDetails: some View { Section { - TextField("Username", text: $accountUserName) + TextField("Username", text: $accountUserName, prompt: Text("textfield.placeholder.username", comment: "Username")) .autocorrectionDisabled() .autocapitalization(.none) .textContentType(.username) - SecureField("Password", text: $accountSecret) + SecureField("Password", text: $accountSecret, prompt: Text("textfield.placeholder.password", comment: "Password")) .textContentType(.password) if accountType == .freshRSS && accountCredentials == nil { - TextField("FreshRSS URL", text: $accountAPIUrl, prompt: Text("fresh.rss.net/api/greader.php")) + TextField("FreshRSS URL", text: $accountAPIUrl, prompt: Text(verbatim: "fresh.rss.net/api/greader.php")) .autocorrectionDisabled() .autocapitalization(.none) } @@ -120,9 +116,9 @@ struct ReaderAPIAddAccountView: View { HStack { Spacer() if accountCredentials == nil { - Text("Add Account", comment: "Button title") + Text("button.title.add-account", comment: "Add Account") } else { - Text("Update Credentials", comment: "Button title") + Text("button.title.update-credentials", comment: "Update Credentials") } Spacer() } diff --git a/iOS/Add/AddFeedViewController.swift b/iOS/Add/AddFeedViewController.swift index d82ffae07..bd9c96373 100644 --- a/iOS/Add/AddFeedViewController.swift +++ b/iOS/Add/AddFeedViewController.swift @@ -42,10 +42,10 @@ class AddFeedViewController: UITableViewController { switch addFeedType { case .reddit: - navigationItem.title = NSLocalizedString("Add Reddit Feed", comment: "Add Reddit Feed") + navigationItem.title = NSLocalizedString("navigation.title.add-reddit-feed", comment: "Add Reddit Feed") navigationItem.leftBarButtonItem = nil case .twitter: - navigationItem.title = NSLocalizedString("Add Twitter Feed", comment: "Add Twitter Feed") + navigationItem.title = NSLocalizedString("navigation.title.add-twitter-feed", comment: "Add Twitter Feed") navigationItem.leftBarButtonItem = nil default: break diff --git a/iOS/Add/Reddit/RedditEnterDetailTableViewController.swift b/iOS/Add/Reddit/RedditEnterDetailTableViewController.swift index be1e75073..d05b2eade 100644 --- a/iOS/Add/Reddit/RedditEnterDetailTableViewController.swift +++ b/iOS/Add/Reddit/RedditEnterDetailTableViewController.swift @@ -21,7 +21,7 @@ class RedditEnterDetailTableViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() - nextBarButtonItem.title = NSLocalizedString("Next", comment: "Next") + nextBarButtonItem.title = NSLocalizedString("button.title.next", comment: "Next") nextBarButtonItem.style = .plain nextBarButtonItem.target = self nextBarButtonItem.action = #selector(nextScene) diff --git a/iOS/Add/Twitter/TwitterEnterDetailTableViewController.swift b/iOS/Add/Twitter/TwitterEnterDetailTableViewController.swift index 91628260d..893b277b7 100644 --- a/iOS/Add/Twitter/TwitterEnterDetailTableViewController.swift +++ b/iOS/Add/Twitter/TwitterEnterDetailTableViewController.swift @@ -19,18 +19,18 @@ class TwitterEnterDetailTableViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() - doneBarButtonItem.title = NSLocalizedString("Next", comment: "Next") + doneBarButtonItem.title = NSLocalizedString("button.title.next", comment: "Next") doneBarButtonItem.style = .plain doneBarButtonItem.target = self doneBarButtonItem.action = #selector(done) navigationItem.rightBarButtonItem = doneBarButtonItem if case .screenName = twitterFeedType { - navigationItem.title = NSLocalizedString("Enter Name", comment: "Enter Name") - detailTextField.placeholder = NSLocalizedString("Screen Name", comment: "Screen Name") + navigationItem.title = NSLocalizedString("navigation.title.enter-name", comment: "Enter Name") + detailTextField.placeholder = NSLocalizedString("textfield.placeholder.screen-name", comment: "Screen Name") } else { - navigationItem.title = NSLocalizedString("Enter Search", comment: "Enter Search") - detailTextField.placeholder = NSLocalizedString("Search Term or #hashtag", comment: "Search Term") + navigationItem.title = NSLocalizedString("navigation.title.enter-search", comment: "Enter Search") + detailTextField.placeholder = NSLocalizedString("textfield.placeholder.search-term-hashtag", comment: "Search Term or #hashtag") } detailTextField.delegate = self diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index 6df9414b1..a86bdac2e 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -256,7 +256,7 @@ private extension AppDelegate { } private func initializeHomeScreenQuickActions() { - let unreadTitle = NSLocalizedString("homescreen.action.firstunread", comment: "First Unread") + let unreadTitle = NSLocalizedString("homescreen.action.first-unread", comment: "First Unread") let unreadIcon = UIApplicationShortcutIcon(systemImageName: "chevron.down.circle") let unreadItem = UIApplicationShortcutItem(type: "com.ranchero.NetNewsWire.FirstUnread", localizedTitle: unreadTitle, localizedSubtitle: nil, icon: unreadIcon, userInfo: nil) @@ -264,7 +264,7 @@ private extension AppDelegate { let searchIcon = UIApplicationShortcutIcon(systemImageName: "magnifyingglass") let searchItem = UIApplicationShortcutItem(type: "com.ranchero.NetNewsWire.ShowSearch", localizedTitle: searchTitle, localizedSubtitle: nil, icon: searchIcon, userInfo: nil) - let addTitle = NSLocalizedString("homescreen.action.addfeed", comment: "Add Feed") + let addTitle = NSLocalizedString("homescreen.action.add-feed", comment: "Add Feed") let addIcon = UIApplicationShortcutIcon(systemImageName: "plus") let addItem = UIApplicationShortcutItem(type: "com.ranchero.NetNewsWire.ShowAdd", localizedTitle: addTitle, localizedSubtitle: nil, icon: addIcon, userInfo: nil) diff --git a/iOS/Article/ArticleExtractorButton.swift b/iOS/Article/ArticleExtractorButton.swift index cc1087888..8580b4db7 100644 --- a/iOS/Article/ArticleExtractorButton.swift +++ b/iOS/Article/ArticleExtractorButton.swift @@ -44,13 +44,13 @@ class ArticleExtractorButton: UIButton { get { switch buttonState { case .error: - return NSLocalizedString("Error - Reader View", comment: "Error - Reader View") + return NSLocalizedString("button.accessibility.title.error-readerview", comment: "Error - Reader View") case .animated: - return NSLocalizedString("Processing - Reader View", comment: "Processing - Reader View") + return NSLocalizedString("button.accessibility.title.processing-readerview", comment: "Processing - Reader View") case .on: - return NSLocalizedString("Selected - Reader View", comment: "Selected - Reader View") + return NSLocalizedString("button.accessibility.title.selected-readerview", comment: "Selected - Reader View") case .off: - return NSLocalizedString("Reader View", comment: "Reader View") + return NSLocalizedString("button.accessibility.title.readerview", comment: "Reader View") } } set { diff --git a/iOS/Article/ArticleSearchBar.swift b/iOS/Article/ArticleSearchBar.swift index c69cfdd4c..f39c7f1ab 100644 --- a/iOS/Article/ArticleSearchBar.swift +++ b/iOS/Article/ArticleSearchBar.swift @@ -60,10 +60,10 @@ import UIKit private func updateUI() { if resultsCount > 0 { - let format = NSLocalizedString("%d of %d", comment: "Results selection and count") + let format = NSLocalizedString("label.search-results.%i.%i", comment: "In English, this shows the current search result selection out of the total number of search results. Example: 3 of 5. The variables are ordered as current selection, total search results count.") resultsLabel.text = String.localizedStringWithFormat(format, selectedResult, resultsCount) } else { - resultsLabel.text = NSLocalizedString("No results", comment: "No results") + resultsLabel.text = NSLocalizedString("label.text.no-results", comment: "No results") } nextButton.isEnabled = selectedResult < resultsCount @@ -101,7 +101,7 @@ private extension ArticleSearchBar { addSubview(background) let doneButton = UIButton() - doneButton.setTitle(NSLocalizedString("Done", comment: "Done"), for: .normal) + doneButton.setTitle(NSLocalizedString("button.title.done", comment: "Done"), for: .normal) doneButton.setTitleColor(UIColor.label, for: .normal) doneButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14) doneButton.isAccessibilityElement = true @@ -129,14 +129,14 @@ private extension ArticleSearchBar { prevButton = UIButton(type: .system) prevButton.setImage(UIImage(systemName: "chevron.up"), for: .normal) - prevButton.accessibilityLabel = "Previous Result" + prevButton.accessibilityLabel = NSLocalizedString("button.title.previous-result", comment: "Previous Result") prevButton.isAccessibilityElement = true prevButton.addTarget(self, action: #selector(previousPressed), for: .touchUpInside) addArrangedSubview(prevButton) nextButton = UIButton(type: .system) nextButton.setImage(UIImage(systemName: "chevron.down"), for: .normal) - nextButton.accessibilityLabel = "Next Result" + nextButton.accessibilityLabel = NSLocalizedString("button.title.next-result", comment: "Next Result") nextButton.isAccessibilityElement = true nextButton.addTarget(self, action: #selector(nextPressed), for: .touchUpInside) addArrangedSubview(nextButton) diff --git a/iOS/Article/ArticleViewController.swift b/iOS/Article/ArticleViewController.swift index a32d2c243..0e2fd1e14 100644 --- a/iOS/Article/ArticleViewController.swift +++ b/iOS/Article/ArticleViewController.swift @@ -220,19 +220,19 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable, Loggi if article.status.read { readBarButtonItem.image = AppAssets.circleOpenImage readBarButtonItem.isEnabled = article.isAvailableToMarkUnread - readBarButtonItem.accLabelText = NSLocalizedString("Mark Article Unread", comment: "Mark Article Unread") + readBarButtonItem.accLabelText = NSLocalizedString("button.title.mark-article-unread", comment: "Mark Article Unread") } else { readBarButtonItem.image = AppAssets.circleClosedImage readBarButtonItem.isEnabled = true - readBarButtonItem.accLabelText = NSLocalizedString("Selected - Mark Article Unread", comment: "Selected - Mark Article Unread") + readBarButtonItem.accLabelText = NSLocalizedString("button.title.selected-mark-article-unread", comment: "Selected - Mark Article Unread") } if article.status.starred { starBarButtonItem.image = AppAssets.starClosedImage - starBarButtonItem.accLabelText = NSLocalizedString("Selected - Star Article", comment: "Selected - Star Article") + starBarButtonItem.accLabelText = NSLocalizedString("button.title.selected-star-article", comment: "Selected - Star Article") } else { starBarButtonItem.image = AppAssets.starOpenImage - starBarButtonItem.accLabelText = NSLocalizedString("Star Article", comment: "Star Article") + starBarButtonItem.accLabelText = NSLocalizedString("button.title.star-article", comment: "Star Article") } configureAppearanceMenu() @@ -291,7 +291,7 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable, Loggi if let currentWebViewController = currentWebViewController { if currentWebViewController.isFullScreenAvailable { - let fullScreenAction = UIAction(title: NSLocalizedString("Full Screen", comment: "Full Screen"), + let fullScreenAction = UIAction(title: NSLocalizedString("button.title.full-screen", comment: "Full Screen"), image: UIImage(systemName: "arrow.up.backward.and.arrow.down.forward"), identifier: nil, discoverabilityTitle: nil, @@ -299,10 +299,10 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable, Loggi state: .off) { [weak self] _ in self?.currentWebViewController?.hideBars() if AppDefaults.shared.hasUsedFullScreenPreviously == false { - let alert = UIAlertController(title: NSLocalizedString("Exit Full Screen", comment: "Full Screen"), - message: NSLocalizedString("To exit Full Screen mode tap the top of the screen.\n\nYou'll only see this message once.", comment: "Full screen explainer."), + let alert = UIAlertController(title: NSLocalizedString("alert.title.exit-full-screen", comment: "Full Screen"), + message: NSLocalizedString("alert.message.exit-full-screen", comment: "To exit Full Screen mode tap the top of the screen.\n\nYou'll only see this message once."), preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("Dismiss", comment: "Dismiss"), style: .default, handler: { _ in + alert.addAction(UIAlertAction(title: NSLocalizedString("button.title.dismiss", comment: "Dismiss"), style: .default, handler: { _ in AppDefaults.shared.hasUsedFullScreenPreviously = true })) self?.present(alert, animated: true, completion: nil) @@ -319,7 +319,7 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable, Loggi private func configureArticleExtractorMenu() { if let feed = article?.webFeed { let extractorOn = feed.isArticleExtractorAlwaysOn ?? false - let readerAction = UIAction(title: NSLocalizedString("button.title.alwaysusreaderview", comment: "Button title: Always Use Reader View"), + let readerAction = UIAction(title: NSLocalizedString("button.title.always-use-reader-view", comment: "Button title: Always Use Reader View"), image: AppAssets.articleExtractorOffSF, identifier: nil, discoverabilityTitle: nil, diff --git a/iOS/Article/FindInArticleActivity.swift b/iOS/Article/FindInArticleActivity.swift index 334a142fb..2ab0501a4 100644 --- a/iOS/Article/FindInArticleActivity.swift +++ b/iOS/Article/FindInArticleActivity.swift @@ -10,7 +10,7 @@ import UIKit class FindInArticleActivity: UIActivity { override var activityTitle: String? { - NSLocalizedString("Find in Article", comment: "Find in Article") + NSLocalizedString("activity.title.find-in-article.titlecase", comment: "Find in Article") } override var activityType: UIActivity.ActivityType? { diff --git a/iOS/Article/OpenInSafariActivity.swift b/iOS/Article/OpenInSafariActivity.swift index 69253e7e9..7b3dc4933 100644 --- a/iOS/Article/OpenInSafariActivity.swift +++ b/iOS/Article/OpenInSafariActivity.swift @@ -13,7 +13,7 @@ class OpenInBrowserActivity: UIActivity { private var activityItems: [Any]? override var activityTitle: String? { - return NSLocalizedString("activity.title.openinbrowser", comment: "Open in Browser") + return NSLocalizedString("activity.title.open-in-browser", comment: "Open in Browser") } override var activityImage: UIImage? { diff --git a/iOS/Article/WebViewController.swift b/iOS/Article/WebViewController.swift index 8215abbc1..0ba555be4 100644 --- a/iOS/Article/WebViewController.swift +++ b/iOS/Article/WebViewController.swift @@ -389,8 +389,8 @@ extension WebViewController: WKNavigationDelegate { if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url, options: [.universalLinksOnly : false], completionHandler: nil) } else { - let alert = UIAlertController(title: NSLocalizedString("Error", comment: "Error"), message: NSLocalizedString("This device cannot send emails.", comment: "This device cannot send emails."), preferredStyle: .alert) - alert.addAction(.init(title: NSLocalizedString("Dismiss", comment: "Dismiss"), style: .cancel, handler: nil)) + let alert = UIAlertController(title: NSLocalizedString("alert.title.error", comment: "Alert Error"), message: NSLocalizedString("alert.message.device-cannot-send-email", comment: "This device cannot send emails."), preferredStyle: .alert) + alert.addAction(.init(title: NSLocalizedString("button.title.dismiss", comment: "Dismiss"), style: .cancel, handler: nil)) self.present(alert, animated: true, completion: nil) } } else if components?.scheme == "tel" { @@ -726,7 +726,7 @@ private extension WebViewController { func prevArticleAction() -> UIAction? { guard coordinator.isPrevArticleAvailable else { return nil } - let title = NSLocalizedString("Previous Article", comment: "Previous Article") + let title = NSLocalizedString("button.title.previous-article", comment: "Previous Article") return UIAction(title: title, image: AppAssets.prevArticleImage) { [weak self] action in self?.coordinator.selectPrevArticle() } @@ -734,7 +734,7 @@ private extension WebViewController { func nextArticleAction() -> UIAction? { guard coordinator.isNextArticleAvailable else { return nil } - let title = NSLocalizedString("Next Article", comment: "Next Article") + let title = NSLocalizedString("button.title.next-article", comment: "Next Article") return UIAction(title: title, image: AppAssets.nextArticleImage) { [weak self] action in self?.coordinator.selectNextArticle() } @@ -743,7 +743,7 @@ private extension WebViewController { func toggleReadAction() -> UIAction? { guard let article = article, !article.status.read || article.isAvailableToMarkUnread else { return nil } - let title = article.status.read ? NSLocalizedString("Mark as Unread", comment: "Mark as Unread") : NSLocalizedString("Mark as Read", comment: "Mark as Read") + let title = article.status.read ? NSLocalizedString("button.title.mark-as-unread.titlecase", comment: "Mark as Unread") : NSLocalizedString("button.title.mark-as-read.titlecase", comment: "Mark as Read") let readImage = article.status.read ? AppAssets.circleClosedImage : AppAssets.circleOpenImage return UIAction(title: title, image: readImage) { [weak self] action in self?.coordinator.toggleReadForCurrentArticle() @@ -752,7 +752,7 @@ private extension WebViewController { func toggleStarredAction() -> UIAction { let starred = article?.status.starred ?? false - let title = starred ? NSLocalizedString("Mark as Unstarred", comment: "Mark as Unstarred") : NSLocalizedString("Mark as Starred", comment: "Mark as Starred") + let title = starred ? NSLocalizedString("button.title.mark-as-unstarred.titlecase", comment: "Mark as Unstarred") : NSLocalizedString("button.title.mark-as-starred.titlecase", comment: "Mark as Starred") let starredImage = starred ? AppAssets.starOpenImage : AppAssets.starClosedImage return UIAction(title: title, image: starredImage) { [weak self] action in self?.coordinator.toggleStarredForCurrentArticle() @@ -761,7 +761,7 @@ private extension WebViewController { func nextUnreadArticleAction() -> UIAction? { guard coordinator.isAnyUnreadAvailable else { return nil } - let title = NSLocalizedString("Next Unread Article", comment: "Next Unread Article") + let title = NSLocalizedString("button.title.next-unread-article", comment: "Next Unread Article") return UIAction(title: title, image: AppAssets.nextUnreadArticleImage) { [weak self] action in self?.coordinator.selectNextUnread() } @@ -769,7 +769,7 @@ private extension WebViewController { func toggleArticleExtractorAction() -> UIAction { let extracted = articleExtractorButtonState == .on - let title = extracted ? NSLocalizedString("Show Feed Article", comment: "Show Feed Article") : NSLocalizedString("Show Reader View", comment: "Show Reader View") + let title = extracted ? NSLocalizedString("button.title.show-feed-article", comment: "Show Feed Article") : NSLocalizedString("button.title.show-reader-view", comment: "Show Reader View") let extractorImage = extracted ? AppAssets.articleExtractorOffSF : AppAssets.articleExtractorOnSF return UIAction(title: title, image: extractorImage) { [weak self] action in self?.toggleArticleExtractor() @@ -777,7 +777,7 @@ private extension WebViewController { } func shareAction() -> UIAction { - let title = NSLocalizedString("Share", comment: "Share") + let title = NSLocalizedString("button.title.share", comment: "Share") return UIAction(title: title, image: AppAssets.shareImage) { [weak self] action in self?.showActivityDialog() } diff --git a/iOS/Inspector/AccountInspectorView.swift b/iOS/Inspector/AccountInspectorView.swift index feeb2a008..c0bfa9a1a 100644 --- a/iOS/Inspector/AccountInspectorView.swift +++ b/iOS/Inspector/AccountInspectorView.swift @@ -72,14 +72,14 @@ struct AccountInspectorView: View { TextField(text: Binding( get: { account.name ?? account.defaultName }, set: { account.name = $0 }), - prompt: Text(account.defaultName)) { - Text("Name", comment: "Textfield for the user to enter account name.") + prompt: Text(verbatim: account.defaultName)) { + Text("textfield.placeholder.name", comment: "Name") } Toggle(isOn: Binding(get: { account.isActive }, set: { account.isActive = $0 })) { - Text("Active", comment: "Toggle denoting if the account is active.") + Text("toggle.account.active", comment: "Active") } } } @@ -91,7 +91,7 @@ struct AccountInspectorView: View { } label: { HStack { Spacer() - Text("Credentials", comment: "Button title") + Text("button.title.credentials", comment: "Credentials") Spacer() } } @@ -105,26 +105,26 @@ struct AccountInspectorView: View { } label: { HStack { Spacer() - Text("Remove Account", comment: "Button title") + Text("button.title.remove-account", comment: "Remove Account") Spacer() } } - .alert(Text("Are you sure you want to remove “\(account.nameForDisplay)”?", comment: "Alert title: confirm account removal"), isPresented: $showRemoveAccountAlert) { + .alert(Text("alert.title.remove-account.\(account.nameForDisplay)", comment: "Are you sure you want to remove “%@“?"), isPresented: $showRemoveAccountAlert) { Button(role: .destructive) { AccountManager.shared.deleteAccount(account) dismiss() } label: { - Text("Remove Account", comment: "Button title") + Text("button.title.remove-account", comment: "Remove Account") } Button(role: .cancel) { // } label: { - Text("Cancel", comment: "Button title") + Text("button.title.cancel", comment: "Cancel") } } message: { - Text("This action cannot be undone.", comment: "Alert message: remove account confirmation") + Text("alert.message.cannot-undo-action", comment: "This action cannot be undone.") } } } @@ -132,7 +132,7 @@ struct AccountInspectorView: View { var cloudKitLimitations: some View { HStack { Spacer() - Text("[iCloud Syncing Limitations & Solutions](https://netnewswire.com/help/iCloud)", comment: "Link to the NetNewsWire iCloud syncing limitations and soltutions website.") + Text("link.markdown.icloud-limitations", comment: "Link to the NetNewsWire iCloud syncing limitations and soltutions website.") Spacer() } } diff --git a/iOS/Inspector/ExtensionInspectorView.swift b/iOS/Inspector/ExtensionInspectorView.swift index b489bf8ad..5a2b169b0 100644 --- a/iOS/Inspector/ExtensionInspectorView.swift +++ b/iOS/Inspector/ExtensionInspectorView.swift @@ -26,24 +26,24 @@ struct ExtensionInspectorView: View { Button(role: .destructive) { showDeactivateConfirmation = true } label: { - Text("Deactivate Extension", comment: "Button title") + Text("button.title.deactivate-extension", comment: "Deactivate Extension") } - .alert(Text("Are you sure you want to deactivate “\(extensionPoint?.title ?? "")?", comment: "Alert title: confirm deactivate extension") , isPresented: $showDeactivateConfirmation) { + .alert(Text("alert.title.deactivate-extension.\(extensionPoint?.title ?? "")", comment: "Are you sure you want to deactivate “%@“?"), isPresented: $showDeactivateConfirmation) { Button(role: .destructive) { ExtensionPointManager.shared.deactivateExtensionPoint(extensionPoint!.extensionPointID) dismiss() } label: { - Text("Deactivate Extension", comment: "Button title") + Text("button.title.deactivate-extension", comment: "Deactivate Extension") } Button(role: .cancel) { // } label: { - Text("Cancel", comment: "Button title") + Text("button.title.cancel", comment: "Cancel") } } message: { - Text("This action cannot be undone.", comment: "Alert message: remove account confirmation") + Text("alert.message.cannot-undo-action", comment: "This action cannot be undone.") } Spacer() } diff --git a/iOS/Inspector/WebFeedInspectorView.swift b/iOS/Inspector/WebFeedInspectorView.swift index ef7a24653..1cf127e46 100644 --- a/iOS/Inspector/WebFeedInspectorView.swift +++ b/iOS/Inspector/WebFeedInspectorView.swift @@ -29,19 +29,19 @@ struct WebFeedInspectorView: View { prompt: nil) Toggle(isOn: Binding(get: { webFeed.isNotifyAboutNewArticles ?? false }, set: { webFeed.isNotifyAboutNewArticles = $0 })) { - Text("Notify About New Articles", comment: "Toggle denoting whether the user has enabled new article notifications for this feed.") + Text("toggle.title.notify-about-new-articles", comment: "New Article Notifications") } if webFeed.isFeedProvider == false { Toggle(isOn: Binding( get: { webFeed.isArticleExtractorAlwaysOn ?? false }, set: { webFeed.isArticleExtractorAlwaysOn = $0 })) { - Text("Always Show Reader View", comment: "Toggle denoting whether the user has enabled Reader view for this feed.") + Text("toggle.title.always-show-reader-view", comment: "Always Show Reader View") } } } - Section(header: Text("Home Page", comment: "Home Page section header in the Feed inspector.")) { + Section(header: Text("label.text.home-page", comment: "Home Page")) { HStack { Text(webFeed.homePageURL?.decodedURLString ?? "") Spacer() @@ -54,7 +54,7 @@ struct WebFeedInspectorView: View { } } - Section(header: Text("Feed URL", comment: "Feed URL section header in the Feed inspector.")) { + Section(header: Text("label.text.feed-url", comment: "Feed URL")) { Text(webFeed.url.description) } } diff --git a/iOS/Intents/AddWebFeedIntentHandler.swift b/iOS/Intents/AddWebFeedIntentHandler.swift index fb0711fdb..aa221f562 100644 --- a/iOS/Intents/AddWebFeedIntentHandler.swift +++ b/iOS/Intents/AddWebFeedIntentHandler.swift @@ -15,7 +15,7 @@ public enum AddWebFeedIntentHandlerError: LocalizedError { public var errorDescription: String? { switch self { case .communicationFailure: - return NSLocalizedString("Unable to communicate with NetNewsWire.", comment: "Communication failure") + return NSLocalizedString("errordescription.localized.communication-failure", comment: "Unable to communicate with NetNewsWire.") } } diff --git a/iOS/KeyboardManager.swift b/iOS/KeyboardManager.swift index 3eaa08537..04cd279fb 100644 --- a/iOS/KeyboardManager.swift +++ b/iOS/KeyboardManager.swift @@ -129,43 +129,43 @@ private extension KeyboardManager { static func globalAuxilaryKeyCommands() -> [UIKeyCommand] { var keys = [UIKeyCommand]() - let addNewFeedTitle = NSLocalizedString("keyboard.command.newwebfeed", comment: "New Web Feed") + let addNewFeedTitle = NSLocalizedString("keyboard.command.new-web-feed", comment: "New Web Feed") keys.append(KeyboardManager.createKeyCommand(title: addNewFeedTitle, action: "addNewFeed:", input: "n", modifiers: [.command])) - let addNewFolderTitle = NSLocalizedString("keyboard.command.newfolder", comment: "New Folder") + let addNewFolderTitle = NSLocalizedString("keyboard.command.new-folder", comment: "New Folder") keys.append(KeyboardManager.createKeyCommand(title: addNewFolderTitle, action: "addNewFolder:", input: "n", modifiers: [.command, .shift])) let refreshTitle = NSLocalizedString("keyboard.command.refresh", comment: "Refresh") keys.append(KeyboardManager.createKeyCommand(title: refreshTitle, action: "refresh:", input: "r", modifiers: [.command])) - let nextUnreadTitle = NSLocalizedString("keyboard.command.nextunread", comment: "Next Unread") + let nextUnreadTitle = NSLocalizedString("keyboard.command.next-unread", comment: "Next Unread") keys.append(KeyboardManager.createKeyCommand(title: nextUnreadTitle, action: "nextUnread:", input: "/", modifiers: [.command])) - let goToTodayTitle = NSLocalizedString("keyboard.command.gototoday", comment: "Go To Today") + let goToTodayTitle = NSLocalizedString("keyboard.command.go-to-today", comment: "Go To Today") keys.append(KeyboardManager.createKeyCommand(title: goToTodayTitle, action: "goToToday:", input: "1", modifiers: [.command])) - let goToAllUnreadTitle = NSLocalizedString("keyboard.command.gotoallunread", comment: "Go To All Unread") + let goToAllUnreadTitle = NSLocalizedString("keyboard.command.go-to-all-unread", comment: "Go To All Unread") keys.append(KeyboardManager.createKeyCommand(title: goToAllUnreadTitle, action: "goToAllUnread:", input: "2", modifiers: [.command])) - let goToStarredTitle = NSLocalizedString("keyboard.command.gotostarred", comment: "Go To Starred") + let goToStarredTitle = NSLocalizedString("keyboard.command.go-to-starred", comment: "Go To Starred") keys.append(KeyboardManager.createKeyCommand(title: goToStarredTitle, action: "goToStarred:", input: "3", modifiers: [.command])) - let gotoSettings = NSLocalizedString("keyboard.command.gotosettings", comment: "Go To Settings") + let gotoSettings = NSLocalizedString("keyboard.command.go-to-settings", comment: "Go To Settings") keys.append(KeyboardManager.createKeyCommand(title: gotoSettings, action: "goToSettings:", input: ",", modifiers: [.command])) - let articleSearchTitle = NSLocalizedString("keyboard.command.articlesearch", comment: "Article Search") + let articleSearchTitle = NSLocalizedString("keyboard.command.article-search", comment: "Article Search") keys.append(KeyboardManager.createKeyCommand(title: articleSearchTitle, action: "articleSearch:", input: "f", modifiers: [.command, .alternate])) - let markAllAsReadTitle = NSLocalizedString("keyboard.command.markallasread", comment: "Mark All as Read") + let markAllAsReadTitle = NSLocalizedString("keyboard.command.mark-all-as-read", comment: "Mark All as Read") keys.append(KeyboardManager.createKeyCommand(title: markAllAsReadTitle, action: "markAllAsRead:", input: "k", modifiers: [.command])) - let cleanUp = NSLocalizedString("keyboard.command.cleanup", comment: "Clean Up") + let cleanUp = NSLocalizedString("keyboard.command.clean-up", comment: "Clean Up") keys.append(KeyboardManager.createKeyCommand(title: cleanUp, action: "cleanUp:", input: "'", modifiers: [.command])) - let toggleReadFeedsFilter = NSLocalizedString("keyboard.command.togglereadfeedsfilter", comment: "Toggle Read Feeds Filter") + let toggleReadFeedsFilter = NSLocalizedString("keyboard.command.toggle-read-feeds-filter", comment: "Toggle Read Feeds Filter") keys.append(KeyboardManager.createKeyCommand(title: toggleReadFeedsFilter, action: "toggleReadFeedsFilter:", input: "f", modifiers: [.command, .shift])) - let toggleReadArticlesFilter = NSLocalizedString("keyboard.command.togglereadarticlesfilter", comment: "Toggle Read Articles Filter") + let toggleReadArticlesFilter = NSLocalizedString("keyboard.command.toggle-read-articles-filter", comment: "Toggle Read Articles Filter") keys.append(KeyboardManager.createKeyCommand(title: toggleReadArticlesFilter, action: "toggleReadArticlesFilter:", input: "h", modifiers: [.command, .shift])) return keys @@ -174,13 +174,13 @@ private extension KeyboardManager { static func hardcodeFeedKeyCommands() -> [UIKeyCommand] { var keys = [UIKeyCommand]() - let nextUpTitle = NSLocalizedString("keyboard.command.selectnextup", comment: "Select Next Up") + let nextUpTitle = NSLocalizedString("keyboard.command.select-next-up", comment: "Select Next Up") keys.append(KeyboardManager.createKeyCommand(title: nextUpTitle, action: "selectNextUp:", input: UIKeyCommand.inputUpArrow, modifiers: [])) - let nextDownTitle = NSLocalizedString("keyboard.command.selectnextdown", comment: "Select Next Down") + let nextDownTitle = NSLocalizedString("keyboard.command.select-next-down", comment: "Select Next Down") keys.append(KeyboardManager.createKeyCommand(title: nextDownTitle, action: "selectNextDown:", input: UIKeyCommand.inputDownArrow, modifiers: [])) - let getFeedInfo = NSLocalizedString("keyboard.command.getfeedinfo", comment: "Get Feed Info") + let getFeedInfo = NSLocalizedString("keyboard.command.get-feed-info", comment: "Get Feed Info") keys.append(KeyboardManager.createKeyCommand(title: getFeedInfo, action: "showFeedInspector:", input: "i", modifiers: .command)) return keys @@ -189,31 +189,31 @@ private extension KeyboardManager { static func hardcodeArticleKeyCommands() -> [UIKeyCommand] { var keys = [UIKeyCommand]() - let openInBrowserTitle = NSLocalizedString("keyboard.command.openinbrowser", comment: "Open In Browser") + let openInBrowserTitle = NSLocalizedString("keyboard.command.open-in-browser", comment: "Open In Browser") keys.append(KeyboardManager.createKeyCommand(title: openInBrowserTitle, action: "openInBrowser:", input: UIKeyCommand.inputRightArrow, modifiers: [.command])) - let toggleReadTitle = NSLocalizedString("keyboard.command.togglereadstatus", comment: "Toggle Read Status") + let toggleReadTitle = NSLocalizedString("keyboard.command.toggle-read-status", comment: "Toggle Read Status") keys.append(KeyboardManager.createKeyCommand(title: toggleReadTitle, action: "toggleRead:", input: "u", modifiers: [.command, .shift])) - let markAboveAsReadTitle = NSLocalizedString("keyboard.command.markaboveasread", comment: "Mark Above as Read") + let markAboveAsReadTitle = NSLocalizedString("keyboard.command.mark-above-as-read", comment: "Mark Above as Read") keys.append(KeyboardManager.createKeyCommand(title: markAboveAsReadTitle, action: "markAboveAsRead:", input: "k", modifiers: [.command, .control])) - let markBelowAsReadTitle = NSLocalizedString("keyboard.command.markbelowasread", comment: "Mark Below as Read") + let markBelowAsReadTitle = NSLocalizedString("keyboard.command.mark-below-as-read", comment: "Mark Below as Read") keys.append(KeyboardManager.createKeyCommand(title: markBelowAsReadTitle, action: "markBelowAsRead:", input: "k", modifiers: [.command, .shift])) - let toggleStarredTitle = NSLocalizedString("keyboard.command.togglestarredstatus", comment: "Toggle Starred Status") + let toggleStarredTitle = NSLocalizedString("keyboard.command.toggle-starred-status", comment: "Toggle Starred Status") keys.append(KeyboardManager.createKeyCommand(title: toggleStarredTitle, action: "toggleStarred:", input: "l", modifiers: [.command, .shift])) - let findInArticleTitle = NSLocalizedString("keyboard.command.findinarticle", comment: "Find in Article") + let findInArticleTitle = NSLocalizedString("keyboard.command.find-in-article", comment: "Find in Article") keys.append(KeyboardManager.createKeyCommand(title: findInArticleTitle, action: "beginFind:", input: "f", modifiers: [.command])) - let getFeedInfo = NSLocalizedString("keyboard.command.getfeedinfo", comment: "Get Feed Info") + let getFeedInfo = NSLocalizedString("keyboard.command.get-feed-info", comment: "Get Feed Info") keys.append(KeyboardManager.createKeyCommand(title: getFeedInfo, action: "showFeedInspector:", input: "i", modifiers: .command)) - let toggleSidebar = NSLocalizedString("keyboard.command.togglesidebar", comment: "Toggle Sidebar") + let toggleSidebar = NSLocalizedString("keyboard.command.toggle-sidebar", comment: "Toggle Sidebar") keys.append(KeyboardManager.createKeyCommand(title: toggleSidebar, action: "toggleSidebar:", input: "s", modifiers: [.command, .control])) - let toggleReaderView = NSLocalizedString("keyboard.command.togglereaderview", comment: "Toggle Reader View") + let toggleReaderView = NSLocalizedString("keyboard.command.toggle-reader-view", comment: "Toggle Reader View") keys.append(KeyboardManager.createKeyCommand(title: toggleReaderView, action: "toggleReaderView:", input: "r", modifiers: [.command, .shift])) return keys diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift index 4b0004c1f..8738cf295 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift @@ -125,10 +125,10 @@ class MasterFeedTableViewCell : VibrantTableViewCell { UIView.animate(withDuration: duration) { if self.isDisclosureExpanded { - self.disclosureButton?.accessibilityLabel = NSLocalizedString("Collapse Folder", comment: "Collapse Folder") + self.disclosureButton?.accessibilityLabel = NSLocalizedString("label.accessibility.collapse-folder", comment: "Collapse Folder") self.disclosureButton?.imageView?.transform = CGAffineTransform(rotationAngle: 1.570796) } else { - self.disclosureButton?.accessibilityLabel = NSLocalizedString("Expand Folder", comment: "Expand Folder") + self.disclosureButton?.accessibilityLabel = NSLocalizedString("label.accessibility.expand-folder", comment: "Expand Folder") self.disclosureButton?.imageView?.transform = CGAffineTransform(rotationAngle: 0) } } diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift index 9451a3539..4e10b0f09 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift @@ -27,9 +27,9 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView { set {} get { if disclosureExpanded { - return NSLocalizedString("Expanded", comment: "Disclosure button expanded state for accessibility") + return NSLocalizedString("label.accessibility.expanded", comment: "Disclosure button expanded state for accessibility") } - return NSLocalizedString("Collapsed", comment: "Disclosure button collapsed state for accessibility") + return NSLocalizedString("label.accessibility.collapsed", comment: "Disclosure button collapsed state for accessibility") } } diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index 3cdd9d964..9d5284e79 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -507,7 +507,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma return } - let title = NSLocalizedString("button.title.markallasread", comment: "Mark All as Read") + let title = NSLocalizedString("button.title.mark-all-as-read", comment: "Mark All as Read") MarkAsReadAlertController.confirm(self, coordinator: coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in self?.coordinator.markAllAsReadInTimeline() } @@ -623,7 +623,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma var menuItems: [UIAction] = [] - let addWebFeedActionTitle = NSLocalizedString("button.title.addwebfeed", comment: "Add Web Feed") + let addWebFeedActionTitle = NSLocalizedString("button.title.add-web-feed", comment: "Add Web Feed") let addWebFeedAction = UIAction(title: addWebFeedActionTitle, image: AppAssets.plus) { _ in self.coordinator.showAddWebFeed() } @@ -631,7 +631,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma if AccountManager.shared.activeAccounts.contains(where: { $0.type == .onMyMac || $0.type == .cloudKit }) { if ExtensionPointManager.shared.isRedditEnabled { - let addRedditFeedActionTitle = NSLocalizedString("button.title.addredditfeed", comment: "Add Reddit Feed") + let addRedditFeedActionTitle = NSLocalizedString("button.title.add-reddit-feed", comment: "Add Reddit Feed") let addRedditFeedAction = UIAction(title: addRedditFeedActionTitle, image: AppAssets.contextMenuReddit.tinted(color: .label)) { _ in self.coordinator.showAddRedditFeed() } @@ -646,14 +646,14 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner, Ma } } - let addWebFolderActionTitle = NSLocalizedString("button.title.addfolder", comment: "Add Folder") + let addWebFolderActionTitle = NSLocalizedString("button.title.add-folder", comment: "Add Folder") let addWebFolderAction = UIAction(title: addWebFolderActionTitle, image: AppAssets.folderOutlinePlus) { _ in self.coordinator.showAddFolder() } menuItems.append(addWebFolderAction) - let contextMenu = UIMenu(title: NSLocalizedString("button.title.additem", comment: "Add Item"), image: nil, identifier: nil, options: [], children: menuItems.reversed()) + let contextMenu = UIMenu(title: NSLocalizedString("button.title.add-item", comment: "Add Item"), image: nil, identifier: nil, options: [], children: menuItems.reversed()) self.addNewItemButton.menu = contextMenu } @@ -976,7 +976,7 @@ private extension MasterFeedViewController { return nil } - let title = NSLocalizedString("button.title.openhomepage", comment: "Open Home Page") + let title = NSLocalizedString("button.title.open-home-page", comment: "Open Home Page") let action = UIAction(title: title, image: AppAssets.safariImage) { [weak self] action in self?.coordinator.showBrowserForFeed(indexPath) } @@ -988,7 +988,7 @@ private extension MasterFeedViewController { return nil } - let title = NSLocalizedString("button.title.openhomepage", comment: "Open Home Page") + let title = NSLocalizedString("button.title.open-home-page", comment: "Open Home Page") let action = UIAlertAction(title: title, style: .default) { [weak self] action in self?.coordinator.showBrowserForFeed(indexPath) completion(true) @@ -1002,7 +1002,7 @@ private extension MasterFeedViewController { return nil } - let title = NSLocalizedString("button.title.copyfeedurl", comment: "Copy Feed URL") + let title = NSLocalizedString("button.title.copy-feed-url", comment: "Copy Feed URL") let action = UIAction(title: title, image: AppAssets.copyImage) { action in UIPasteboard.general.url = url } @@ -1015,7 +1015,7 @@ private extension MasterFeedViewController { return nil } - let title = NSLocalizedString("button.title.copyfeedurl", comment: "Copy Feed URL") + let title = NSLocalizedString("button.title.copy-feed-url", comment: "Copy Feed URL") let action = UIAlertAction(title: title, style: .default) { action in UIPasteboard.general.url = url completion(true) @@ -1030,7 +1030,7 @@ private extension MasterFeedViewController { return nil } - let title = NSLocalizedString("button.title.copyhomepageurl", comment: "Copy Home Page URL") + let title = NSLocalizedString("button.title.copy-home-pageurl", comment: "Copy Home Page URL") let action = UIAction(title: title, image: AppAssets.copyImage) { action in UIPasteboard.general.url = url } @@ -1044,7 +1044,7 @@ private extension MasterFeedViewController { return nil } - let title = NSLocalizedString("button.title.copyhomepageurl", comment: "Copy Home Page URL") + let title = NSLocalizedString("button.title.copy-home-pageurl", comment: "Copy Home Page URL") let action = UIAlertAction(title: title, style: .default) { action in UIPasteboard.general.url = url completion(true) @@ -1059,7 +1059,7 @@ private extension MasterFeedViewController { return nil } - let title = NSLocalizedString("button.title.markallasread", comment: "Mark All as Read") + let title = NSLocalizedString("button.title.mark-all-as-read.titlecase", comment: "Mark All as Read") let cancel = { completion(true) } @@ -1084,7 +1084,7 @@ private extension MasterFeedViewController { } func renameAction(indexPath: IndexPath) -> UIAction { - let title = NSLocalizedString("button.title.rename", comment: "Rename Feed") + let title = NSLocalizedString("button.title.rename", comment: "Rename") let action = UIAction(title: title, image: AppAssets.editImage) { [weak self] action in self?.rename(indexPath: indexPath) } @@ -1096,7 +1096,7 @@ private extension MasterFeedViewController { return nil } - let title = NSLocalizedString("button.title.getinfo", comment: "Get Info") + let title = NSLocalizedString("button.title.get-info", comment: "Get Info") let action = UIAction(title: title, image: AppAssets.infoImage) { [weak self] action in self?.coordinator.showFeedInspector(for: webFeed) } @@ -1104,7 +1104,7 @@ private extension MasterFeedViewController { } func getAccountInfoAction(account: Account) -> UIAction { - let title = NSLocalizedString("button.title.getinfo", comment: "Get Info") + let title = NSLocalizedString("button.title.get-info", comment: "Get Info") let action = UIAction(title: title, image: AppAssets.infoImage) { [weak self] action in self?.coordinator.showAccountInspector(for: account) } @@ -1124,7 +1124,7 @@ private extension MasterFeedViewController { return nil } - let title = NSLocalizedString("button.title.getinfo", comment: "Get Info") + let title = NSLocalizedString("button.title.get-info", comment: "Get Info") let action = UIAlertAction(title: title, style: .default) { [weak self] action in self?.coordinator.showFeedInspector(for: webFeed) completion(true) @@ -1139,7 +1139,7 @@ private extension MasterFeedViewController { return nil } - let title = NSLocalizedString("button.title.markallasread", comment: "Mark All as Read") + let title = NSLocalizedString("button.title.mark-all-as-read.titlecase", comment: "Mark All as Read") let action = UIAction(title: title, image: AppAssets.markAllAsReadImage) { [weak self] action in MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in if let articles = try? feed.fetchUnreadArticles() { @@ -1156,7 +1156,7 @@ private extension MasterFeedViewController { return nil } - let title = NSLocalizedString("button.title.markallasread", comment: "Mark All as Read") + let title = NSLocalizedString("button.title.mark-all-as-read.titlecase", comment: "Mark All as Read") let action = UIAction(title: title, image: AppAssets.markAllAsReadImage) { [weak self] action in MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in // If you don't have this delay the screen flashes when it executes this code @@ -1175,7 +1175,7 @@ private extension MasterFeedViewController { func rename(indexPath: IndexPath) { guard let feed = coordinator.nodeFor(indexPath)?.representedObject as? Feed else { return } - let formatString = NSLocalizedString("button.title.renamefeed.%@", comment: "Rename feed. The variable provided is the feed name.") + let formatString = NSLocalizedString("alert.title.rename-feed.%@", comment: "Rename feed. The variable provided is the feed name. In English: Rename “%@”") let title = NSString.localizedStringWithFormat(formatString as NSString, feed.nameForDisplay) as String let alertController = UIAlertController(title: title, message: nil, preferredStyle: .alert) @@ -1232,12 +1232,12 @@ private extension MasterFeedViewController { let title: String let message: String if feed is Folder { - title = NSLocalizedString("button.title.deletefolder", comment: "Delete folder") - let localizedInformativeText = NSLocalizedString("alert.message.deletefolder.%@", comment: "Asks the user for confirmation that they wish to delete a folder. The variable provided is the folder name.") + title = NSLocalizedString("alert.title.delete-folder", comment: "Delete folder") + let localizedInformativeText = NSLocalizedString("alert.message.delete-folder.%@", comment: "Asks the user for confirmation that they wish to delete a folder. The variable provided is the folder name. In English, the message is: Are you sure you want to delete the “%@” folder?") message = NSString.localizedStringWithFormat(localizedInformativeText as NSString, feed.nameForDisplay) as String } else { - title = NSLocalizedString("button.title.deletefeed", comment: "Delete feed") - let localizedInformativeText = NSLocalizedString("alert.message.deletefeed.%@", comment: "Asks the user for confirmation that they wish to delete a feed. The variable provided is the feed name.") + title = NSLocalizedString("button.title.delete-feed", comment: "Delete feed") + let localizedInformativeText = NSLocalizedString("alert.message.delete-feed.%@", comment: "Asks the user for confirmation that they wish to delete a folder. The variable provided is the folder name. In English, the message is: Are you sure you want to delete the “%@” feed?") message = NSString.localizedStringWithFormat(localizedInformativeText as NSString, feed.nameForDisplay) as String } diff --git a/iOS/MasterTimeline/Cell/MasterTimelineCellData.swift b/iOS/MasterTimeline/Cell/MasterTimelineCellData.swift index fa06cdadc..91cf05803 100644 --- a/iOS/MasterTimeline/Cell/MasterTimelineCellData.swift +++ b/iOS/MasterTimeline/Cell/MasterTimelineCellData.swift @@ -11,7 +11,7 @@ import Articles struct MasterTimelineCellData { - private static let noText = NSLocalizedString("(No Text)", comment: "No Text") + private static let noText = NSLocalizedString("label.text.no-text", comment: "(No Text)") let title: String let attributedTitle: NSAttributedString diff --git a/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift b/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift index 88c9fd782..594dbfa98 100644 --- a/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift +++ b/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift @@ -263,6 +263,7 @@ private extension MasterTimelineTableViewCell { } func updateAccessiblityLabel() { + #warning("This needs to be localized.") let starredStatus = cellData.starred ? "\(NSLocalizedString("Starred", comment: "Starred article for accessibility")), " : "" let unreadStatus = cellData.read ? "" : "\(NSLocalizedString("Unread", comment: "Unread")), " let label = starredStatus + unreadStatus + "\(cellData.feedName), \(cellData.title), \(cellData.summary), \(cellData.dateString)" diff --git a/iOS/MasterTimeline/MarkAsReadAlertController.swift b/iOS/MasterTimeline/MarkAsReadAlertController.swift index 7d75ac49c..abee797e2 100644 --- a/iOS/MasterTimeline/MarkAsReadAlertController.swift +++ b/iOS/MasterTimeline/MarkAsReadAlertController.swift @@ -49,7 +49,7 @@ struct MarkAsReadAlertController { let title = NSLocalizedString("alert.title.markasread", comment: "Mark As Read") let message = NSLocalizedString("alert.message.markasread.turnoffconfirmation", comment: "Message that indicates a user can turn off the Mark As Read confirmation in Settings.") let cancelTitle = NSLocalizedString("button.title.cancel", comment: "Cancel") - let settingsTitle = NSLocalizedString("button.title.opensettings", comment: "Open Settings") + let settingsTitle = NSLocalizedString("button.title.open-settings", comment: "Open Settings") let alertController = UIAlertController(title: title, message: message, preferredStyle: .actionSheet) let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { _ in diff --git a/iOS/MasterTimeline/MasterTimelineTitleView.swift b/iOS/MasterTimeline/MasterTimelineTitleView.swift index 3a5b83ab5..59698a97d 100644 --- a/iOS/MasterTimeline/MasterTimelineTitleView.swift +++ b/iOS/MasterTimeline/MasterTimelineTitleView.swift @@ -22,7 +22,8 @@ class MasterTimelineTitleView: UIView { set { } get { if let name = label.text { - let unreadLabel = NSLocalizedString("unread", comment: "Unread label for accessiblity") + #warning("This needs to be localized.") + let unreadLabel = NSLocalizedString("label.accessibility.unread", comment: "Unread label for accessiblity") return "\(name) \(unreadCountView.unreadCount) \(unreadLabel)" } else { diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index b9c769e04..455151ea6 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -160,7 +160,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner } @IBAction func markAllAsRead(_ sender: Any) { - let title = NSLocalizedString("Mark All as Read", comment: "Mark All as Read") + let title = NSLocalizedString("button.title.mark-all-as-read.titlecase", comment: "Mark All as Read") if let source = sender as? UIBarButtonItem { MarkAsReadAlertController.confirm(self, coordinator: coordinator, confirmTitle: title, sourceType: source) { [weak self] in @@ -275,8 +275,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner // Set up the read action let readTitle = article.status.read ? - NSLocalizedString("button.title.markasunread", comment: "Mark as Unread. Used to mark an article unread") : - NSLocalizedString("button.title.markasread", comment: "Mark as Read. Used to mark an article Read") + NSLocalizedString("button.title.mark-as-unread.titlecase", comment: "Mark as Unread. Used to mark an article unread") : + NSLocalizedString("button.title.mark-as-read.titlecase", comment: "Mark as Read. Used to mark an article Read") let readAction = UIContextualAction(style: .normal, title: readTitle) { [weak self] (action, view, completion) in self?.coordinator.toggleRead(article) @@ -658,10 +658,10 @@ private extension MasterTimelineViewController { if coordinator.isReadArticlesFiltered { filterButton?.image = AppAssets.filterActiveImage - filterButton?.accLabelText = NSLocalizedString("Selected - Filter Read Articles", comment: "Selected - Filter Read Articles") + filterButton?.accLabelText = NSLocalizedString("label.accessibility.selected-filter-read-artciles", comment: "Selected - Filter Read Articles") } else { filterButton?.image = AppAssets.filterInactiveImage - filterButton?.accLabelText = NSLocalizedString("Filter Read Articles", comment: "Filter Read Articles") + filterButton?.accLabelText = NSLocalizedString("label.accessibility.filter-read-artciles", comment: "Filter Read Articles") } tableView.selectRow(at: nil, animated: false, scrollPosition: .top) @@ -753,8 +753,8 @@ private extension MasterTimelineViewController { guard !article.status.read || article.isAvailableToMarkUnread else { return nil } let title = article.status.read ? - NSLocalizedString("button.title.markasunread", comment: "Mark as Unread. Used to mark an article unread") : - NSLocalizedString("button.title.markasread", comment: "Mark as Read. Used to mark an article Read") + NSLocalizedString("button.title.mark-as-unread.titlecase", comment: "Mark as Unread. Used to mark an article unread") : + NSLocalizedString("button.title.mark-as-read.titlecase", comment: "Mark as Read. Used to mark an article Read") let image = article.status.read ? AppAssets.circleClosedImage : AppAssets.circleOpenImage let action = UIAction(title: title, image: image) { [weak self] action in @@ -767,8 +767,8 @@ private extension MasterTimelineViewController { func toggleArticleStarStatusAction(_ article: Article) -> UIAction { let title = article.status.starred ? - NSLocalizedString("button.title.markasunstarred", comment: "Mark as Unstarred. Used to mark an article as unstarred") : - NSLocalizedString("button.title.markasstarred", comment: "Mark as Starred. Used to mark an article as starred") + NSLocalizedString("button.title.mark-as-unstarred.titlecase", comment: "Mark as Unstarred. Used to mark an article as unstarred") : + NSLocalizedString("button.title.mark-as-starred.titlecase", comment: "Mark as Starred. Used to mark an article as starred") let image = article.status.starred ? AppAssets.starOpenImage : AppAssets.starClosedImage let action = UIAction(title: title, image: image) { [weak self] action in @@ -783,7 +783,7 @@ private extension MasterTimelineViewController { return nil } - let title = NSLocalizedString("button.title.markaboveasread", comment: "Mark Above as Read. Used to mark articles above the current article as read.") + let title = NSLocalizedString("button.title.mark-above-as-read.titlecase", comment: "Mark Above as Read. Used to mark articles above the current article as read.") let image = AppAssets.markAboveAsReadImage let action = UIAction(title: title, image: image) { [weak self] action in MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in @@ -798,7 +798,7 @@ private extension MasterTimelineViewController { return nil } - let title = NSLocalizedString("button.title.markbelowasread", comment: "Mark Below as Read. Used to mark articles below the current article as read.") + let title = NSLocalizedString("button.title.mark-below-as-read.titlecase", comment: "Mark Below as Read. Used to mark articles below the current article as read.") let image = AppAssets.markBelowAsReadImage let action = UIAction(title: title, image: image) { [weak self] action in MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in @@ -813,7 +813,7 @@ private extension MasterTimelineViewController { return nil } - let title = NSLocalizedString("button.title.markaboveasread", comment: "Mark Above as Read. Used to mark articles above the current article as read.") + let title = NSLocalizedString("button.title.mark-above-as-read.titlecase", comment: "Mark Above as Read. Used to mark articles above the current article as read.") let cancel = { completion(true) } @@ -832,7 +832,7 @@ private extension MasterTimelineViewController { return nil } - let title = NSLocalizedString("button.title.markbelowasread", comment: "Mark Below as Read. Used to mark articles below the current article as read.") + let title = NSLocalizedString("button.title.mark-below-as-read.titlecase", comment: "Mark Below as Read. Used to mark articles below the current article as read.") let cancel = { completion(true) } @@ -850,7 +850,7 @@ private extension MasterTimelineViewController { guard let webFeed = article.webFeed, !coordinator.timelineFeedIsEqualTo(webFeed) else { return nil } - let title = NSLocalizedString("button.title.gotofeed", comment: "Go To Feed. Use to navigate to the user to the article list for a feed.") + let title = NSLocalizedString("button.title.go-to-feed.titlecase", comment: "Go To Feed. Use to navigate to the user to the article list for a feed.") let action = UIAction(title: title, image: AppAssets.openInSidebarImage) { [weak self] action in self?.coordinator.discloseWebFeed(webFeed, animations: [.scroll, .navigation]) } @@ -861,7 +861,7 @@ private extension MasterTimelineViewController { guard let webFeed = article.webFeed, !coordinator.timelineFeedIsEqualTo(webFeed) else { return nil } - let title = NSLocalizedString("button.title.gotofeed", comment: "Go To Feed. Use to navigate to the user to the article list for a feed.") + let title = NSLocalizedString("button.title.go-to-feed", comment: "Go To Feed. Use to navigate to the user to the article list for a feed.") let action = UIAlertAction(title: title, style: .default) { [weak self] action in self?.coordinator.discloseWebFeed(webFeed, animations: [.scroll, .navigation]) completion(true) @@ -881,7 +881,7 @@ private extension MasterTimelineViewController { } - let localizedMenuText = NSLocalizedString("button.title.markallasread.%@", comment: "Mark All as Read in ”feed”. The variable name is the feed name.") + let localizedMenuText = NSLocalizedString("button.title.mark-all-as-read.%@", comment: "Mark All as Read in ”feed”. The variable name is the feed name.") let title = NSString.localizedStringWithFormat(localizedMenuText as NSString, webFeed.nameForDisplay) as String let action = UIAction(title: title, image: AppAssets.markAllAsReadImage) { [weak self] action in @@ -903,7 +903,7 @@ private extension MasterTimelineViewController { return nil } - let localizedMenuText = NSLocalizedString("button.title.markallasread.%@", comment: "Mark All as Read in ”feed”. The variable name is the feed name.") + let localizedMenuText = NSLocalizedString("button.title.mark-all-as-read.%@", comment: "Mark All as Read in ”feed”. The variable name is the feed name.") let title = NSString.localizedStringWithFormat(localizedMenuText as NSString, webFeed.nameForDisplay) as String let cancel = { completion(true) @@ -920,7 +920,7 @@ private extension MasterTimelineViewController { func copyArticleURLAction(_ article: Article) -> UIAction? { guard let url = article.preferredURL else { return nil } - let title = NSLocalizedString("button.title.copyarticleurl", comment: "Copy Article URL") + let title = NSLocalizedString("button.title.copy-article-url", comment: "Copy Article URL") let action = UIAction(title: title, image: AppAssets.copyImage) { action in UIPasteboard.general.url = url } @@ -929,7 +929,7 @@ private extension MasterTimelineViewController { func copyExternalURLAction(_ article: Article) -> UIAction? { guard let externalLink = article.externalLink, externalLink != article.preferredLink, let url = URL(string: externalLink) else { return nil } - let title = NSLocalizedString("button.title.copyexternalurl", comment: "Copy External URL") + let title = NSLocalizedString("button.title.copy-external-url", comment: "Copy External URL") let action = UIAction(title: title, image: AppAssets.copyImage) { action in UIPasteboard.general.url = url } @@ -939,7 +939,7 @@ private extension MasterTimelineViewController { func openInBrowserAction(_ article: Article) -> UIAction? { guard let _ = article.preferredURL else { return nil } - let title = NSLocalizedString("button.title.openinbrowser", comment: "Open In Browser") + let title = NSLocalizedString("button.title.open-in-browser", comment: "Open In Browser") let action = UIAction(title: title, image: AppAssets.safariImage) { [weak self] action in self?.coordinator.showBrowserForArticle(article) } @@ -949,7 +949,7 @@ private extension MasterTimelineViewController { func openInBrowserAlertAction(_ article: Article, completion: @escaping (Bool) -> Void) -> UIAlertAction? { guard let _ = article.preferredURL else { return nil } - let title = NSLocalizedString("button.title.openinbrowser", comment: "Open In Browser") + let title = NSLocalizedString("button.title.open-in-browser", comment: "Open In Browser") let action = UIAlertAction(title: title, style: .default) { [weak self] action in self?.coordinator.showBrowserForArticle(article) completion(true) diff --git a/iOS/Resources/en.lproj/Localizable.strings b/iOS/Resources/en.lproj/Localizable.strings new file mode 100644 index 000000000..00f54f09d --- /dev/null +++ b/iOS/Resources/en.lproj/Localizable.strings @@ -0,0 +1,754 @@ +/* Cancel */ +"action.title.cancel" = "Cancel"; + +/* Delete */ +"action.title.delete" = "Delete"; + +/* More */ +"action.title.more" = "More"; + +/* Rename */ +"action.title.rename" = "Rename"; + +/* Choose an account to receive the imported feeds and folders */ +"actionsheet.title.choose-opml-destination" = "Choose an account to receive the imported feeds and folders"; + +/* Choose an account with the subscriptions to export */ +"actionsheet.title.choose-opml-export-account" = "Choose an account with the subscriptions to export"; + +/* Find in Article */ +"activity.title.find-in-article.titlecase" = "Find in Article"; + +/* Open in Browser */ +"activity.title.open-in-browser" = "Open in Browser"; + +/* The variable is the author's home page. In English, the alert message is: Author‘s website:\n%@ */ +"alert.message.author-website.%@" = "Author's website:\n%@"; + +/* The action cannot be undone. + This action cannot be undone. */ +"alert.message.cannot-undo-action" = "This action cannot be undone."; + +/* Asks the user for confirmation that they wish to delete a folder. The variable provided is the folder name. In English, the message is: Are you sure you want to delete the “%@” feed? */ +"alert.message.delete-feed.%@" = "Are you sure you want to delete the “%@” feed?"; + +/* Asks the user for confirmation that they wish to delete a folder. The variable provided is the folder name. In English, the message is: Are you sure you want to delete the “%@” folder? */ +"alert.message.delete-folder.%@" = "Are you sure you want to delete the “%@” folder?"; + +/* This device cannot send emails. */ +"alert.message.device-cannot-send-email" = "This device cannot send emails."; + +/* In English: The theme “%@” already exists. Do you want to overwrite it? + This message details that this theme is a duplicate and gives the user the option to overwrite the existing theme. In English, the message is: The theme “%@” already exists. Overwrite it? */ +"alert.message.duplicate-theme.%@" = "The theme “%@” already exists. Overwrite it?"; + +/* To exit Full Screen mode tap the top of the screen.\n\nYou'll only see this message once. */ +"alert.message.exit-full-screen" = "To exit Full Screen mode tap the top of the screen.\n\nYou'll only see this message once."; + +/* Are you sure you want to import “%@” by %@ */ +"alert.message.import-theme.%@.%@" = "Are you sure you want to import “%@” by %@?"; + +/* The theme “%@” has been imported. */ +"alert.message.imported-theme-successfully.%@" = "The theme “%@” has been imported."; + +/* Message that indicates a user can turn off the Mark As Read confirmation in Settings. */ +"alert.message.markasread.turnoffconfirmation" = "You can turn off this confirmation in Settings."; + +/* Subscriptions have been imported to your “%@“ account. */ +"alert.message.opml-import-success.%@" = "Subscriptions have been imported to your “%@“ account."; + +/* Your subscriptions have been exported successfully. */ +"alert.message.opml.opml-export-success" = "Your subscriptions have been exported successfully."; + +/* Error message when theme data is corrupted. The variable is a description provided by Apple. In English, the message is: This theme cannot be used because of data corruption in the Info.plist. %@. */ +"alert.message.theme-data-corrupted.%@" = "This theme cannot be used because of data corruption in the Info.plist. %@."; + +/* This alert message provides confirmation that the theme has been installed. In English, the message is: The theme “%@” has been installed. */ +"alert.message.theme-installed.%@" = "The theme “%@” has been installed."; + +/* Error message when a key is missing. In English, the message is: This theme cannot be used because the the key—“%@”—is not found in the Info.plist. */ +"alert.message.theme-key-missing.%@" = "This theme cannot be used because the the key—“%@”—is not found in the Info.plist."; + +/* Error message when a type is mismatched. In English, the message is: This theme cannot be used because the the type—“%@”—is mismatched in the Info.plist. */ +"alert.message.theme-type-mismatch.%@" = "This theme cannot be used because the the type—“%@”—is mismatched in the Info.plist."; + +/* Error message when a value is missing. In English, the message is: This theme cannot be used because the the value—“%@”—is not found in the Info.plist. */ +"alert.message.theme-value-missing.%@" = "This theme cannot be used because the the value—“%@”—is not found in the Info.plist."; + +/* Are you sure you want to deactivate “%@“? */ +"alert.title.deactivate-extension.%@" = "Are you sure you want to deactivate “%@“?"; + +/* Delete folder */ +"alert.title.delete-folder" = "Delete Folder"; + +/* In English: Are you sure you want to delete “%@”? */ +"alert.title.delete-theme.%@" = "Are you sure you want to delete “%@”?"; + +/* Duplicate Theme */ +"alert.title.duplicate-theme" = "Duplicate Theme"; + +/* Alert Error + Error */ +"alert.title.error" = "Error"; + +/* Full Screen */ +"alert.title.exit-full-screen" = "Exit Full Screen"; + +/* Import Theme */ +"alert.title.import-theme" = "Import Theme"; + +/* Imported Successfully */ +"alert.title.imported-theme-succesfully" = "Imported Successfully"; + +/* Install Theme */ +"alert.title.install-theme" = "Install Theme"; + +/* Variable ordering is theme name; author name. In English, the alert title is: Install theme “%@” by %@? */ +"alert.title.install-theme.%@.%@" = "Install theme “%@” by %@?"; + +/* Mark As Read */ +"alert.title.markasread" = "Mark As Read"; + +/* Alert title: Exported Successfully */ +"alert.title.opml.opml-export-success" = "Exported Successfully"; + +/* Alert title: Imported Successfully */ +"alert.title.opml.opml-import-success" = "Imported Successfully"; + +/* Are you sure you want to remove “%@“? */ +"alert.title.remove-account.%@" = "Are you sure you want to remove “%@“?"; + +/* Rename feed. The variable provided is the feed name. In English: Rename “%@” */ +"alert.title.rename-feed.%@" = "Rename “%@”"; + +/* Theme installed */ +"alert.title.theme-installed" = "Theme Installed"; + +/* Automatic */ +"appdefaults.colorpalette.automatic" = "Automatic"; + +/* Dark */ +"appdefaults.colorpalette.dark" = "Dark"; + +/* Light */ +"appdefaults.colorpalette.light" = "Light"; + +/* Error - Reader View */ +"button.accessibility.title.error-readerview" = "Error - Reader View"; + +/* Processing - Reader View */ +"button.accessibility.title.processing-readerview" = "Processing - Reader View"; + +/* Reader View */ +"button.accessibility.title.readerview" = "Reader View"; + +/* Selected - Reader View */ +"button.accessibility.title.selected-readerview" = "Selected - Reader View"; + +/* About */ +"button.title.about" = "About"; + +/* Activate Account */ +"button.title.activate-account" = "Activate Account"; + +/* Add Account */ +"button.title.add-account" = "Add Account"; + +/* Add Folder */ +"button.title.add-folder" = "Add Folder"; + +/* Add Item */ +"button.title.add-item" = "Add Item"; + +/* Add Reddit Feed */ +"button.title.add-reddit-feed" = "Add Reddit Feed"; + +/* Add Web Feed */ +"button.title.add-web-feed" = "Add Web Feed"; + +/* Add Twitter Feed */ +"button.title.addtwitterfeed" = "Add Twitter Feed"; + +/* Button title: Always Use Reader View */ +"button.title.always-use-reader-view" = "Always Use Reader View"; + +/* Article Themes */ +"button.title.artice-themes" = "Article Themes"; + +/* Button title + Cancel */ +"button.title.cancel" = "Cancel"; + +/* Close */ +"button.title.close" = "Close"; + +/* Copy Article URL */ +"button.title.copy-article-url" = "Copy Article URL"; + +/* Copy External URL */ +"button.title.copy-external-url" = "Copy External URL"; + +/* Copy Feed URL */ +"button.title.copy-feed-url" = "Copy Feed URL"; + +/* Copy Home Page URL */ +"button.title.copy-home-pageurl" = "Copy Home Page URL"; + +/* Credentials */ +"button.title.credentials" = "Credentials"; + +/* Deactivate */ +"button.title.deactivate" = "Deactivate"; + +/* Deactivate Extension */ +"button.title.deactivate-extension" = "Deactivate Extension"; + +/* Deactivate Account */ +"button.title.deactivate-account" = "Deactivate Account"; + +/* Button title: Default */ +"button.title.default" = "Default"; + +/* Delete + Delete Feed */ +"button.title.delete" = "Delete"; + +/* Delete feed */ +"button.title.delete-feed" = "Delete Feed"; + +/* Delete Theme */ +"button.title.delete-theme" = "Delete Theme"; + +/* Dismiss */ +"button.title.dismiss" = "Dismiss"; + +/* Display & Behaviors */ +"button.title.display-and-behaviors" = "Display & Behaviors"; + +/* Done */ +"button.title.done" = "Done"; + +/* Enable Extension */ +"button.title.enable-extension" = "Enable Extension"; + +/* Export Subscriptions */ +"button.title.export-subscriptions" = "Export Subscriptions"; + +/* Full Screen */ +"button.title.full-screen" = "Full Screen"; + +/* Get Info */ +"button.title.get-info" = "Get Info"; + +/* Go To Feed. Use to navigate to the user to the article list for a feed. */ +"button.title.go-to-feed" = "Go To Feed"; + +/* Go To Feed. Use to navigate to the user to the article list for a feed. */ +"button.title.go-to-feed.titlecase" = "Go to Feed"; + +/* Import Subscriptions */ +"button.title.import-subscriptions" = "Import Subscriptions"; + +/* Import Theme */ +"button.title.import-theme" = "Import Theme"; + +/* Manage Accounts */ +"button.title.manage-accounts" = "Manage Accounts"; + +/* Manage Extensions */ +"button.title.manage-extensions" = "Manage Extensions"; + +/* Mark Above as Read. Used to mark articles above the current article as read. */ +"button.title.mark-above-as-read.titlecase" = "Mark Above as Read"; + +/* Mark All as Read */ +"button.title.mark-all-as-read" = "Mark All as Read"; + +/* Mark All as Read in ”feed”. The variable name is the feed name. */ +"button.title.mark-all-as-read.%@" = "Mark All as Read in “%@“"; + +/* Mark All as Read */ +"button.title.mark-all-as-read.titlecase" = "Mark All as Read"; + +/* Mark Article Unread */ +"button.title.mark-article-unread" = "Mark Article Unread"; + +/* Mark as Read + Mark as Read. Used to mark an article Read */ +"button.title.mark-as-read.titlecase" = "Mark as Read"; + +/* Mark as Starred + Mark as Starred. Used to mark an article as starred */ +"button.title.mark-as-starred.titlecase" = "Mark as Starred"; + +/* Mark as Unread + Mark as Unread. Used to mark an article unread */ +"button.title.mark-as-unread.titlecase" = "Mark as Unread"; + +/* Mark as Unstarred + Mark as Unstarred. Used to mark an article as unstarred */ +"button.title.mark-as-unstarred.titlecase" = "Mark as Unstarred"; + +/* Mark Below as Read. Used to mark articles below the current article as read. */ +"button.title.mark-below-as-read.titlecase" = "Mark Below as Read"; + +/* More */ +"button.title.more" = "More"; + +/* NetNewsWire Help */ +"button.title.netnewswire-help" = "NetNewsWire Help"; + +/* NetNewsWire Website */ +"button.title.netnewswire-website" = "NetNewsWire Website"; + +/* New Article Notifications */ +"button.title.new-article-notifications" = "New Article Notifications"; + +/* Next */ +"button.title.next" = "Next"; + +/* Next Article */ +"button.title.next-article" = "Next Article"; + +/* Next Result */ +"button.title.next-result" = "Next Result"; + +/* Next Unread Article */ +"button.title.next-unread-article" = "Next Unread Article"; + +/* Open Home Page */ +"button.title.open-home-page" = "Open Home Page"; + +/* Open In Browser */ +"button.title.open-in-browser" = "Open In Browser"; + +/* Open Settings */ +"button.title.open-settings" = "Open Settngs"; + +/* Open System Settings */ +"button.title.open-system-settings" = "Open System Settings"; + +/* Overwrite */ +"button.title.overwrite" = "Overwrite"; + +/* Overwrite Theme */ +"button.title.overwrite-theme" = "Overwrite Theme"; + +/* Previous Article */ +"button.title.previous-article" = "Previous Article"; + +/* Previous Result */ +"button.title.previous-result" = "Previous Result"; + +/* Remove Account */ +"button.title.remove-account" = "Remove Account"; + +/* Rename */ +"button.title.rename" = "Rename"; + +/* Selected - Mark Article Unread */ +"button.title.selected-mark-article-unread" = "Selected - Mark Article Unread"; + +/* Selected - Star Article */ +"button.title.selected-star-article" = "Selected - Star Article"; + +/* Share */ +"button.title.share" = "Share"; + +/* Show Feed Article */ +"button.title.show-feed-article" = "Show Feed Article"; + +/* Show Reader View */ +"button.title.show-reader-view" = "Show Read View"; + +/* Show Website */ +"button.title.show-website" = "Show Website"; + +/* Star. Used when marking an article as starred. */ +"button.title.star" = "Star"; + +/* Star Article */ +"button.title.star-article" = "Star Article"; + +/* Timeline Layout */ +"button.title.timeline-layout" = "Timeline Layout"; + +/* Unstar. Used when removing the starred status from an article */ +"button.title.unstar" = "Unstar"; + +/* Update Credentials */ +"button.title.update-credentials" = "Update Credentials"; + +/* Use iCloud */ +"button.title.use-cloudkit" = "Use iCloud"; + +/* Unable to communicate with NetNewsWire. */ +"errordescription.localized.communication-failure" = "Unable to communicate with NetNewsWire."; + +/* Add Feed */ +"homescreen.action.add-feed" = "Add Feed"; + +/* First Unread */ +"homescreen.action.first-unread" = "First Unread"; + +/* Search */ +"homescreen.action.search" = "Search"; + +/* Article Search */ +"keyboard.command.article-search" = "Article Search"; + +/* Clean Up */ +"keyboard.command.clean-up" = "Clean Up"; + +/* Find in Article */ +"keyboard.command.find-in-article" = "Find in Article"; + +/* Get Feed Info */ +"keyboard.command.get-feed-info" = "Get Feed Info"; + +/* Go To All Unread */ +"keyboard.command.go-to-all-unread" = "Go To All Unread"; + +/* Go To Settings */ +"keyboard.command.go-to-settings" = "Go To Settings"; + +/* Go To Starred */ +"keyboard.command.go-to-starred" = "Go To Starred"; + +/* Go To Today */ +"keyboard.command.go-to-today" = "Go To Today"; + +/* Mark Above as Read */ +"keyboard.command.mark-above-as-read" = "Mark Above as Read"; + +/* Mark All as Read */ +"keyboard.command.mark-all-as-read" = "Mark All as Read"; + +/* Mark Below as Read */ +"keyboard.command.mark-below-as-read" = "Mark Below as Read"; + +/* New Folder */ +"keyboard.command.new-folder" = "New Folder"; + +/* New Web Feed */ +"keyboard.command.new-web-feed" = "New Web Feed"; + +/* Next Unread */ +"keyboard.command.next-unread" = "Next Unread"; + +/* Open In Browser */ +"keyboard.command.open-in-browser" = "Open In Browser"; + +/* Refresh */ +"keyboard.command.refresh" = "Refresh"; + +/* Select Next Down */ +"keyboard.command.select-next-down" = "Select Next Down"; + +/* Select Next Up */ +"keyboard.command.select-next-up" = "Select Next Up"; + +/* Toggle Read Articles Filter */ +"keyboard.command.toggle-read-articles-filter" = "Toggle Read Articles Filter"; + +/* Toggle Reader View */ +"keyboard.command.toggle-reader-view" = "Toggle Reader View"; + +/* Toggle Read Feeds Filter */ +"keyboard.command.toggle-read-feeds-filter" = "Toggle Read Feeds Filter"; + +/* Toggle Read Status */ +"keyboard.command.toggle-read-status" = "Toggle Read Status"; + +/* Toggle Sidebar */ +"keyboard.command.toggle-sidebar" = "Toggle Sidebar"; + +/* Toggle Starred Status */ +"keyboard.command.toggle-starred-status" = "Toggle Starred Status"; + +/* Collapse Folder */ +"label.accessibility.collapse-folder" = "Collapse Folder"; + +/* Disclosure button collapsed state for accessibility */ +"label.accessibility.collapsed" = "Collapsed"; + +/* Expand Folder */ +"label.accessibility.expand-folder" = "Expand Folder"; + +/* Disclosure button expanded state for accessibility */ +"label.accessibility.expanded" = "Expanded"; + +/* Filter Read Articles */ +"label.accessibility.filter-read-artciles" = "Filter Read Articles"; + +/* Selected - Filter Read Articles */ +"label.accessibility.selected-filter-read-artciles" = "Selected - Filter Read Articles"; + +/* Unread label for accessiblity */ +"label.accessibility.unread" = "Unread"; + +/* Markdown formatted link to netnewswire.com */ +"label.markdown.netnewswire-website" = "[netnewswire.com](https://netnewswire.com)"; + +/* Feed name */ +"label.placeholder.feed-name" = "Feed name"; + +/* In English, this shows the current search result selection out of the total number of search results. Example: 3 of 5. The variables are ordered as current selection, total search results count. */ +"label.search-results.%i.%i" = "%1$i of %2$i"; + +/* Add, delete, enable, or disable accounts and extensions. */ +"label.text.account-and-extensions-explainer" = "Add, delete, enable, or disable accounts and extensions."; + +/* Settings: Accounts & Extensions section header. */ +"label.text.accounts-and-extensions" = "Account & Extensions"; + +/* Active Accounts */ +"label.text.active-accounts" = "Active Account"; + +/* Active Extensions */ +"label.text.active-extensions" = "Active Extensions"; + +/* Additional Contributors */ +"label.text.additional-contributors" = "Additional Contributors"; + +/* Settings: Appearance section header. */ +"label.text.appearance" = "Appearance"; + +/* Manage the look, feel, and behavior of NetNewsWire. */ +"label.text.appearance-explainer" = "Manage the look, feel, and behavior of NetNewsWire."; + +/* Application */ +"label.text.application" = "Application"; + +/* Articles */ +"label.text.articles" = "Articles"; + +/* 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) */ +"label.text.bazqux-explainer" = "Sign in to your BazQux account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a BazQux account? \n\n[Sign Up Here](https://bazqux.com)"; + +/* iCloud */ +"label.text.cloudkit-account" = "iCloud"; + +/* Your iCloud account syncs your feeds across your Mac and iOS devices */ +"label.text.cloudkit-account-footer" = "Your iCloud account syncs your feeds across your Mac and iOS devices"; + +/* NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices. */ +"label.text.cloudkit-explainer" = "NetNewsWire will use your iCloud account to sync your subscriptions across your Mac and iOS devices."; + +/* Default Themes */ +"label.text.default-themes" = "Default Themes"; + +/* These themes cannot be deleted. */ +"label.text.default-themes-explainer" = "These themes cannot be deleted."; + +/* Device Permissions */ +"label.text.device-permissions" = "Device Permissions"; + +/* Configure NetNewsWire's access to Siri, background app refresh, mobile data, and more. */ +"label.text.device-permissions-explainer" = "Configure NetNewsWire's access to Siri, background app refresh, mobile data, and more."; + +/* Feed Providers */ +"label.text.feed-providers" = "Feed Providers"; + +/* Feed Providers allow you to subscribe to some pages as if they were RSS feeds */ +"label.text.feed-providers-explainer" = "Feed Providers allow you to subscribe to some pages as if they were RSS feeds."; + +/* Feed URL */ +"label.text.feed-url" = "Feed URL"; + +/* 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) */ +"label.text.feedbin-explainer" = "Sign in to your Feedbin account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a Feedbin account? \n\n[Sign Up Here](https://feedbin.com/signup)"; + +/* 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) */ +"label.text.freshrss-explainer" = "Sign in to your FreshRSS instance and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have an FreshRSS instance? \n\n[Sign Up Here](https://freshrss.org)"; + +/* Home Page */ +"label.text.home-page" = "Home Page"; + +/* Icon Size */ +"label.text.icon-size" = "Icon Size"; + +/* Inactive Accounts */ +"label.text.inactive-accounts" = "Inactive Account"; + +/* 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) */ +"label.text.inoreader-explainer" = "Sign in to your InoReader account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have an InoReader account? \n\n[Sign Up Here](https://www.inoreader.com)"; + +/* Local Account */ +"label.text.local-account" = "Local Account"; + +/* Local accounts do not sync your feeds across devices */ +"label.text.local-account-explainer" = "Local accounts do not sync your feeds across devices"; + +/* By Brent Simmons and the Ranchero Software team. */ +"label.text.netnewswire-byline" = "By Brent Simmons and the Ranchero Software team."; + +/* 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) */ +"label.text.newsblur-explainer" = "Sign in to your NewsBlur account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a NewsBlur account?\n\n[Sign Up Here](https://newsblur.com)"; + +/* No results */ +"label.text.no-results" = "No results"; + +/* (No Text) */ +"label.text.no-text" = "(No Text)"; + +/* Number of Lines */ +"label.text.number-of-lines" = "Number of Lines"; + +/* Primary Contributors */ +"label.text.primary-contributors" = "Primary Contributors"; + +/* Self-Hosted Accounts */ +"label.text.self-hosted-accounts" = "Self-Hosted Accounts"; + +/* Self-hosted accounts sync your feeds across all your devices */ +"label.text.self-hosted-accounts-explainer" = "Self-hosted accounts sync your feeds across all your devices"; + +/* Thanks */ +"label.text.thanks" = "Thanks"; + +/* Created by %@ */ +"label.text.theme-created-byline.%@" = "Created by %@"; + +/* 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) */ +"label.text.theoldreader-explainer" = "Sign in to your The Old Reader account and sync your feeds across your devices. Your username and password will be encrypted and stored in Keychain.\n\nDon’t have a The Old Reader account? \n\n[Sign Up Here](https://theoldreader.com)"; + +/* Third Party Themes */ +"label.text.third-party-themes" = "Third Party Themes"; + +/* Timeline */ +"label.text.timeline" = "Timeline"; + +/* Relative time that the account was last refreshed. The variable is a named relative time. Example: Updated 8 minutes ago */ +"label.text.updatedat.%@" = "Updated %@"; + +/* Text indicating that feeds have just been updated. Example: Updated Just Now */ +"label.text.updatednow" = "Updated Just Now"; + +/* Always Dark */ +"label.text.use-dark-appearance" = "Always Dark"; + +/* Use Device */ +"label.text.use-device-appearance" = "Use Device"; + +/* Always Light */ +"label.text.use-light-appearance" = "Always Light"; + +/* Web Account */ +"label.text.web-account" = "Web Account"; + +/* Web accounts sync your feeds across all your devices */ +"label.text.web-account-explainer" = "Web accounts sync your feeds across all your devices"; + +/* Link to the NetNewsWire iCloud syncing limitations and soltutions website. + Link which opens webpage describing iCloud syncing limitations. */ +"link.markdown.icloud-limitations" = "[iCloud Syncing Limitations & Solutions](https://netnewswire.com/help/iCloud)"; + +/* Article Appearance menu title */ +"menu.title.articleappearance" = "Article Appearance"; + +/* About */ +"navigation.title.about" = "About"; + +/* Add Account */ +"navigation.title.add-account" = "Add Account"; + +/* Add Extensions */ +"navigation.title.add-extensions" = "Add Extensions"; + +/* Add Reddit Feed */ +"navigation.title.add-reddit-feed" = "Add Reddit Feed"; + +/* Add Twitter Feed */ +"navigation.title.add-twitter-feed" = "Add Twitter Feed"; + +/* Article Themes */ +"navigation.title.article-themes" = "Article Themes"; + +/* Display & Behaviors */ +"navigation.title.display-and-behaviors" = "Display & Behaviors"; + +/* Enter Name */ +"navigation.title.enter-name" = "Enter Name"; + +/* Manage Accounts */ +"navigation.title.manage-accounts" = "Manage Accounts"; + +/* Manage Extensions */ +"navigation.title.manage-extensions" = "Manage Extensions"; + +/* New Article Notifications */ +"navigation.title.new-article-notifications" = "New Article Notifications"; + +/* Settings */ +"navigation.title.settings" = "Settings"; + +/* Timeline Layout */ +"navigation.title.timeline-layout" = "Timeline Layout"; + +/* Search Articles */ +"searchbar.placeholder.searcharticles" = "Search Articles"; + +/* Title used when desribing the search when scoped to all articles. */ +"searchbar.scope.allarticles" = "All Articles"; + +/* Title used when describing the search when scoped to the current timeline. */ +"searchbar.scope.here" = "Here"; + +/* All Unread pseudo-feed title */ +"smartfeed.title.allunread" = "All Unread"; + +/* Starred pseudo-feed title */ +"smartfeed.title.starred" = "Starred"; + +/* Today pseudo-feed title */ +"smartfeed.title.today" = "Today"; + +/* Smart Feeds group title */ +"smartfeeds.title" = "Smart Feeds"; + +/* Email Address */ +"textfield.placeholder.email-address" = "Email Address"; + +/* Name */ +"textfield.placeholder.name" = "Name"; + +/* Password */ +"textfield.placeholder.password" = "Password"; + +/* Screen Name */ +"textfield.placeholder.screen-name" = "Screen Name"; + +/* Search Term or #hashtag */ +"textfield.placeholder.search-term-hashtag" = "Search Term or #hashtag"; + +/* Username */ +"textfield.placeholder.username" = "Username"; + +/* Username or Email */ +"textfield.placeholder.username-or-email" = "Username or Email"; + +/* Active */ +"toggle.account.active" = "Active"; + +/* Always Show Reader View */ +"toggle.title.always-show-reader-view" = "Always Show Reader View"; + +/* Confirm Mark All as Read */ +"toggle.title.confirm-mark-all-as-read" = "Confirm Mark All as Read"; + +/* Group by Feed */ +"toggle.title.group-by-feed" = "Group by Feed"; + +/* New Article Notifications */ +"toggle.title.notify-about-new-articles" = "New Article Notifications"; + +/* Open Links in NetNewsWire */ +"toggle.title.open-links-in-netnewswire" = "Open Links in NetNewsWire"; + +/* Refresh to Clear Articles */ +"toggle.title.refresh-to-clear-articles" = "Refresh to Clear Articles"; + +/* Sort Oldest to Newest */ +"toggle.title.sort-oldest-to-newest" = "Sort Oldest to Newest"; + diff --git a/iOS/Settings/Account and Extensions/Accounts/AccountsManagementView.swift b/iOS/Settings/Account and Extensions/Accounts/AccountsManagementView.swift index d1fa6f4c6..19d0da441 100644 --- a/iOS/Settings/Account and Extensions/Accounts/AccountsManagementView.swift +++ b/iOS/Settings/Account and Extensions/Accounts/AccountsManagementView.swift @@ -68,19 +68,19 @@ struct AccountsManagementView: View { var body: some View { List { - Section(header: Text("Active Accounts", comment: "Active accounts section header")) { + Section(header: Text("label.text.active-accounts", comment: "Active Accounts")) { ForEach(viewModel.sortedActiveAccounts, id: \.self) { account in accountRow(account) } } - Section(header: Text("Inactive Accounts", comment: "Inactive accounts section header")) { + Section(header: Text("label.text.inactive-accounts", comment: "Inactive Accounts")) { ForEach(viewModel.sortedInactiveAccounts, id: \.self) { account in accountRow(account) } } } - .navigationTitle(Text("Manage Accounts", comment: "Navigation title: Manage Accounts")) + .navigationTitle(Text("navigation.title.manage-accounts", comment: "Manage Accounts")) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button { @@ -93,21 +93,21 @@ struct AccountsManagementView: View { .sheet(isPresented: $viewModel.showAddAccountSheet) { AddAccountListView() } - .alert(Text("Are you sure you want to remove “\(viewModel.accountToDelete?.nameForDisplay ?? "")”?", comment: "Alert title: confirm account removal"), + .alert(Text("alert.title.remove-account.\(viewModel.accountToDelete?.nameForDisplay ?? "")", comment: "Are you sure you want to remove “%@“?"), isPresented: $viewModel.showAccountDeletionAlert) { Button(role: .destructive) { AccountManager.shared.deleteAccount(viewModel.accountToDelete!) } label: { - Text("Remove Account", comment: "Button title") + Text("button.title.remove-account", comment: "Remove Account") } Button(role: .cancel) { viewModel.restoreAccount(viewModel.accountToDelete!) } label: { - Text("Cancel", comment: "Button title") + Text("button.title.cancel", comment: "Cancel") } } message: { - Text("This action cannot be undone.", comment: "Alert message: remove account confirmation") + Text("alert.message.cannot-undo-action", comment: "The action cannot be undone.") } } @@ -119,7 +119,7 @@ struct AccountsManagementView: View { .resizable() .aspectRatio(contentMode: .fit) .frame(width: 25, height: 25) - Text(account.nameForDisplay) + Text(verbatim: account.nameForDisplay) } .swipeActions(edge: .trailing, allowsFullSwipe: false) { if account != AccountManager.shared.defaultAccount { @@ -127,21 +127,27 @@ struct AccountsManagementView: View { viewModel.temporarilyDeleteAccount(account) } label: { Label { - Text("Remove Account", comment: "Button title") + Text("button.title.remove-account", comment: "Remove Account") } icon: { Image(systemName: "trash") } } } Button { - withAnimation { - account.isActive.toggle() - } + account.isActive.toggle() } label: { - if account.isActive { - Image(systemName: "minus.circle") - } else { - Image(systemName: "togglepower") + Label { + if account.isActive { + Text("button.title.deactivate-account", comment: "Deactivate Account") + } else { + Text("button.title.activate-account", comment: "Activate Account") + } + } icon: { + if account.isActive { + Image(systemName: "minus.circle") + } else { + Image(systemName: "togglepower") + } } }.tint(account.isActive ? .yellow : Color(uiColor: AppAssets.primaryAccentColor)) } diff --git a/iOS/Settings/Account and Extensions/Accounts/AddAccountListView.swift b/iOS/Settings/Account and Extensions/Accounts/AddAccountListView.swift index b56e2b3a4..a4a6138ea 100644 --- a/iOS/Settings/Account and Extensions/Accounts/AddAccountListView.swift +++ b/iOS/Settings/Account and Extensions/Accounts/AddAccountListView.swift @@ -74,7 +74,7 @@ struct AddAccountListView: View { webAccountSection selfHostedSection } - .navigationTitle(Text("Add Account", comment: "Navigation title: Add Account")) + .navigationTitle(Text("navigation.title.add-account", comment: "Add Account")) .navigationBarTitleDisplayMode(.inline) .listItemTint(.primary) .toolbar { @@ -82,7 +82,7 @@ struct AddAccountListView: View { Button(role: .cancel) { dismiss() } label: { - Text("Cancel", comment: "Button title") + Text("button.title.cancel", comment: "Button title") } } } @@ -100,11 +100,11 @@ struct AddAccountListView: View { Text(viewModel.showAddAccountSheet.accountType.localizedAccountName()) } } - .alert(Text("Error", comment: "Alert title: Error"), + .alert(Text("alert.title.error", comment: "Error"), isPresented: $viewModel.showAddAccountError.1, actions: { }, message: { - Text("\(viewModel.showAddAccountError.0?.localizedDescription ?? "Unknown Error")") + Text(verbatim: "\(viewModel.showAddAccountError.0?.localizedDescription ?? "Unknown Error")") }) .dismissOnAccountAdd() } @@ -116,7 +116,7 @@ struct AddAccountListView: View { viewModel.showAddAccountSheet = (true, .onMyMac) } label: { Label { - Text(AccountType.onMyMac.localizedAccountName()) + Text(verbatim: AccountType.onMyMac.localizedAccountName()) .foregroundColor(.primary) } icon: { Image(uiImage: AppAssets.image(for: .onMyMac)!) @@ -125,9 +125,9 @@ struct AddAccountListView: View { } } } header: { - Text("Local", comment: "Add Account: Local account section header") + Text("label.text.local-account", comment: "Local Account") } footer: { - Text("Local accounts do not sync your feeds across devices", comment: "Local account section footer") + Text("label.text.local-account-explainer", comment: "Local accounts do not sync your feeds across devices") } } @@ -148,9 +148,9 @@ struct AddAccountListView: View { } .disabled(interactionDisabled(for: .cloudKit)) } header: { - Text("iCloud", comment: "Add Account: iCloud section header") + Text("label.text.cloudkit-account", comment: "iCloud") } footer: { - Text("Your iCloud account syncs your feeds across your Mac and iOS devices", comment: "Add Account: iCloud section footer") + Text("label.text.cloudkit-account-footer", comment: "Your iCloud account syncs your feeds across your Mac and iOS devices") } } @@ -180,9 +180,9 @@ struct AddAccountListView: View { } } } header: { - Text("Web Account", comment: "Add Account: Web Account section header") + Text("label.text.web-account", comment: "Web Account") } footer: { - Text("Web accounts sync your feeds across all your devices", comment: "Add Account: Web Account section footer") + Text("label.text.web-account-explainer", comment: "Web accounts sync your feeds across all your devices") } } @@ -202,9 +202,9 @@ struct AddAccountListView: View { } } } header: { - Text("Self-Hosted", comment: "Add Accont: Self-hosted section header") + Text("label.text.self-hosted-accounts", comment: "Self-Hosted Accounts") } footer: { - Text("Self-hosted accounts sync your feeds across all your devices", comment: "Add Account: Self-hosted section footer") + Text("label.text.self-hosted-accounts-explainer", comment: "Self-hosted accounts sync your feeds across all your devices") } } diff --git a/iOS/Settings/Account and Extensions/Extensions/AddExtensionListView.swift b/iOS/Settings/Account and Extensions/Extensions/AddExtensionListView.swift index 970d5929d..f8a4dbb74 100644 --- a/iOS/Settings/Account and Extensions/Extensions/AddExtensionListView.swift +++ b/iOS/Settings/Account and Extensions/Extensions/AddExtensionListView.swift @@ -18,8 +18,8 @@ struct AddExtensionListView: View { var body: some View { NavigationView { List { - Section(header: Text("Feed 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.")) { + Section(header: Text("label.text.feed-providers", comment: "Feed Providers"), + footer: Text("label.text.feed-providers-explainer", comment: "Feed Providers allow you to subscribe to some pages as if they were RSS feeds")) { ForEach(0..( @@ -36,7 +36,7 @@ struct DisplayAndBehaviorsView: View { // TODO: Add Reader Mode Defaults here. See #3684. } } - .navigationTitle(Text("Display & Behaviors", comment: "Navigation title for Display & Behaviours")) + .navigationTitle(Text("navigation.title.display-and-behaviors", comment: "Display & Behaviors")) .tint(Color(uiColor: AppAssets.primaryAccentColor)) } diff --git a/iOS/Settings/Appearance/TimelineCustomizerView.swift b/iOS/Settings/Appearance/TimelineCustomizerView.swift index 07404c7d9..b9252fc0a 100644 --- a/iOS/Settings/Appearance/TimelineCustomizerView.swift +++ b/iOS/Settings/Appearance/TimelineCustomizerView.swift @@ -15,7 +15,7 @@ struct TimelineCustomizerView: View { var body: some View { List { - Section(header: Text("Icon Size", comment: "Timline Customiser: Icon size section header")) { + Section(header: Text("label.text.icon-size", comment: "Icon Size")) { ZStack { TickMarkSliderView(minValue: 1, maxValue: 3, currentValue: Binding(get: { Float(appDefaults.timelineIconSize.rawValue) @@ -27,7 +27,7 @@ struct TimelineCustomizerView: View { .listRowBackground(Color.clear) .listRowSeparator(.hidden) - Section(header: Text("Number of Lines", comment: "Timeline customiser: Number of lines section header")) { + Section(header: Text("label.text.number-of-lines", comment: "Number of Lines")) { ZStack { TickMarkSliderView(minValue: 1, maxValue: 5, currentValue: Binding(get: { Float(appDefaults.timelineNumberOfLines) @@ -45,7 +45,7 @@ struct TimelineCustomizerView: View { } } .listStyle(.grouped) - .navigationTitle(Text("Timeline Layout", comment: "Navigation bar title for Timeline Layout")) + .navigationTitle(Text("navigation.title.timeline-layout", comment: "Timeline Layout")) } @@ -70,11 +70,11 @@ struct TimelineCustomizerView: View { .bold() .lineLimit(appDefaults.timelineNumberOfLines) HStack { - Text("Feed name", comment: "Feed name placeholder used in timeline preview") + Text("label.placeholder.feed-name", comment: "Feed name") .foregroundColor(.secondary) .font(.caption) Spacer() - Text("08:51", comment: "Sample time used in timeline preview") + Text(verbatim: localizedTime()) .foregroundColor(.secondary) .font(.caption) }.padding(0) @@ -84,6 +84,13 @@ struct TimelineCustomizerView: View { .padding(.vertical, 4) .padding(.leading, 4) } + + func localizedTime() -> String { + let now = Date.now + let formatter = DateFormatter() + formatter.setLocalizedDateFormatFromTemplate("hh:mm") + return formatter.string(from: now) + } } struct TimelineCustomizerView_Previews: PreviewProvider { diff --git a/iOS/Settings/ArticleThemeImporter.swift b/iOS/Settings/ArticleThemeImporter.swift index 8e184842a..df1337199 100644 --- a/iOS/Settings/ArticleThemeImporter.swift +++ b/iOS/Settings/ArticleThemeImporter.swift @@ -20,19 +20,19 @@ struct ArticleThemeImporter: Logging { return } - let localizedTitleText = NSLocalizedString("Install theme “%@” by %@?", comment: "Theme message text") + let localizedTitleText = NSLocalizedString("alert.title.install-theme.%@.%@", comment: "Variable ordering is theme name; author name. In English, the alert title is: Install theme “%@” by %@?") let title = NSString.localizedStringWithFormat(localizedTitleText as NSString, theme.name, theme.creatorName) as String - let localizedMessageText = NSLocalizedString("Author‘s website:\n%@", comment: "Authors website") + let localizedMessageText = NSLocalizedString("alert.message.author-website.%@", comment: "The variable is the author's home page. In English, the alert message is: Author‘s website:\n%@") let message = NSString.localizedStringWithFormat(localizedMessageText as NSString, theme.creatorHomePage) as String let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) - let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel") + let cancelTitle = NSLocalizedString("button.title.cancel", comment: "Cancel") alertController.addAction(UIAlertAction(title: cancelTitle, style: .cancel)) if let url = URL(string: theme.creatorHomePage) { - let visitSiteTitle = NSLocalizedString("Show Website", comment: "Show Website") + let visitSiteTitle = NSLocalizedString("button.title.show-website", comment: "Show Website") let visitSiteAction = UIAlertAction(title: visitSiteTitle, style: .default) { action in UIApplication.shared.open(url) Self.importTheme(controller: controller, filename: filename) @@ -50,20 +50,20 @@ struct ArticleThemeImporter: Logging { } } - let installThemeTitle = NSLocalizedString("Install Theme", comment: "Install Theme") + let installThemeTitle = NSLocalizedString("alert.title.install-theme", comment: "Install Theme") let installThemeAction = UIAlertAction(title: installThemeTitle, style: .default) { action in if ArticleThemesManager.shared.themeExists(filename: filename) { - let title = NSLocalizedString("Duplicate Theme", comment: "Duplicate Theme") - let localizedMessageText = NSLocalizedString("The theme “%@” already exists. Overwrite it?", comment: "Overwrite theme") + let title = NSLocalizedString("alert.title.duplicate-theme", comment: "Duplicate Theme") + let localizedMessageText = NSLocalizedString("alert.message.duplicate-theme.%@", comment: "This message details that this theme is a duplicate and gives the user the option to overwrite the existing theme. In English, the message is: The theme “%@” already exists. Overwrite it?") let message = NSString.localizedStringWithFormat(localizedMessageText as NSString, theme.name) as String let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) - let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel") + let cancelTitle = NSLocalizedString("button.title.cancel", comment: "Cancel") alertController.addAction(UIAlertAction(title: cancelTitle, style: .cancel)) - let overwriteAction = UIAlertAction(title: NSLocalizedString("Overwrite", comment: "Overwrite"), style: .default) { action in + let overwriteAction = UIAlertAction(title: NSLocalizedString("button.title.overwrite", comment: "Overwrite"), style: .default) { action in importTheme() } alertController.addAction(overwriteAction) @@ -88,14 +88,14 @@ struct ArticleThemeImporter: Logging { private extension ArticleThemeImporter { static func confirmImportSuccess(controller: UIViewController, themeName: String) { - let title = NSLocalizedString("Theme installed", comment: "Theme installed") + let title = NSLocalizedString("alert.title.theme-installed", comment: "Theme installed") - let localizedMessageText = NSLocalizedString("The theme “%@” has been installed.", comment: "Theme installed") + let localizedMessageText = NSLocalizedString("alert.message.theme-installed.%@", comment: "This alert message provides confirmation that the theme has been installed. In English, the message is: The theme “%@” has been installed.") let message = NSString.localizedStringWithFormat(localizedMessageText as NSString, themeName) as String let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) - let doneTitle = NSLocalizedString("Done", comment: "Done") + let doneTitle = NSLocalizedString("button.title.done", comment: "Done") alertController.addAction(UIAlertAction(title: doneTitle, style: .default)) controller.present(alertController, animated: true) diff --git a/iOS/Settings/General/SettingsRows.swift b/iOS/Settings/General/SettingsRows.swift index 92d78b572..32d19066f 100644 --- a/iOS/Settings/General/SettingsRows.swift +++ b/iOS/Settings/General/SettingsRows.swift @@ -18,7 +18,7 @@ struct SettingsViewRows { /// This row, when tapped, will open iOS System Settings. static var openSystemSettings: some View { Label { - Text("Open System Settings", comment: "Button: opens device Settings app.") + Text("button.title.open-system-settings", comment: "Open System Settings") } icon: { Image("system.settings") .resizable() @@ -36,7 +36,7 @@ struct SettingsViewRows { static var configureNewArticleNotifications: some View { NavigationLink(destination: NewArticleNotificationsView()) { Label { - Text("New Article Notifications", comment: "Button: opens New Article Notifications view") + Text("button.title.new-article-notifications", comment: "New Article Notifications") } icon: { Image("notifications.sounds") .resizable() @@ -51,7 +51,7 @@ struct SettingsViewRows { static var addAccount: some View { NavigationLink(destination: AccountsManagementView()) { Label { - Text("Manage Accounts", comment: "Button: opens Accounts Management view") + Text("button.title.manage-accounts", comment: "Manage Accounts") } icon: { Image("app.account") .resizable() @@ -66,7 +66,7 @@ struct SettingsViewRows { static var manageExtensions: some View { NavigationLink(destination: ExtensionsManagementView()) { Label { - Text("Manage Extensions", comment: "Button: opens Extensions Management view") + Text("button.title.manage-extensions", comment: "Manage Extensions") } icon: { Image("app.extension") .resizable() @@ -83,7 +83,7 @@ struct SettingsViewRows { showImportActionSheet.wrappedValue.toggle() } label: { Label { - Text("Import Subscriptions", comment: "Button: opens import subscriptions view") + Text("button.title.import-subscriptions", comment: "Import Subscriptions") .foregroundColor(.primary) } icon: { @@ -102,7 +102,7 @@ struct SettingsViewRows { showExportActionSheet.wrappedValue.toggle() } label: { Label { - Text("Export Subscriptions", comment: "Button: opens Export Subscriptions view") + Text("button.title.export-subscriptions", comment: "Export Subscriptions") .foregroundColor(.primary) } icon: { @@ -119,7 +119,7 @@ struct SettingsViewRows { /// - Returns: `Toggle` static func sortOldestToNewest(_ preference: Binding) -> some View { Toggle(isOn: preference) { - Text("Sort Oldest to Newest", comment: "Toggle: Sort articles from oldest to newest when enabled.") + Text("toggle.title.sort-oldest-to-newest", comment: "Sort Oldest to Newest") } } @@ -128,7 +128,7 @@ struct SettingsViewRows { /// - Returns: `Toggle` static func groupByFeed(_ preference: Binding) -> some View { Toggle(isOn: preference) { - Text("Group by Feed", comment: "Toggle: groups articles by feed when enabled.") + Text("toggle.title.group-by-feed", comment: "Group by Feed") } } @@ -137,7 +137,7 @@ struct SettingsViewRows { /// - Returns: `Toggle` static func refreshToClearReadArticles(_ preference: Binding) -> some View { Toggle(isOn: preference) { - Text("Refresh to Clear Read Articles", comment: "Toggle: when enabled, articles will be cleared when the timeline is refreshed") + Text("toggle.title.refresh-to-clear-articles", comment: "Refresh to Clear Articles") } } @@ -147,7 +147,7 @@ struct SettingsViewRows { NavigationLink { TimelineCustomizerView() } label: { - Text("Timeline Layout", comment: "Button: opens the timeline customiser") + Text("button.title.timeline-layout", comment: "Timeline Layout") } } @@ -156,7 +156,7 @@ struct SettingsViewRows { static var themeSelection: some View { NavigationLink(destination: ArticleThemeManagerView()) { HStack { - Text("Article Theme", comment: "Button: opens the Article Theme manager view") + Text("button.title.artice-themes", comment: "Article Themes") Spacer() Text(ArticleThemesManager.shared.currentTheme.name) .font(.callout) @@ -170,7 +170,7 @@ struct SettingsViewRows { /// - Returns: `Toggle` static func confirmMarkAllAsRead(_ preference: Binding) -> some View { Toggle(isOn: preference) { - Text("Confirm Mark All as Read", comment: "Toggle: when enabled, the app will confirm whether to mark all items as read") + Text("toggle.title.confirm-mark-all-as-read", comment: "Confirm Mark All as Read") } } @@ -179,7 +179,7 @@ struct SettingsViewRows { /// - Returns: `Toggle` static func openLinksInNetNewsWire(_ preference: Binding) -> some View { Toggle(isOn: preference) { - Text("Open Links in NetNewsWire", comment: "Toggle: when enabled, links will open in NetNewsWire") + Text("toggle.title.open-links-in-netnewswire", comment: "Open Links in NetNewsWire") } } @@ -190,7 +190,7 @@ struct SettingsViewRows { static func configureAppearance(_ isShown: Binding) -> some View { NavigationLink(destination: DisplayAndBehaviorsView(), isActive: isShown) { Label { - Text("Display & Behaviours", comment: "Button: opens the Display and Appearance view.") + Text("button.title.display-and-behaviors", comment: "Display & Behaviors") } icon: { Image("app.appearance") .resizable() @@ -228,7 +228,7 @@ struct SettingsViewRows { AboutView() } label: { Label { - Text("About", comment: "Button: opens the NetNewsWire about view.") + Text("button.title.about", comment: "About") } icon: { Image(systemName: "info.circle") .resizable() diff --git a/iOS/Settings/General/SettingsView.swift b/iOS/Settings/General/SettingsView.swift index 32b3b13b0..18e74455a 100644 --- a/iOS/Settings/General/SettingsView.swift +++ b/iOS/Settings/General/SettingsView.swift @@ -23,18 +23,18 @@ struct SettingsView: View { NavigationView { List { // Device Permissions - Section(header: Text("Device Permissions", 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.")) { + Section(header: Text("label.text.device-permissions", comment: "Device Permissions"), + footer: Text("label.text.device-permissions-explainer", comment: "Configure NetNewsWire's access to Siri, background app refresh, mobile data, and more.")) { SettingsViewRows.openSystemSettings } // Account/Extensions/OPML Management - 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.")) { + Section(header: Text("label.text.accounts-and-extensions", comment: "Settings: Accounts & Extensions section header."), + footer: Text("label.text.account-and-extensions-explainer", comment: "Add, delete, enable, or disable accounts and extensions.")) { SettingsViewRows.addAccount SettingsViewRows.manageExtensions SettingsViewRows.importOPML(showImportActionSheet: $viewModel.showImportActionSheet) - .confirmationDialog(Text("Choose an account to receive the imported feeds and folders", comment: "Import OPML confirmation title."), + .confirmationDialog(Text("actionsheet.title.choose-opml-destination", comment: "Choose an account to receive the imported feeds and folders"), 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("Choose an account with the subscriptions to export", comment: "Export OPML confirmation title."), + .confirmationDialog(Text("actionsheet.title.choose-opml-export-account", comment: "Choose an account with the subscriptions to export"), 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", comment: "Settings: Appearance section header."), - footer: Text("Manage the look, feel, and behavior of NetNewsWire.", comment: "Settings: Appearance section footer.")) { + Section(header: Text("label.text.appearance", comment: "Settings: Appearance section header."), + footer: Text("label.text.appearance-explainer", comment: "Manage the look, feel, and behavior of NetNewsWire.")) { 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", comment: "Navigation bar title for Settings.")) + .navigationTitle(Text("navigation.title.settings", comment: "Settings")) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading, content: { - Button(action: { dismiss() }, label: { Text("Done", comment: "Button title") }) + Button(action: { dismiss() }, label: { Text("button.title.done", comment: "Done") }) }) } .sheet(isPresented: $viewModel.showAddAccountView) { @@ -144,18 +144,18 @@ struct SettingsView: View { viewModel.showImportExportError = true } }) - .alert(Text("Imported Successfully", comment: "Alert title: imported OPML file successfully."), + .alert(Text("alert.title.opml.opml-import-success", comment: "Alert title: Imported Successfully"), isPresented: $viewModel.showImportSuccess, actions: {}, - message: { Text("Subscriptions have been imported to your \(viewModel.importAccount?.nameForDisplay ?? "") account.", comment: "Alert message: imported OPML file successfully.") }) - .alert(Text("Exported Successfully", comment: "Alert title: exported OPML file successfully."), + message: { Text("alert.message.opml-import-success.%@", comment: "Subscriptions have been imported to your “%@“ account.") }) + .alert(Text("alert.title.opml.opml-export-success", comment: "Alert title: Exported Successfully"), isPresented: $viewModel.showExportSuccess, actions: {}, - message: { Text("Your OPML file has been successfully exported.", comment: "Alert message: exported OPML file successfully.") }) - .alert(Text("Error", comment: "Alert title: Error"), + message: { Text("alert.message.opml.opml-export-success", comment: "Your subscriptions have been exported successfully.") }) + .alert(Text("alert.title.error", comment: "Error"), isPresented: $viewModel.showImportExportError, actions: {}, - message: { Text(viewModel.importExportError?.localizedDescription ?? "Import/Export Error") } ) + message: { Text(verbatim: viewModel.importExportError?.localizedDescription ?? "") } ) }.navigationViewStyle(.stack) } } diff --git a/iOS/Settings/Help/AboutView.swift b/iOS/Settings/Help/AboutView.swift index 69b4b3a55..7106c876e 100644 --- a/iOS/Settings/Help/AboutView.swift +++ b/iOS/Settings/Help/AboutView.swift @@ -13,21 +13,21 @@ struct AboutView: View, LoadableAboutData { var body: some View { List { Section(header: aboutHeaderView) {} - Section(header: Text("Primary Contributors", comment: "About: Primary Contributors section header")) { + Section(header: Text("label.text.primary-contributors", comment: "Primary Contributors")) { ForEach(0.. Void)? = nil) { if let decodingError = error as? DecodingError { - let errorTitle = NSLocalizedString("Error", comment: "Error") + let errorTitle = NSLocalizedString("alert.title.error", comment: "Error") var informativeText: String = "" switch decodingError { case .typeMismatch(let type, _): - let localizedError = NSLocalizedString("This theme cannot be used because the the type—“%@”—is mismatched in the Info.plist", comment: "Type mismatch") - informativeText = NSString.localizedStringWithFormat(localizedError as NSString, type as! CVarArg) as String + let localizedError = NSLocalizedString("alert.message.theme-type-mismatch.%@", comment: "Error message when a type is mismatched. In English, the message is: This theme cannot be used because the the type—“%@”—is mismatched in the Info.plist.") + informativeText = String.localizedStringWithFormat(localizedError, type as! CVarArg) presentError(title: errorTitle, message: informativeText, dismiss: dismiss) case .valueNotFound(let value, _): - let localizedError = NSLocalizedString("This theme cannot be used because the the value—“%@”—is not found in the Info.plist.", comment: "Decoding value missing") - informativeText = NSString.localizedStringWithFormat(localizedError as NSString, value as! CVarArg) as String + let localizedError = NSLocalizedString("alert.message.theme-value-missing.%@", comment: "Error message when a value is missing. In English, the message is: This theme cannot be used because the the value—“%@”—is not found in the Info.plist.") + informativeText = String.localizedStringWithFormat(localizedError, value as! CVarArg) presentError(title: errorTitle, message: informativeText, dismiss: dismiss) case .keyNotFound(let codingKey, _): - let localizedError = NSLocalizedString("This theme cannot be used because the the key—“%@”—is not found in the Info.plist.", comment: "Decoding key missing") - informativeText = NSString.localizedStringWithFormat(localizedError as NSString, codingKey.stringValue) as String + let localizedError = NSLocalizedString("alert.message.theme-key-missing.%@", comment: "Error message when a key is missing. In English, the message is: This theme cannot be used because the the key—“%@”—is not found in the Info.plist.") + informativeText = String.localizedStringWithFormat(localizedError, codingKey.stringValue) presentError(title: errorTitle, message: informativeText, dismiss: dismiss) case .dataCorrupted(let context): guard let error = context.underlyingError as NSError?, @@ -36,7 +36,7 @@ extension UIViewController { presentError(title: errorTitle, message: informativeText, dismiss: dismiss) return } - let localizedError = NSLocalizedString("This theme cannot be used because of data corruption in the Info.plist. %@.", comment: "Decoding key missing") + let localizedError = NSLocalizedString("alert.message.theme-data-corrupted.%@", comment: "Error message when theme data is corrupted. The variable is a description provided by Apple. In English, the message is: This theme cannot be used because of data corruption in the Info.plist. %@.") informativeText = NSString.localizedStringWithFormat(localizedError as NSString, debugDescription) as String presentError(title: errorTitle, message: informativeText, dismiss: dismiss) @@ -45,7 +45,7 @@ extension UIViewController { presentError(title: errorTitle, message: informativeText, dismiss: dismiss) } } else { - let errorTitle = NSLocalizedString("Error", comment: "Error") + let errorTitle = NSLocalizedString("alert.title.error", comment: "Error") presentError(title: errorTitle, message: error.localizedDescription, dismiss: dismiss) } } diff --git a/iOS/en.lproj/LaunchScreenPad.strings b/iOS/en.lproj/LaunchScreenPad.strings new file mode 100644 index 000000000..43cbd2c8d --- /dev/null +++ b/iOS/en.lproj/LaunchScreenPad.strings @@ -0,0 +1,6 @@ + +/* Class = "UIBarButtonItem"; title = "Item"; ObjectID = "AK3-N5-4ke"; Note = "Settings bar button item"; */ +"AK3-N5-4ke.title" = "Item"; + +/* Class = "UINavigationItem"; title = "Feeds"; ObjectID = "lE1-xw-gjH"; Note = "Navigation bar title: Feeds"; */ +"lE1-xw-gjH.title" = "Feeds"; diff --git a/iOS/en.lproj/LaunchScreenPhone.strings b/iOS/en.lproj/LaunchScreenPhone.strings new file mode 100644 index 000000000..f72cda50e --- /dev/null +++ b/iOS/en.lproj/LaunchScreenPhone.strings @@ -0,0 +1,6 @@ + +/* Class = "UIBarButtonItem"; title = "Item"; ObjectID = "AK3-N5-4ke"; Note = "Bar button item: Settings"; */ +"AK3-N5-4ke.title" = "Item"; + +/* Class = "UINavigationItem"; title = "Feeds"; ObjectID = "lE1-xw-gjH"; Note = "Navigation bar title: Feeds"; */ +"lE1-xw-gjH.title" = "Feeds"; diff --git a/iOS/en.lproj/Main.strings b/iOS/en.lproj/Main.strings new file mode 100644 index 000000000..1e26ef220 --- /dev/null +++ b/iOS/en.lproj/Main.strings @@ -0,0 +1,42 @@ + +/* Class = "UILabel"; text = "Label"; ObjectID = "0Hz-Dv-MhU"; Note = "This does not need to be localized as it is provided by feed data."; */ +"0Hz-Dv-MhU.text" = "Label"; + +/* Class = "UIBarButtonItem"; title = "Next Article"; ObjectID = "2qz-M5-Yhk"; Note = "Button title: Next Article"; */ +"2qz-M5-Yhk.title" = "Next Article"; + +/* Class = "UIBarButtonItem"; title = "Next Unread"; ObjectID = "2w5-e9-C2V"; Note = "Button title: Next Unread"; */ +"2w5-e9-C2V.title" = "Next Unread"; + +/* Class = "UILabel"; text = "Blog Author"; ObjectID = "7GV-PV-YVq"; Note = "This does not need to be localized as it is provided by feed data."; */ +"7GV-PV-YVq.text" = "Blog Author"; + +/* Class = "UIViewController"; title = "Detail"; ObjectID = "JEX-9P-axG"; */ +"JEX-9P-axG.title" = "Detail"; + +/* Class = "UIBarButtonItem"; title = "Settings"; ObjectID = "TlU-Pg-ATe"; Note = "Settings bar button item."; */ +"TlU-Pg-ATe.title" = "Settings"; + +/* Class = "UILabel"; text = "Blog Name"; ObjectID = "YsT-Lt-Zry"; Note = "This does not need to be localized as it is provided by feed data."; */ +"YsT-Lt-Zry.text" = "Blog Name"; + +/* Class = "UINavigationItem"; title = "Feeds"; ObjectID = "Zdf-7t-Un8"; Note = "Title used in the navigation bar for the Feeds list."; */ +"Zdf-7t-Un8.title" = "Feeds"; + +/* Class = "UIBarButtonItem"; title = "Item"; ObjectID = "fTv-eX-72r"; Note = "Mark All as Read button title."; */ +"fTv-eX-72r.title" = "Item"; + +/* Class = "UIBarButtonItem"; title = "Toggle Read"; ObjectID = "hy0-LS-MzE"; Note = "Button title: Toggle Read"; */ +"hy0-LS-MzE.title" = "Toggle Read"; + +/* Class = "UILabel"; text = "Article Title"; ObjectID = "iFp-rn-HhQ"; Note = "This does not need to be localized as it is provided by feed data."; */ +"iFp-rn-HhQ.text" = "Article Title"; + +/* Class = "UIBarButtonItem"; title = "Previous Article"; ObjectID = "v4j-fq-23N"; Note = "Button title: Previous Article"; */ +"v4j-fq-23N.title" = "Previous Article"; + +/* Class = "UIBarButtonItem"; title = "Toggle Starred"; ObjectID = "wU4-eH-wC9"; Note = "Button title: Toggle Starred"; */ +"wU4-eH-wC9.title" = "Toggle Starred"; + +/* Class = "UINavigationItem"; title = "Timeline"; ObjectID = "wcC-1L-ug4"; Note = "Title used in the navigation bar for the Timeline."; */ +"wcC-1L-ug4.title" = "Timeline";