Localize strings in code

This commit is contained in:
Danny Moesch 2019-01-14 20:57:45 +01:00 committed by Mingshen Sun
parent 2d5ca58bd9
commit 1b4040135e
36 changed files with 626 additions and 334 deletions

View file

@ -26,6 +26,7 @@
30B04860209A5141001013CA /* PasswordTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B0485F209A5141001013CA /* PasswordTest.swift */; }; 30B04860209A5141001013CA /* PasswordTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B0485F209A5141001013CA /* PasswordTest.swift */; };
30BF5EC821EA8FB5000E4154 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30BF5ECA21EA8FB5000E4154 /* Localizable.strings */; }; 30BF5EC821EA8FB5000E4154 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30BF5ECA21EA8FB5000E4154 /* Localizable.strings */; };
30BF5ECF21EA90D1000E4154 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BF5ECE21EA90D1000E4154 /* String.swift */; }; 30BF5ECF21EA90D1000E4154 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BF5ECE21EA90D1000E4154 /* String.swift */; };
30BF5ED721ED2434000E4154 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 30BF5ED521ED2434000E4154 /* Localizable.stringsdict */; };
30FD2F78214D9E0E005E0A92 /* ParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FD2F77214D9E0E005E0A92 /* ParserTest.swift */; }; 30FD2F78214D9E0E005E0A92 /* ParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FD2F77214D9E0E005E0A92 /* ParserTest.swift */; };
61326CDA7A73757FB68DCB04 /* Pods_passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB3F5541E51ADC8C6B56642 /* Pods_passKit.framework */; }; 61326CDA7A73757FB68DCB04 /* Pods_passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB3F5541E51ADC8C6B56642 /* Pods_passKit.framework */; };
A20691F41F2A3D0E0096483D /* SecurePasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20691F31F2A3D0E0096483D /* SecurePasteboard.swift */; }; A20691F41F2A3D0E0096483D /* SecurePasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20691F31F2A3D0E0096483D /* SecurePasteboard.swift */; };
@ -208,6 +209,7 @@
30B0485F209A5141001013CA /* PasswordTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordTest.swift; sourceTree = "<group>"; }; 30B0485F209A5141001013CA /* PasswordTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordTest.swift; sourceTree = "<group>"; };
30BF5EC921EA8FB5000E4154 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; }; 30BF5EC921EA8FB5000E4154 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
30BF5ECE21EA90D1000E4154 /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = String.swift; path = Localization/String.swift; sourceTree = "<group>"; }; 30BF5ECE21EA90D1000E4154 /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = String.swift; path = Localization/String.swift; sourceTree = "<group>"; };
30BF5ED621ED2434000E4154 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
30FD2F77214D9E0E005E0A92 /* ParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParserTest.swift; sourceTree = "<group>"; }; 30FD2F77214D9E0E005E0A92 /* ParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParserTest.swift; sourceTree = "<group>"; };
31C3033E8868D05B2C55C8B1 /* Pods-passExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-passExtension/Pods-passExtension.debug.xcconfig"; sourceTree = "<group>"; }; 31C3033E8868D05B2C55C8B1 /* Pods-passExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-passExtension/Pods-passExtension.debug.xcconfig"; sourceTree = "<group>"; };
3A5620D17DF5E86B61761D0E /* Pods_pass.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_pass.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3A5620D17DF5E86B61761D0E /* Pods_pass.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_pass.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -703,6 +705,7 @@
DC917BDF1E2E8231000FDF54 /* LaunchScreen.storyboard */, DC917BDF1E2E8231000FDF54 /* LaunchScreen.storyboard */,
DC917BDA1E2E8231000FDF54 /* Main.storyboard */, DC917BDA1E2E8231000FDF54 /* Main.storyboard */,
30BF5ECA21EA8FB5000E4154 /* Localizable.strings */, 30BF5ECA21EA8FB5000E4154 /* Localizable.strings */,
30BF5ED521ED2434000E4154 /* Localizable.stringsdict */,
); );
path = pass; path = pass;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1015,6 +1018,7 @@
30BF5EC821EA8FB5000E4154 /* Localizable.strings in Resources */, 30BF5EC821EA8FB5000E4154 /* Localizable.strings in Resources */,
DCFB77A31E500D9C008DE471 /* PasswordDetailTitleTableViewCell.xib in Resources */, DCFB77A31E500D9C008DE471 /* PasswordDetailTitleTableViewCell.xib in Resources */,
DC917BDE1E2E8231000FDF54 /* Assets.xcassets in Resources */, DC917BDE1E2E8231000FDF54 /* Assets.xcassets in Resources */,
30BF5ED721ED2434000E4154 /* Localizable.stringsdict in Resources */,
DCDDEAB01E4639F300F68193 /* LabelTableViewCell.xib in Resources */, DCDDEAB01E4639F300F68193 /* LabelTableViewCell.xib in Resources */,
DC917BDC1E2E8231000FDF54 /* Main.storyboard in Resources */, DC917BDC1E2E8231000FDF54 /* Main.storyboard in Resources */,
A2802BFA1E70813A00879216 /* SliderTableViewCell.xib in Resources */, A2802BFA1E70813A00879216 /* SliderTableViewCell.xib in Resources */,
@ -1324,6 +1328,14 @@
name = Localizable.strings; name = Localizable.strings;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
30BF5ED521ED2434000E4154 /* Localizable.stringsdict */ = {
isa = PBXVariantGroup;
children = (
30BF5ED621ED2434000E4154 /* en */,
);
name = Localizable.stringsdict;
sourceTree = "<group>";
};
A239F59A2158C08C00576CBF /* MainInterface.storyboard */ = { A239F59A2158C08C00576CBF /* MainInterface.storyboard */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
children = ( children = (

View file

@ -140,7 +140,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
* The store could not be migrated to the current model version. * The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was. Check the error message to determine what the actual problem was.
*/ */
fatalError("Unresolved error \(error), \(error.userInfo)") fatalError("UnresolvedError".localize("\(error), \(error.userInfo)"))
} }
}) })
return container return container
@ -157,7 +157,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// Replace this implementation with code to handle the error appropriately. // Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)") fatalError("UnresolvedError".localize("\(nserror), \(nserror.userInfo)"))
} }
} }
} }

View file

@ -11,7 +11,7 @@ import passKit
class AboutRepositoryTableViewController: BasicStaticTableViewController { class AboutRepositoryTableViewController: BasicStaticTableViewController {
private static let VALUE_NOT_AVAILABLE = "Value not available" private static let VALUE_NOT_AVAILABLE = "ValueNotAvailable".localize()
private var needRefresh = false private var needRefresh = false
private var indicator: UIActivityIndicatorView = { private var indicator: UIActivityIndicatorView = {
@ -62,12 +62,12 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
let type = UITableViewCellAccessoryType.none let type = UITableViewCellAccessoryType.none
strongSelf.tableData = [ strongSelf.tableData = [
// section 0 // section 0
[[.style: CellDataStyle.value1, .accessoryType: type, .title: "Passwords", .detailText: passwords], [[.style: CellDataStyle.value1, .accessoryType: type, .title: "Passwords".localize(), .detailText: passwords],
[.style: CellDataStyle.value1, .accessoryType: type, .title: "Size", .detailText: size], [.style: CellDataStyle.value1, .accessoryType: type, .title: "Size".localize(), .detailText: size],
[.style: CellDataStyle.value1, .accessoryType: type, .title: "Local Commits", .detailText: localCommits], [.style: CellDataStyle.value1, .accessoryType: type, .title: "LocalCommits".localize(), .detailText: localCommits],
[.style: CellDataStyle.value1, .accessoryType: type, .title: "Last Synced", .detailText: lastSynced], [.style: CellDataStyle.value1, .accessoryType: type, .title: "LastSynced".localize(), .detailText: lastSynced],
[.style: CellDataStyle.value1, .accessoryType: type, .title: "Commits", .detailText: commits], [.style: CellDataStyle.value1, .accessoryType: type, .title: "Commits".localize(), .detailText: commits],
[.title: "Commit Logs", .action: "segue", .link: "showCommitLogsSegue"], [.title: "CommitLogs".localize(), .action: "segue", .link: "showCommitLogsSegue"],
], ],
] ]
strongSelf.indicator.stopAnimating() strongSelf.indicator.stopAnimating()
@ -95,7 +95,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
private func lastSyncedTimeString() -> String { private func lastSyncedTimeString() -> String {
guard let date = self.passwordStore.lastSyncedTime else { guard let date = self.passwordStore.lastSyncedTime else {
return "Oops! Sync again?" return "SyncAgain?".localize()
} }
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateStyle = .medium formatter.dateStyle = .medium

View file

@ -13,13 +13,13 @@ class AboutTableViewController: BasicStaticTableViewController {
override func viewDidLoad() { override func viewDidLoad() {
tableData = [ tableData = [
// section 0 // section 0
[[.title: "Website", .action: "link", .link: "https://github.com/mssun/pass-ios.git"], [[.title: "Website".localize(), .action: "link", .link: "https://github.com/mssun/pass-ios.git"],
[.title: "Help", .action: "link", .link: "https://github.com/mssun/passforios/wiki"], [.title: "Help".localize(), .action: "link", .link: "https://github.com/mssun/passforios/wiki"],
[.title: "Contact Developer", .action: "link", .link: "mailto:developer@passforios.mssun.me?subject=Pass%20for%20iOS"],], [.title: "ContactDeveloper".localize(), .action: "link", .link: "mailto:developer@passforios.mssun.me?subject=Pass%20for%20iOS"],],
// section 1, // section 1,
[[.title: "Open Source Components", .action: "segue", .link: "showOpenSourceComponentsSegue"], [[.title: "OpenSourceComponents".localize(), .action: "segue", .link: "showOpenSourceComponentsSegue"],
[.title: "Special Thanks", .action: "segue", .link: "showSpecialThanksSegue"],], [.title: "SpecialThanks".localize(), .action: "segue", .link: "showSpecialThanksSegue"],],
] ]
super.viewDidLoad() super.viewDidLoad()
} }
@ -29,7 +29,7 @@ class AboutTableViewController: BasicStaticTableViewController {
let view = UIView() let view = UIView()
let footerLabel = UILabel(frame: CGRect(x: 8, y: 15, width: tableView.frame.width, height: 60)) let footerLabel = UILabel(frame: CGRect(x: 8, y: 15, width: tableView.frame.width, height: 60))
footerLabel.numberOfLines = 0 footerLabel.numberOfLines = 0
footerLabel.text = "Pass for iOS \(Bundle.main.releaseVersionNumber!) (\(Bundle.main.buildVersionNumber!))" footerLabel.text = "PassForIos".localize() + " \(Bundle.main.releaseVersionNumber!) (\(Bundle.main.buildVersionNumber!))"
footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote) footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
footerLabel.textColor = UIColor.lightGray footerLabel.textColor = UIColor.lightGray
footerLabel.textAlignment = .center footerLabel.textAlignment = .center
@ -41,7 +41,7 @@ class AboutTableViewController: BasicStaticTableViewController {
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 1 { if section == 1 {
return "Acknowledgements".uppercased() return "Acknowledgements".localize().uppercased()
} }
return nil return nil
} }

View file

@ -32,8 +32,8 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
if identifier == "saveAddPasswordSegue" { if identifier == "saveAddPasswordSegue" {
// check PGP key // check PGP key
guard passwordStore.privateKey != nil else { guard passwordStore.privateKey != nil else {
let alertTitle = "Cannot Add Password" let alertTitle = "CannotAddPassword".localize()
let alertMessage = "PGP Key is not set. Please set your PGP Key first." let alertMessage = "PgpKeyNotSet.".localize()
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil) Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
return false return false
} }

View file

@ -41,45 +41,38 @@ class AdvancedSettingsTableViewController: UITableViewController {
self.gitSignatureTableViewCell.detailTextLabel?.text = "\(gitSignatureName) <\(gitSignatureEmail)>" self.gitSignatureTableViewCell.detailTextLabel?.text = "\(gitSignatureName) <\(gitSignatureEmail)>"
if SharedDefaults[.gitSignatureName] == nil && SharedDefaults[.gitSignatureEmail] == nil { if SharedDefaults[.gitSignatureName] == nil && SharedDefaults[.gitSignatureEmail] == nil {
self.gitSignatureTableViewCell.detailTextLabel?.font = UIFont.systemFont(ofSize: 17) self.gitSignatureTableViewCell.detailTextLabel?.font = UIFont.systemFont(ofSize: 17)
gitSignatureTableViewCell.detailTextLabel?.text = "Not Set" gitSignatureTableViewCell.detailTextLabel?.text = "NotSet".localize()
} }
} }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
if tableView.cellForRow(at: indexPath) == eraseDataTableViewCell { if tableView.cellForRow(at: indexPath) == eraseDataTableViewCell {
let alert = UIAlertController(title: "Erase Password Store Data?", message: "This will delete all local data and settings. Password store data on your remote server will not be affected.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "ErasePasswordStoreData?".localize(), message: "EraseExplanation.".localize(), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Erase Password Data", style: UIAlertActionStyle.destructive, handler: {[unowned self] (action) -> Void in alert.addAction(UIAlertAction(title: "ErasePasswordStoreData".localize(), style: UIAlertActionStyle.destructive, handler: {[unowned self] (action) -> Void in
SVProgressHUD.show(withStatus: "Erasing ...") SVProgressHUD.show(withStatus: "Erasing...".localize())
self.passwordStore.erase() self.passwordStore.erase()
self.navigationController!.popViewController(animated: true) self.navigationController!.popViewController(animated: true)
SVProgressHUD.showSuccess(withStatus: "Done") SVProgressHUD.showSuccess(withStatus: "Done".localize())
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
})) }))
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.cancel, handler:nil)) alert.addAction(UIAlertAction(title: "Dismiss".localize(), style: UIAlertActionStyle.cancel, handler:nil))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} else if tableView.cellForRow(at: indexPath) == discardChangesTableViewCell { } else if tableView.cellForRow(at: indexPath) == discardChangesTableViewCell {
let alert = UIAlertController(title: "Discard All Changes?", message: "Do you want to permanently discard all changes to the local copy of your password data? You cannot undo this action.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "DiscardAllLocalChanges?".localize(), message: "DiscardExplanation.".localize(), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Discard All Changes", style: UIAlertActionStyle.destructive, handler: {[unowned self] (action) -> Void in alert.addAction(UIAlertAction(title: "DiscardAllLocalChanges".localize(), style: UIAlertActionStyle.destructive, handler: {[unowned self] (action) -> Void in
SVProgressHUD.show(withStatus: "Resetting ...") SVProgressHUD.show(withStatus: "Resetting...".localize())
do { do {
let numberDiscarded = try self.passwordStore.reset() let numberDiscarded = try self.passwordStore.reset()
self.navigationController!.popViewController(animated: true) self.navigationController!.popViewController(animated: true)
switch numberDiscarded { SVProgressHUD.showSuccess(withStatus: "DiscardedCommits(%d)".localize(numberDiscarded))
case 0:
SVProgressHUD.showSuccess(withStatus: "No local commits")
case 1:
SVProgressHUD.showSuccess(withStatus: "Discarded 1 commit")
default:
SVProgressHUD.showSuccess(withStatus: "Discarded \(numberDiscarded) commits")
}
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} catch { } catch {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
})) }))
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.cancel, handler:nil)) alert.addAction(UIAlertAction(title: "Dismiss".localize(), style: UIAlertActionStyle.cancel, handler:nil))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} }
} }

View file

@ -101,8 +101,8 @@ class BasicStaticTableViewController: UITableViewController, MFMailComposeViewCo
sendEmail(toRecipients: [urlComponents.path], subject: subject) sendEmail(toRecipients: [urlComponents.path], subject: subject)
} else { } else {
let email = urlComponents.path let email = urlComponents.path
let alertTitle = "Cannot open Mail App" let alertTitle = "CannotOpenMail".localize()
let alertMessage = "Email copied: \(email)" let alertMessage = "CopiedEmail".localize(email)
Utils.copyToPasteboard(textToCopy: email) Utils.copyToPasteboard(textToCopy: email)
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil) Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
} }

