mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Localization work
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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 = "<group>"; };
|
||||
DF28B454294FE74A00C4D8CA /* ExtensionSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionSectionHeader.swift; sourceTree = "<group>"; };
|
||||
DF28B4562950163F00C4D8CA /* EnableExtensionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionViewModel.swift; sourceTree = "<group>"; };
|
||||
DF332714295BBBB900BFD911 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = "<group>"; };
|
||||
DF332716295BBBBF00BFD911 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LaunchScreenPad.strings; sourceTree = "<group>"; };
|
||||
DF332718295BBBC200BFD911 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LaunchScreenPhone.strings; sourceTree = "<group>"; };
|
||||
DF3630EA2936183D00326FB8 /* OPMLDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLDocument.swift; sourceTree = "<group>"; };
|
||||
DF3630EE293618A900326FB8 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
|
||||
DF394EFF29357A180081EB6E /* NewArticleNotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewArticleNotificationsView.swift; sourceTree = "<group>"; };
|
||||
@@ -1630,6 +1634,7 @@
|
||||
DFD406FB291FB63B00C02962 /* SettingsHelpSheets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsHelpSheets.swift; sourceTree = "<group>"; };
|
||||
DFD406FE291FDC0C00C02962 /* DisplayAndBehaviorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayAndBehaviorsView.swift; sourceTree = "<group>"; };
|
||||
DFD6AACB27ADE80900463FAD /* NewsFax.nnwtheme */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = NewsFax.nnwtheme; sourceTree = "<group>"; };
|
||||
DFD86797295D553D0070D62D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DFE522A22953DEF400376B77 /* CustomInsetGroupedRowStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomInsetGroupedRowStyle.swift; sourceTree = "<group>"; };
|
||||
DFFC4E7328E95C01006B82AF /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||
FF3ABF09232599450074C542 /* ArticleSorterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorterTests.swift; sourceTree = "<group>"; };
|
||||
@@ -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 = "<group>";
|
||||
@@ -4727,6 +4735,7 @@
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
84C9FCA02262A1B300D921D6 /* Base */,
|
||||
DF332714295BBBB900BFD911 /* en */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
@@ -4735,10 +4744,19 @@
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
84C9FCA32262A1B800D921D6 /* Base */,
|
||||
DF332718295BBBC200BFD911 /* en */,
|
||||
);
|
||||
name = LaunchScreenPhone.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DFD86798295D553D0070D62D /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
DFD86797295D553D0070D62D /* en */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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? {
|
||||
|
||||
@@ -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? {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
754
iOS/Resources/en.lproj/Localizable.strings
Normal file
754
iOS/Resources/en.lproj/Localizable.strings
Normal file
@@ -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";
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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..<availableExtensionPointTypes.count, id: \.self) { i in
|
||||
Button {
|
||||
showExtensionPointView = (availableExtensionPointTypes[i], true)
|
||||
@@ -35,7 +35,7 @@ struct AddExtensionListView: View {
|
||||
}
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationTitle(Text("Add Extensions", comment: "Navigation title: Add Extensions"))
|
||||
.navigationTitle(Text("navigation.title.add-extensions", comment: "Add Extensions"))
|
||||
.sheet(isPresented: $showExtensionPointView.1, content: {
|
||||
if showExtensionPointView.0 != nil {
|
||||
EnableExtensionPointView(extensionPoint: showExtensionPointView.0!)
|
||||
@@ -46,7 +46,7 @@ struct AddExtensionListView: View {
|
||||
Button(role: .cancel) {
|
||||
dismiss()
|
||||
} label: {
|
||||
Text("Cancel", comment: "Button title")
|
||||
Text("button.title.cancel", comment: "Cancel")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@ struct EnableExtensionPointView: View {
|
||||
Section(footer: extensionExplainer) {}
|
||||
Section { enableButton }
|
||||
}
|
||||
.alert(Text("Error", comment: "Alert title: Error"), isPresented: $extensionError.1, actions: {
|
||||
.alert(Text("alert.title.error", comment: "Error"), isPresented: $extensionError.1, actions: {
|
||||
}, message: {
|
||||
Text(extensionError.0?.localizedDescription ?? "Unknown Error")
|
||||
Text(verbatim: extensionError.0?.localizedDescription ?? "Unknown Error")
|
||||
})
|
||||
.alert(Text("Error", comment: "Alert title: Error"), isPresented: $viewModel.showExtensionError.1, actions: {
|
||||
.alert(Text("alert.title.error", comment: "Error"), isPresented: $viewModel.showExtensionError.1, actions: {
|
||||
}, message: {
|
||||
Text(viewModel.showExtensionError.0?.localizedDescription ?? "Unknown Error")
|
||||
Text(verbatim: viewModel.showExtensionError.0?.localizedDescription ?? "Unknown Error")
|
||||
})
|
||||
.navigationTitle(extensionPoint.title)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
@@ -56,7 +56,7 @@ struct EnableExtensionPointView: View {
|
||||
} label: {
|
||||
HStack {
|
||||
Spacer()
|
||||
Text("Enable Extension", comment: "Button title")
|
||||
Text("button.title.enable-extension", comment: "Enable Extension")
|
||||
Spacer()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ struct ExtensionsManagementView: View {
|
||||
List {
|
||||
activeExtensionsSection
|
||||
}
|
||||
.navigationTitle(Text("Manage Extensions", comment: "Navigation title: Manage Extensions"))
|
||||
.navigationTitle(Text("navigation.title.manage-extensions", comment: " Manage Extensions"))
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
@@ -33,13 +33,13 @@ struct ExtensionsManagementView: View {
|
||||
.sheet(isPresented: $showAddExtensionView) {
|
||||
AddExtensionListView()
|
||||
}
|
||||
.alert(Text("Are you sure you want to deactivate “\(extensionToDeactivate?.value.title ?? "")?", comment: "Alert title: confirm deactivate extension"),
|
||||
.alert(Text("alert.title.deactive-extension.\(extensionToDeactivate?.value.extensionPointID.extensionPointType.title ?? "").\(extensionToDeactivate?.value.title ?? "")", comment: "Are you sure you want to deactivate the %@ extension “%@“? Note: the ordering of the variables is "),
|
||||
isPresented: $showDeactivateAlert) {
|
||||
|
||||
Button(role: .destructive) {
|
||||
ExtensionPointManager.shared.deactivateExtensionPoint(extensionToDeactivate!.value.extensionPointID)
|
||||
} label: {
|
||||
Text("Deactivate Extension", comment: "Button: deactivate extension.")
|
||||
Text("button.title.deactivate-extension", comment: "Deactivate Extension")
|
||||
}
|
||||
|
||||
Button(role: .cancel) {
|
||||
@@ -49,7 +49,7 @@ struct ExtensionsManagementView: View {
|
||||
}
|
||||
|
||||
} message: {
|
||||
Text("This action cannot be undone.", comment: "Alert message: confirmation that deactivation of extension cannot be undone.")
|
||||
Text("alert.message.cannot-undo-action", comment: "This action cannot be undone.")
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .ActiveExtensionPointsDidChange)) { _ in
|
||||
availableExtensionPointTypes = ExtensionPointManager.shared.availableExtensionPointTypes.sorted(by: { $0.title < $1.title })
|
||||
@@ -58,7 +58,7 @@ struct ExtensionsManagementView: View {
|
||||
}
|
||||
|
||||
private var activeExtensionsSection: some View {
|
||||
Section(header: Text("Active Extensions", comment: "Active Extensions section header")) {
|
||||
Section(header: Text("label.text.active-extensions", comment: "Active Extensions")) {
|
||||
ForEach(0..<ExtensionPointManager.shared.activeExtensionPoints.count, id: \.self) { i in
|
||||
let point = Array(ExtensionPointManager.shared.activeExtensionPoints)[i]
|
||||
NavigationLink {
|
||||
@@ -74,7 +74,7 @@ struct ExtensionsManagementView: View {
|
||||
extensionToDeactivate = point
|
||||
showDeactivateAlert = true
|
||||
} label: {
|
||||
Text("Deactivate", comment: "Button: deactivates extension")
|
||||
Text("button.title.deactivate-extension", comment: "Deactivate Extension")
|
||||
Image(systemName: "minus.circle")
|
||||
}.tint(.red)
|
||||
}
|
||||
|
||||
@@ -21,21 +21,21 @@ struct ArticleThemeManagerView: View {
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section(header: Text("Ranchero Software Themes", comment: "Section header for installed themes"), footer: Text("These themes cannot be deleted.", comment: "Section footer for installed themes.")) {
|
||||
Section(header: Text("label.text.default-themes", comment: "Default Themes"), footer: Text("label.text.default-themes-explainer", comment: "These themes cannot be deleted.")) {
|
||||
articleThemeRow(try! themeManager.articleThemeWithThemeName("Default"))
|
||||
ForEach(0..<installedFirstPartyThemes.count, id: \.self) { i in
|
||||
articleThemeRow(installedFirstPartyThemes[i])
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Third Party Themes", comment: "Section header for third party themes")) {
|
||||
Section(header: Text("label.text.third-party-themes", comment: "Third Party Themes")) {
|
||||
ForEach(0..<installedThirdPartyThemes.count, id: \.self) { i in
|
||||
articleThemeRow(installedThirdPartyThemes[i])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.navigationTitle(Text("Article Themes", comment: "Navigation bar title for Article Themes"))
|
||||
.navigationTitle(Text("navigation.title.article-themes", comment: "Article Themes"))
|
||||
.task {
|
||||
updateThemesArrays()
|
||||
}
|
||||
@@ -45,7 +45,7 @@ struct ArticleThemeManagerView: View {
|
||||
showImportThemeView = true
|
||||
} label: {
|
||||
Label {
|
||||
Text("Import Theme", comment: "Button title")
|
||||
Text("button.title.import-theme", comment: "Import Theme")
|
||||
} icon: {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
@@ -69,23 +69,23 @@ struct ArticleThemeManagerView: View {
|
||||
showImportErrorAlert = (failure, true)
|
||||
}
|
||||
}
|
||||
.alert(Text("Are you sure you want to delete “\(showDeleteConfirmation.0)”?", comment: "Alert title: confirm theme deletion"),
|
||||
.alert(Text("alert.title.delete-theme.\(showDeleteConfirmation.0)", comment: "In English: Are you sure you want to delete “%@”?"),
|
||||
isPresented: $showDeleteConfirmation.1, actions: {
|
||||
Button(role: .destructive) {
|
||||
themeManager.deleteTheme(themeName: showDeleteConfirmation.0)
|
||||
} label: {
|
||||
Text("Delete Theme", comment: "Button title")
|
||||
Text("button.title.delete-theme", comment: "Delete Theme")
|
||||
}
|
||||
|
||||
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: confirm theme deletion")
|
||||
Text("alert.message.cannot-undo-action", comment: "This action cannot be undone.")
|
||||
})
|
||||
.alert(Text("Import Theme", comment: "Alert title: confirm theme import"),
|
||||
.alert(Text("alert.title.import-theme", comment: "Import Theme"),
|
||||
isPresented: $showImportConfirmationAlert.1,
|
||||
actions: {
|
||||
Button {
|
||||
@@ -107,40 +107,35 @@ struct ArticleThemeManagerView: View {
|
||||
} label: {
|
||||
let exists = themeManager.themeExists(filename: showImportConfirmationAlert.0?.path ?? "")
|
||||
if exists == true {
|
||||
Text("Overwrite Theme", comment: "Button title")
|
||||
Text("button.title.overwrite-theme", comment: "Overwrite Theme")
|
||||
} else {
|
||||
Text("Import Theme", comment: "Button title")
|
||||
Text("button.title.import-theme", comment: "Import Theme")
|
||||
}
|
||||
}
|
||||
|
||||
Button(role: .cancel) {
|
||||
|
||||
} label: {
|
||||
Text("Cancel", comment: "Button title")
|
||||
Text("button.title.cancel", comment: "Cancel")
|
||||
}
|
||||
}, message: {
|
||||
let exists = themeManager.themeExists(filename: showImportConfirmationAlert.0?.path ?? "")
|
||||
if exists {
|
||||
Text("The theme “\(showImportConfirmationAlert.0?.name ?? "")” already exists. Do you want to overwrite it?", comment: "Alert message: confirm theme import and overwrite of existing theme")
|
||||
Text("alert.message.duplicate-theme.\(showImportConfirmationAlert.0?.name ?? "")", comment: "In English: The theme “%@” already exists. Do you want to overwrite it?")
|
||||
} else {
|
||||
Text("Are you sure you want to import “\(showImportConfirmationAlert.0?.name ?? "")” by \(showImportConfirmationAlert.0?.creatorName ?? "")?", comment: "Alert message: confirm theme import")
|
||||
Text("alert.message.import-theme.\(showImportConfirmationAlert.0?.name ?? "").\(showImportConfirmationAlert.0?.creatorName ?? "")", comment: "Are you sure you want to import “%@” by %@")
|
||||
}
|
||||
})
|
||||
.alert(Text("Imported Successfully", comment: "Alert title: theme imported successfully"),
|
||||
.alert(Text("alert.title.imported-theme-succesfully", comment: "Imported Successfully"),
|
||||
isPresented: $showImportSuccessAlert,
|
||||
actions: {
|
||||
Button(role: .cancel) {
|
||||
|
||||
} label: {
|
||||
Text("Dismiss", comment: "Button title")
|
||||
}
|
||||
}, message: {
|
||||
Text("The theme “\(showImportConfirmationAlert.0?.name ?? "")” has been imported.", comment: "Alert message: theme imported successfully")
|
||||
actions: { },
|
||||
message: {
|
||||
Text("alert.message.imported-theme-successfully.\(showImportConfirmationAlert.0?.name ?? "")", comment: "The theme “%@” has been imported.")
|
||||
})
|
||||
.alert(Text("Error", comment: "Alert title: Error"),
|
||||
.alert(Text("alert.title.error", comment: "Error"),
|
||||
isPresented: $showImportErrorAlert.1,
|
||||
actions: { }, message: {
|
||||
Text("\(showImportErrorAlert.0?.localizedDescription ?? "")")
|
||||
Text(verbatim: "\(showImportErrorAlert.0?.localizedDescription ?? "")")
|
||||
})
|
||||
.onReceive(themeManager.objectWillChange) { _ in
|
||||
updateThemesArrays()
|
||||
@@ -153,9 +148,9 @@ struct ArticleThemeManagerView: View {
|
||||
} label: {
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text(theme.name)
|
||||
Text(verbatim: theme.name)
|
||||
.foregroundColor(.primary)
|
||||
Text("Created by \(theme.creatorName)", comment: "Article theme creator byline.")
|
||||
Text("label.text.theme-created-byline.\(theme.creatorName)", comment: "Created by %@")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
@@ -174,7 +169,7 @@ struct ArticleThemeManagerView: View {
|
||||
Button {
|
||||
showDeleteConfirmation = (theme.name, true)
|
||||
} label: {
|
||||
Text("Delete", comment: "Button title")
|
||||
Text("button.title.delete", comment: "Delete")
|
||||
Image(systemName: "trash")
|
||||
}
|
||||
.tint(.red)
|
||||
|
||||
@@ -28,7 +28,7 @@ struct ColorPaletteSelectorView: View {
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 40.0, height: 40.0)
|
||||
Text("Always Light", comment: "Button: always use light display mode")
|
||||
Text("label.text.use-light-appearance", comment: "Always Light")
|
||||
.font(.subheadline)
|
||||
if AppDefaults.userInterfaceColorPalette == .light {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
@@ -47,7 +47,7 @@ struct ColorPaletteSelectorView: View {
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 40.0, height: 40.0)
|
||||
Text("Always Dark", comment: "Button: always use dark display mode")
|
||||
Text("label.text.use-dark-appearance", comment: "Always Dark")
|
||||
.font(.subheadline)
|
||||
if AppDefaults.userInterfaceColorPalette == .dark {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
@@ -66,7 +66,7 @@ struct ColorPaletteSelectorView: View {
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 40.0, height: 40.0)
|
||||
Text("Use Device", comment: "Button: always use device display mode")
|
||||
Text("label.text.use-device-appearance", comment: "Use Device")
|
||||
.font(.subheadline)
|
||||
if AppDefaults.userInterfaceColorPalette == .automatic {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
|
||||
@@ -14,19 +14,19 @@ struct DisplayAndBehaviorsView: View {
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section(header: Text("Application", comment: "Display & Behaviours: Application section header")) {
|
||||
Section(header: Text("label.text.application", comment: "Application")) {
|
||||
ColorPaletteSelectorView()
|
||||
.listRowBackground(Color.clear)
|
||||
}
|
||||
|
||||
Section(header: Text("Timeline", comment: "Display & Behaviours: Timeline section header")) {
|
||||
Section(header: Text("label.text.timeline", comment: "Timeline")) {
|
||||
SettingsViewRows.sortOldestToNewest($appDefaults.timelineSortDirectionBool)
|
||||
SettingsViewRows.groupByFeed($appDefaults.timelineGroupByFeed)
|
||||
SettingsViewRows.refreshToClearReadArticles($appDefaults.refreshClearsReadArticles)
|
||||
SettingsViewRows.timelineLayout
|
||||
}
|
||||
|
||||
Section(header: Text("Articles", comment: "Display & Behaviours: Articles section header")) {
|
||||
Section(header: Text("label.text.articles", comment: "Articles")) {
|
||||
SettingsViewRows.themeSelection
|
||||
SettingsViewRows.confirmMarkAllAsRead($appDefaults.confirmMarkAllAsRead)
|
||||
SettingsViewRows.openLinksInNetNewsWire(Binding<Bool>(
|
||||
@@ -36,7 +36,7 @@ struct DisplayAndBehaviorsView: View {
|
||||
// TODO: Add Reader Mode Defaults here. See #3684.
|
||||
}
|
||||
}
|
||||
.navigationTitle(Text("Display & Behaviors", comment: "Navigation title for Display & Behaviours"))
|
||||
.navigationTitle(Text("navigation.title.display-and-behaviors", comment: "Display & Behaviors"))
|
||||
.tint(Color(uiColor: AppAssets.primaryAccentColor))
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<Bool>) -> 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<Bool>) -> 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<Bool>) -> 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<Bool>) -> 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<Bool>) -> 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<Bool>) -> 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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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..<about.PrimaryContributors.count, id: \.self) { i in
|
||||
contributorView(about.PrimaryContributors[i])
|
||||
}
|
||||
}
|
||||
Section(header: Text("Additional Contributors", comment: "About: Additional Contributors section header")) {
|
||||
Section(header: Text("label.text.additional-contributors", comment: "Additional Contributors")) {
|
||||
ForEach(0..<about.AdditionalContributors.count, id: \.self) { i in
|
||||
contributorView(about.AdditionalContributors[i])
|
||||
}
|
||||
}
|
||||
Section(header: Text("Thanks", comment: "About: Thanks section header"), footer: thanks, content: {})
|
||||
Section(header: Text("label.text.thanks", comment: "Thanks"), footer: thanks, content: {})
|
||||
Section(footer: copyright, content: {})
|
||||
}
|
||||
.listStyle(.insetGrouped)
|
||||
.navigationTitle(Text("About", comment: "Navigation title: About"))
|
||||
.navigationTitle(Text("navigation.title.about", comment: "About"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
@@ -46,10 +46,10 @@ struct AboutView: View, LoadableAboutData {
|
||||
.foregroundColor(.secondary)
|
||||
.font(.callout)
|
||||
|
||||
Text("By Brent Simmons and the Ranchero Software team.", comment: "NetNewsWire byline.")
|
||||
Text("label.text.netnewswire-byline", comment: "By Brent Simmons and the Ranchero Software team.")
|
||||
.font(.subheadline)
|
||||
|
||||
Text("[netnewswire.com](https://netnewswire.com)")
|
||||
Text("label.markdown.netnewswire-website", comment: "Markdown formatted link to netnewswire.com")
|
||||
|
||||
}
|
||||
Spacer()
|
||||
|
||||
@@ -16,9 +16,9 @@ public enum HelpSheet: CustomStringConvertible, CaseIterable {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .help:
|
||||
return String(localized: "NetNewsWire Help", comment: "Button: opens NetNewsWire Help page")
|
||||
return NSLocalizedString("button.title.netnewswire-help", comment: "NetNewsWire Help")
|
||||
case .website:
|
||||
return String(localized: "NetNewsWire Website", comment: "Button: opens NetNewsWire website")
|
||||
return NSLocalizedString("button.title.netnewswire-website", comment: "NetNewsWire Website")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ struct NewArticleNotificationsView: View, Logging {
|
||||
.id(feed.webFeedID)
|
||||
}
|
||||
}
|
||||
.navigationTitle(Text("New Article Notifications", comment: "Navigation title: New Article Notifications"))
|
||||
.navigationTitle(Text("navigation.title.new-article-notifications", comment: "New Article Notifications"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ class ShareViewController: SLComposeServiceViewController, ShareFolderPickerCont
|
||||
|
||||
let folderPickerController = ShareFolderPickerController()
|
||||
|
||||
folderPickerController.navigationController?.title = NSLocalizedString("Folder", comment: "Folder")
|
||||
folderPickerController.navigationController?.title = NSLocalizedString("navigation.title.folder", comment: "Folder")
|
||||
folderPickerController.delegate = self
|
||||
folderPickerController.containers = self.flattenedContainers
|
||||
folderPickerController.selectedContainerID = self.selectedContainer?.containerID
|
||||
|
||||
@@ -14,20 +14,20 @@ extension UIViewController {
|
||||
|
||||
func presentError(_ error: Error, dismiss: (() -> 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)
|
||||
}
|
||||
}
|
||||
|
||||
6
iOS/en.lproj/LaunchScreenPad.strings
Normal file
6
iOS/en.lproj/LaunchScreenPad.strings
Normal file
@@ -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";
|
||||
6
iOS/en.lproj/LaunchScreenPhone.strings
Normal file
6
iOS/en.lproj/LaunchScreenPhone.strings
Normal file
@@ -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";
|
||||
42
iOS/en.lproj/Main.strings
Normal file
42
iOS/en.lproj/Main.strings
Normal file
@@ -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";
|
||||
Reference in New Issue
Block a user