diff --git a/FlowCrypt.xcodeproj/xcshareddata/xcschemes/Debug FlowCrypt.xcscheme b/FlowCrypt.xcodeproj/xcshareddata/xcschemes/Debug FlowCrypt.xcscheme index 06a61ff0b..1e4717285 100644 --- a/FlowCrypt.xcodeproj/xcshareddata/xcschemes/Debug FlowCrypt.xcscheme +++ b/FlowCrypt.xcodeproj/xcshareddata/xcschemes/Debug FlowCrypt.xcscheme @@ -105,7 +105,8 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - allowLocationSimulation = "NO"> + allowLocationSimulation = "YES" + showNonLocalizedStrings = "YES"> diff --git a/FlowCrypt/Controllers/Bootstrap/InvalidStorageViewController.swift b/FlowCrypt/Controllers/Bootstrap/InvalidStorageViewController.swift index fbb031f40..17bfa98b0 100644 --- a/FlowCrypt/Controllers/Bootstrap/InvalidStorageViewController.swift +++ b/FlowCrypt/Controllers/Bootstrap/InvalidStorageViewController.swift @@ -86,7 +86,8 @@ extension InvalidStorageViewController: ASTableDelegate, ASTableDataSource { case .title: return SetupTitleNode( SetupTitleNode.Input( - title: "invalid_storage_text".localized + title: "invalid_storage_text" + .localized .attributed( .regular(16), color: .mainTextColor, @@ -111,11 +112,13 @@ extension InvalidStorageViewController: ASTableDelegate, ASTableDataSource { case .button: return ButtonCellNode( input: ButtonCellNode.Input( - title: "invalid_storage_reset_button".localized.attributed( - .bold(16), - color: .white, - alignment: .center - ), + title: "invalid_storage_reset_button" + .localized + .attributed( + .bold(16), + color: .white, + alignment: .center + ), color: .red ) ) { [weak self] in diff --git a/FlowCrypt/Controllers/CheckMailAuth/CheckMailAuthViewController.swift b/FlowCrypt/Controllers/CheckMailAuth/CheckMailAuthViewController.swift index 1d72c3610..16dfa4a68 100644 --- a/FlowCrypt/Controllers/CheckMailAuth/CheckMailAuthViewController.swift +++ b/FlowCrypt/Controllers/CheckMailAuth/CheckMailAuthViewController.swift @@ -50,7 +50,7 @@ extension CheckMailAuthViewController { node.delegate = self node.dataSource = self - title = "FlowCrypt" + title = "app_name".localized } private func unauthStateNode(for indexPath: IndexPath) -> ASCellNode { switch indexPath.row { diff --git a/FlowCrypt/Controllers/Compose/ComposeViewController.swift b/FlowCrypt/Controllers/Compose/ComposeViewController.swift index 3f164cfef..e1ab108c6 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewController.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewController.swift @@ -795,7 +795,9 @@ extension ComposeViewController { private func enableGoogleContactsNode() -> ASCellNode { TextWithIconNode(input: .init( - title: "compose_enable_google_contacts_search".localized.attributed(.regular(16)), + title: "compose_enable_google_contacts_search" + .localized + .attributed(.regular(16)), image: UIImage(named: "gmail_icn")) ) } @@ -1211,7 +1213,8 @@ extension ComposeViewController: PHPickerViewControllerDelegate { let url = url, let composeMessageAttachment = MessageAttachment(fileURL: url) else { - let message = isVideo ? "files_picking_videos_error_message".localized + let message = isVideo + ? "files_picking_videos_error_message".localized : "files_picking_photos_error_message".localized let errorMessage = error.flatMap({ "." + $0.localizedDescription }) ?? "" showAlert(message: message + errorMessage) @@ -1318,7 +1321,7 @@ extension ComposeViewController { preferredStyle: .alert ) let okAction = UIAlertAction( - title: "OK", + title: "ok".localized, style: .cancel ) { _ in } let settingsAction = UIAlertAction( @@ -1340,7 +1343,7 @@ extension ComposeViewController { preferredStyle: .alert ) let okAction = UIAlertAction( - title: "OK", + title: "ok".localized, style: .cancel ) { _ in } let settingsAction = UIAlertAction( diff --git a/FlowCrypt/Controllers/Compose/ComposeViewDecorator.swift b/FlowCrypt/Controllers/Compose/ComposeViewDecorator.swift index ec41cd794..aeb261c4c 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewDecorator.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewDecorator.swift @@ -23,11 +23,13 @@ struct ComposeViewDecorator { func styledTextViewInput(with height: CGFloat, accessibilityIdentifier: String? = nil) -> TextViewCellNode.Input { TextViewCellNode.Input( - placeholder: "message_compose_secure".localized.attributed( - .regular(17), - color: .lightGray, - alignment: .left - ), + placeholder: "message_compose_secure" + .localized + .attributed( + .regular(17), + color: .lightGray, + alignment: .left + ), preferredHeight: height, textColor: .mainTextColor, accessibilityIdentifier: accessibilityIdentifier @@ -40,7 +42,7 @@ struct ComposeViewDecorator { accessibilityIdentifier: String? = nil ) -> TextFieldCellNode.Input { TextFieldCellNode.Input( - placeholder: text.localized.attributed( + placeholder: text.attributed( .regular(17), color: .lightGray, alignment: .left diff --git a/FlowCrypt/Controllers/Inbox/InboxViewDecorator.swift b/FlowCrypt/Controllers/Inbox/InboxViewDecorator.swift index 96475e774..007ecd272 100644 --- a/FlowCrypt/Controllers/Inbox/InboxViewDecorator.swift +++ b/FlowCrypt/Controllers/Inbox/InboxViewDecorator.swift @@ -45,7 +45,7 @@ struct InboxViewDecorator { func emptyStateNodeInput(for size: CGSize, title: String) -> TextCellNode.Input { TextCellNode.Input( backgroundColor: .backgroundColor, - title: "\(title) is empty", + title: title + " " + "empty".localized, withSpinner: false, size: size, insets: UIEdgeInsets( diff --git a/FlowCrypt/Controllers/Search/SearchViewController.swift b/FlowCrypt/Controllers/Search/SearchViewController.swift index 9b78efb10..56068af34 100644 --- a/FlowCrypt/Controllers/Search/SearchViewController.swift +++ b/FlowCrypt/Controllers/Search/SearchViewController.swift @@ -116,7 +116,6 @@ extension SearchViewController { // MARK: - MessageHandlerViewConroller extension SearchViewController: MsgListViewController { - // TODO: - ANTON - check func getUpdatedIndex(for message: InboxRenderable) -> Int? { guard let message = message.wrappedMessage else { return nil @@ -243,8 +242,7 @@ extension SearchViewController: UISearchControllerDelegate, UISearchBarDelegate } private func update(searchController: UISearchController) { - searchController.searchBar.searchTextField - .attributedPlaceholder = "search_placeholder" + searchController.searchBar.searchTextField.attributedPlaceholder = "search_placeholder" .localized .attributed( .regular(14), diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewDecorator.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewDecorator.swift index bebba6668..f5aa923a3 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewDecorator.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewDecorator.swift @@ -25,7 +25,7 @@ struct BackupViewDecorator { switch state { case .idle: - title = "Fetching backups..." + title = "backup_screen_fetching_backups".localized subtitle = "" case let .backups(keys): title = "backup_screen_found".localized diff --git a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListDecorator.swift b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListDecorator.swift index f996dedd8..2bb1b1c47 100644 --- a/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListDecorator.swift +++ b/FlowCrypt/Controllers/Settings/Contacts/Contacts List/ContactsListDecorator.swift @@ -33,9 +33,7 @@ struct ContactsListDecorator { darkStyle: .lightGray, lightStyle: .darkGray ) - - let keysCount = "%@ public key(s)".localizeWithArguments(recipient.pubKeys.count) - + let keysCount = "%@ public key(s)".localizePluralsWithArguments(recipient.pubKeys.count) return ContactCellNode.Input( name: name.attributed(.medium(16)), email: recipient.email.attributed(.medium(14)), diff --git a/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift b/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift index 919a6007e..349bc3d31 100644 --- a/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift @@ -165,9 +165,12 @@ extension SetupBackupsViewController { try await self.recoverAccount(with: self.fetchedEncryptedKeys, and: passPhrase) } catch { hideSpinner() - showAlert(error: error, message: "Failed to set up account", onOk: { - // todo - what to do? maybe nothing, since they should now see the same button again that they can press again - }) + showAlert( + error: error, + message: "error_setup_failed".localized, + onOk: { + // todo - what to do? maybe nothing, since they should now see the same button again that they can press again + }) } } } diff --git a/FlowCrypt/Controllers/Setup/SetupCreatePassphraseAbstractViewController.swift b/FlowCrypt/Controllers/Setup/SetupCreatePassphraseAbstractViewController.swift index ecf3000b2..d5d821178 100644 --- a/FlowCrypt/Controllers/Setup/SetupCreatePassphraseAbstractViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupCreatePassphraseAbstractViewController.swift @@ -149,15 +149,14 @@ extension SetupCreatePassphraseAbstractViewController { return await withCheckedContinuation { (continuation: CheckedContinuation) in DispatchQueue.main.async { let alert = UIAlertController( - title: "Pass Phrase", - message: "Confirm Pass Phrase", + title: "setup_pass_phrase_title".localized, + message: "setup_pass_phrase_confirm".localized, preferredStyle: .alert ) alert.addTextField { textField in textField.isSecureTextEntry = true textField.accessibilityLabel = "textField" } - alert.addAction(UIAlertAction(title: "cancel".localized, style: .default) { _ in return continuation.resume(returning: nil) }) diff --git a/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift index 6afb06fc5..7f45c837b 100644 --- a/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift @@ -60,7 +60,7 @@ final class SetupEKMKeyViewController: SetupCreatePassphraseAbstractViewControll let isErrorHandled = self.handleCommon(error: error) if !isErrorHandled { - showAlert(error: error, message: "Could not finish setup, please try again") + showAlert(error: error, message: "error_setup_try_again".localized) } } } diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index 2fc4394b3..6b197954e 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -63,7 +63,7 @@ final class SetupGenerateKeyViewController: SetupCreatePassphraseAbstractViewCon let isErrorHandled = handleCommon(error: error) if !isErrorHandled { - showAlert(error: error, message: "Could not finish setup, please try again") + showAlert(error: error, message: "error_setup_try_again".localized) } } } diff --git a/FlowCrypt/Controllers/Setup/SetupInitialViewController.swift b/FlowCrypt/Controllers/Setup/SetupInitialViewController.swift index 31dff95bd..5394653f3 100644 --- a/FlowCrypt/Controllers/Setup/SetupInitialViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupInitialViewController.swift @@ -185,9 +185,12 @@ extension SetupInitialViewController { } ) case .keysAreNotDecrypted: - showAlert(message: "organisational_rules_ekm_keys_are_not_decrypted_error".localized, onOk: { [weak self] in - self?.signOut() - }) + showAlert( + message: "organisational_rules_ekm_keys_are_not_decrypted_error".localized, + onOk: { [weak self] in + self?.signOut() + } + ) } } catch { if case .noPrivateKeysUrlString = error as? EmailKeyManagerApiError { diff --git a/FlowCrypt/Controllers/Setup/SetupManuallyImportKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupManuallyImportKeyViewController.swift index 279946c88..0b93d6f4d 100644 --- a/FlowCrypt/Controllers/Setup/SetupManuallyImportKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupManuallyImportKeyViewController.swift @@ -171,7 +171,7 @@ extension SetupManuallyImportKeyViewController { if privateKey.isEmpty { userInfoMessage = "import_no_backups_clipboard".localized + user } else { - userInfoMessage = "Found \(privateKey.count) key\(privateKey.count > 1 ? "s" : "")" + userInfoMessage = "Found %@ key(s)".localizePluralsWithArguments(privateKey.count) proceedToPassPhrase(with: user, keys: privateKey) } } diff --git a/FlowCrypt/Controllers/Setup/SetupViewDecorator.swift b/FlowCrypt/Controllers/Setup/SetupViewDecorator.swift index c063dede4..a15dd480d 100644 --- a/FlowCrypt/Controllers/Setup/SetupViewDecorator.swift +++ b/FlowCrypt/Controllers/Setup/SetupViewDecorator.swift @@ -74,9 +74,9 @@ struct SetupViewDecorator { switch subtitleType { case let .fetchedKeys(count): - subtitle = "Found \(count) key backup\(count > 1 ? "s" : "")" + subtitle = "Found %@ key backup(s)".localizePluralsWithArguments(count) case let .fetchedEKMKeys(count): - subtitle = "Fetched %@ key(s) on EKM".localizeWithArguments(count) + subtitle = "Fetched %@ key(s) on EKM".localizePluralsWithArguments(count) case .common: subtitle = "setup_description".localized case .choosingPassPhrase: diff --git a/FlowCrypt/Controllers/SetupImap/SetupImapViewController.swift b/FlowCrypt/Controllers/SetupImap/SetupImapViewController.swift index 2896cec9e..2bb9435a2 100644 --- a/FlowCrypt/Controllers/SetupImap/SetupImapViewController.swift +++ b/FlowCrypt/Controllers/SetupImap/SetupImapViewController.swift @@ -153,7 +153,7 @@ extension SetupImapViewController { // MARK: - Setup extension SetupImapViewController { private func setupUI() { - title = "Email Provider" + title = "setup_providers".localized observeKeyboardNotifications() } @@ -519,7 +519,7 @@ extension SetupImapViewController { } private func handleConnection(error: Error) { - showAlert(error: error, message: "Connection Error") + showAlert(error: error, message: "error_connection".localized) } private func handleSuccessfulConnection() { diff --git a/FlowCrypt/Controllers/SetupImap/SetupImapViewDecorator.swift b/FlowCrypt/Controllers/SetupImap/SetupImapViewDecorator.swift index 69564e791..ec8dd7e60 100644 --- a/FlowCrypt/Controllers/SetupImap/SetupImapViewDecorator.swift +++ b/FlowCrypt/Controllers/SetupImap/SetupImapViewDecorator.swift @@ -72,15 +72,15 @@ struct SetupImapViewDecorator { case let .account(part): switch part { case .email: - placeholder = "Email" + placeholder = "setup_imap_email".localized keyboardType = .emailAddress accessibilityIdentifier = "Email" case .password: - placeholder = "Password" + placeholder = "setup_imap_password".localized isSecure = true accessibilityIdentifier = "Password" case .username: - placeholder = "Username" + placeholder = "setup_imap_username".localized case .title: placeholder = nil } @@ -89,14 +89,14 @@ struct SetupImapViewDecorator { case let .imap(part): switch part { case .port: - placeholder = "IMAP port" + placeholder = "setup_imap_port".localized keyboardType = .numberPad accessibilityIdentifier = "IMAP port" case .security: - placeholder = "Security type" + placeholder = "setup_imap_security_type".localized accessibilityIdentifier = "IMAP type" case .server: - placeholder = "IMAP server" + placeholder = "setup_imap_server".localized case .title: placeholder = nil } @@ -105,14 +105,14 @@ struct SetupImapViewDecorator { case let .smtp(part): switch part { case .port: - placeholder = "SMTP port" + placeholder = "setup_imap_smptp_port".localized keyboardType = .numberPad accessibilityIdentifier = "SMTP port" case .security: - placeholder = "Security type" + placeholder = "setup_imap_security_type".localized accessibilityIdentifier = "SMTP type" case .server: - placeholder = "SMTP server" + placeholder = "setup_imap_smtp_server".localized case .title: placeholder = nil } @@ -121,10 +121,10 @@ struct SetupImapViewDecorator { case let .other(part): switch part { case .name: - placeholder = "SMTP username" + placeholder = "setup_imap_smtp_username".localized case .password: isSecure = true - placeholder = "SMTP password" + placeholder = "setup_imap_smtp_password".localized case .title: placeholder = nil } diff --git a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewDecorator.swift b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewDecorator.swift index f8fa0785c..eb0b17fe6 100644 --- a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewDecorator.swift +++ b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewDecorator.swift @@ -51,13 +51,13 @@ extension FolderViewModel { static var menuItems: [FolderViewModel] { [ FolderViewModel( - name: "Settings", + name: "folder_settings".localized, path: "", image: UIImage(named: "settings")?.tinted(.mainTextColor), itemType: .settings ), FolderViewModel( - name: "Log out", + name: "log_out".localized, path: "", image: UIImage(named: "exit")?.tinted(.mainTextColor), itemType: .logOut diff --git a/FlowCrypt/Controllers/Threads/AlertsFactory.swift b/FlowCrypt/Controllers/Threads/AlertsFactory.swift index c360464ff..7b480f04c 100644 --- a/FlowCrypt/Controllers/Threads/AlertsFactory.swift +++ b/FlowCrypt/Controllers/Threads/AlertsFactory.swift @@ -27,8 +27,10 @@ enum AlertsFactory { alert.addTextField { tf in tf.isSecureTextEntry = true } - - let saveAction = UIAlertAction(title: "Ok", style: .default) { _ in + let saveAction = UIAlertAction( + title: "ok".localized, + style: .default + ) { _ in guard let textField = alert.textFields?.first, let passPhrase = textField.text, passPhrase.isNotEmpty @@ -41,8 +43,10 @@ enum AlertsFactory { onCompletion(passPhrase) } } - - let cancelAction = UIAlertAction(title: "Cancel", style: .destructive) { _ in + let cancelAction = UIAlertAction( + title: "cancel".localized, + style: .destructive + ) { _ in alert.dismiss(animated: true) { onCancel() } diff --git a/FlowCrypt/Controllers/Threads/MessageActionsHandler.swift b/FlowCrypt/Controllers/Threads/MessageActionsHandler.swift index 55687efa9..1b1d627c0 100644 --- a/FlowCrypt/Controllers/Threads/MessageActionsHandler.swift +++ b/FlowCrypt/Controllers/Threads/MessageActionsHandler.swift @@ -112,15 +112,15 @@ extension MessageActionsHandler where Self: UIViewController { func awaitUserConfirmation(_ completion: @escaping () -> Void) { let alert = UIAlertController( - title: "Are you sure?", - message: "You're about to permanently delete a message", + title: "message_permanently_delete_title".localized, + message: "message_permanently_delete".localized, preferredStyle: .alert ) alert.addAction( - UIAlertAction(title: "Cancel", style: .default) + UIAlertAction(title: "cancel".localized, style: .default) ) alert.addAction( - UIAlertAction(title: "OK", style: .default) { _ in + UIAlertAction(title: "ok".localized, style: .default) { _ in completion() } ) diff --git a/FlowCrypt/Core/Models/KeyDetails.swift b/FlowCrypt/Core/Models/KeyDetails.swift index 56723673e..cc7eb6c40 100644 --- a/FlowCrypt/Core/Models/KeyDetails.swift +++ b/FlowCrypt/Core/Models/KeyDetails.swift @@ -37,14 +37,6 @@ extension KeyDetails { return fingerPrint } - @available(*, deprecated, message: "Use primaryFingerprint instead") - var longid: String { - guard let longid = ids.first?.longid else { - fatalError("longid for KeyDetail is missing") - } - return longid - } - var pgpUserEmails: [String] { users.map { MCOAddress(nonEncodedRFC822String: $0).mailbox } } diff --git a/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift b/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift index 5131730f6..dd29705b0 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Provider/MessageService.swift @@ -165,15 +165,21 @@ final class MessageService { if let firstBlockParseErr = firstBlockParseErr { // Swift failed to parse one of the MsgBlock returned from TypeScript Core - text = "Internal error: could not parse MsgBlock. Please report this error to us.\n\n\(firstBlockParseErr.content)" + text = "error_internal_parse_block".localized + + "\n\n\(firstBlockParseErr.content)" + messageType = .error(.other) signature = nil } else if let decryptErrBlock = firstDecryptErrBlock { // message failed to decrypt or process let err = decryptErrBlock.decryptErr?.error let hideContent = err?.type == .badMdc || err?.type == .noMdc - let rawMsg = hideContent ? "(content hidden for security)" : decryptErrBlock.content - text = "Could not decrypt:\n\(err?.type.rawValue ?? "UNKNOWN"): \(err?.message ?? "??")\n\n\n\(rawMsg)" + let rawMsg = hideContent + ? "content_hidden".localized + : decryptErrBlock.content + + text = "error_decrypt".localized + + "\n\(err?.type.rawValue ?? "unknown".localized): \(err?.message ?? "??")\n\n\n\(rawMsg)" messageType = .error(err?.type ?? .other) signature = nil } else { diff --git a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageQuoteType.swift b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageQuoteType.swift index 09ea38df7..987044413 100644 --- a/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageQuoteType.swift +++ b/FlowCrypt/Functionality/Mail Provider/MessagesList Provider/Model/MessageQuoteType.swift @@ -16,9 +16,9 @@ extension MessageQuoteType { var subjectPrefix: String { switch self { case .reply, .replyAll: - return "Re: " + return "re".localized case .forward: - return "Fwd: " + return "fwd".localized } } diff --git a/FlowCrypt/Functionality/Services/AppStartup.swift b/FlowCrypt/Functionality/Services/AppStartup.swift index ad777e25a..027d61fb4 100644 --- a/FlowCrypt/Functionality/Services/AppStartup.swift +++ b/FlowCrypt/Functionality/Services/AppStartup.swift @@ -131,11 +131,14 @@ struct AppStartup { @MainActor private func showErrorAlert(of error: Error, on window: UIWindow) { let alert = UIAlertController( - title: "Startup Error", + title: "error_startup".localized, message: "\(error.localizedDescription)", preferredStyle: .alert ) - let retry = UIAlertAction(title: "Retry", style: .default) { _ in + let retry = UIAlertAction( + title: "retry_title".localized, + style: .default + ) { _ in self.initializeApp(window: window) } alert.addAction(retry) diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift index 8d31395ba..99b686ee8 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift @@ -60,7 +60,7 @@ extension BackupService: BackupServiceType { cc: [], bcc: [], from: userId.toMime, - subject: "Your FlowCrypt Backup", + subject: "backup_subject".localized, replyToMimeMsg: nil, atts: attachments, pubKeys: nil, diff --git a/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift b/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift index 2b2141ba7..a8c272d6f 100644 --- a/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift +++ b/FlowCrypt/Functionality/Services/Compose Message Service/ComposeMessageService.swift @@ -343,9 +343,9 @@ extension ComposeMessageService { return "
" } + // TODO: - Anton - compose_password_link private func createMessageBodyWithPasswordLink(sender: String, url: String) -> SendableMsgBody { - let text = "\(sender) has sent you a password-encrypted email.\n\nTo open message copy and paste the following link: \(url)" - + let text = "compose_password_link".localizeWithArguments(sender, url) let aStyle = "padding: 2px 6px; background: #2199e8; color: #fff; display: inline-block; text-decoration: none;" let html = """ \(sender) has sent you a password-encrypted email Click here to Open Message diff --git a/FlowCrypt/Resources/Localizable.stringsdict b/FlowCrypt/Resources/Localizable.stringsdict index a639e660d..2e5de6e67 100644 --- a/FlowCrypt/Resources/Localizable.stringsdict +++ b/FlowCrypt/Resources/Localizable.stringsdict @@ -2,41 +2,77 @@ + Found %@ key backup(s) + + NSStringLocalizedFormatKey + %#@backup@ + backup + + NSStringFormatValueTypeKey + d + NSStringFormatSpecTypeKey + NSStringPluralRuleType + one + Found %d key backup + zero + No backups found + other + Found %d key backups + + + Found %@ key(s) + + NSStringLocalizedFormatKey + %#@keys@ + keys + + NSStringFormatValueTypeKey + d + NSStringFormatSpecTypeKey + NSStringPluralRuleType + one + Found %d key + zero + No backups found for account: + other + Found %d keys + + Fetched %@ key(s) on EKM - - NSStringLocalizedFormatKey - %#@keys@ - keys - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - d - zero - No EKM keys fetched - one - Fetched %d key on EKM - other - Fetched %d keys on EKM - - - %@ public key(s) - - NSStringLocalizedFormatKey - %#@keys@ - keys - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - d - zero - No public keys - one - %d public key - other - %d public keys - - + + NSStringLocalizedFormatKey + %#@keys@ + keys + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + zero + No EKM keys fetched + one + Fetched %d key on EKM + other + Fetched %d keys on EKM + + + %@ public key(s) + + NSStringLocalizedFormatKey + %#@keys@ + keys + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + zero + No public keys + one + %d public key + other + %d public keys + + diff --git a/FlowCrypt/Resources/en.lproj/Localizable.strings b/FlowCrypt/Resources/en.lproj/Localizable.strings index 20cc5ffa8..20cd613d8 100644 --- a/FlowCrypt/Resources/en.lproj/Localizable.strings +++ b/FlowCrypt/Resources/en.lproj/Localizable.strings @@ -1,4 +1,5 @@ // COMMON +"app_name" = "FlowCrypt"; "loading_title" = "Loading"; "downloading_title" = "Downloading"; "encrypting_title" = "Encrypting..."; @@ -22,6 +23,14 @@ "to" = "to"; "cc" = "cc"; "bcc" = "bcc"; +"log_out" = "Log out"; +"empty" = "is empty"; +"re" = "Re: "; +"fwd" = "Fwd: "; +"unknown" = "UNKNOWN"; +"placeholder" = "PLACEHOLDER"; +"backup_subject" = "Your FlowCrypt Backup"; +"content_hidden" = "(content hidden for security)"; // EMAIL "email_removed" = "Email moved to Trash"; @@ -63,6 +72,14 @@ "error_app_connection" = "Cannot load inbox: connection error (offline)"; "error_general_text" = "Something went wrong."; "error_no_folders" = "There are no folders on your account"; +"error_connection" = "Connection Error"; +"error_setup_try_again" = "Could not finish setup, please try again"; +"error_setup_failed" = "Failed to set up account"; +"error_internal_parse_block" = "Internal error: could not parse MsgBlock. Please report this error to us."; +"error_decrypt" = "Could not decrypt:"; +"error_startup" = "Startup Error"; + + // KeyServiceError "keyServiceError_retrieve_error" = "Could not retrieve keys from DataService. Please restart the app and try again."; @@ -105,10 +122,13 @@ "compose_uploading" = "Uploading"; "compose_sent" = "Sent"; "compose_missing_contacts_scopes" = "To enable contacts search, please allow this device to access %@ of your Google account"; +"compose_password_link" = "%@ has sent you a password-encrypted email.\n\nTo open message copy and paste the following link: %@"; +// Folders "folder_all_mail" = "All Mail"; "folder_all_inbox" = "Inbox"; "folder_add_account" = "Add account"; +"folder_settings" = "Settings"; // Sign in "sign_in_privacy" = "Privacy"; @@ -136,6 +156,22 @@ "setup_save_pass_locally" = "Save pass phrase"; "setup_save_pass_temporarily" = "Remember pass phrase temporarily"; "setup_enter_pass_phrase" = "Please enter pass phrase"; +"setup_providers" = "Email Provider"; +"setup_pass_phrase_title" = "Pass Phrase"; +"setup_pass_phrase_confirm" = "Confirm Pass Phrase"; + +// Setup IMAP +"setup_imap_email" = "Email"; +"setup_imap_password" = "Password"; +"setup_imap_username" = "Username"; +"setup_imap_port" = "IMAP port"; +"setup_imap_security_type" = "Security type"; +"setup_imap_server" = "IMAP server"; +"setup_imap_smptp_port" = "SMTP port"; +"setup_imap_security_type" = "Security type"; +"setup_imap_smtp_server" = "SMTP server"; +"setup_imap_smtp_username" = "SMTP username"; +"setup_imap_smtp_password" = "SMTP password"; // Key Import "import_key_title" = "Import Key"; @@ -233,6 +269,7 @@ "backup_screen_not_found" = "There are no bcakups on this account"; "backup_screen_not_found_description" = "If you lose your device, or it stops working, your encrypted email will be unreadable."; +"backup_screen_fetching_backups" = "Fetching backups..."; // Backup option screen "backup_option_screen_title" = "Backup options"; @@ -304,3 +341,11 @@ "imap_error_msg_info" = "Can't parse message without \"%@\""; "submit_key_error" = "Failed to submit Public Key"; + +"message_permanently_delete" = "You're about to permanently delete a message"; +"message_permanently_delete_title" = "Are you sure?"; + +"contacts_fingerprint" = "Fingerprint:"; +"contacts_created" = "Created:"; +"contacts_expires" = "Expires:"; +"contacts_user" = "User:"; diff --git a/FlowCryptAppTests/Core/FlowCryptCoreTests.swift b/FlowCryptAppTests/Core/FlowCryptCoreTests.swift index 30d45be69..b56da2385 100644 --- a/FlowCryptAppTests/Core/FlowCryptCoreTests.swift +++ b/FlowCryptAppTests/Core/FlowCryptCoreTests.swift @@ -69,7 +69,6 @@ final class FlowCryptCoreTests: XCTestCase { XCTAssertNil(k0.private) XCTAssertNil(k0.isFullyDecrypted) XCTAssertNil(k0.isFullyEncrypted) - XCTAssertEqual(k0.longid, TestData.k0.longid) XCTAssertEqual(k0.lastModified, 1543925225) XCTAssertNil(k0.expiration) // k1 is private @@ -77,7 +76,6 @@ final class FlowCryptCoreTests: XCTestCase { XCTAssertNotNil(k1.private) XCTAssertEqual(k1.isFullyDecrypted, false) XCTAssertEqual(k1.isFullyEncrypted, true) - XCTAssertEqual(k1.longid, TestData.k1.longid) XCTAssertEqual(k1.lastModified, 1563630809) XCTAssertNil(k1.expiration) // todo - could test user ids diff --git a/FlowCryptAppTests/Functionality/Services/Account Server Services/EnterpriseServerApiTests.swift b/FlowCryptAppTests/Functionality/Services/Account Server Services/EnterpriseServerApiTests.swift index d8f404488..053d2fd42 100644 --- a/FlowCryptAppTests/Functionality/Services/Account Server Services/EnterpriseServerApiTests.swift +++ b/FlowCryptAppTests/Functionality/Services/Account Server Services/EnterpriseServerApiTests.swift @@ -16,7 +16,7 @@ final class EnterpriseServerApiTests: XCTestCase { let service = EnterpriseServerApi() // act - let result = try await service.getActiveFesUrl(for: "user@nonexistentdomain.test") + let result = try? await service.getActiveFesUrl(for: "user@nonexistentdomain.test") // assert XCTAssertNil(result) diff --git a/FlowCryptCommon/Extensions/LocalizationExtensions.swift b/FlowCryptCommon/Extensions/LocalizationExtensions.swift index 2879cc7b0..7f2ad8c64 100644 --- a/FlowCryptCommon/Extensions/LocalizationExtensions.swift +++ b/FlowCryptCommon/Extensions/LocalizationExtensions.swift @@ -21,8 +21,12 @@ public extension String { return LocalizedString(self) } - @inline(__always) func localizeWithArguments(_ arguments: CVarArg...) -> String { - let format = localize(self) - return String(format: format, arguments: arguments) + @inline(__always) func localizeWithArguments(_ arguments: String...) -> String { + String(format: localize(self), arguments: arguments) + } + + /// use to localize plurals with Localizable.stringsdict + @inline(__always) func localizePluralsWithArguments(_ arguments: Int...) -> String { + String(format: localize(self), arguments: arguments) } } diff --git a/FlowCryptCommon/Extensions/UIViewControllerExtensions.swift b/FlowCryptCommon/Extensions/UIViewControllerExtensions.swift index c93cba0ef..bf8945bb1 100644 --- a/FlowCryptCommon/Extensions/UIViewControllerExtensions.swift +++ b/FlowCryptCommon/Extensions/UIViewControllerExtensions.swift @@ -58,7 +58,11 @@ public extension UIViewController { message: message, preferredStyle: .alert ) - alert.addAction(UIAlertAction(title: "OK", style: .destructive) { _ in onOk?() }) + let ok = UIAlertAction( + title: "ok".localized, + style: .destructive + ) { _ in onOk?() } + alert.addAction(ok) self.present(alert, animated: true, completion: nil) } @@ -76,8 +80,16 @@ public extension UIViewController { message: message, preferredStyle: .alert ) - alert.addAction(UIAlertAction(title: "Retry", style: .cancel) { _ in onRetry?() }) - alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in onOk?() }) + let retry = UIAlertAction( + title: "retry_title".localized, + style: .cancel + ) { _ in onRetry?() } + let ok = UIAlertAction( + title: "ok".localized, + style: .default + ) { _ in onOk?() } + alert.addAction(retry) + alert.addAction(ok) present(alert, animated: true, completion: nil) } diff --git a/FlowCryptUI/Cell Nodes/ContactKeyCellNode.swift b/FlowCryptUI/Cell Nodes/ContactKeyCellNode.swift index 5e779536b..50d21583a 100644 --- a/FlowCryptUI/Cell Nodes/ContactKeyCellNode.swift +++ b/FlowCryptUI/Cell Nodes/ContactKeyCellNode.swift @@ -44,15 +44,18 @@ public final class ContactKeyCellNode: CellNode { super.init() - fingerprintTitleNode.attributedText = "Fingerprint:".attributed(.bold(16)) + fingerprintTitleNode.attributedText = "contacts_fingerprint".localized + .attributed(.bold(16)) fingerprintNode.attributedText = input.fingerprint fingerprintNode.accessibilityIdentifier = "fingerprintValue" - createdAtTitleNode.attributedText = "Created:".attributed(.bold(16)) + createdAtTitleNode.attributedText = "contacts_created".localized + .attributed(.bold(16)) createdAtNode.attributedText = input.createdAt createdAtNode.accessibilityIdentifier = "createdAtValue" - expiresTitleNode.attributedText = "Expires:".attributed(.bold(16)) + expiresTitleNode.attributedText = "contacts_expires".localized + .attributed(.bold(16)) expiresNode.attributedText = input.expires expiresNode.accessibilityIdentifier = "expiresValue" diff --git a/FlowCryptUI/Cell Nodes/ContactUserCellNode.swift b/FlowCryptUI/Cell Nodes/ContactUserCellNode.swift index a9cd99f46..a6f0203c5 100644 --- a/FlowCryptUI/Cell Nodes/ContactUserCellNode.swift +++ b/FlowCryptUI/Cell Nodes/ContactUserCellNode.swift @@ -24,7 +24,8 @@ public final class ContactUserCellNode: CellNode { public init(input: Input) { self.input = input - userTitleNode.attributedText = "User:".attributed(.bold(16)) + userTitleNode.attributedText = "contacts_user".localized + .attributed(.bold(16)) userNode.attributedText = input.user userNode.accessibilityIdentifier = "userEmail" } diff --git a/FlowCryptUI/Cell Nodes/TextFieldCellNode.swift b/FlowCryptUI/Cell Nodes/TextFieldCellNode.swift index b51daeaa2..f75ff6826 100644 --- a/FlowCryptUI/Cell Nodes/TextFieldCellNode.swift +++ b/FlowCryptUI/Cell Nodes/TextFieldCellNode.swift @@ -23,7 +23,7 @@ public final class TextFieldCellNode: CellNode { public let accessibilityIdentifier: String? public init( - placeholder: NSAttributedString = NSAttributedString(string: "PLACEHOLDER"), + placeholder: NSAttributedString = .init(string: "placeholder".localized), isSecureTextEntry: Bool = false, textInsets: CGFloat = .zero, textAlignment: NSTextAlignment = .left, diff --git a/appium/tests/screenobjects/base.screen.ts b/appium/tests/screenobjects/base.screen.ts index 8c31ed299..ac887d101 100644 --- a/appium/tests/screenobjects/base.screen.ts +++ b/appium/tests/screenobjects/base.screen.ts @@ -4,7 +4,7 @@ import ElementHelper from "../helpers/ElementHelper"; const SELECTORS = { ERROR_HEADER: '-ios class chain:**/XCUIElementTypeStaticText[`label == "Error"`]', - OK_BUTTON: '~OK', + OK_BUTTON: '~Ok', ERROR_FES_HEADER: '-ios class chain:**/XCUIElementTypeStaticText[`label == "Startup Error"`]', RETRY_BUTTON: '~Retry', CURRENT_MODAL: '-ios predicate string:type == "XCUIElementTypeAlert"' diff --git a/appium/tests/screenobjects/email.screen.ts b/appium/tests/screenobjects/email.screen.ts index 69cacdc8a..c96e664c9 100644 --- a/appium/tests/screenobjects/email.screen.ts +++ b/appium/tests/screenobjects/email.screen.ts @@ -20,7 +20,7 @@ const SELECTORS = { DELETE_BUTTON: '~Delete', DOWNLOAD_BUTTON: '~Download', CANCEL_BUTTON: '~Cancel', - CONFIRM_DELETING: '~OK', + CONFIRM_DELETING: '~Ok', SENDER_EMAIL: '~aid-message-sender-label', ENCRYPTION_BADGE: '~aid-encryption-badge', SIGNATURE_BADGE: '~aid-signature-badge',