View file

@ -51,7 +51,7 @@ class CommitLogsTableViewController: UITableViewController {
do { do {
return try passwordStore.getRecentCommits(count: 20) return try passwordStore.getRecentCommits(count: 20)
} catch { } catch {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
return [] return []
} }
} }

View file

@ -58,22 +58,22 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
override func viewDidLoad() { override func viewDidLoad() {
tableData = [ tableData = [
// section 0 // section 0
[[.title: "About Repository", .action: "segue", .link: "showAboutRepositorySegue"],], [[.title: "AboutRepository".localize(), .action: "segue", .link: "showAboutRepositorySegue"],],
// section 1 // section 1
[ [
[.title: "Password Generator Flavor", .action: "none", .style: CellDataStyle.value1], [.title: "PasswordGeneratorFlavor".localize(), .action: "none", .style: CellDataStyle.value1],
], ],
// section 2 // section 2
[ [
[.title: "Remember PGP Key Passphrase", .action: "none",], [.title: "RememberPgpKeyPassphrase".localize(), .action: "none",],
[.title: "Remember Git Credential Passphrase", .action: "none",], [.title: "RememberGitCredentialPassphrase".localize(), .action: "none",],
], ],
[ [
[.title: "Show Folders", .action: "none",], [.title: "ShowFolders".localize(), .action: "none",],
[.title: "Hide Unknown Fields", .action: "none",], [.title: "HideUnknownFields".localize(), .action: "none",],
[.title: "Hide OTP Fields", .action: "none",], [.title: "HideOtpFields".localize(), .action: "none",],
], ],
] ]
@ -84,7 +84,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = super.tableView(tableView, cellForRowAt: indexPath) let cell = super.tableView(tableView, cellForRowAt: indexPath)
switch cell.textLabel!.text! { switch cell.textLabel!.text! {
case "Hide Unknown Fields": case "HideUnknownFields".localize():
cell.accessoryType = .none cell.accessoryType = .none
let detailButton = UIButton(type: .detailDisclosure) let detailButton = UIButton(type: .detailDisclosure)
hideUnknownSwitch.frame = CGRect(x: detailButton.bounds.width+10, y: 0, width: hideUnknownSwitch.bounds.width, height: hideUnknownSwitch.bounds.height) hideUnknownSwitch.frame = CGRect(x: detailButton.bounds.width+10, y: 0, width: hideUnknownSwitch.bounds.width, height: hideUnknownSwitch.bounds.height)
@ -96,7 +96,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
cell.accessoryView = accessoryView cell.accessoryView = accessoryView
cell.selectionStyle = .none cell.selectionStyle = .none
hideUnknownSwitch.isOn = SharedDefaults[.isHideUnknownOn] hideUnknownSwitch.isOn = SharedDefaults[.isHideUnknownOn]
case "Hide OTP Fields": case "HideOtpFields".localize():
cell.accessoryType = .none cell.accessoryType = .none
let detailButton = UIButton(type: .detailDisclosure) let detailButton = UIButton(type: .detailDisclosure)
hideOTPSwitch.frame = CGRect(x: detailButton.bounds.width+10, y: 0, width: hideOTPSwitch.bounds.width, height: hideOTPSwitch.bounds.height) hideOTPSwitch.frame = CGRect(x: detailButton.bounds.width+10, y: 0, width: hideOTPSwitch.bounds.width, height: hideOTPSwitch.bounds.height)
@ -108,19 +108,19 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
cell.accessoryView = accessoryView cell.accessoryView = accessoryView
cell.selectionStyle = .none cell.selectionStyle = .none
hideOTPSwitch.isOn = SharedDefaults[.isHideOTPOn] hideOTPSwitch.isOn = SharedDefaults[.isHideOTPOn]
case "Remember PGP Key Passphrase": case "RememberPgpKeyPassphrase".localize():
cell.accessoryType = .none cell.accessoryType = .none
cell.selectionStyle = .none cell.selectionStyle = .none
cell.accessoryView = rememberPGPPassphraseSwitch cell.accessoryView = rememberPGPPassphraseSwitch
case "Remember Git Credential Passphrase": case "RememberGitCredentialPassphrase".localize():
cell.accessoryType = .none cell.accessoryType = .none
cell.selectionStyle = .none cell.selectionStyle = .none
cell.accessoryView = rememberGitCredentialPassphraseSwitch cell.accessoryView = rememberGitCredentialPassphraseSwitch
case "Show Folders": case "ShowFolders".localize():
cell.accessoryType = .none cell.accessoryType = .none
cell.selectionStyle = .none cell.selectionStyle = .none
cell.accessoryView = showFolderSwitch cell.accessoryView = showFolderSwitch
case "Password Generator Flavor": case "PasswordGeneratorFlavor".localize():
cell.accessoryType = .disclosureIndicator cell.accessoryType = .disclosureIndicator
cell.detailTextLabel?.text = PasswordGeneratorFlavour.from(SharedDefaults[.passwordGeneratorFlavor]).name cell.detailTextLabel?.text = PasswordGeneratorFlavour.from(SharedDefaults[.passwordGeneratorFlavor]).name
default: break default: break
@ -131,7 +131,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
super.tableView(tableView, didSelectRowAt: indexPath) super.tableView(tableView, didSelectRowAt: indexPath)
let cell = tableView.cellForRow(at: indexPath)! let cell = tableView.cellForRow(at: indexPath)!
if cell.textLabel!.text! == "Password Generator Flavor" { if cell.textLabel!.text! == "PasswordGeneratorFlavor".localize() {
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
showPasswordGeneratorFlavorActionSheet(sourceCell: cell) showPasswordGeneratorFlavorActionSheet(sourceCell: cell)
} }
@ -141,12 +141,12 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
var randomFlavorActionTitle = "" var randomFlavorActionTitle = ""
var appleFlavorActionTitle = "" var appleFlavorActionTitle = ""
randomFlavorActionTitle = "✓ Random String"
appleFlavorActionTitle = "Apple's Keychain Style"
if SharedDefaults[.passwordGeneratorFlavor] == PasswordGeneratorFlavour.RANDOM.rawValue { if SharedDefaults[.passwordGeneratorFlavor] == PasswordGeneratorFlavour.RANDOM.rawValue {
randomFlavorActionTitle = "" + "RandomString".localize()
appleFlavorActionTitle = "ApplesKeychainStyle".localize()
} else { } else {
randomFlavorActionTitle = "Random String" randomFlavorActionTitle = "RandomString".localize()
appleFlavorActionTitle = "Apple's Keychain Style" appleFlavorActionTitle = "" + "ApplesKeychainStyle".localize()
} }
let randomFlavorAction = UIAlertAction(title: randomFlavorActionTitle, style: .default) { _ in let randomFlavorAction = UIAlertAction(title: randomFlavorActionTitle, style: .default) { _ in
SharedDefaults[.passwordGeneratorFlavor] = PasswordGeneratorFlavour.RANDOM.rawValue SharedDefaults[.passwordGeneratorFlavor] = PasswordGeneratorFlavour.RANDOM.rawValue
@ -158,7 +158,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
sourceCell.detailTextLabel?.text = PasswordGeneratorFlavour.APPLE.name sourceCell.detailTextLabel?.text = PasswordGeneratorFlavour.APPLE.name
} }
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) let cancelAction = UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)
optionMenu.addAction(randomFlavorAction) optionMenu.addAction(randomFlavorAction)
optionMenu.addAction(appleFlavorAction) optionMenu.addAction(appleFlavorAction)
optionMenu.addAction(cancelAction) optionMenu.addAction(cancelAction)
@ -168,15 +168,15 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
} }
@objc func tapHideUnknownSwitchDetailButton(_ sender: Any?) { @objc func tapHideUnknownSwitchDetailButton(_ sender: Any?) {
let alertMessage = "Only \"key: value\" format in additional fields is supported. Unsupported fields will be given \"unknown\" keys. Turn on this switch to hide unsupported fields." let alertMessage = "HideUnknownFieldsExplanation.".localize()
let alertTitle = "Hide Unknown Fields" let alertTitle = "HideUnknownFields".localize()
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil) Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
} }
@objc func tapHideOTPSwitchDetailButton(_ sender: Any?) { @objc func tapHideOTPSwitchDetailButton(_ sender: Any?) {
let keywordsString = Constants.OTP_KEYWORDS.joined(separator: ", ") let keywordsString = Constants.OTP_KEYWORDS.joined(separator: ", ")
let alertMessage = "Turn on this switch to hide the fields related to one time passwords (i.e., \(keywordsString))." let alertMessage = "HideOtpFieldsExplanation.".localize(keywordsString)
let alertTitle = "Hide One Time Password Fields" let alertTitle = "HideOtpFields".localize()
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil) Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
} }

View file

@ -32,7 +32,7 @@ class GitConfigSettingTableViewController: UITableViewController {
let name = nameTextField.text!.isEmpty ? Globals.gitSignatureDefaultName : nameTextField.text! let name = nameTextField.text!.isEmpty ? Globals.gitSignatureDefaultName : nameTextField.text!
let email = emailTextField.text!.isEmpty ? Globals.gitSignatureDefaultEmail : nameTextField.text! let email = emailTextField.text!.isEmpty ? Globals.gitSignatureDefaultEmail : nameTextField.text!
guard GTSignature(name: name, email: email, time: nil) != nil else { guard GTSignature(name: name, email: email, time: nil) != nil else {
Utils.alert(title: "Error", message: "Invalid name or email.", controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: "InvalidNameOrEmail".localize(), controller: self, completion: nil)
return false return false
} }
} }

View file

