Localization work

This commit is contained in:
Stuart Breckenridge
2022-12-30 21:53:07 +08:00
parent d457b2dd53
commit 2bca08195e
54 changed files with 1172 additions and 350 deletions

View File

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

View File

@@ -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 */

View File

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

View File

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

View File

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

View File

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

View File

@@ -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\nDont 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\nDont have a Feedbin account? [Sign Up Here](https://feedbin.com/signup)")
.multilineTextAlignment(.center)
}
return Text("").multilineTextAlignment(.center)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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? {

View File

@@ -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? {

View File

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

View File

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

View File

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

View File

@@ -29,19 +29,19 @@ struct WebFeedInspectorView: View {
prompt: nil)
Toggle(isOn: Binding(get: { webFeed.isNotifyAboutNewArticles ?? false }, set: { webFeed.isNotifyAboutNewArticles = $0 })) {
Text("Notify About New Articles", 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)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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: Authors 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\nDont 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\nDont 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\nDont 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\nDont 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\nDont 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\nDont 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\nDont 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\nDont 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\nDont 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\nDont 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\nDont 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\nDont 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";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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("Authors 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: Authors 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)

View File

@@ -18,7 +18,7 @@ struct SettingsViewRows {
/// This row, when tapped, will open iOS System Settings.
static var openSystemSettings: some View {
Label {
Text("Open System Settings", 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()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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