@ -29,7 +29,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
numberOfSegments = 0 numberOfSegments = 0
previousSegment = "" previousSegment = ""
key = "" key = ""
message = "Looking for the starting frame." message = "LookingForStartingFrame.".localize()
hasStarted = false hasStarted = false
isDone = false isDone = false
} }
@ -52,7 +52,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
// check the number of segments // check the number of segments
numberOfSegments = numberOfSegments + 1 numberOfSegments = numberOfSegments + 1
guard numberOfSegments <= ScannedSSHKey.maxNumberOfGif else { guard numberOfSegments <= ScannedSSHKey.maxNumberOfGif else {
key = "Too many QR codes" key = "TooManyQrCodes".localize()
return return
} }
@ -64,7 +64,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
} }
// update message // update message
message = "\(numberOfSegments) scanned QR codes." message = "ScannedQrCodes(%d)".localize(numberOfSegments)
} }
} }
var scanned = ScannedSSHKey() var scanned = ScannedSSHKey()
@ -74,7 +74,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
armorPrivateKeyTextView.text = SharedDefaults[.gitSSHPrivateKeyArmor] armorPrivateKeyTextView.text = SharedDefaults[.gitSSHPrivateKeyArmor]
armorPrivateKeyTextView.delegate = self armorPrivateKeyTextView.delegate = self
scanPrivateKeyCell?.textLabel?.text = "Scan Private Key QR Codes" scanPrivateKeyCell?.textLabel?.text = "ScanPrivateKeyQrCodes".localize()
scanPrivateKeyCell?.textLabel?.textColor = Globals.blue scanPrivateKeyCell?.textLabel?.textColor = Globals.blue
scanPrivateKeyCell?.selectionStyle = .default scanPrivateKeyCell?.selectionStyle = .default
scanPrivateKeyCell?.accessoryType = .disclosureIndicator scanPrivateKeyCell?.accessoryType = .disclosureIndicator
@ -85,7 +85,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
do { do {
try passwordStore.initGitSSHKey(with: armorPrivateKeyTextView.text) try passwordStore.initGitSSHKey(with: armorPrivateKeyTextView.text)
} catch { } catch {
Utils.alert(title: "Cannot Save", message: "Cannot Save SSH Key", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "CannotSaveSshKey".localize(), controller: self, completion: nil)
} }
SharedDefaults[.gitSSHKeySource] = "armor" SharedDefaults[.gitSSHKeySource] = "armor"
self.navigationController!.popViewController(animated: true) self.navigationController!.popViewController(animated: true)
@ -113,7 +113,7 @@ class GitSSHKeyArmorSettingTableViewController: UITableViewController, UITextVie
func checkScannedOutput(line: String) -> (accept: Bool, message: String) { func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
scanned.addSegment(segment: line) scanned.addSegment(segment: line)
if scanned.isDone { if scanned.isDone {
return (accept: true, message: "Done") return (accept: true, message: "Done".localize())
} else { } else {
return (accept: false, message: scanned.message) return (accept: false, message: scanned.message)
} }

View file

@ -84,7 +84,7 @@ class GitServerSettingTableViewController: UITableViewController {
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "Prepare Repository") SVProgressHUD.show(withStatus: "PrepareRepository".localize())
var gitCredential: GitCredential var gitCredential: GitCredential
if auth == "Password" { if auth == "Password" {
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: username)) gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: username))
@ -112,7 +112,7 @@ class GitServerSettingTableViewController: UITableViewController {
}, },
checkoutProgressBlock: { (path, completedSteps, totalSteps) in checkoutProgressBlock: { (path, completedSteps, totalSteps) in
DispatchQueue.main.async { DispatchQueue.main.async {
SVProgressHUD.showProgress(Float(completedSteps)/Float(totalSteps), status: "Checkout Master Branch") SVProgressHUD.showProgress(Float(completedSteps)/Float(totalSteps), status: "CheckingOutBranch".localize(branchName))
} }
}) })
DispatchQueue.main.async { DispatchQueue.main.async {
@ -121,16 +121,16 @@ class GitServerSettingTableViewController: UITableViewController {
SharedDefaults[.gitBranchName] = branchName SharedDefaults[.gitBranchName] = branchName
SharedDefaults[.gitAuthenticationMethod] = auth SharedDefaults[.gitAuthenticationMethod] = auth
SVProgressHUD.dismiss() SVProgressHUD.dismiss()
let savePassphraseAlert = UIAlertController(title: "Done", message: "Do you want to save the Git credential password/passphrase?", preferredStyle: UIAlertControllerStyle.alert) let savePassphraseAlert = UIAlertController(title: "Done".localize(), message: "WantToSaveGitCredential?".localize(), preferredStyle: UIAlertControllerStyle.alert)
// no // no
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: UIAlertActionStyle.default) { _ in
SharedDefaults[.isRememberGitCredentialPassphraseOn] = false SharedDefaults[.isRememberGitCredentialPassphraseOn] = false
self.passwordStore.gitPassword = nil self.passwordStore.gitPassword = nil
self.passwordStore.gitSSHPrivateKeyPassphrase = nil self.passwordStore.gitSSHPrivateKeyPassphrase = nil
self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self) self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self)
}) })
// yes // yes
savePassphraseAlert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.destructive) {_ in savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertActionStyle.destructive) {_ in
SharedDefaults[.isRememberGitCredentialPassphraseOn] = true SharedDefaults[.isRememberGitCredentialPassphraseOn] = true
self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self) self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self)
}) })
@ -141,9 +141,9 @@ class GitServerSettingTableViewController: UITableViewController {
let error = error as NSError let error = error as NSError
var message = error.localizedDescription var message = error.localizedDescription
if let underlyingError = error.userInfo[NSUnderlyingErrorKey] as? NSError { if let underlyingError = error.userInfo[NSUnderlyingErrorKey] as? NSError {
message = "\(message)\nUnderlying error: \(underlyingError.localizedDescription)" message = "\(message)\n\("UnderlyingError".localize()): \(underlyingError.localizedDescription)"
} }
Utils.alert(title: "Error", message: message, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: message, controller: self, completion: nil)
} }
} }
} }
@ -156,7 +156,7 @@ class GitServerSettingTableViewController: UITableViewController {
} else if cell == authSSHKeyCell { } else if cell == authSSHKeyCell {
if !passwordStore.gitSSHKeyExists() { if !passwordStore.gitSSHKeyExists() {
Utils.alert(title: "Cannot Select SSH Key", message: "Please setup SSH key first.", controller: self, completion: nil) Utils.alert(title: "CannotSelectSshKey".localize(), message: "PleaseSetupSshKeyFirst.".localize(), controller: self, completion: nil)
authenticationMethod = "Password" authenticationMethod = "Password"
} else { } else {
authenticationMethod = "SSH Key" authenticationMethod = "SSH Key"
@ -170,7 +170,7 @@ class GitServerSettingTableViewController: UITableViewController {
// some sanity checks // some sanity checks
guard let gitURL = URL(string: gitURLTextField.text!) else { guard let gitURL = URL(string: gitURLTextField.text!) else {
Utils.alert(title: "Cannot Save", message: "Please set the Git repository URL.", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "SetGitRepositoryUrl".localize(), controller: self, completion: nil)
return return
} }
@ -179,28 +179,28 @@ class GitServerSettingTableViewController: UITableViewController {
break break
case let val where val == "ssh": case let val where val == "ssh":
guard let sshUsername = gitURL.user, sshUsername.isEmpty == false else { guard let sshUsername = gitURL.user, sshUsername.isEmpty == false else {
Utils.alert(title: "Cannot Save", message: "Cannot find the username in the Git repository URL. Example URL: ssh://git@server/path/to/repo.git.", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "CannotFindUsername.".localize(), controller: self, completion: nil)
return return
} }
guard let username = usernameTextField.text, username == sshUsername else { guard let username = usernameTextField.text, username == sshUsername else {
Utils.alert(title: "Cannot Save", message: "Please check the entered username and the username in the Git repository URL. They should match.", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "CheckEnteredUsername.".localize(), controller: self, completion: nil)
return return
} }
case let val where val == "http": case let val where val == "http":
Utils.alert(title: "Cannot Save", message: "Please use https instead of http.", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "UseHttps.".localize(), controller: self, completion: nil)
return return
default: default:
Utils.alert(title: "Cannot Save", message: "Please specify the scheme of the Git repository URL (https or ssh).", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "SpecifySchema.".localize(), controller: self, completion: nil)
return return
} }
if passwordStore.repositoryExisted() { if passwordStore.repositoryExisted() {
let alert = UIAlertController(title: "Overwrite?", message: "This operation will overwrite your current password store data (repository). Data on your remote server will not be affected.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Overwrite?".localize(), message: "OperationWillOverwriteData.".localize(), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Overwrite", style: UIAlertActionStyle.destructive, handler: { _ in alert.addAction(UIAlertAction(title: "Overwrite".localize(), style: UIAlertActionStyle.destructive, handler: { _ in
// perform segue only after a successful clone // perform segue only after a successful clone
self.cloneAndSegueIfSuccess() self.cloneAndSegueIfSuccess()
})) }))
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil)) alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertActionStyle.cancel, handler: nil))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} else { } else {
// perform segue only after a successful clone // perform segue only after a successful clone
@ -210,9 +210,9 @@ class GitServerSettingTableViewController: UITableViewController {
func showSSHKeyActionSheet() { func showSSHKeyActionSheet() {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
var urlActionTitle = "Download from URL" var urlActionTitle = "DownloadFromUrl".localize()
var armorActionTitle = "ASCII-Armor Encrypted Key" var armorActionTitle = "AsciiArmorEncryptedKey".localize()
var fileActionTitle = "iTunes File Sharing" var fileActionTitle = "ITunesFileSharing".localize()
if SharedDefaults[.gitSSHKeySource] == "url" { if SharedDefaults[.gitSSHKeySource] == "url" {
urlActionTitle = "\(urlActionTitle)" urlActionTitle = "\(urlActionTitle)"
@ -227,41 +227,41 @@ class GitServerSettingTableViewController: UITableViewController {
let armorAction = UIAlertAction(title: armorActionTitle, style: .default) { _ in let armorAction = UIAlertAction(title: armorActionTitle, style: .default) { _ in
self.performSegue(withIdentifier: "setGitSSHKeyByArmorSegue", sender: self) self.performSegue(withIdentifier: "setGitSSHKeyByArmorSegue", sender: self)
} }
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) let cancelAction = UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)
optionMenu.addAction(urlAction) optionMenu.addAction(urlAction)
optionMenu.addAction(armorAction) optionMenu.addAction(armorAction)
if passwordStore.gitSSHKeyExists(inFileSharing: true) { if passwordStore.gitSSHKeyExists(inFileSharing: true) {
// might keys updated via iTunes, or downloaded/pasted inside the app // might keys updated via iTunes, or downloaded/pasted inside the app
fileActionTitle.append(" (Import)") fileActionTitle.append(" (\("Import".localize()))")
let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in
do { do {
try self.passwordStore.gitSSHKeyImportFromFileSharing() try self.passwordStore.gitSSHKeyImportFromFileSharing()
SharedDefaults[.gitSSHKeySource] = "file" SharedDefaults[.gitSSHKeySource] = "file"
SVProgressHUD.showSuccess(withStatus: "Imported") SVProgressHUD.showSuccess(withStatus: "Imported".localize())
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} catch { } catch {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
} }
optionMenu.addAction(fileAction) optionMenu.addAction(fileAction)
} else { } else {
fileActionTitle.append(" (Tips)") fileActionTitle.append(" (\("Tips".localize()))")
let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in
let title = "Tips" let title = "Tips".localize()
let message = "Copy your ASCII-armored private key to Pass with the name \"ssh_key\" (without quotes) via iTunes. Then come back and click \"iTunes File Sharing\" to finish." let message = "CopyPrivateKeyToPass.".localize()
Utils.alert(title: title, message: message, controller: self) Utils.alert(title: title, message: message, controller: self)
} }
optionMenu.addAction(fileAction) optionMenu.addAction(fileAction)
} }
if SharedDefaults[.gitSSHKeySource] != nil { if SharedDefaults[.gitSSHKeySource] != nil {
let deleteAction = UIAlertAction(title: "Remove Git SSH Keys", style: .destructive) { _ in let deleteAction = UIAlertAction(title: "RemoveSShKeys".localize(), style: .destructive) { _ in
self.passwordStore.removeGitSSHKeys() self.passwordStore.removeGitSSHKeys()
SharedDefaults[.gitSSHKeySource] = nil SharedDefaults[.gitSSHKeySource] = nil
if let sshLabel = self.sshLabel { if let sshLabel = self.sshLabel {
sshLabel.isEnabled = false sshLabel.isEnabled = false
self.checkAuthenticationMethod(method: "Password") self.checkAuthenticationMethod(method: "Password".localize())
} }
} }
optionMenu.addAction(deleteAction) optionMenu.addAction(deleteAction)
@ -278,23 +278,23 @@ class GitServerSettingTableViewController: UITableViewController {
var message = "" var message = ""
switch credential { switch credential {
case .http: case .http:
message = "Please fill in the password of your Git account." message = "FillInGitAccountPassword.".localize()
case .ssh: case .ssh:
message = "Please fill in the passphrase of your SSH key." message = "FillInSshKeyPassphrase.".localize()
} }
DispatchQueue.main.async { DispatchQueue.main.async {
SVProgressHUD.dismiss() SVProgressHUD.dismiss()
let alert = UIAlertController(title: "Password", message: message, preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Password".localize(), message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addTextField(configurationHandler: {(textField: UITextField!) in alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = lastPassword ?? "" textField.text = lastPassword ?? ""
textField.isSecureTextEntry = true textField.isSecureTextEntry = true
}) })
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertActionStyle.default, handler: {_ in
password = alert.textFields!.first!.text password = alert.textFields!.first!.text
sem.signal() sem.signal()
})) }))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in alert.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel) { _ in
password = nil password = nil
sem.signal() sem.signal()
}) })

View file

@ -36,7 +36,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
numberOfSegments = 0 numberOfSegments = 0
previousSegment = "" previousSegment = ""
key = "" key = ""
message = "Looking for the starting frame." message = "LookingForStartingFrame.".localize()
hasStarted = false hasStarted = false
isDone = false isDone = false
} }
@ -55,12 +55,12 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
switch keyType { switch keyType {
case .publicKey: case .publicKey:
if findPrivate { if findPrivate {
message = "Please scan public key." message = "ScanPrivateKey.".localize()
} }
hasStarted = findPublic hasStarted = findPublic
case .privateKey: case .privateKey:
if findPublic { if findPublic {
message = "Please scan private key." message = "ScanPrivateKey.".localize()
} }
hasStarted = findPrivate hasStarted = findPrivate
} }
@ -72,7 +72,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
// check the number of segments // check the number of segments
numberOfSegments = numberOfSegments + 1 numberOfSegments = numberOfSegments + 1
guard numberOfSegments <= ScannedPGPKey.maxNumberOfGif else { guard numberOfSegments <= ScannedPGPKey.maxNumberOfGif else {
key = "Too many QR codes" key = "TooManyQrCodes"
return return
} }
@ -83,7 +83,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
} }
// update message // update message
message = "\(numberOfSegments) scanned QR codes." message = "ScannedQrCodes(%d)".localize(numberOfSegments)
} }
} }
var scanned = ScannedPGPKey() var scanned = ScannedPGPKey()
@ -94,12 +94,12 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
armorPrivateKeyTextView.text = SharedDefaults[.pgpPrivateKeyArmor] armorPrivateKeyTextView.text = SharedDefaults[.pgpPrivateKeyArmor]
pgpPassphrase = passwordStore.pgpKeyPassphrase pgpPassphrase = passwordStore.pgpKeyPassphrase
scanPublicKeyCell?.textLabel?.text = "Scan Public Key QR Codes" scanPublicKeyCell?.textLabel?.text = "ScanPublicKeyQrCodes".localize()
scanPublicKeyCell?.textLabel?.textColor = Globals.blue scanPublicKeyCell?.textLabel?.textColor = Globals.blue
scanPublicKeyCell?.selectionStyle = .default scanPublicKeyCell?.selectionStyle = .default
scanPublicKeyCell?.accessoryType = .disclosureIndicator scanPublicKeyCell?.accessoryType = .disclosureIndicator
scanPrivateKeyCell?.textLabel?.text = "Scan Private Key QR Codes" scanPrivateKeyCell?.textLabel?.text = "ScanPrivateKeyQrCodes".localize()
scanPrivateKeyCell?.textLabel?.textColor = Globals.blue scanPrivateKeyCell?.textLabel?.textColor = Globals.blue
scanPrivateKeyCell?.selectionStyle = .default scanPrivateKeyCell?.selectionStyle = .default
scanPrivateKeyCell?.accessoryType = .disclosureIndicator scanPrivateKeyCell?.accessoryType = .disclosureIndicator
@ -107,25 +107,25 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
@IBAction func save(_ sender: Any) { @IBAction func save(_ sender: Any) {
guard armorPublicKeyTextView.text.isEmpty == false else { guard armorPublicKeyTextView.text.isEmpty == false else {
Utils.alert(title: "Cannot Save", message: "Please set public key first.", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "SetPublicKey.".localize(), controller: self, completion: nil)
return return
} }
guard armorPrivateKeyTextView.text.isEmpty == false else { guard armorPrivateKeyTextView.text.isEmpty == false else {
Utils.alert(title: "Cannot Save", message: "Please set private key first.", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKey.".localize(), controller: self, completion: nil)
return return
} }
let savePassphraseAlert = UIAlertController(title: "Passphrase", message: "Do you want to save the passphrase for later decryption?", preferredStyle: UIAlertControllerStyle.alert) let savePassphraseAlert = UIAlertController(title: "Passphrase".localize(), message: "WantToSavePassphrase?".localize(), preferredStyle: UIAlertControllerStyle.alert)
// no // no
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: UIAlertActionStyle.default) { _ in
self.pgpPassphrase = nil self.pgpPassphrase = nil
SharedDefaults[.isRememberPGPPassphraseOn] = false SharedDefaults[.isRememberPGPPassphraseOn] = false
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
}) })
// yes // yes
savePassphraseAlert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.destructive) {_ in savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertActionStyle.destructive) {_ in
// ask for the passphrase // ask for the passphrase
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertActionStyle.default, handler: {_ in
self.pgpPassphrase = alert.textFields?.first?.text self.pgpPassphrase = alert.textFields?.first?.text
SharedDefaults[.isRememberPGPPassphraseOn] = true SharedDefaults[.isRememberPGPPassphraseOn] = true
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
@ -163,7 +163,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe
func checkScannedOutput(line: String) -> (accept: Bool, message: String) { func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
scanned.addSegment(segment: line) scanned.addSegment(segment: line)
if scanned.isDone { if scanned.isDone {
return (accept: true, message: "Done") return (accept: true, message: "Done".localize())
} else { } else {
return (accept: false, message: scanned.message) return (accept: false, message: scanned.message)
} }

View file

@ -26,11 +26,11 @@ class PGPKeySettingTableViewController: UITableViewController {
private func validatePGPKeyURL(input: String?) -> Bool { private func validatePGPKeyURL(input: String?) -> Bool {
guard let path = input, let url = URL(string: path) else { guard let path = input, let url = URL(string: path) else {
Utils.alert(title: "Cannot Save PGP Key", message: "Please set PGP Key URL first.", controller: self, completion: nil) Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlFirst.".localize(), controller: self, completion: nil)
return false return false
} }
guard let scheme = url.scheme, scheme == "https", scheme == "https" else { guard let scheme = url.scheme, scheme == "https", scheme == "https" else {
Utils.alert(title: "Cannot Save PGP Key", message: "HTTP connection is not supported.", controller: self, completion: nil) Utils.alert(title: "CannotSavePgpKey".localize(), message: "HttpNotSupported.".localize(), controller: self, completion: nil)
return false return false
} }
return true return true
@ -41,18 +41,18 @@ class PGPKeySettingTableViewController: UITableViewController {
validatePGPKeyURL(input: pgpPrivateKeyURLTextField.text) == true else { validatePGPKeyURL(input: pgpPrivateKeyURLTextField.text) == true else {
return return
} }
let savePassphraseAlert = UIAlertController(title: "Passphrase", message: "Do you want to save the passphrase for later decryption?", preferredStyle: UIAlertControllerStyle.alert) let savePassphraseAlert = UIAlertController(title: "Passphrase".localize(), message: "WantToSavePassphrase?".localize(), preferredStyle: UIAlertControllerStyle.alert)
// no // no
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: UIAlertActionStyle.default) { _ in
self.pgpPassphrase = nil self.pgpPassphrase = nil
SharedDefaults[.isRememberPGPPassphraseOn] = false SharedDefaults[.isRememberPGPPassphraseOn] = false
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
}) })
// yes // yes
savePassphraseAlert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.destructive) {_ in savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertActionStyle.destructive) {_ in
// ask for the passphrase // ask for the passphrase
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertActionStyle.default, handler: {_ in
self.pgpPassphrase = alert.textFields?.first?.text self.pgpPassphrase = alert.textFields?.first?.text
SharedDefaults[.isRememberPGPPassphraseOn] = true SharedDefaults[.isRememberPGPPassphraseOn] = true
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)

View file

@ -81,8 +81,8 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
if self.shouldPopCurrentView { if self.shouldPopCurrentView {
let alert = UIAlertController(title: "Notice", message: "All previous local changes have been discarded. Your current Password Store will be shown.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Notice".localize(), message: "PreviousChangesDiscarded.".localize(), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertActionStyle.default, handler: {_ in
_ = self.navigationController?.popViewController(animated: true) _ = self.navigationController?.popViewController(animated: true)
})) }))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
@ -93,8 +93,8 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
let sem = DispatchSemaphore(value: 0) let sem = DispatchSemaphore(value: 0)
var passphrase = "" var passphrase = ""
DispatchQueue.main.async { DispatchQueue.main.async {
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertActionStyle.default, handler: {_ in
passphrase = alert.textFields!.first!.text! passphrase = alert.textFields!.first!.text!
sem.signal() sem.signal()
})) }))
@ -113,7 +113,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
@objc private func decryptThenShowPassword() { @objc private func decryptThenShowPassword() {
guard let passwordEntity = passwordEntity else { guard let passwordEntity = passwordEntity else {
Utils.alert(title: "Cannot Show Password", message: "The password does not exist.", controller: self, handler: {(UIAlertAction) -> Void in Utils.alert(title: "CannotShowPassword".localize(), message: "PasswordDoesNotExist".localize(), controller: self, handler: {(UIAlertAction) -> Void in
self.navigationController!.popViewController(animated: true) self.navigationController!.popViewController(animated: true)
}) })
return return
@ -127,11 +127,11 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
// remove the wrong passphrase so that users could enter it next time // remove the wrong passphrase so that users could enter it next time
self.passwordStore.pgpKeyPassphrase = nil self.passwordStore.pgpKeyPassphrase = nil
// alert: cancel or try again // alert: cancel or try again
let alert = UIAlertController(title: "Cannot Show Password", message: error.localizedDescription, preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "CannotShowPassword".localize(), message: error.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.default) { _ in alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertActionStyle.default) { _ in
self.navigationController!.popViewController(animated: true) self.navigationController!.popViewController(animated: true)
}) })
alert.addAction(UIAlertAction(title: "Try again", style: UIAlertActionStyle.destructive) {_ in alert.addAction(UIAlertAction(title: "TryAgain".localize(), style: UIAlertActionStyle.destructive) {_ in
self.decryptThenShowPassword() self.decryptThenShowPassword()
}) })
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
@ -194,15 +194,15 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
@IBAction private func saveEditPassword(segue: UIStoryboardSegue) { @IBAction private func saveEditPassword(segue: UIStoryboardSegue) {
if self.password!.changed != 0 { if self.password!.changed != 0 {
SVProgressHUD.show(withStatus: "Saving") SVProgressHUD.show(withStatus: "Saving".localize())
do { do {
self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: self.password!) self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: self.password!)
} catch { } catch {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
self.setTableData() self.setTableData()
self.tableView.reloadData() self.tableView.reloadData()
SVProgressHUD.showSuccess(withStatus: "Success") SVProgressHUD.showSuccess(withStatus: "Success".localize())
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} }
} }
@ -211,7 +211,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
do { do {
try passwordStore.delete(passwordEntity: passwordEntity!) try passwordStore.delete(passwordEntity: passwordEntity!)
} catch { } catch {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
let _ = navigationController?.popViewController(animated: true) let _ = navigationController?.popViewController(animated: true)
} }
@ -242,7 +242,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
// show one time password // show one time password
if password.otpType != .none { if password.otpType != .none {
if let (title, otp) = self.password?.getOtpStrings() { if let (title, otp) = self.password?.getOtpStrings() {
section = TableSection(type: .addition, header: "One Time Password") section = TableSection(type: .addition, header: "OneTimePassword".localize())
section.item.append(title => otp) section.item.append(title => otp)
tableData.append(section) tableData.append(section)
oneTimePasswordIndexPath = IndexPath(row: 0, section: tableData.count - 1) oneTimePasswordIndexPath = IndexPath(row: 0, section: tableData.count - 1)
@ -259,7 +259,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
// misc section // misc section
section = TableSection(type: .misc) section = TableSection(type: .misc)
section.item.append(AdditionField(title: "Show Raw")) section.item.append(AdditionField(title: "ShowRaw".localize()))
tableData.append(section) tableData.append(section)
} }
@ -315,10 +315,10 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
if let tappedCell = self.tableView.cellForRow(at: tapIndexPath) as? LabelTableViewCell { if let tappedCell = self.tableView.cellForRow(at: tapIndexPath) as? LabelTableViewCell {
tappedCell.becomeFirstResponder() tappedCell.becomeFirstResponder()
let menuController = UIMenuController.shared let menuController = UIMenuController.shared
let revealItem = UIMenuItem(title: "Reveal", action: #selector(LabelTableViewCell.revealPassword(_:))) let revealItem = UIMenuItem(title: "Reveal".localize(), action: #selector(LabelTableViewCell.revealPassword(_:)))
let concealItem = UIMenuItem(title: "Conceal", action: #selector(LabelTableViewCell.concealPassword(_:))) let concealItem = UIMenuItem(title: "Conceal".localize(), action: #selector(LabelTableViewCell.concealPassword(_:)))
let nextHOTPItem = UIMenuItem(title: "Next Password", action: #selector(LabelTableViewCell.getNextHOTP(_:))) let nextHOTPItem = UIMenuItem(title: "NextPassword".localize(), action: #selector(LabelTableViewCell.getNextHOTP(_:)))
let openURLItem = UIMenuItem(title: "Copy Password & Open Link", action: #selector(LabelTableViewCell.openLink(_:))) let openURLItem = UIMenuItem(title: "CopyAndOpen".localize(), action: #selector(LabelTableViewCell.openLink(_:)))
menuController.menuItems = [revealItem, concealItem, nextHOTPItem, openURLItem] menuController.menuItems = [revealItem, concealItem, nextHOTPItem, openURLItem]
menuController.setTargetRect(tappedCell.contentLabel.frame, in: tappedCell.contentLabel.superview!) menuController.setTargetRect(tappedCell.contentLabel.frame, in: tappedCell.contentLabel.superview!)
menuController.setMenuVisible(true, animated: true) menuController.setMenuVisible(true, animated: true)
@ -340,7 +340,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
func getNextHOTP() { func getNextHOTP() {
guard password != nil, passwordEntity != nil, password?.otpType == .hotp else { guard password != nil, passwordEntity != nil, password?.otpType == .hotp else {
DispatchQueue.main.async { DispatchQueue.main.async {
Utils.alert(title: "Error", message: "Get next password of a non-HOTP entry.", controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: "GetNextPasswordOfNonHotp.".localize(), controller: self, completion: nil)
} }
return; return;
} }
@ -355,9 +355,9 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
do { do {
self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: self.password!) self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: self.password!)
} catch { } catch {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
SVProgressHUD.showSuccess(withStatus: "Password Copied\nCounter Updated") SVProgressHUD.showSuccess(withStatus: "PasswordCopied".localize() + "\n" + "CounterUpdated".localize())
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} }
} }
@ -365,7 +365,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
func openLink(to address: String?) { func openLink(to address: String?) {
guard address != nil, let url = URL(string: formActualWebAddress(from: address!)) else { guard address != nil, let url = URL(string: formActualWebAddress(from: address!)) else {
return DispatchQueue.main.async { return DispatchQueue.main.async {
Utils.alert(title: "Error", message: "Cannot find a valid URL", controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: "CannotFindValidUrl".localize(), controller: self, completion: nil)
} }
} }
SecurePasteboard.shared.copy(textToCopy: password?.password) SecurePasteboard.shared.copy(textToCopy: password?.password)
@ -436,7 +436,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
detailTextLabel.textAlignment = .center detailTextLabel.textAlignment = .center
detailTextLabel.textColor = .gray detailTextLabel.textColor = .gray
detailTextLabel.text = "\(numberOfHiddenFields) hidden field\(numberOfHiddenFields > 1 ? "s" : "")" detailTextLabel.text = "HiddenFields(%d)".localize(numberOfHiddenFields)
} }
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
@ -451,7 +451,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote) footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
footerLabel.textColor = UIColor.gray footerLabel.textColor = UIColor.gray
let dateString = self.passwordStore.getLatestUpdateInfo(filename: password!.url.path) let dateString = self.passwordStore.getLatestUpdateInfo(filename: password!.url.path)
footerLabel.text = "Last Updated: \(dateString)" footerLabel.text = "LastUpdated".localize(dateString)
view.addSubview(footerLabel) view.addSubview(footerLabel)
return view return view
} }
@ -481,7 +481,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let section = tableData[indexPath.section] let section = tableData[indexPath.section]
if section.type == .misc { if section.type == .misc {
if section.item[indexPath.row].title == "Show Raw" { if section.item[indexPath.row].title == "ShowRaw".localize() {
performSegue(withIdentifier: "showRawPasswordSegue", sender: self) performSegue(withIdentifier: "showRawPasswordSegue", sender: self)
} }
} }

View file

@ -29,7 +29,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
private var navigationItemTitle: String? private var navigationItemTitle: String?
private var sectionHeaderTitles = ["name", "password", "additions",""].map {$0.uppercased()} private var sectionHeaderTitles = ["name", "password", "additions",""].map {$0.uppercased()}
private var sectionFooterTitles = ["", "", "Use \"key: value\" format for additional fields.", ""] private var sectionFooterTitles = ["", "", "UseKeyValueFormat.".localize(), ""]
private let nameSection = 0 private let nameSection = 0
private let passwordSection = 1 private let passwordSection = 1
private let additionsSection = 2 private let additionsSection = 2
@ -47,23 +47,24 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
super.loadView() super.loadView()
deletePasswordCell = UITableViewCell(style: .default, reuseIdentifier: "default") deletePasswordCell = UITableViewCell(style: .default, reuseIdentifier: "default")
deletePasswordCell!.textLabel?.text = "Delete Password" deletePasswordCell!.textLabel?.text = "DeletePassword".localize()
deletePasswordCell!.textLabel?.textColor = Globals.red deletePasswordCell!.textLabel?.textColor = Globals.red
deletePasswordCell?.selectionStyle = .default deletePasswordCell?.selectionStyle = .default
scanQRCodeCell = UITableViewCell(style: .default, reuseIdentifier: "default") scanQRCodeCell = UITableViewCell(style: .default, reuseIdentifier: "default")
scanQRCodeCell?.textLabel?.text = "Add One-Time Password" scanQRCodeCell?.textLabel?.text = "AddOneTimePassword".localize()
scanQRCodeCell?.textLabel?.textColor = Globals.blue scanQRCodeCell?.textLabel?.textColor = Globals.blue
scanQRCodeCell?.selectionStyle = .default scanQRCodeCell?.selectionStyle = .default
scanQRCodeCell?.accessoryType = .disclosureIndicator scanQRCodeCell?.accessoryType = .disclosureIndicator
memorablePasswordGeneratorCell = UITableViewCell(style: .default, reuseIdentifier: "default") memorablePasswordGeneratorCell = UITableViewCell(style: .default, reuseIdentifier: "default")
memorablePasswordGeneratorCell?.textLabel?.text = "Get a Memorable One: xkpasswd" memorablePasswordGeneratorCell?.textLabel?.text = "GetMemorableOne".localize()
memorablePasswordGeneratorCell?.textLabel?.textColor = Globals.blue memorablePasswordGeneratorCell?.textLabel?.textColor = Globals.blue
memorablePasswordGeneratorCell?.selectionStyle = .default memorablePasswordGeneratorCell?.selectionStyle = .default
memorablePasswordGeneratorCell?.accessoryType = .disclosureIndicator memorablePasswordGeneratorCell?.accessoryType = .disclosureIndicator
} }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
if navigationItemTitle != nil { if navigationItemTitle != nil {
@ -113,7 +114,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
currentPasswordLength <= maximumLength { currentPasswordLength <= maximumLength {
defaultLength = currentPasswordLength defaultLength = currentPasswordLength
} }
passwordLengthCell?.reset(title: "Length", passwordLengthCell?.reset(title: "Length".localize(),
minimumValue: minimumLength, minimumValue: minimumLength,
maximumValue: maximumLength, maximumValue: maximumLength,
defaultValue: defaultLength) defaultValue: defaultLength)
@ -161,11 +162,11 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath) let selectedCell = tableView.cellForRow(at: indexPath)
if selectedCell == deletePasswordCell { if selectedCell == deletePasswordCell {
let alert = UIAlertController(title: "Delete Password?", message: nil, preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "DeletePassword?".localize(), message: nil, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Delete", style: UIAlertActionStyle.destructive, handler: {[unowned self] (action) -> Void in alert.addAction(UIAlertAction(title: "Delete".localize(), style: UIAlertActionStyle.destructive, handler: {[unowned self] (action) -> Void in
self.performSegue(withIdentifier: "deletePasswordSegue", sender: self) self.performSegue(withIdentifier: "deletePasswordSegue", sender: self)
})) }))
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler:nil)) alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertActionStyle.cancel, handler:nil))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} else if selectedCell == scanQRCodeCell { } else if selectedCell == scanQRCodeCell {
self.performSegue(withIdentifier: "showQRScannerSegue", sender: self) self.performSegue(withIdentifier: "showQRScannerSegue", sender: self)
@ -185,11 +186,11 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
// check whether the current password looks like an OTP field // check whether the current password looks like an OTP field
func generateAndCopyPassword() { func generateAndCopyPassword() {
if let currentPassword = fillPasswordCell?.getContent(), Constants.isOtpRelated(line: currentPassword) { if let currentPassword = fillPasswordCell?.getContent(), Constants.isOtpRelated(line: currentPassword) {
let alert = UIAlertController(title: "Overwrite?", message: "Overwrite the one-time password configuration?", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Overwrite?".localize(), message: "OverwriteOtpConfiguration?".localize(), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.destructive, handler: {_ in alert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertActionStyle.destructive, handler: {_ in
self.generateAndCopyPasswordNoOtpCheck() self.generateAndCopyPasswordNoOtpCheck()
})) }))
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil)) alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertActionStyle.cancel, handler: nil))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} else { } else {
self.generateAndCopyPasswordNoOtpCheck() self.generateAndCopyPasswordNoOtpCheck()
@ -243,9 +244,9 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
// MARK: - QRScannerControllerDelegate Methods // MARK: - QRScannerControllerDelegate Methods
func checkScannedOutput(line: String) -> (accept: Bool, message: String) { func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
if let url = URL(string: line), let _ = Token(url: url) { if let url = URL(string: line), let _ = Token(url: url) {
return (accept: true, message: "Valid token URL") return (accept: true, message: "ValidTokenUrl".localize())
} else { } else {
return (accept: false, message: "Invalid token URL") return (accept: false, message: "InvalidTokenUrl".localize())
} }
} }
@ -302,20 +303,20 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
func checkName() -> Bool { func checkName() -> Bool {
// the name field should not be empty // the name field should not be empty
guard let name = nameCell?.getContent(), name.isEmpty == false else { guard let name = nameCell?.getContent(), name.isEmpty == false else {
Utils.alert(title: "Cannot Save", message: "Please fill in the name.", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "FillInName.".localize(), controller: self, completion: nil)
return false return false
} }
// the name should not start with / // the name should not start with /
guard name.hasPrefix("/") == false else { guard name.hasPrefix("/") == false else {
Utils.alert(title: "Cannot Save", message: "Please remove the prefix \"/\" from your password name.", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "RemovePrefix.".localize(), controller: self, completion: nil)
return false return false
} }
// the name field should be a valid url // the name field should be a valid url
guard let path = name.stringByAddingPercentEncodingForRFC3986(), guard let path = name.stringByAddingPercentEncodingForRFC3986(),
var passwordURL = URL(string: path) else { var passwordURL = URL(string: path) else {
Utils.alert(title: "Cannot Save", message: "Password name is invalid.", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "PasswordNameInvalid.".localize(), controller: self, completion: nil)
return false return false
} }
@ -324,7 +325,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
while passwordURL.path != "." { while passwordURL.path != "." {
passwordURL = passwordURL.deletingLastPathComponent() passwordURL = passwordURL.deletingLastPathComponent()
if passwordURL.path != "." && passwordURL.path.count >= previousPathLength { if passwordURL.path != "." && passwordURL.path.count >= previousPathLength {
Utils.alert(title: "Cannot Save", message: "Cannot parse the filename. Please check and simplify the password name.", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "CannotParseFilename.".localize(), controller: self, completion: nil)
return false return false
} }
previousPathLength = passwordURL.path.count previousPathLength = passwordURL.path.count
@ -337,8 +338,8 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
let copiedLinesSplit = UIPasteboard.general.string?.components(separatedBy: CharacterSet.whitespacesAndNewlines).filter({ !$0.isEmpty }) let copiedLinesSplit = UIPasteboard.general.string?.components(separatedBy: CharacterSet.whitespacesAndNewlines).filter({ !$0.isEmpty })
if copiedLinesSplit?.count ?? 0 > 0 { if copiedLinesSplit?.count ?? 0 > 0 {
let generatedPassword = copiedLinesSplit![0] let generatedPassword = copiedLinesSplit![0]
let alert = UIAlertController(title: "Wanna use it?", message: "", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "WannaUseIt?".localize(), message: "", preferredStyle: UIAlertControllerStyle.alert)
let message = NSMutableAttributedString(string: "It seems like you have copied something. The first string is:\n") let message = NSMutableAttributedString(string: "\("SeemsLikeYouHaveCopiedSomething.".localize()) \("FirstStringIs:".localize())\n")
message.append(Utils.attributedPassword(plainPassword: generatedPassword)) message.append(Utils.attributedPassword(plainPassword: generatedPassword))
alert.setValue(message, forKey: "attributedMessage") alert.setValue(message, forKey: "attributedMessage")
alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.default, handler: {[unowned self] (action) -> Void in alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.default, handler: {[unowned self] (action) -> Void in
@ -349,7 +350,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
// make sure the clipboard gets cleared in 45s // make sure the clipboard gets cleared in 45s
SecurePasteboard.shared.copy(textToCopy: generatedPassword) SecurePasteboard.shared.copy(textToCopy: generatedPassword)
})) }))
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler:nil)) alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertActionStyle.cancel, handler:nil))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} }
} }

View file

@ -56,7 +56,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
return nil return nil
}() }()
private lazy var backUIBarButtonItem: UIBarButtonItem = { private lazy var backUIBarButtonItem: UIBarButtonItem = {
let backUIBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(self.backAction(_:))) let backUIBarButtonItem = UIBarButtonItem(title: "Back".localize(), style: .plain, target: self, action: #selector(self.backAction(_:)))
return backUIBarButtonItem return backUIBarButtonItem
}() }()
@ -110,18 +110,18 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
if let controller = segue.source as? AddPasswordTableViewController { if let controller = segue.source as? AddPasswordTableViewController {
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "Saving") SVProgressHUD.show(withStatus: "Saving".localize())
DispatchQueue.global(qos: .userInitiated).async { DispatchQueue.global(qos: .userInitiated).async {
do { do {
let _ = try self.passwordStore.add(password: controller.password!) let _ = try self.passwordStore.add(password: controller.password!)
DispatchQueue.main.async { DispatchQueue.main.async {
// will trigger reloadTableView() by a notification // will trigger reloadTableView() by a notification
SVProgressHUD.showSuccess(withStatus: "Done") SVProgressHUD.showSuccess(withStatus: "Done".localize())
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} }
} catch { } catch {
DispatchQueue.main.async { DispatchQueue.main.async {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
} }
} }
@ -131,13 +131,13 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
private func syncPasswords() { private func syncPasswords() {
guard passwordStore.repositoryExisted() else { guard passwordStore.repositoryExisted() else {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(800)) { DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(800)) {
Utils.alert(title: "Error", message: "There is no password store right now.", controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: "NoPasswordStore.".localize(), controller: self, completion: nil)
} }
return return
} }
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "Sync Password Store") SVProgressHUD.show(withStatus: "SyncingPasswordStore".localize())
var gitCredential: GitCredential var gitCredential: GitCredential
if SharedDefaults[.gitAuthenticationMethod] == "Password" { if SharedDefaults[.gitAuthenticationMethod] == "Password" {
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: SharedDefaults[.gitUsername]!)) gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: SharedDefaults[.gitUsername]!))
@ -159,13 +159,13 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
if self.passwordStore.numberOfLocalCommits ?? 0 > 0 { if self.passwordStore.numberOfLocalCommits ?? 0 > 0 {
try self.passwordStore.pushRepository(credential: gitCredential, requestGitPassword: self.requestGitPassword(credential:lastPassword:), transferProgressBlock: {(current, total, bytes, stop) in try self.passwordStore.pushRepository(credential: gitCredential, requestGitPassword: self.requestGitPassword(credential:lastPassword:), transferProgressBlock: {(current, total, bytes, stop) in
DispatchQueue.main.async { DispatchQueue.main.async {
SVProgressHUD.showProgress(Float(current)/Float(total), status: "Push Remote Repository") SVProgressHUD.showProgress(Float(current)/Float(total), status: "PushingToRemoteRepository".localize())
} }
}) })
} }
DispatchQueue.main.async { DispatchQueue.main.async {
self.reloadTableView(parent: nil) self.reloadTableView(parent: nil)
SVProgressHUD.showSuccess(withStatus: "Done") SVProgressHUD.showSuccess(withStatus: "Done".localize())
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
self.syncControl.endRefreshing() self.syncControl.endRefreshing()
} }
@ -176,14 +176,14 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
let error = error as NSError let error = error as NSError
var message = error.localizedDescription var message = error.localizedDescription
if let underlyingError = error.userInfo[NSUnderlyingErrorKey] as? NSError { if let underlyingError = error.userInfo[NSUnderlyingErrorKey] as? NSError {
message = "\(message)\nUnderlying error: \(underlyingError.localizedDescription)" message = "\(message)\n\("UnderlyingError".localize(underlyingError.localizedDescription))"
if underlyingError.localizedDescription.contains("Wrong passphrase") { if underlyingError.localizedDescription.contains("WrongPassphrase".localize()) {
message = "\(message)\nRecovery suggestion: Wrong credential password/passphrase has been removed, please try again." message = "\(message)\n\("RecoverySuggestion".localize())"
gitCredential.delete() gitCredential.delete()
} }
} }
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(800)) { DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(800)) {
Utils.alert(title: "Error", message: message, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: message, controller: self, completion: nil)
} }
} }
} }
@ -194,7 +194,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
super.viewDidAppear(animated) super.viewDidAppear(animated)
if SharedDefaults[.isShowFolderOn] { if SharedDefaults[.isShowFolderOn] {
searchController.searchBar.scopeButtonTitles = ["Current", "All"] searchController.searchBar.scopeButtonTitles = ["Current".localize(), "All".localize()]
} else { } else {
searchController.searchBar.scopeButtonTitles = nil searchController.searchBar.scopeButtonTitles = nil
} }
@ -365,8 +365,8 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
let sem = DispatchSemaphore(value: 0) let sem = DispatchSemaphore(value: 0)
var passphrase = "" var passphrase = ""
DispatchQueue.main.async { DispatchQueue.main.async {
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertActionStyle.default, handler: {_ in
passphrase = alert.textFields!.first!.text! passphrase = alert.textFields!.first!.text!
sem.signal() sem.signal()
})) }))
@ -381,7 +381,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
let _ = sem.wait(timeout: DispatchTime.distantFuture) let _ = sem.wait(timeout: DispatchTime.distantFuture)
DispatchQueue.main.async { DispatchQueue.main.async {
// bring back // bring back
SVProgressHUD.show(withStatus: "Decrypting") SVProgressHUD.show(withStatus: "Decrypting".localize())
} }
if SharedDefaults[.isRememberPGPPassphraseOn] { if SharedDefaults[.isRememberPGPPassphraseOn] {
self.passwordStore.pgpKeyPassphrase = passphrase self.passwordStore.pgpKeyPassphrase = passphrase
@ -391,28 +391,28 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
private func decryptThenCopyPassword(from indexPath: IndexPath) { private func decryptThenCopyPassword(from indexPath: IndexPath) {
guard self.passwordStore.privateKey != nil else { guard self.passwordStore.privateKey != nil else {
Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil) Utils.alert(title: "CannotCopyPassword".localize(), message: "SetPgpKey.".localize(), controller: self, completion: nil)
return return
} }
let passwordEntity = getPasswordEntry(by: indexPath).passwordEntity! let passwordEntity = getPasswordEntry(by: indexPath).passwordEntity!
UIImpactFeedbackGenerator(style: .medium).impactOccurred() UIImpactFeedbackGenerator(style: .medium).impactOccurred()
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.dark) SVProgressHUD.setDefaultStyle(.dark)
SVProgressHUD.show(withStatus: "Decrypting") SVProgressHUD.show(withStatus: "Decrypting".localize())
DispatchQueue.global(qos: .userInteractive).async { DispatchQueue.global(qos: .userInteractive).async {
var decryptedPassword: Password? var decryptedPassword: Password?
do { do {
decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, requestPGPKeyPassphrase: self.requestPGPKeyPassphrase) decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, requestPGPKeyPassphrase: self.requestPGPKeyPassphrase)
DispatchQueue.main.async { DispatchQueue.main.async {
SecurePasteboard.shared.copy(textToCopy: decryptedPassword?.password) SecurePasteboard.shared.copy(textToCopy: decryptedPassword?.password)
SVProgressHUD.showSuccess(withStatus: "Password copied. We will clear the pasteboard in 45 seconds.") SVProgressHUD.showSuccess(withStatus: "PasswordCopiedToPasteboard.".localize())
SVProgressHUD.dismiss(withDelay: 0.6) SVProgressHUD.dismiss(withDelay: 0.6)
} }
} catch { } catch {
DispatchQueue.main.async { DispatchQueue.main.async {
// remove the wrong passphrase so that users could enter it next time // remove the wrong passphrase so that users could enter it next time
self.passwordStore.pgpKeyPassphrase = nil self.passwordStore.pgpKeyPassphrase = nil
Utils.alert(title: "Cannot Copy Password", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
} }
} }
@ -453,7 +453,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "showPasswordDetail" { if identifier == "showPasswordDetail" {
guard self.passwordStore.privateKey != nil else { guard self.passwordStore.privateKey != nil else {
Utils.alert(title: "Cannot Show Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil) Utils.alert(title: "CannotShowPassword".localize(), message: "SetPgpKey.".localize(), controller: self, completion: nil)
if let s = sender as? UITableViewCell { if let s = sender as? UITableViewCell {
let selectedIndexPath = tableView.indexPath(for: s)! let selectedIndexPath = tableView.indexPath(for: s)!
tableView.deselectRow(at: selectedIndexPath, animated: true) tableView.deselectRow(at: selectedIndexPath, animated: true)
@ -462,7 +462,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
} else if identifier == "addPasswordSegue" { } else if identifier == "addPasswordSegue" {
guard self.passwordStore.publicKey != nil, self.passwordStore.storeRepository != nil else { guard self.passwordStore.publicKey != nil, self.passwordStore.storeRepository != nil else {
Utils.alert(title: "Cannot Add Password", message: "Please make sure PGP Key and Git Server are properly set.", controller: self, completion: nil) Utils.alert(title: "CannotAddPassword".localize(), message: "MakeSurePgpAndGitProperlySet.".localize(), controller: self, completion: nil)
return false return false
} }
} }
@ -537,13 +537,13 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
self.tableView.layer.removeAnimation(forKey: "UITableViewReloadDataAnimationKey") self.tableView.layer.removeAnimation(forKey: "UITableViewReloadDataAnimationKey")
// set the sync control title // set the sync control title
let atribbutedTitle = "Last Synced: \(lastSyncedTimeString())" let atribbutedTitle = "LastSynced".localize() + ": \(lastSyncedTimeString())"
syncControl.attributedTitle = NSAttributedString(string: atribbutedTitle) syncControl.attributedTitle = NSAttributedString(string: atribbutedTitle)
} }
private func lastSyncedTimeString() -> String { private func lastSyncedTimeString() -> String {
guard let date = self.passwordStore.lastSyncedTime else { guard let date = self.passwordStore.lastSyncedTime else {
return "Oops! Sync again?" return "SyncAgain?".localize()
} }
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateStyle = .medium formatter.dateStyle = .medium
@ -615,23 +615,23 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
var message = "" var message = ""
switch credential { switch credential {
case .http: case .http:
message = "Please fill in the password of your Git account." message = "FillInGitAccountPassword.".localize()
case .ssh: case .ssh:
message = "Please fill in the password of your SSH key." message = "FillInSshKeyPassphrase.".localize()
} }
DispatchQueue.main.async { DispatchQueue.main.async {
SVProgressHUD.dismiss() SVProgressHUD.dismiss()
let alert = UIAlertController(title: "Password", message: message, preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Password".localize(), message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addTextField(configurationHandler: {(textField: UITextField!) in alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = lastPassword ?? "" textField.text = lastPassword ?? ""
textField.isSecureTextEntry = true textField.isSecureTextEntry = true
}) })
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertActionStyle.default, handler: {_ in
password = alert.textFields!.first!.text password = alert.textFields!.first!.text
sem.signal() sem.signal()
})) }))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in alert.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel) { _ in
password = nil password = nil
sem.signal() sem.signal()
}) })

View file

@ -66,7 +66,7 @@ class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDeleg
// Move the message label to the front // Move the message label to the front
scannerOutput.layer.cornerRadius = 10 scannerOutput.layer.cornerRadius = 10
scannerOutput.text = "No QR code detected" scannerOutput.text = "NoQrCodeDetected.".localize()
view.bringSubview(toFront: scannerOutput) view.bringSubview(toFront: scannerOutput)
// Initialize QR Code Frame to highlight the QR code // Initialize QR Code Frame to highlight the QR code
@ -109,7 +109,7 @@ class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDeleg
captureSession?.stopRunning() captureSession?.stopRunning()
delegate?.handleScannedOutput(line: scanned) delegate?.handleScannedOutput(line: scanned)
DispatchQueue.main.async { DispatchQueue.main.async {
SVProgressHUD.showSuccess(withStatus: "Done") SVProgressHUD.showSuccess(withStatus: "Done".localize())
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
self.navigationController?.popViewController(animated: true) self.navigationController?.popViewController(animated: true)
} }
@ -119,21 +119,21 @@ class QRScannerController: UIViewController, AVCaptureMetadataOutputObjectsDeleg
scannerOutput.text = scanned scannerOutput.text = scanned
} }
} else { } else {
scannerOutput.text = "No string value" scannerOutput.text = "NoStringValue".localize()
} }
} else { } else {
qrCodeFrameView?.frame = CGRect.zero qrCodeFrameView?.frame = CGRect.zero
scannerOutput.text = "No QR code detected" scannerOutput.text = "NoQrCodeDetected.".localize()
} }
} }
func presentCameraSettings() { func presentCameraSettings() {
let alertController = UIAlertController(title: "Error", let alertController = UIAlertController(title: "Error".localize(),
message: "Camera access denied.\nWARNING: Toggle the camera permission resets the app! Save your changes.", message: "CameraAccessDenied.".localize() + "\n" + "WarningToggleCameraPermissionsResetsApp.".localize(),
preferredStyle: .alert) preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Cancel", style: .default)) alertController.addAction(UIAlertAction(title: "Cancel".localize(), style: .default))
alertController.addAction(UIAlertAction(title: "Settings", style: .cancel) { _ in alertController.addAction(UIAlertAction(title: "Settings".localize(), style: .cancel) { _ in
if let url = URL(string: UIApplicationOpenSettingsURLString) { if let url = URL(string: UIApplicationOpenSettingsURLString) {
UIApplication.shared.open(url, options: [:], completionHandler: { _ in UIApplication.shared.open(url, options: [:], completionHandler: { _ in
// Handle // Handle

View file

@ -23,7 +23,7 @@ class SSHKeySettingTableViewController: UITableViewController {
@IBAction func doneButtonTapped(_ sender: UIButton) { @IBAction func doneButtonTapped(_ sender: UIButton) {
guard let privateKeyURL = URL(string: privateKeyURLTextField.text!.trimmed) else { guard let privateKeyURL = URL(string: privateKeyURLTextField.text!.trimmed) else {
Utils.alert(title: "Cannot Save", message: "Please set Private Key URL first.", controller: self, completion: nil) Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKeyUrl.".localize(), controller: self, completion: nil)
return return
} }
@ -32,7 +32,7 @@ class SSHKeySettingTableViewController: UITableViewController {
do { do {
try Data(contentsOf: privateKeyURL).write(to: URL(fileURLWithPath: Globals.gitSSHPrivateKeyPath), options: .atomic) try Data(contentsOf: privateKeyURL).write(to: URL(fileURLWithPath: Globals.gitSSHPrivateKeyPath), options: .atomic)
} catch { } catch {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
SharedDefaults[.gitSSHKeySource] = "url" SharedDefaults[.gitSSHKeySource] = "url"
self.navigationController!.popViewController(animated: true) self.navigationController!.popViewController(animated: true)

View file

@ -35,21 +35,21 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "Fetching PGP Key") SVProgressHUD.show(withStatus: "FetchingPgpKey".localize())
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do { do {
try self.passwordStore.initPGPKey(from: SharedDefaults[.pgpPublicKeyURL]!, keyType: .public) try self.passwordStore.initPGPKey(from: SharedDefaults[.pgpPublicKeyURL]!, keyType: .public)
try self.passwordStore.initPGPKey(from: SharedDefaults[.pgpPrivateKeyURL]!, keyType: .secret) try self.passwordStore.initPGPKey(from: SharedDefaults[.pgpPrivateKeyURL]!, keyType: .secret)
DispatchQueue.main.async { DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID
SVProgressHUD.showSuccess(withStatus: "Success") SVProgressHUD.showSuccess(withStatus: "Success".localize())
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
Utils.alert(title: "Remember to Remove the Key", message: "Remember to remove the key from the server.", controller: self, completion: nil) Utils.alert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromServer.".localize(), controller: self, completion: nil)
} }
} catch { } catch {
DispatchQueue.main.async { DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" self.pgpKeyTableViewCell.detailTextLabel?.text = "NotSet".localize()
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
} }
} }
@ -65,20 +65,20 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "Fetching PGP Key") SVProgressHUD.show(withStatus: "FetchingPgpKey".localize())
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do { do {
try self.passwordStore.initPGPKey(with: SharedDefaults[.pgpPublicKeyArmor] ?? "", keyType: .public) try self.passwordStore.initPGPKey(with: SharedDefaults[.pgpPublicKeyArmor] ?? "", keyType: .public)
try self.passwordStore.initPGPKey(with: SharedDefaults[.pgpPrivateKeyArmor] ?? "", keyType: .secret) try self.passwordStore.initPGPKey(with: SharedDefaults[.pgpPrivateKeyArmor] ?? "", keyType: .secret)
DispatchQueue.main.async { DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID
SVProgressHUD.showSuccess(withStatus: "Success") SVProgressHUD.showSuccess(withStatus: "Success".localize())
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} }
} catch { } catch {
DispatchQueue.main.async { DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" self.pgpKeyTableViewCell.detailTextLabel?.text = "NotSet".localize()
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
} }
} }
@ -90,20 +90,20 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
SharedDefaults[.pgpKeySource] = "file" SharedDefaults[.pgpKeySource] = "file"
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light) SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "Fetching PGP Key") SVProgressHUD.show(withStatus: "FetchingPgpKey".localize())
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do { do {
try self.passwordStore.pgpKeyImportFromFileSharing() try self.passwordStore.pgpKeyImportFromFileSharing()
try self.passwordStore.initPGPKeys() try self.passwordStore.initPGPKeys()
DispatchQueue.main.async { DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID
SVProgressHUD.showSuccess(withStatus: "Imported") SVProgressHUD.showSuccess(withStatus: "Imported".localize())
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} }
} catch { } catch {
DispatchQueue.main.async { DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" self.pgpKeyTableViewCell.detailTextLabel?.text = "NotSet".localize()
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
} }
} }
@ -133,9 +133,9 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
private func setPasscodeLockCell() { private func setPasscodeLockCell() {
if passcodeLock.hasPasscode { if passcodeLock.hasPasscode {
self.passcodeTableViewCell.detailTextLabel?.text = "On" self.passcodeTableViewCell.detailTextLabel?.text = "On".localize()
} else { } else {
self.passcodeTableViewCell.detailTextLabel?.text = "Off" self.passcodeTableViewCell.detailTextLabel?.text = "Off".localize()
} }
} }
@ -143,13 +143,13 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
if let pgpKeyID = self.passwordStore.pgpKeyID { if let pgpKeyID = self.passwordStore.pgpKeyID {
pgpKeyTableViewCell.detailTextLabel?.text = pgpKeyID pgpKeyTableViewCell.detailTextLabel?.text = pgpKeyID
} else { } else {
pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" pgpKeyTableViewCell.detailTextLabel?.text = "NotSet".localize()
} }
} }
private func setPasswordRepositoryTableViewCellDetailText() { private func setPasswordRepositoryTableViewCellDetailText() {
if SharedDefaults[.gitURL] == nil { if SharedDefaults[.gitURL] == nil {
passwordRepositoryTableViewCell.detailTextLabel?.text = "Not Set" passwordRepositoryTableViewCell.detailTextLabel?.text = "NotSet".localize()
} else { } else {
passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]!.host passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]!.host
} }
@ -176,9 +176,9 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
func showPGPKeyActionSheet() { func showPGPKeyActionSheet() {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
var urlActionTitle = "Download from URL" var urlActionTitle = "DownloadFromUrl".localize()
var armorActionTitle = "ASCII-Armor Encrypted Key" var armorActionTitle = "AsciiArmorEncryptedKey".localize()
var fileActionTitle = "iTunes File Sharing" var fileActionTitle = "ITunesFileSharing".localize()
if SharedDefaults[.pgpKeySource] == "url" { if SharedDefaults[.pgpKeySource] == "url" {
urlActionTitle = "\(urlActionTitle)" urlActionTitle = "\(urlActionTitle)"
@ -193,26 +193,26 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
let armorAction = UIAlertAction(title: armorActionTitle, style: .default) { _ in let armorAction = UIAlertAction(title: armorActionTitle, style: .default) { _ in
self.performSegue(withIdentifier: "setPGPKeyByASCIISegue", sender: self) self.performSegue(withIdentifier: "setPGPKeyByASCIISegue", sender: self)
} }
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) let cancelAction = UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)
optionMenu.addAction(urlAction) optionMenu.addAction(urlAction)
optionMenu.addAction(armorAction) optionMenu.addAction(armorAction)
if passwordStore.pgpKeyExists(inFileSharing: true) { if passwordStore.pgpKeyExists(inFileSharing: true) {
fileActionTitle.append(" (Import)") fileActionTitle.append(" (\("Import".localize()))")
let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in
// passphrase related // passphrase related
let savePassphraseAlert = UIAlertController(title: "Passphrase", message: "Do you want to save the passphrase for later decryption?", preferredStyle: UIAlertControllerStyle.alert) let savePassphraseAlert = UIAlertController(title: "Passphrase".localize(), message: "WantToSavePassphrase?".localize(), preferredStyle: UIAlertControllerStyle.alert)
// no // no
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: UIAlertActionStyle.default) { _ in
self.passwordStore.pgpKeyPassphrase = nil self.passwordStore.pgpKeyPassphrase = nil
SharedDefaults[.isRememberPGPPassphraseOn] = false SharedDefaults[.isRememberPGPPassphraseOn] = false
self.saveImportedPGPKey() self.saveImportedPGPKey()
}) })
// yes // yes
savePassphraseAlert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.destructive) {_ in savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertActionStyle.destructive) {_ in
// ask for the passphrase // ask for the passphrase
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertActionStyle.default, handler: {_ in
self.passwordStore.pgpKeyPassphrase = alert.textFields?.first?.text self.passwordStore.pgpKeyPassphrase = alert.textFields?.first?.text
SharedDefaults[.isRememberPGPPassphraseOn] = true SharedDefaults[.isRememberPGPPassphraseOn] = true
self.saveImportedPGPKey() self.saveImportedPGPKey()
@ -227,10 +227,10 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
} }
optionMenu.addAction(fileAction) optionMenu.addAction(fileAction)
} else { } else {
fileActionTitle.append(" (Tips)") fileActionTitle.append(" (\("Tips".localize()))")
let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in
let title = "Tips" let title = "Tips".localize()
let message = "Copy your ASCII-armored public and private keys to Pass with names \"gpg_key.pub\" and \"gpg_key\" (without quotes) via iTunes. Then come back and click \"iTunes File Sharing\" to finish." let message = "CopyPrivateKeyToPass.".localize()
Utils.alert(title: title, message: message, controller: self) Utils.alert(title: title, message: message, controller: self)
} }
optionMenu.addAction(fileAction) optionMenu.addAction(fileAction)
@ -238,9 +238,9 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
if SharedDefaults[.pgpKeySource] != nil { if SharedDefaults[.pgpKeySource] != nil {
let deleteAction = UIAlertAction(title: "Remove PGP Keys", style: .destructive) { _ in let deleteAction = UIAlertAction(title: "RemovePgpKeys".localize(), style: .destructive) { _ in
self.passwordStore.removePGPKeys() self.passwordStore.removePGPKeys()
self.pgpKeyTableViewCell.detailTextLabel?.text = "Not Set" self.pgpKeyTableViewCell.detailTextLabel?.text = "NotSet".localize()
} }
optionMenu.addAction(deleteAction) optionMenu.addAction(deleteAction)
} }
@ -255,7 +255,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
let passcodeRemoveViewController = PasscodeLockViewController() let passcodeRemoveViewController = PasscodeLockViewController()
let removePasscodeAction = UIAlertAction(title: "Remove Passcode", style: .destructive) { [weak self] _ in let removePasscodeAction = UIAlertAction(title: "RemovePasscode".localize(), style: .destructive) { [weak self] _ in
passcodeRemoveViewController.successCallback = { passcodeRemoveViewController.successCallback = {
self?.passcodeLock.delete() self?.passcodeLock.delete()
self?.setPasscodeLockCell() self?.setPasscodeLockCell()
@ -263,11 +263,11 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
self?.present(passcodeRemoveViewController, animated: true, completion: nil) self?.present(passcodeRemoveViewController, animated: true, completion: nil)
} }
let changePasscodeAction = UIAlertAction(title: "Change Passcode", style: .default) { [weak self] _ in let changePasscodeAction = UIAlertAction(title: "ChangePasscode".localize(), style: .default) { [weak self] _ in
self?.setPasscodeLock() self?.setPasscodeLock()
} }
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) let cancelAction = UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)
optionMenu.addAction(removePasscodeAction) optionMenu.addAction(removePasscodeAction)
optionMenu.addAction(changePasscodeAction) optionMenu.addAction(changePasscodeAction)
optionMenu.addAction(cancelAction) optionMenu.addAction(cancelAction)
@ -292,20 +292,20 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
func setPasscodeLock() { func setPasscodeLock() {
// prepare the alert for setting the passcode // prepare the alert for setting the passcode
setPasscodeLockAlert = UIAlertController(title: "Set passcode", message: "Fill in your passcode for Pass (at least 4 characters)", preferredStyle: .alert) setPasscodeLockAlert = UIAlertController(title: "SetPasscode".localize(), message: "FillInAppPasscode.".localize(), preferredStyle: .alert)
setPasscodeLockAlert?.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in setPasscodeLockAlert?.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in
textField.placeholder = "Password" textField.placeholder = "Password".localize()
textField.isSecureTextEntry = true textField.isSecureTextEntry = true
textField.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControlEvents.editingChanged) textField.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControlEvents.editingChanged)
}) })
setPasscodeLockAlert?.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in setPasscodeLockAlert?.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in
textField.placeholder = "Password Confirmation" textField.placeholder = "PasswordConfirmation".localize()
textField.isSecureTextEntry = true textField.isSecureTextEntry = true
textField.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControlEvents.editingChanged) textField.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControlEvents.editingChanged)
}) })
// save action // save action
let saveAction = UIAlertAction(title: "Save", style: .default) { (action:UIAlertAction) -> Void in let saveAction = UIAlertAction(title: "Save".localize(), style: .default) { (action:UIAlertAction) -> Void in
let passcode: String = self.setPasscodeLockAlert!.textFields![0].text! let passcode: String = self.setPasscodeLockAlert!.textFields![0].text!
self.passcodeLock.save(passcode: passcode) self.passcodeLock.save(passcode: passcode)
// refresh the passcode lock cell ("On") // refresh the passcode lock cell ("On")
@ -314,7 +314,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
saveAction.isEnabled = false // disable the Save button by default saveAction.isEnabled = false // disable the Save button by default
// cancel action // cancel action
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) let cancelAction = UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)
// present // present
setPasscodeLockAlert?.addAction(saveAction) setPasscodeLockAlert?.addAction(saveAction)

View file

@ -10,7 +10,7 @@ import UIKit
class SpecialThanksTableViewController: BasicStaticTableViewController { class SpecialThanksTableViewController: BasicStaticTableViewController {
let openSourceComponents = [ let openSourceComponents = [
["Contributors", ["Contributors".localize(),
"https://github.com/mssun/passforios/graphs/contributors"], "https://github.com/mssun/passforios/graphs/contributors"],
["Password Store", ["Password Store",
"https://passwordstore.org"], "https://passwordstore.org"],

View file

@ -14,7 +14,7 @@ extension Utils {
static func alert(title: String, message: String, controller: UIViewController, handler: ((UIAlertAction) -> Void)? = nil, completion: (() -> Void)? = nil) { static func alert(title: String, message: String, controller: UIViewController, handler: ((UIAlertAction) -> Void)? = nil, completion: (() -> Void)? = nil) {
SVProgressHUD.dismiss() SVProgressHUD.dismiss()
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: handler)) alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertActionStyle.default, handler: handler))
controller.present(alert, animated: true, completion: completion) controller.present(alert, animated: true, completion: completion)
} }
} }

View file

@ -51,7 +51,7 @@ class LabelTableViewCell: UITableViewCell {
} }
} }
contentLabel.font = Globals.passwordFont contentLabel.font = Globals.passwordFont
} else if title.caseInsensitiveCompare("hmac-based") == .orderedSame { } else if title.caseInsensitiveCompare("HmacBased".localize()) == .orderedSame {
type = .HOTP type = .HOTP
if isReveal { if isReveal {
contentLabel.text = content contentLabel.text = content

View file

@ -6,5 +6,239 @@
Copyright © 2019 Bob Sun. All rights reserved. Copyright © 2019 Bob Sun. All rights reserved.
*/ */
// General
"PassForIos" = "Pass for iOS";
"Passphrase" = "Passphrase";
"Passwords" = "Passwords";
"Apple" = "Apple"; "Apple" = "Apple";
"Settings" = "Settings";
"Contributors" = "Contributors";
// OTP related
"TimeBased" = "time-based";
"HmacBased" = "HMAC-based";
"None" = "None";
"ExpiresIn" = "(expires in %ds)";
// General (error) messages
"Error" = "Error";
"CannotSave" = "Cannot Save";
"UnresolvedError" = "Unresolved error %@";
"MigrationError" = "Migration error: %@";
"UnderlyingError" = "Underlying Error: %@";
"ErrorSaving" = "Error saving: %@";
"CannotCopyPassword" = "Cannot copy password";
"CannotAddPassword" = "Cannot add password";
"WrongPassphrase" = "Wrong passphrase";
"MakeSurePgpAndGitProperlySet." = "Please make sure PGP key and Git server are properly set.";
"RecoverySuggestion" = "Recovery suggestion: Wrong credential password/passphrase has been removed, please try again.";
"NSURLFileAllocatedSizeKeyShouldAlwaysReturnValue." = "Huh? NSURLFileAllocatedSizeKey should always return a value.";
// Settings
"PasswordGeneratorFlavor" = "Password Generator Flavor";
"RememberPgpKeyPassphrase" = "Remember PGP Key Passphrase";
"RememberGitCredentialPassphrase" = "Remember Git Credential Passphrase";
"ShowFolders" = "Show Folders";
"HideUnknownFields" = "Hide Unknown Fields";
"HideUnknownFieldsExplanation." = "Only \"key: value\" format in additional fields is supported. Unsupported fields will be given \"unknown\" keys. Turn on this switch to hide unsupported fields.";
"HideOtpFields" = "Hide OTP Fields";
"HideOtpFieldsExplanation." = "Turn on this switch to hide the fields related to one time passwords (i.e., %@).";
"Random" = "Random"; "Random" = "Random";
"RandomString" = "Random String";
"ApplesKeychainStyle" = "Apple's Keychain Style";
// Git
"FailedToFetchPasswords" = "Failed to fetch passwords";
"FailedToFetchPasswordEntities" = "Failed to fetch password entities: %@";
"FailedToInsertPasswordEntity" = "Failed to insert password entity: %@";
"FailedToDeletePasswordEntity" = "Failed to delete password entity: %@";
"FailedToSavePasswordEntity" = "Failed to save password entity: %@";
"FailureToSaveContext" = "Failure to save context: %@";
"RepositoryRemoteMasterNotFoundError." = "Cannot find remote branch origin/master.";
"KeyImportError." = "Cannot import the key.";
"PasswordDuplicatedError." = "Cannot add the password; password is duplicated.";
"GitResetError." = "Cannot identify the latest synced commit.";
"PGPPublicKeyNotExistError." = "PGP public key doesn't exist.";
"WrongPasswordFilename." = "Cannot write to the password file.";
"DecryptionError." = "Cannot decrypt password.";
"UnknownError." = "Unknown error.";
"PrepareRepository" = "Prepare Repository";
"CheckingOutBranch" = "Checking out branch '%@'";
"WantToSaveGitCredential?" = "Do you want to save the Git credential password/passphrase?";
// Repository
"RepositoryNotSetError." = "Git repository is not set.";
"SetGitRepositoryUrl" = "Please set the Git repository URL.";
"CannotFindUsername." = "Cannot find the username in the Git repository URL. Example URL: ssh://git@server/path/to/repo.git.";
"CheckEnteredUsername." = "Please check the entered username and the username in the Git repository URL. They should match.";
"UseHttps." = "Please use HTTPS instead of HTTP.";
"SpecifySchema." = "Please specify the scheme of the Git repository URL (HTTPS or SSH).";
"Overwrite?" = "Overwrite?";
"Overwrite" = "Overwrite";
"OperationWillOverwriteData." = "This operation will overwrite your current password store data (repository). Data on your remote server will not be affected.";
"DownloadFromUrl" = "Download from URL";
"AsciiArmorEncryptedKey" = "ASCII-Armor Encrypted Key";
"ITunesFileSharing" = "iTunes File Sharing";
"Import" = "Import";
"Imported" = "Imported";
"Tips" = "Tips";
"CopyPrivateKeyToPass." = "Copy your ASCII-armored private key to Pass with the name \"ssh_key\" (without quotes) via iTunes. Then come back and click \"iTunes File Sharing\" to finish.";
"FillInGitAccountPassword." = "Please fill in the password of your Git account.";
"NoPasswordStore." = "There is no password store right now.";
"SyncingPasswordStore" = "Syncing Password Store";
"PushingToRemoteRepository" = "Pushing to Remote Repository";
// SSH
"FillInSshKeyPassphrase." = "Please fill in the passphrase of your SSH key.";
"CannotSelectSshKey" = "Cannot Select SSH Key";
"PleaseSetupSshKeyFirst." = "Please setup SSH key first.";
"RemoveSShKeys" = "Remove Git SSH Keys";
"SetPrivateKeyUrl." = "Please set private key URL first.";
// QR code scanning
"LookingForStartingFrame." = "Looking for the starting frame.";
"TooManyQrCodes" = "Too many QR codes";
"ScanPrivateKeyQrCodes" = "Scan Private Key QR Codes";
"CannotSaveSshKey" = "Cannot Save SSH Key";
"ScanPublicKey." = "Please scan public key.";
"ScanPrivateKey." = "Please scan private key.";
"ScanPrivateKeyQrCodes" = "Scan Private Key QR codes";
"ScanPublicKeyQrCodes" = "Scan Public Key QR codes";
"SetPrivateKey." = "Please set private key first.";
"SetPublicKey." = "Please set public key first.";
"NoQrCodeDetected." = "No QR code detected.";
"NoStringValue" = "No string value";
"CameraAccessDenied." = "Camera access denied.";
"WarningToggleCameraPermissionsResetsApp." = "WARNING: Toggle the camera permission resets the app! Save your changes.";
// PGP
"Decrypting" = "Decrypting";
"PgpKeyNotSet." = "PGP Key is not set. Please set your PGP Key first.";
"FillInPgpPassphrase." = "Please fill in the passphrase of your PGP secret key.";
"SetPgpKey." = "PGP Key is not set. Please set your PGP Key first.";
"WantToSavePassphrase?" = "Do you want to save the passphrase for later decryption?";
"CannotSavePgpKey" = "Cannot Save PGP Key";
"SetPgpKeyUrlFirst." = "Please set PGP key URL first.";
"FetchingPgpKey" = "Fetching PGP Key";
"RememberToRemoveKey" = "Remember to Remove the Key";
"RememberToRemoveKeyFromServer." = "Remember to remove the key from the server.";
"RemovePgpKeys" = "Remove PGP Keys";
// App passcode
"RemovePasscode" = "Remove Passcode";
"ChangePasscode" = "Change Passcode";
"SetPasscode" = "Set Passcode";
"PasswordConfirmation" = "Password Confirmation";
"FillInAppPasscode." = "Fill in your passcode for Pass (at least 4 characters).";
// Git signature
"NotSet" = "Not Set";
"InvalidNameOrEmail" = "Invalid name or e-mail";
// Erase password store
"ErasePasswordStoreData?" = "Erase password store data?";
"ErasePasswordStoreData" = "Erase Password Store Data";
"EraseExplanation." = "This will delete all local data and settings. Password store data on your remote server will not be affected.";
"Erasing..." = "Erasing ...";
// Discard local changes
"DiscardAllLocalChanges?" = "Discard all local changes?";
"DiscardAllLocalChanges" = "Discard All Local Changes";
"DiscardExplanation." = "Do you want to permanently discard all changes to the local copy of your password data? You cannot undo this action.";
"Resetting..." = "Resetting ...";
// Time related
"Unknown" = "Unknown";
"JustNow" = "Just now";
"TimeAgo" = "%@ ago";
// Commit messages
"AddPassword." = "Add password for %@ to store using Pass for iOS.";
"RemovePassword." = "Remove %@ from store using Pass for iOS.";
"EditPassword." = "Edit password for %@ using Pass for iOS.";
"RenamePassword." = "Rename %@ to %@ using Pass for iOS.";
// Menu buttons
"Ok" = "OK";
"Cancel" = "Cancel";
"Dismiss" = "Dismiss";
"Done" = "Done";
"Yes" = "Yes";
"No" = "No";
"TryAgain" = "Try Again";
"Delete" = "Delete";
"Back" = "Back";
"Current" = "Current";
"All" = "All";
"On" = "On";
"Off" = "Off";
"Save" = "Save";
// Lock screen
"EnterPasscode" = "Enter passcode for Pass";
"Passcode" = "Passcode";
"TouchId" = "Touch ID";
"FaceId" = "Face ID";
"AuthenticationNeeded." = "Authentication is needed to access Pass.";
// About repository
"AboutRepository" = "About Repository";
"ValueNotAvailable" = "Value not available";
"Size" = "Size";
"LocalCommits" = "Local Commits";
"LastSynced" = "Last Synced";
"Commits" = "Commits";
"CommitLogs" = "Commit Logs";
"SyncAgain?" = "Oops! Sync again?";
// About app
"Website" = "Website";
"Help" = "Help";
"ContactDeveloper" = "Contact Developer";
"OpenSourceComponents" = "Open Source Components";
"SpecialThanks" = "Special Thanks";
"Acknowledgements" = "Acknowledgements";
"ValueNotAvailable" = "Value not available";
// External applications
"CannotOpenMail" = "Cannot open Mail app";
"CopiedEmail" = "Copied email %@";
"HttpNotSupported." = "HTTP connection is not supported.";
// Password view
"Notice" = "Notice";
"PreviousChangesDiscarded." = "All previous local changes have been discarded. Your current Password Store will be shown.";
"CannotShowPassword" = "Cannot show password";
"PasswordDoesNotExist" = "The password does not exist.";
"Saving" = "Saving";
"Success" = "Success";
"OneTimePassword" = "One-Time Password";
"ShowRaw" = "Show Raw";
"Reveal" = "Reveal";
"Conceal" = "Conceal";
"NextPassword" = "Next Password";
"CopyAndOpen" = "Copy Password & Open Link";
"GetNextPasswordOfNonHotp." = "Get next password of a non-HOTP entry.";
"PasswordCopied" = "Password Copied";
"CounterUpdated" = "Counter Updated";
"CannotFindValidUrl" = "Cannot find a valid URL";
"LastUpdated" = "Last Updated: %@";
"PasswordCopiedToPasteboard." = "Password copied. We will clear the pasteboard in 45 seconds.";
// Password editor
"UseKeyValueFormat." = "Use \"key: value\" format for additional fields.";
"DeletePassword" = "Delete Password";
"AddOneTimePassword" = "Add One-Time Password";
"GetMemorableOne" = "Get a Memorable One: xkpasswd";
"Length" = "Length";
"DeletePassword?" = "Delete Password?";
"OverwriteOtpConfiguration?" = "Overwrite the one-time password configuration?";
"ValidTokenUrl" = "Valid token URL";
"InvalidTokenUrl" = "Invalid token URL";
"FillInName." = "Please fill in the name.";
"RemovePrefix." = "Please remove the prefix \"/\" from your password name.";
"PasswordNameInvalid." = "Password name is invalid.";
"CannotParseFilename." = "Cannot parse the filename. Please check and simplify the password name.";
"WannaUseIt?" = "Wanna use it?";
"SeemsLikeYouHaveCopiedSomething." = "It seems like you have copied something.";
"FirstStringIs:" = "The first string is:";

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ScannedQrCodes(%d)</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@code@</string>
<key>codes</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>zero</key>
<string>No scanned QR code</string>
<key>one</key>
<string>One scanned QR code</string>
<key>other</key>
<string>%d scanned QR codes</string>
</dict>
</dict>
<key>DiscardedCommits(%d)</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@commits@</string>
<key>commits</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string>No local commits</string>
<key>one</key>
<string>Discarded one commit</string>
<key>other</key>
<string>Discarded %d commits</string>
</dict>
</dict>
<key>HiddenFields(%d)</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@fields@</string>
<key>fields</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string>No hidden field</string>
<key>one</key>
<string>One hidden field</string>
<key>other</key>
<string>%d hidden fields</string>
</dict>
</dict>
<key>WrongAttempts(%d)</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@attempts@</string>
<key>attempts</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string>No wrong attempt</string>
<key>one</key>
<string>One wrong attempt</string>
<key>other</key>
<string>%d wrong attempts</string>
</dict>
</dict>
</dict>
</plist>

View file

@ -146,7 +146,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
let entry = getPasswordEntry(by: indexPath) let entry = getPasswordEntry(by: indexPath)
guard self.passwordStore.privateKey != nil else { guard self.passwordStore.privateKey != nil else {
Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil) Utils.alert(title: "CannotCopyPassword".localize(), message: "PgpKeyNotSet.".localize(), controller: self, completion: nil)
return return
} }
@ -166,7 +166,7 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
DispatchQueue.main.async { DispatchQueue.main.async {
// remove the wrong passphrase so that users could enter it next time // remove the wrong passphrase so that users could enter it next time
self.passwordStore.pgpKeyPassphrase = nil self.passwordStore.pgpKeyPassphrase = nil
Utils.alert(title: "Cannot Copy Password", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
} }
} }
@ -187,8 +187,8 @@ class CredentialProviderViewController: ASCredentialProviderViewController, UITa
let sem = DispatchSemaphore(value: 0) let sem = DispatchSemaphore(value: 0)
var passphrase = "" var passphrase = ""
DispatchQueue.main.async { DispatchQueue.main.async {
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertController.Style.alert) let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: {_ in alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertAction.Style.default, handler: {_ in
passphrase = alert.textFields!.first!.text! passphrase = alert.textFields!.first!.text!
sem.signal() sem.signal()
})) }))

View file

@ -154,7 +154,7 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
let entry = getPasswordEntry(by: indexPath) let entry = getPasswordEntry(by: indexPath)
guard self.passwordStore.privateKey != nil else { guard self.passwordStore.privateKey != nil else {
Utils.alert(title: "Cannot Copy Password", message: "PGP Key is not set. Please set your PGP Key first.", controller: self, completion: nil) Utils.alert(title: "CannotCopyPassword".localize(), message: "PgpKeyNotSet.".localize(), controller: self, completion: nil)
return return
} }
@ -192,7 +192,7 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
DispatchQueue.main.async { DispatchQueue.main.async {
// remove the wrong passphrase so that users could enter it next time // remove the wrong passphrase so that users could enter it next time
self.passwordStore.pgpKeyPassphrase = nil self.passwordStore.pgpKeyPassphrase = nil
Utils.alert(title: "Cannot Copy Password", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: self, completion: nil)
} }
} }
} }
@ -214,8 +214,8 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
let sem = DispatchSemaphore(value: 0) let sem = DispatchSemaphore(value: 0)
var passphrase = "" var passphrase = ""
DispatchQueue.main.async { DispatchQueue.main.async {
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertActionStyle.default, handler: {_ in
passphrase = alert.textFields!.first!.text! passphrase = alert.textFields!.first!.text!
sem.signal() sem.signal()
})) }))

View file

@ -30,7 +30,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
super.loadView() super.loadView()
let passcodeLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40)) let passcodeLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
passcodeLabel.text = "Enter passcode for Pass" passcodeLabel.text = "EnterPasscode".localize()
passcodeLabel.font = UIFont.boldSystemFont(ofSize: 18) passcodeLabel.font = UIFont.boldSystemFont(ofSize: 18)
passcodeLabel.textColor = UIColor.black passcodeLabel.textColor = UIColor.black
passcodeLabel.textAlignment = .center passcodeLabel.textAlignment = .center
@ -48,7 +48,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
let passcodeTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 300, height: 40)) let passcodeTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
passcodeTextField.borderStyle = UITextBorderStyle.roundedRect passcodeTextField.borderStyle = UITextBorderStyle.roundedRect
passcodeTextField.placeholder = "passcode" passcodeTextField.placeholder = "Passcode".localize()
passcodeTextField.isSecureTextEntry = true passcodeTextField.isSecureTextEntry = true
passcodeTextField.clearButtonMode = UITextFieldViewMode.whileEditing passcodeTextField.clearButtonMode = UITextFieldViewMode.whileEditing
passcodeTextField.delegate = self passcodeTextField.delegate = self
@ -71,10 +71,10 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
var authError: NSError? var authError: NSError?
if #available(iOS 8.0, macOS 10.12.1, *) { if #available(iOS 8.0, macOS 10.12.1, *) {
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) { if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
var biometryType = "Touch ID" var biometryType = "TouchId".localize()
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
if myContext.biometryType == LABiometryType.faceID { if myContext.biometryType == LABiometryType.faceID {
biometryType = "Face ID" biometryType = "FaceId".localize()
} }
} }
biometryAuthButton.setTitle(biometryType, for: .normal) biometryAuthButton.setTitle(biometryType, for: .normal)
@ -83,7 +83,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
} }
let cancelButton = UIButton(type: .custom) let cancelButton = UIButton(type: .custom)
cancelButton.setTitle("Cancel", for: .normal) cancelButton.setTitle("Cancel".localize(), for: .normal)
cancelButton.setTitleColor(Globals.blue, for: .normal) cancelButton.setTitleColor(Globals.blue, for: .normal)
cancelButton.addTarget(self, action: #selector(passcodeLockDidCancel), for: .touchUpInside) cancelButton.addTarget(self, action: #selector(passcodeLockDidCancel), for: .touchUpInside)
cancelButton.isHidden = !self.isCancellable cancelButton.isHidden = !self.isCancellable
@ -167,7 +167,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
@objc func bioButtonPressedAction(_ uiButton: UIButton) { @objc func bioButtonPressedAction(_ uiButton: UIButton) {
let myContext = LAContext() let myContext = LAContext()
let myLocalizedReasonString = "Authentication is needed to access Pass." let myLocalizedReasonString = "AuthenticationNeeded.".localize()
var authError: NSError? var authError: NSError?
if #available(iOS 8.0, *) { if #available(iOS 8.0, *) {
@ -188,11 +188,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
if textField == passcodeTextField { if textField == passcodeTextField {
if !PasscodeLock.shared.check(passcode: textField.text ?? "") { if !PasscodeLock.shared.check(passcode: textField.text ?? "") {
passcodeFailedAttempts = passcodeFailedAttempts + 1 passcodeFailedAttempts = passcodeFailedAttempts + 1
if passcodeFailedAttempts == 1 { passcodeWrongAttemptsLabel?.text = "WrongAttempts(%d)".localize(passcodeFailedAttempts)
passcodeWrongAttemptsLabel?.text = "1 wrong attempt"
} else {
passcodeWrongAttemptsLabel?.text = "\(passcodeFailedAttempts) wrong attempts"
}
} }
} }
textField.resignFirstResponder() textField.resignFirstResponder()

View file

@ -23,27 +23,6 @@ public enum AppError: Error {
extension AppError: LocalizedError { extension AppError: LocalizedError {
public var errorDescription: String? { public var errorDescription: String? {
switch self { return String(describing: self).localize()
case .RepositoryNotSetError:
return "Git repository is not set."
case let .RepositoryRemoteBranchNotFoundError(remoteBranchName):
return "Cannot find remote branch 'origin/\(remoteBranchName)'."
case let .RepositoryBranchNotFound(branchName):
return "Branch with name '\(branchName)' not found in repository."
case .KeyImportError:
return "Cannot import the key."
case .PasswordDuplicatedError:
return "Cannot add the password: password duplicated."
case .GitResetError:
return "Cannot identify the latest synced commit."
case .PGPPublicKeyNotExistError:
return "PGP public key doesn't exist."
case .WrongPasswordFilename:
return "Cannot write to the password file."
case .DecryptionError:
return "Cannot decrypt password."
case .UnknownError:
return "Unknown error."
}
} }
} }

View file

@ -77,7 +77,7 @@ public extension FileManager {
fileSize = try fileSize ?? resourceValueForKey(URLResourceKey.fileAllocatedSizeKey) fileSize = try fileSize ?? resourceValueForKey(URLResourceKey.fileAllocatedSizeKey)
guard let size = fileSize else { guard let size = fileSize else {
preconditionFailure("huh? NSURLFileAllocatedSizeKey should always return a value") preconditionFailure("NSURLFileAllocatedSizeKeyShouldAlwaysReturnValue.".localize())
} }
// We're good, add up the value. // We're good, add up the value.

View file

@ -56,7 +56,7 @@ public class Utils {
public static func alert(title: String, message: String, controller: UIViewController, handler: ((UIAlertAction) -> Void)? = nil, completion: (() -> Void)? = nil) { public static func alert(title: String, message: String, controller: UIViewController, handler: ((UIAlertAction) -> Void)? = nil, completion: (() -> Void)? = nil) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: handler)) alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertActionStyle.default, handler: handler))
controller.present(alert, animated: true, completion: completion) controller.present(alert, animated: true, completion: completion)
} }
} }

View file

@ -175,9 +175,9 @@ public class Password {
if case let .timer(period) = otpToken!.generator.factor { if case let .timer(period) = otpToken!.generator.factor {
let timeSinceEpoch = Date().timeIntervalSince1970 let timeSinceEpoch = Date().timeIntervalSince1970
let validTime = Int(period - timeSinceEpoch.truncatingRemainder(dividingBy: period)) let validTime = Int(period - timeSinceEpoch.truncatingRemainder(dividingBy: period))
description += " (expires in \(validTime)s)" description += " " + "ExpiresIn".localize(validTime)
} }
return (description, otpToken!.currentPassword ?? "error") return (description, otpToken!.currentPassword ?? "Error".localize())
} }
// return the password strings // return the password strings

View file

@ -91,7 +91,7 @@ public class PasswordStore {
* The store could not be migrated to the current model version. * The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was. Check the error message to determine what the actual problem was.
*/ */
fatalError("Unresolved error \(error), \(error.userInfo)") fatalError("UnresolvedError".localize("\(error.localizedDescription), \(error.userInfo)"))
} }
}) })
return container.viewContext return container.viewContext
@ -162,7 +162,7 @@ public class PasswordStore {
} }
try fm.moveItem(atPath: Globals.repositoryPathLegacy, toPath: Globals.repositoryPath) try fm.moveItem(atPath: Globals.repositoryPathLegacy, toPath: Globals.repositoryPath)
} catch { } catch {
print("Migration error: \(error)") print("MigrationError".localize(error))
} }
updatePasswordEntityCoreData() updatePasswordEntityCoreData()
} }
@ -266,7 +266,7 @@ public class PasswordStore {
return false return false
} }
} catch { } catch {
fatalError("Failed to fetch password entities: \(error)") fatalError("FailedToFetchPasswordEntities".localize(error))
} }
return true return true
} }
@ -282,7 +282,7 @@ public class PasswordStore {
return false return false
} }
} catch { } catch {
fatalError("Failed to fetch password entities: \(error)") fatalError("FailedToFetchPasswordEntities".localize(error))
} }
return true return true
} }
@ -293,7 +293,7 @@ public class PasswordStore {
passwordEntityFetchRequest.predicate = NSPredicate(format: "path = %@ and isDir = %@", path, isDir as NSNumber) passwordEntityFetchRequest.predicate = NSPredicate(format: "path = %@ and isDir = %@", path, isDir as NSNumber)
return try context.fetch(passwordEntityFetchRequest).first as? PasswordEntity return try context.fetch(passwordEntityFetchRequest).first as? PasswordEntity
} catch { } catch {
fatalError("Failed to fetch password entities: \(error)") fatalError("FailedToFetchPasswordEntities".localize(error))
} }
} }
@ -417,7 +417,7 @@ public class PasswordStore {
do { do {
try context.save() try context.save()
} catch { } catch {
print("Error with save: \(error)") print("ErrorSaving".localize(error))
} }
} }
@ -445,7 +445,7 @@ public class PasswordStore {
let fetchedPasswordEntities = try context.fetch(passwordEntityFetch) as! [PasswordEntity] let fetchedPasswordEntities = try context.fetch(passwordEntityFetch) as! [PasswordEntity]
return fetchedPasswordEntities.sorted { $0.name!.caseInsensitiveCompare($1.name!) == .orderedAscending } return fetchedPasswordEntities.sorted { $0.name!.caseInsensitiveCompare($1.name!) == .orderedAscending }
} catch { } catch {
fatalError("Failed to fetch passwords: \(error)") fatalError("FailedToFetchPasswords".localize(error))
} }
} }
@ -458,7 +458,7 @@ public class PasswordStore {
let fetchedPasswordEntities = try context.fetch(passwordEntityFetch) as! [PasswordEntity] let fetchedPasswordEntities = try context.fetch(passwordEntityFetch) as! [PasswordEntity]
return fetchedPasswordEntities.sorted { $0.name!.caseInsensitiveCompare($1.name!) == .orderedAscending } return fetchedPasswordEntities.sorted { $0.name!.caseInsensitiveCompare($1.name!) == .orderedAscending }
} catch { } catch {
fatalError("Failed to fetch passwords: \(error)") fatalError("FailedToFetchPasswords".localize(error))
} }
} }
@ -470,7 +470,7 @@ public class PasswordStore {
let passwordEntities = try context.fetch(passwordEntityFetchRequest) as! [PasswordEntity] let passwordEntities = try context.fetch(passwordEntityFetchRequest) as! [PasswordEntity]
return passwordEntities return passwordEntities
} catch { } catch {
fatalError("Failed to fetch passwords: \(error)") fatalError("FailedToFetchPasswords".localize(error))
} }
} }
@ -484,32 +484,32 @@ public class PasswordStore {
try context.save() try context.save()
} }
} catch { } catch {
fatalError("Failed to save: \(error)") fatalError("ErrorSaving".localize(error))
} }
} }
public func getLatestUpdateInfo(filename: String) -> String { public func getLatestUpdateInfo(filename: String) -> String {
guard let storeRepository = storeRepository else { guard let storeRepository = storeRepository else {
return "Unknown" return "Unknown".localize()
} }
guard let blameHunks = try? storeRepository.blame(withFile: filename, options: nil).hunks, guard let blameHunks = try? storeRepository.blame(withFile: filename, options: nil).hunks,
let latestCommitTime = blameHunks.map({ let latestCommitTime = blameHunks.map({
$0.finalSignature?.time?.timeIntervalSince1970 ?? 0 $0.finalSignature?.time?.timeIntervalSince1970 ?? 0
}).max() else { }).max() else {
return "Unknown" return "Unknown".localize()
} }
let lastCommitDate = Date(timeIntervalSince1970: latestCommitTime) let lastCommitDate = Date(timeIntervalSince1970: latestCommitTime)
let currentDate = Date() let currentDate = Date()
var autoFormattedDifference: String var autoFormattedDifference: String
if currentDate.timeIntervalSince(lastCommitDate) <= 60 { if currentDate.timeIntervalSince(lastCommitDate) <= 60 {
autoFormattedDifference = "Just now" autoFormattedDifference = "JustNow".localize()
} else { } else {
let diffDate = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: lastCommitDate, to: currentDate) let diffDate = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: lastCommitDate, to: currentDate)
let dateComponentsFormatter = DateComponentsFormatter() let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.unitsStyle = .full dateComponentsFormatter.unitsStyle = .full
dateComponentsFormatter.maximumUnitCount = 2 dateComponentsFormatter.maximumUnitCount = 2
dateComponentsFormatter.includesApproximationPhrase = true dateComponentsFormatter.includesApproximationPhrase = true
autoFormattedDifference = dateComponentsFormatter.string(from: diffDate)!.appending(" ago") autoFormattedDifference = "TimeAgo".localize(dateComponentsFormatter.string(from: diffDate)!)
} }
return autoFormattedDifference return autoFormattedDifference
} }
@ -646,7 +646,7 @@ public class PasswordStore {
try self.context.save() try self.context.save()
ret = passwordEntity ret = passwordEntity
} catch { } catch {
fatalError("Failed to insert a PasswordEntity: \(error)") fatalError("FailedToInsertPasswordEntity".localize(error))
} }
} }
return ret return ret
@ -658,7 +658,7 @@ public class PasswordStore {
let saveURL = storeURL.appendingPathComponent(password.url.path) let saveURL = storeURL.appendingPathComponent(password.url.path)
try self.encrypt(password: password).write(to: saveURL) try self.encrypt(password: password).write(to: saveURL)
try gitAdd(path: password.url.path) try gitAdd(path: password.url.path)
let _ = try gitCommit(message: "Add password for \(password.url.deletingPathExtension().path) to store using Pass for iOS.") let _ = try gitCommit(message: "AddPassword.".localize(password.url.deletingPathExtension().path))
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
return newPasswordEntity return newPasswordEntity
} }
@ -668,7 +668,7 @@ public class PasswordStore {
try gitRm(path: deletedFileURL.path) try gitRm(path: deletedFileURL.path)
try deletePasswordEntities(passwordEntity: passwordEntity) try deletePasswordEntities(passwordEntity: passwordEntity)
try deleteDirectoryTree(at: deletedFileURL) try deleteDirectoryTree(at: deletedFileURL)
let _ = try gitCommit(message: "Remove \(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!) from store using Pass for iOS.") let _ = try gitCommit(message: "RemovePassword.".localize(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!))
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
} }
@ -679,7 +679,7 @@ public class PasswordStore {
let saveURL = storeURL.appendingPathComponent(passwordEntity.getURL()!.path) let saveURL = storeURL.appendingPathComponent(passwordEntity.getURL()!.path)
try self.encrypt(password: password).write(to: saveURL) try self.encrypt(password: password).write(to: saveURL)
try gitAdd(path: passwordEntity.getURL()!.path) try gitAdd(path: passwordEntity.getURL()!.path)
let _ = try gitCommit(message: "Edit password for \(passwordEntity.getURL()!.deletingPathExtension().path.removingPercentEncoding!) using Pass for iOS.") let _ = try gitCommit(message: "EditPassword.".localize(passwordEntity.getURL()!.deletingPathExtension().path.removingPercentEncoding!))
newPasswordEntity = passwordEntity newPasswordEntity = passwordEntity
newPasswordEntity?.synced = false newPasswordEntity?.synced = false
} }
@ -696,8 +696,7 @@ public class PasswordStore {
// delete // delete
try deleteDirectoryTree(at: deletedFileURL) try deleteDirectoryTree(at: deletedFileURL)
try deletePasswordEntities(passwordEntity: passwordEntity) try deletePasswordEntities(passwordEntity: passwordEntity)
let _ = try gitCommit(message: "Rename \(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!) to \(password.url.deletingPathExtension().path.removingPercentEncoding!) using Pass for iOS.") let _ = try gitCommit(message: "RenamePassword.".localize(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!, password.url.deletingPathExtension().path.removingPercentEncoding!))
} }
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
return newPasswordEntity return newPasswordEntity
@ -712,7 +711,7 @@ public class PasswordStore {
do { do {
try self.context.save() try self.context.save()
} catch { } catch {
fatalError("Failed to delete a PasswordEntity: \(error)") fatalError("FailedToDeletePasswordEntity".localize(error))
} }
} }
} }
@ -721,7 +720,7 @@ public class PasswordStore {
do { do {
try context.save() try context.save()
} catch { } catch {
fatalError("Failed to save a PasswordEntity: \(error)") fatalError("FailedToSavePasswordEntity".localize(error))
} }
} }
@ -752,11 +751,11 @@ public class PasswordStore {
do { do {
try self.context.save() try self.context.save()
} catch { } catch {
fatalError("Failure to save context: \(error)") fatalError("FailureToSaveContext".localize(error))
} }
} }
} catch { } catch {
fatalError("Failure to save context: \(error)") fatalError("FailureToSaveContext".localize(error))
} }
} }
} }

View file

@ -9,12 +9,12 @@
import OneTimePassword import OneTimePassword
public enum OtpType: String { public enum OtpType: String {
case totp = "time-based" case totp = "TimeBased"
case hotp = "HMAC-based" case hotp = "HmacBased"
case none case none = "None"
var description: String { var description: String {
return rawValue return rawValue.localize()
} }
init(token: Token?) { init(token: Token?) {

View file

@ -304,9 +304,11 @@ class PasswordTest: XCTestCase {
func testOtpStringsTotpToken() { func testOtpStringsTotpToken() {
let password = getPasswordObjectWith(content: TOTP_URL) let password = getPasswordObjectWith(content: TOTP_URL)
let otpStrings = password.getOtpStrings() let otpStrings = password.getOtpStrings()
let otpDescription = otpStrings!.description
XCTAssertNotNil(otpStrings) XCTAssertNotNil(otpStrings)
XCTAssert(otpStrings!.description.hasPrefix("time-based (expires in")) XCTAssert(otpDescription.hasPrefix("TimeBased".localize() + " ("))
XCTAssert(otpDescription.hasSuffix(")"))
} }
func testOtpStringsHotpToken() { func testOtpStringsHotpToken() {
@ -314,6 +316,6 @@ class PasswordTest: XCTestCase {
let otpStrings = password.getOtpStrings() let otpStrings = password.getOtpStrings()
XCTAssertNotNil(otpStrings) XCTAssertNotNil(otpStrings)
XCTAssertEqual(otpStrings!.description, "HMAC-based") XCTAssertEqual(otpStrings!.description, "HmacBased".localize())
} }
} }

View file

@ -38,8 +38,8 @@ class OtpTypeTest: XCTestCase {
} }
func testDescription() { func testDescription() {
XCTAssertEqual(OtpType(name: "totp").description, "time-based") XCTAssertEqual(OtpType(name: "totp").description, "TimeBased".localize())
XCTAssertEqual(OtpType(name: "hotp").description, "HMAC-based") XCTAssertEqual(OtpType(name: "hotp").description, "HmacBased".localize())
XCTAssertEqual(OtpType(name: nil).description, "none") XCTAssertEqual(OtpType(name: nil).description, "None".localize())
} }
} }