Make string naming scheme (almost) match Android

This way we can drop in existing Android translations
This commit is contained in:
nielsandriesse 2020-07-27 11:32:47 +10:00
parent 11d4add8f9
commit cfade8b0b8
31 changed files with 396 additions and 484 deletions

View File

@ -212,7 +212,7 @@ final class ConversationCell : UITableViewCell {
} }
} else { } else {
if threadViewModel.threadRecord.isNoteToSelf() { if threadViewModel.threadRecord.isNoteToSelf() {
return NSLocalizedString("Note to Self", comment: "") return NSLocalizedString("NOTE_TO_SELF", comment: "")
} else { } else {
let hexEncodedPublicKey = threadViewModel.contactIdentifier! let hexEncodedPublicKey = threadViewModel.contactIdentifier!
return UserDisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey) ?? hexEncodedPublicKey return UserDisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey) ?? hexEncodedPublicKey

View File

@ -8,11 +8,11 @@ final class FakeChatView : UIView {
} }
private lazy var chatBubbles = [ private lazy var chatBubbles = [
getChatBubble(withText: NSLocalizedString("What's Session?", comment: ""), wasSentByCurrentUser: true), getChatBubble(withText: NSLocalizedString("view_fake_chat_bubble_1", comment: ""), wasSentByCurrentUser: true),
getChatBubble(withText: NSLocalizedString("It's a decentralized, encrypted messaging app.", comment: ""), wasSentByCurrentUser: false), getChatBubble(withText: NSLocalizedString("view_fake_chat_bubble_2", comment: ""), wasSentByCurrentUser: false),
getChatBubble(withText: NSLocalizedString("So it doesn't collect my personal information or my conversation metadata? How does it work?", comment: ""), wasSentByCurrentUser: true), getChatBubble(withText: NSLocalizedString("view_fake_chat_bubble_3", comment: ""), wasSentByCurrentUser: true),
getChatBubble(withText: NSLocalizedString("Using a combination of advanced anonymous routing and end-to-end encryption technologies.", comment: ""), wasSentByCurrentUser: false), getChatBubble(withText: NSLocalizedString("view_fake_chat_bubble_4", comment: ""), wasSentByCurrentUser: false),
getChatBubble(withText: NSLocalizedString("Friends don't let friends use compromised messengers. You're welcome.", comment: ""), wasSentByCurrentUser: false) getChatBubble(withText: NSLocalizedString("view_fake_chat_bubble_5", comment: ""), wasSentByCurrentUser: false)
] ]
private lazy var scrollView: UIScrollView = { private lazy var scrollView: UIScrollView = {

View File

@ -64,7 +64,7 @@ final class OptionView : UIView {
let recommendedLabel = UILabel() let recommendedLabel = UILabel()
recommendedLabel.textColor = Colors.accent recommendedLabel.textColor = Colors.accent
recommendedLabel.font = .boldSystemFont(ofSize: Values.verySmallFontSize) recommendedLabel.font = .boldSystemFont(ofSize: Values.verySmallFontSize)
recommendedLabel.text = NSLocalizedString("Recommended", comment: "") recommendedLabel.text = NSLocalizedString("vc_pn_mode_recommended_option_tag", comment: "")
stackView.addArrangedSubview(recommendedLabel) stackView.addArrangedSubview(recommendedLabel)
} }
// Set up tap gesture recognizer // Set up tap gesture recognizer

View File

@ -56,7 +56,7 @@ final class SeedReminderView : UIView {
labelStackView.spacing = 4 labelStackView.spacing = 4
// Set up button // Set up button
let button = Button(style: .prominentOutline, size: .small) let button = Button(style: .prominentOutline, size: .small)
button.setTitle(NSLocalizedString("Continue", comment: ""), for: UIControl.State.normal) button.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
button.set(.width, to: 80) button.set(.width, to: 80)
button.addTarget(self, action: #selector(handleContinueButtonTapped), for: UIControl.Event.touchUpInside) button.addTarget(self, action: #selector(handleContinueButtonTapped), for: UIControl.Event.touchUpInside)
// Set up content stack view // Set up content stack view

View File

@ -29,7 +29,7 @@ final class SessionRestorationView : UIView {
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize) titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize)
titleLabel.text = NSLocalizedString("Session Out of Sync", comment: "") titleLabel.text = "Session Out of Sync"
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
titleLabel.textAlignment = .center titleLabel.textAlignment = .center
@ -37,7 +37,7 @@ final class SessionRestorationView : UIView {
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("Would you like to restore your session? This can help resolve issues. Your messages will be preserved.", comment: "") explanationLabel.text = "Would you like to restore your session? This can help resolve issues. Your messages will be preserved."
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
@ -48,7 +48,7 @@ final class SessionRestorationView : UIView {
restoreButton.backgroundColor = Colors.accent restoreButton.backgroundColor = Colors.accent
restoreButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) restoreButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
restoreButton.setTitleColor(Colors.text, for: UIControl.State.normal) restoreButton.setTitleColor(Colors.text, for: UIControl.State.normal)
restoreButton.setTitle(NSLocalizedString("Restore", comment: ""), for: UIControl.State.normal) restoreButton.setTitle(NSLocalizedString("session_reset_banner_restore_button_title", comment: ""), for: UIControl.State.normal)
restoreButton.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside) restoreButton.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside)
// Set up dismiss button // Set up dismiss button
let dismissButton = UIButton() let dismissButton = UIButton()
@ -57,7 +57,7 @@ final class SessionRestorationView : UIView {
dismissButton.backgroundColor = Colors.buttonBackground dismissButton.backgroundColor = Colors.buttonBackground
dismissButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) dismissButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
dismissButton.setTitleColor(Colors.text, for: UIControl.State.normal) dismissButton.setTitleColor(Colors.text, for: UIControl.State.normal)
dismissButton.setTitle(NSLocalizedString("Dismiss", comment: ""), for: UIControl.State.normal) dismissButton.setTitle(NSLocalizedString("session_reset_banner_dismiss_button_title", comment: ""), for: UIControl.State.normal)
dismissButton.addTarget(self, action: #selector(dismiss), for: UIControl.Event.touchUpInside) dismissButton.addTarget(self, action: #selector(dismiss), for: UIControl.Event.touchUpInside)
// Set up button stack view // Set up button stack view
let buttonStackView = UIStackView(arrangedSubviews: [ dismissButton, restoreButton ]) let buttonStackView = UIStackView(arrangedSubviews: [ dismissButton, restoreButton ])
@ -73,7 +73,7 @@ final class SessionRestorationView : UIView {
// Update explanation label if possible // Update explanation label if possible
if let contactID = thread.contactIdentifier() { if let contactID = thread.contactIdentifier() {
let displayName = UserDisplayNameUtilities.getPrivateChatDisplayName(for: contactID) ?? contactID let displayName = UserDisplayNameUtilities.getPrivateChatDisplayName(for: contactID) ?? contactID
explanationLabel.text = String(format: NSLocalizedString("Would you like to restore your session with %@? This can help resolve issues. Your messages will be preserved.", comment: ""), displayName) explanationLabel.text = String(format: "Would you like to restore your session with %@? This can help resolve issues. Your messages will be preserved.", displayName)
} }
} }

View File

@ -72,7 +72,7 @@ class BaseVC : UIViewController {
guard DeviceLinkingUtilities.shouldShowUnexpectedDeviceLinkRequestReceivedAlert else { return } guard DeviceLinkingUtilities.shouldShowUnexpectedDeviceLinkRequestReceivedAlert else { return }
DispatchQueue.main.async { DispatchQueue.main.async {
let alert = UIAlertController(title: "Device Link Request Received", message: "Open the device link screen by going to \"Settings\" > \"Devices\" > \"Link a Device\" to link your devices.", preferredStyle: .alert) let alert = UIAlertController(title: "Device Link Request Received", message: "Open the device link screen by going to \"Settings\" > \"Devices\" > \"Link a Device\" to link your devices.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
} }
} }

View File

@ -76,7 +76,7 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
result.backgroundColor = Colors.accent result.backgroundColor = Colors.accent
result.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) result.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
result.setTitleColor(Colors.text, for: UIControl.State.normal) result.setTitleColor(Colors.text, for: UIControl.State.normal)
result.setTitle(NSLocalizedString("Authorize", comment: ""), for: UIControl.State.normal) result.setTitle(NSLocalizedString("modal_link_device_master_mode_authorize_button_title", comment: ""), for: UIControl.State.normal)
return result return result
}() }()
@ -130,14 +130,14 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
} }
titleLabel.text = { titleLabel.text = {
switch mode { switch mode {
case .master: return NSLocalizedString("Waiting for Device", comment: "") case .master: return NSLocalizedString("modal_link_device_master_mode_title_1", comment: "")
case .slave: return NSLocalizedString("Waiting for Authorization", comment: "") case .slave: return NSLocalizedString("modal_link_device_slave_mode_title_1", comment: "")
} }
}() }()
subtitleLabel.text = { subtitleLabel.text = {
switch mode { switch mode {
case .master: return NSLocalizedString("Download Session on your other device and tap \"Link to an existing account\" at the bottom of the landing screen. If you have an existing account on your other device already you will have to delete that account first.", comment: "") case .master: return NSLocalizedString("modal_link_device_master_mode_explanation_1", comment: "")
case .slave: return NSLocalizedString("Please check that the words below match those shown on your other device", comment: "") case .slave: return NSLocalizedString("modal_link_device_slave_mode_explanation_1", comment: "")
} }
}() }()
mnemonicLabel.isHidden = (mode == .master) mnemonicLabel.isHidden = (mode == .master)
@ -157,8 +157,8 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
func requestUserAuthorization(for deviceLink: DeviceLink) { func requestUserAuthorization(for deviceLink: DeviceLink) {
self.deviceLink = deviceLink self.deviceLink = deviceLink
qrCodeImageViewContainer.isHidden = true qrCodeImageViewContainer.isHidden = true
titleLabel.text = NSLocalizedString("Linking Request Received", comment: "") titleLabel.text = NSLocalizedString("modal_link_device_master_mode_title_2", comment: "")
subtitleLabel.text = NSLocalizedString("Please check that the words below match those shown on your other device", comment: "") subtitleLabel.text = NSLocalizedString("modal_link_device_master_mode_explanation_2", comment: "")
let hexEncodedPublicKey = deviceLink.slave.publicKey.removing05PrefixIfNeeded() let hexEncodedPublicKey = deviceLink.slave.publicKey.removing05PrefixIfNeeded()
mnemonicLabel.text = Mnemonic.hash(hexEncodedString: hexEncodedPublicKey) mnemonicLabel.text = Mnemonic.hash(hexEncodedString: hexEncodedPublicKey)
mnemonicLabel.isHidden = false mnemonicLabel.isHidden = false
@ -172,8 +172,8 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
mainStackView.insertArrangedSubview(spinner, at: 0) mainStackView.insertArrangedSubview(spinner, at: 0)
spinner.set(.height, to: 64) spinner.set(.height, to: 64)
spinner.startAnimating() spinner.startAnimating()
titleLabel.text = NSLocalizedString("Authorizing Device Link", comment: "") titleLabel.text = NSLocalizedString("modal_link_device_master_mode_title_3", comment: "")
subtitleLabel.text = NSLocalizedString("Please wait while the device link is created. This can take up to a minute.", comment: "") subtitleLabel.text = NSLocalizedString("modal_link_device_master_mode_explanation_3", comment: "")
mnemonicLabel.isHidden = true mnemonicLabel.isHidden = true
buttonStackView.isHidden = true buttonStackView.isHidden = true
let deviceLink = self.deviceLink! let deviceLink = self.deviceLink!
@ -204,7 +204,7 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
let _ = FileServerAPI.removeDeviceLink(signedDeviceLink) // Attempt to roll back let _ = FileServerAPI.removeDeviceLink(signedDeviceLink) // Attempt to roll back
DispatchQueue.main.async { DispatchQueue.main.async {
self?.close() self?.close()
let alert = UIAlertController(title: NSLocalizedString("Device Linking Failed", comment: ""), message: NSLocalizedString("Please check your internet connection and try again", comment: ""), preferredStyle: .alert) let alert = UIAlertController(title: "Device Linking Failed", message: "Please check your internet connection and try again", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
self?.presentingViewController?.present(alert, animated: true, completion: nil) self?.presentingViewController?.present(alert, animated: true, completion: nil)
} }
@ -213,7 +213,7 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
print("[Loki] Failed to add device link due to error: \(error).") print("[Loki] Failed to add device link due to error: \(error).")
DispatchQueue.main.async { DispatchQueue.main.async {
self?.close() // TODO: Show a message to the user self?.close() // TODO: Show a message to the user
let alert = UIAlertController(title: NSLocalizedString("Device Linking Failed", comment: ""), message: NSLocalizedString("Please check your internet connection and try again", comment: ""), preferredStyle: .alert) let alert = UIAlertController(title: "Device Linking Failed", message: "Please check your internet connection and try again", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
self?.presentingViewController?.present(alert, animated: true, completion: nil) self?.presentingViewController?.present(alert, animated: true, completion: nil)
} }
@ -225,8 +225,8 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
session.stopListeningForLinkingAuthorization() session.stopListeningForLinkingAuthorization()
spinner.stopAnimating() spinner.stopAnimating()
spinner.isHidden = true spinner.isHidden = true
titleLabel.text = NSLocalizedString("Device Link Authorized", comment: "") titleLabel.text = NSLocalizedString("modal_link_device_slave_mode_title_2", comment: "")
subtitleLabel.text = NSLocalizedString("Your device has been linked successfully", comment: "") subtitleLabel.text = NSLocalizedString("modal_link_device_slave_mode_explanation_2", comment: "")
mnemonicLabel.isHidden = true mnemonicLabel.isHidden = true
buttonStackView.isHidden = true buttonStackView.isHidden = true
FileServerAPI.addDeviceLink(deviceLink).catch { error in FileServerAPI.addDeviceLink(deviceLink).catch { error in

View File

@ -23,9 +23,9 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.text = NSLocalizedString("You haven't linked any devices yet", comment: "") explanationLabel.text = NSLocalizedString("vc_linked_devices_empty_state_message", comment: "")
let linkNewDeviceButton = Button(style: .prominentOutline, size: .large) let linkNewDeviceButton = Button(style: .prominentOutline, size: .large)
linkNewDeviceButton.setTitle(NSLocalizedString("Link a Device (Beta)", comment: ""), for: UIControl.State.normal) linkNewDeviceButton.setTitle(NSLocalizedString("vc_linked_devices_empty_state_button_title", comment: ""), for: UIControl.State.normal)
linkNewDeviceButton.addTarget(self, action: #selector(linkNewDevice), for: UIControl.Event.touchUpInside) linkNewDeviceButton.addTarget(self, action: #selector(linkNewDevice), for: UIControl.Event.touchUpInside)
linkNewDeviceButton.set(.width, to: 180) linkNewDeviceButton.set(.width, to: 180)
let result = UIStackView(arrangedSubviews: [ explanationLabel, linkNewDeviceButton ]) let result = UIStackView(arrangedSubviews: [ explanationLabel, linkNewDeviceButton ])
@ -40,7 +40,7 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
super.viewDidLoad() super.viewDidLoad()
setUpGradientBackground() setUpGradientBackground()
setUpNavBarStyle() setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Devices", comment: "")) setNavBarTitle(NSLocalizedString("vc_linked_devices_title", comment: ""))
// Set up link new device button // Set up link new device button
let linkNewDeviceButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(linkNewDevice)) let linkNewDeviceButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(linkNewDevice))
linkNewDeviceButton.tintColor = Colors.text linkNewDeviceButton.tintColor = Colors.text
@ -108,7 +108,7 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
deviceLinkingModal.modalTransitionStyle = .crossDissolve deviceLinkingModal.modalTransitionStyle = .crossDissolve
present(deviceLinkingModal, animated: true, completion: nil) present(deviceLinkingModal, animated: true, completion: nil)
} else { } else {
let alert = UIAlertController(title: NSLocalizedString("Multi Device Limit Reached", comment: ""), message: NSLocalizedString("It's currently not allowed to link more than one device.", comment: ""), preferredStyle: .alert) let alert = UIAlertController(title: NSLocalizedString("vc_linked_devices_multi_device_limit_reached_modal_title", comment: ""), message: NSLocalizedString("It's currently not allowed to link more than one device.", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
present(alert, animated: true, completion: nil) present(alert, animated: true, completion: nil)
} }
@ -118,7 +118,7 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
defer { tableView.deselectRow(at: indexPath, animated: true) } defer { tableView.deselectRow(at: indexPath, animated: true) }
let deviceLink = deviceLinks[indexPath.row] let deviceLink = deviceLinks[indexPath.row]
let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
sheet.addAction(UIAlertAction(title: NSLocalizedString("Change Name", comment: ""), style: .default) { [weak self] _ in sheet.addAction(UIAlertAction(title: NSLocalizedString("vc_device_list_bottom_sheet_change_name_button_title", comment: ""), style: .default) { [weak self] _ in
guard let self = self else { return } guard let self = self else { return }
let deviceNameModal = DeviceNameModal() let deviceNameModal = DeviceNameModal()
deviceNameModal.device = deviceLink.other deviceNameModal.device = deviceLink.other
@ -127,10 +127,10 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
deviceNameModal.modalTransitionStyle = .crossDissolve deviceNameModal.modalTransitionStyle = .crossDissolve
self.present(deviceNameModal, animated: true, completion: nil) self.present(deviceNameModal, animated: true, completion: nil)
}) })
sheet.addAction(UIAlertAction(title: NSLocalizedString("Unlink", comment: ""), style: .destructive) { [weak self] _ in sheet.addAction(UIAlertAction(title: NSLocalizedString("vc_device_list_bottom_sheet_unlink_device_button_title", comment: ""), style: .destructive) { [weak self] _ in
self?.removeDeviceLink(deviceLink) self?.removeDeviceLink(deviceLink)
}) })
sheet.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { _ in }) sheet.addAction(UIAlertAction(title: NSLocalizedString("cancel", comment: ""), style: .cancel) { _ in })
present(sheet, animated: true, completion: nil) present(sheet, animated: true, completion: nil)
} }
@ -166,7 +166,7 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
}) })
self?.updateDeviceLinks() self?.updateDeviceLinks()
}.catch { [weak self] _ in }.catch { [weak self] _ in
let alert = UIAlertController(title: NSLocalizedString("Couldn't Unlink Device", comment: ""), message: NSLocalizedString("Please check your internet connection and try again", comment: ""), preferredStyle: .alert) let alert = UIAlertController(title: "Couldn't Unlink Device", message: "Please check your internet connection and try again", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil) self?.present(alert, animated: true, completion: nil)
} }

View File

@ -10,7 +10,7 @@ final class DeviceNameModal : Modal {
result.textColor = Colors.text result.textColor = Colors.text
result.font = .systemFont(ofSize: Values.mediumFontSize) result.font = .systemFont(ofSize: Values.mediumFontSize)
result.textAlignment = .center result.textAlignment = .center
let placeholder = NSMutableAttributedString(string: NSLocalizedString("Enter a Name", comment: "")) let placeholder = NSMutableAttributedString(string: NSLocalizedString("modal_edit_device_name_text_field_hint", comment: ""))
placeholder.addAttribute(.foregroundColor, value: Colors.text.withAlphaComponent(Values.unimportantElementOpacity), range: NSRange(location: 0, length: placeholder.length)) placeholder.addAttribute(.foregroundColor, value: Colors.text.withAlphaComponent(Values.unimportantElementOpacity), range: NSRange(location: 0, length: placeholder.length))
result.attributedPlaceholder = placeholder result.attributedPlaceholder = placeholder
result.tintColor = Colors.accent result.tintColor = Colors.accent
@ -31,7 +31,7 @@ final class DeviceNameModal : Modal {
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize) titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize)
titleLabel.text = NSLocalizedString("Change Device Name", comment: "") titleLabel.text = "Change Device Name"
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
titleLabel.textAlignment = .center titleLabel.textAlignment = .center
@ -39,7 +39,7 @@ final class DeviceNameModal : Modal {
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("Enter the new display name for your device below", comment: "") explanationLabel.text = "Enter the new display name for your device below"
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
@ -95,7 +95,7 @@ final class DeviceNameModal : Modal {
UserDefaults.standard[.slaveDeviceName(device.publicKey)] = name UserDefaults.standard[.slaveDeviceName(device.publicKey)] = name
delegate?.handleDeviceNameChanged(to: name, for: device) delegate?.handleDeviceNameChanged(to: name, for: device)
} else { } else {
let alert = UIAlertController(title: NSLocalizedString("Error", comment: ""), message: NSLocalizedString("Please pick a name", comment: ""), preferredStyle: .alert) let alert = UIAlertController(title: "Error", message: "Please pick a name", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil))
present(alert, animated: true, completion: nil) present(alert, animated: true, completion: nil)
} }

View File

@ -7,7 +7,7 @@ final class DisplayNameVC : BaseVC {
// MARK: Components // MARK: Components
private lazy var displayNameTextField: TextField = { private lazy var displayNameTextField: TextField = {
let result = TextField(placeholder: NSLocalizedString("Enter a display name", comment: "")) let result = TextField(placeholder: NSLocalizedString("vc_display_name_text_field_hint", comment: ""))
result.layer.borderColor = Colors.text.cgColor result.layer.borderColor = Colors.text.cgColor
return result return result
}() }()
@ -22,14 +22,14 @@ final class DisplayNameVC : BaseVC {
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize) titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("Pick your display name", comment: "") titleLabel.text = NSLocalizedString("vc_display_name_title_2", comment: "")
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
// Set up explanation label // Set up explanation label
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = "This will be your name when you use Session. It can be your real name, an alias, or anything else you like." explanationLabel.text = NSLocalizedString("vc_display_name_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
// Set up spacers // Set up spacers
@ -43,7 +43,7 @@ final class DisplayNameVC : BaseVC {
registerButtonBottomOffsetConstraint = registerButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset) registerButtonBottomOffsetConstraint = registerButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
// Set up register button // Set up register button
let registerButton = Button(style: .prominentFilled, size: .large) let registerButton = Button(style: .prominentFilled, size: .large)
registerButton.setTitle(NSLocalizedString("Continue", comment: ""), for: UIControl.State.normal) registerButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside) registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
// Set up register button container // Set up register button container
@ -128,15 +128,15 @@ final class DisplayNameVC : BaseVC {
} }
let displayName = displayNameTextField.text!.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) let displayName = displayNameTextField.text!.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
guard !displayName.isEmpty else { guard !displayName.isEmpty else {
return showError(title: NSLocalizedString("Please pick a display name", comment: "")) return showError(title: NSLocalizedString("vc_display_name_display_name_missing_error", comment: ""))
} }
let allowedCharacters = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_ ") let allowedCharacters = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_ ")
let hasInvalidCharacters = !displayName.allSatisfy { $0.unicodeScalars.allSatisfy { allowedCharacters.contains($0) } } let hasInvalidCharacters = !displayName.allSatisfy { $0.unicodeScalars.allSatisfy { allowedCharacters.contains($0) } }
guard !hasInvalidCharacters else { guard !hasInvalidCharacters else {
return showError(title: NSLocalizedString("Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters", comment: "")) return showError(title: NSLocalizedString("vc_display_name_display_name_invalid_error", comment: ""))
} }
guard !OWSProfileManager.shared().isProfileNameTooLong(displayName) else { guard !OWSProfileManager.shared().isProfileNameTooLong(displayName) else {
return showError(title: NSLocalizedString("Please pick a shorter display name", comment: "")) return showError(title: NSLocalizedString("vc_display_name_display_name_too_long_error", comment: ""))
} }
OWSProfileManager.shared().updateLocalProfileName(displayName, avatarImage: nil, success: { }, failure: { _ in }, requiresSync: false) // Try to save the user name but ignore the result OWSProfileManager.shared().updateLocalProfileName(displayName, avatarImage: nil, success: { }, failure: { _ in }, requiresSync: false) // Try to save the user name but ignore the result
let pnModeVC = PNModeVC() let pnModeVC = PNModeVC()

View File

@ -35,12 +35,12 @@ final class GroupMembersVC : BaseVC, UITableViewDataSource {
super.viewDidLoad() super.viewDidLoad()
setUpGradientBackground() setUpGradientBackground()
setUpNavBarStyle() setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Group Members", comment: "")) setNavBarTitle("Group Members")
// Set up explanation label // Set up explanation label
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("The ability to add members to a closed group is coming soon.", comment: "") explanationLabel.text = "The ability to add members to a closed group is coming soon."
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping

View File

@ -30,7 +30,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
let attributedTitle = NSMutableAttributedString(string: title) let attributedTitle = NSMutableAttributedString(string: title)
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "80%")) attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "80%"))
result.title = attributedTitle result.title = attributedTitle
result.subtitle = NSLocalizedString("Secure your account by saving your recovery phrase", comment: "") result.subtitle = NSLocalizedString("view_seed_reminder_subtitle_1", comment: "")
result.setProgress(0.8, animated: false) result.setProgress(0.8, animated: false)
result.delegate = self result.delegate = self
return result return result
@ -70,9 +70,9 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.text = NSLocalizedString("You don't have any contacts yet", comment: "") explanationLabel.text = NSLocalizedString("vc_home_empty_state_message", comment: "")
let createNewPrivateChatButton = Button(style: .prominentOutline, size: .large) let createNewPrivateChatButton = Button(style: .prominentOutline, size: .large)
createNewPrivateChatButton.setTitle(NSLocalizedString("Start a Session", comment: ""), for: UIControl.State.normal) createNewPrivateChatButton.setTitle(NSLocalizedString("vc_home_empty_state_button_title", comment: ""), for: UIControl.State.normal)
createNewPrivateChatButton.addTarget(self, action: #selector(createNewPrivateChat), for: UIControl.Event.touchUpInside) createNewPrivateChatButton.addTarget(self, action: #selector(createNewPrivateChat), for: UIControl.Event.touchUpInside)
createNewPrivateChatButton.set(.width, to: 180) createNewPrivateChatButton.set(.width, to: 180)
let result = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ]) let result = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ])
@ -91,7 +91,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
setUpNavBarStyle() setUpNavBarStyle()
} }
updateNavigationBarButtons() updateNavigationBarButtons()
setNavBarTitle(NSLocalizedString("Messages", comment: "")) setNavBarTitle("Messages")
// Set up seed reminder view if needed // Set up seed reminder view if needed
let userDefaults = UserDefaults.standard let userDefaults = UserDefaults.standard
let hasViewedSeed = userDefaults[.hasViewedSeed] let hasViewedSeed = userDefaults[.hasViewedSeed]
@ -372,7 +372,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
publicChat = LokiDatabaseUtilities.getPublicChat(for: thread.uniqueId!, in: transaction) publicChat = LokiDatabaseUtilities.getPublicChat(for: thread.uniqueId!, in: transaction)
} }
let delete = UITableViewRowAction(style: .destructive, title: NSLocalizedString("Delete", comment: "")) { [weak self] _, _ in let delete = UITableViewRowAction(style: .destructive, title: NSLocalizedString("TXT_DELETE_TITLE", comment: "")) { [weak self] _, _ in
let alert = UIAlertController(title: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_TITLE", comment: ""), message: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_MESSAGE", comment: ""), preferredStyle: .alert) let alert = UIAlertController(title: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_TITLE", comment: ""), message: NSLocalizedString("CONVERSATION_DELETE_CONFIRMATION_ALERT_MESSAGE", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { _ in alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { _ in
try! Storage.writeSync { transaction in try! Storage.writeSync { transaction in

View File

@ -8,11 +8,11 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
// MARK: Components // MARK: Components
private lazy var tabBar: TabBar = { private lazy var tabBar: TabBar = {
let tabs = [ let tabs = [
TabBar.Tab(title: NSLocalizedString("Open Group URL", comment: "")) { [weak self] in TabBar.Tab(title: NSLocalizedString("vc_join_public_chat_enter_group_url_tab_title", comment: "")) { [weak self] in
guard let self = self else { return } guard let self = self else { return }
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil) self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
}, },
TabBar.Tab(title: NSLocalizedString("Scan QR Code", comment: "")) { [weak self] in TabBar.Tab(title: NSLocalizedString("vc_join_public_chat_scan_qr_code_tab_title", comment: "")) { [weak self] in
guard let self = self else { return } guard let self = self else { return }
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil) self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
} }
@ -33,7 +33,7 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
}() }()
private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = { private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = {
let message = NSLocalizedString("Scan the QR code of the open group you'd like to join", comment: "") let message = NSLocalizedString("vc_join_public_chat_scan_qr_code_explanation", comment: "")
let result = ScanQRCodeWrapperVC(message: message) let result = ScanQRCodeWrapperVC(message: message)
result.delegate = self result.delegate = self
return result return result
@ -44,7 +44,7 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
super.viewDidLoad() super.viewDidLoad()
setUpGradientBackground() setUpGradientBackground()
setUpNavBarStyle() setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Join Open Group", comment: "")) setNavBarTitle(NSLocalizedString("vc_join_public_chat_title", comment: ""))
let navigationBar = navigationController!.navigationBar let navigationBar = navigationController!.navigationBar
// Set up navigation bar buttons // Set up navigation bar buttons
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close)) let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
@ -128,7 +128,7 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
fileprivate func joinPublicChatIfPossible(with chatURL: String) { fileprivate func joinPublicChatIfPossible(with chatURL: String) {
guard !isJoining else { return } guard !isJoining else { return }
guard let url = URL(string: chatURL), let scheme = url.scheme, scheme == "https", url.host != nil else { guard let url = URL(string: chatURL), let scheme = url.scheme, scheme == "https", url.host != nil else {
return showError(title: NSLocalizedString("Invalid URL", comment: ""), message: NSLocalizedString("Please check the URL you entered and try again", comment: "")) return showError(title: NSLocalizedString("invalid_url", comment: ""), message: "Please check the URL you entered and try again")
} }
isJoining = true isJoining = true
let channelID: UInt64 = 1 let channelID: UInt64 = 1
@ -154,11 +154,11 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
} }
.catch(on: DispatchQueue.main) { [weak self] error in .catch(on: DispatchQueue.main) { [weak self] error in
self?.dismiss(animated: true, completion: nil) // Dismiss the loader self?.dismiss(animated: true, completion: nil) // Dismiss the loader
var title = NSLocalizedString("Couldn't Join", comment: "") var title = "Couldn't Join"
var message = "" var message = ""
if case HTTP.Error.httpRequestFailed(let statusCode, _) = error, statusCode == 401 || statusCode == 403 { if case HTTP.Error.httpRequestFailed(let statusCode, _) = error, statusCode == 401 || statusCode == 403 {
title = NSLocalizedString("Unauthorized", comment: "") title = "Unauthorized"
message = NSLocalizedString("Please ask the open group operator to add you to the group.", comment: "") message = "Please ask the open group operator to add you to the group."
} }
self?.isJoining = false self?.isJoining = false
self?.showError(title: title, message: message) self?.showError(title: title, message: message)
@ -180,7 +180,7 @@ private final class EnterChatURLVC : UIViewController {
// MARK: Components // MARK: Components
private lazy var chatURLTextField: TextField = { private lazy var chatURLTextField: TextField = {
let result = TextField(placeholder: "Enter an open group URL") let result = TextField(placeholder: "vc_enter_chat_url_text_field_hint")
result.keyboardType = .URL result.keyboardType = .URL
result.autocapitalizationType = .none result.autocapitalizationType = .none
return result return result
@ -194,13 +194,13 @@ private final class EnterChatURLVC : UIViewController {
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
explanationLabel.font = .systemFont(ofSize: Values.verySmallFontSize) explanationLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
explanationLabel.text = NSLocalizedString("Open groups can be joined by anyone and do not provide full privacy protection", comment: "") explanationLabel.text = NSLocalizedString("vc_enter_chat_url_privacy_warning", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
// Next button // Next button
let nextButton = Button(style: .prominentOutline, size: .large) let nextButton = Button(style: .prominentOutline, size: .large)
nextButton.setTitle(NSLocalizedString("Next", comment: ""), for: UIControl.State.normal) nextButton.setTitle(NSLocalizedString("next", comment: ""), for: UIControl.State.normal)
nextButton.addTarget(self, action: #selector(joinPublicChatIfPossible), for: UIControl.Event.touchUpInside) nextButton.addTarget(self, action: #selector(joinPublicChatIfPossible), for: UIControl.Event.touchUpInside)
let nextButtonContainer = UIView() let nextButtonContainer = UIView()
nextButtonContainer.addSubview(nextButton) nextButtonContainer.addSubview(nextButton)
@ -280,7 +280,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("Session needs camera access to scan QR codes", comment: "") explanationLabel.text = NSLocalizedString("vc_scan_qr_code_camera_access_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
@ -288,7 +288,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let callToActionButton = UIButton() let callToActionButton = UIButton()
callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
callToActionButton.setTitleColor(Colors.accent, for: UIControl.State.normal) callToActionButton.setTitleColor(Colors.accent, for: UIControl.State.normal)
callToActionButton.setTitle(NSLocalizedString("Enable Camera Access", comment: ""), for: UIControl.State.normal) callToActionButton.setTitle(NSLocalizedString("vc_scan_qr_code_grant_camera_access_button_title", comment: ""), for: UIControl.State.normal)
callToActionButton.addTarget(self, action: #selector(requestCameraAccess), for: UIControl.Event.touchUpInside) callToActionButton.addTarget(self, action: #selector(requestCameraAccess), for: UIControl.Event.touchUpInside)
// Set up stack view // Set up stack view
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ]) let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ])

View File

@ -11,7 +11,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
private lazy var registerButton: Button = { private lazy var registerButton: Button = {
let result = Button(style: .prominentFilled, size: .large) let result = Button(style: .prominentFilled, size: .large)
result.setTitle(NSLocalizedString("Create Session ID", comment: ""), for: UIControl.State.normal) result.setTitle(NSLocalizedString("vc_landing_register_button_title", comment: ""), for: UIControl.State.normal)
result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
result.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside) result.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
return result return result
@ -19,7 +19,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
private lazy var restoreButton: Button = { private lazy var restoreButton: Button = {
let result = Button(style: .prominentOutline, size: .large) let result = Button(style: .prominentOutline, size: .large)
result.setTitle(NSLocalizedString("Continue your Session", comment: ""), for: UIControl.State.normal) result.setTitle(NSLocalizedString("vc_landing_restore_button_title", comment: ""), for: UIControl.State.normal)
result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
result.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside) result.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside)
return result return result
@ -27,7 +27,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
private lazy var linkButton: Button = { private lazy var linkButton: Button = {
let result = Button(style: .regularBorderless, size: .small) let result = Button(style: .regularBorderless, size: .small)
result.setTitle(NSLocalizedString("Link to an existing account", comment: ""), for: UIControl.State.normal) result.setTitle(NSLocalizedString("vc_landing_link_button_title", comment: ""), for: UIControl.State.normal)
result.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) result.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
result.addTarget(self, action: #selector(linkDevice), for: UIControl.Event.touchUpInside) result.addTarget(self, action: #selector(linkDevice), for: UIControl.Event.touchUpInside)
return result return result
@ -43,7 +43,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize) titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("Your Session begins here...", comment: "") titleLabel.text = NSLocalizedString("vc_landing_title_2", comment: "")
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
// Set up title label container // Set up title label container
@ -85,7 +85,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true
// Show device unlinked alert if needed // Show device unlinked alert if needed
if UserDefaults.standard[.wasUnlinked] { if UserDefaults.standard[.wasUnlinked] {
let alert = UIAlertController(title: NSLocalizedString("Device Unlinked", comment: ""), message: NSLocalizedString("Your device was unlinked successfully", comment: ""), preferredStyle: .alert) let alert = UIAlertController(title: "Device Unlinked", message: NSLocalizedString("vc_landing_device_unlinked_dialog_title", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil))
present(alert, animated: true, completion: nil) present(alert, animated: true, completion: nil)
UserDefaults.removeAll() UserDefaults.removeAll()
@ -128,7 +128,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
// MARK: Device Linking // MARK: Device Linking
func requestDeviceLink(with hexEncodedPublicKey: String) { func requestDeviceLink(with hexEncodedPublicKey: String) {
guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else { guard ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) else {
let alert = UIAlertController(title: NSLocalizedString("Invalid Session ID", comment: ""), message: NSLocalizedString("Please make sure the Session ID you entered is correct and try again.", comment: ""), preferredStyle: .alert) let alert = UIAlertController(title: NSLocalizedString("invalid_session_id", comment: ""), message: "Please make sure the Session ID you entered is correct and try again.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil))
return present(alert, animated: true, completion: nil) return present(alert, animated: true, completion: nil)
} }

View File

@ -8,11 +8,11 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon
// MARK: Components // MARK: Components
private lazy var tabBar: TabBar = { private lazy var tabBar: TabBar = {
let tabs = [ let tabs = [
TabBar.Tab(title: NSLocalizedString("Enter Session ID", comment: "")) { [weak self] in TabBar.Tab(title: NSLocalizedString("vc_link_device_enter_session_id_tab_title", comment: "")) { [weak self] in
guard let self = self else { return } guard let self = self else { return }
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil) self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
}, },
TabBar.Tab(title: NSLocalizedString("Scan QR Code", comment: "")) { [weak self] in TabBar.Tab(title: NSLocalizedString("vc_link_device_scan_qr_code_tab_title", comment: "")) { [weak self] in
guard let self = self else { return } guard let self = self else { return }
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil) self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
} }
@ -33,7 +33,7 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon
}() }()
private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = { private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = {
let message = NSLocalizedString("Navigate to \"Settings\" > \"Devices\" > \"Link a Device\" on your other device and then scan the QR code that comes up to start the linking process.", comment: "") let message = NSLocalizedString("vc_link_device_scan_qr_code_explanation", comment: "")
let result = ScanQRCodeWrapperVC(message: message) let result = ScanQRCodeWrapperVC(message: message)
result.delegate = self result.delegate = self
return result return result
@ -44,7 +44,7 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon
super.viewDidLoad() super.viewDidLoad()
setUpGradientBackground() setUpGradientBackground()
setUpNavBarStyle() setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Link Device", comment: "")) setNavBarTitle(NSLocalizedString("vc_link_device_title", comment: ""))
let navigationBar = navigationController!.navigationBar let navigationBar = navigationController!.navigationBar
// Set up navigation bar buttons // Set up navigation bar buttons
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close)) let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
@ -143,9 +143,9 @@ private final class EnterPublicKeyVC : UIViewController {
// MARK: Components // MARK: Components
private lazy var publicKeyTextField: TextField = { private lazy var publicKeyTextField: TextField = {
if isIPhone6OrSmaller { if isIPhone6OrSmaller {
return TextField(placeholder: NSLocalizedString("Enter your Session ID", comment: ""), customHeight: 56, customVerticalInset: 12) return TextField(placeholder: NSLocalizedString("vc_enter_session_id_text_field_hint", comment: ""), customHeight: 56, customVerticalInset: 12)
} else { } else {
return TextField(placeholder: NSLocalizedString("Enter your Session ID", comment: "")) return TextField(placeholder: NSLocalizedString("vc_enter_session_id_text_field_hint", comment: ""))
} }
}() }()
@ -157,19 +157,19 @@ private final class EnterPublicKeyVC : UIViewController {
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone6OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize) titleLabel.font = .boldSystemFont(ofSize: isIPhone6OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("Link your device", comment: "") titleLabel.text = NSLocalizedString("vc_enter_session_id_title", comment: "")
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
// Set up explanation label // Set up explanation label
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = "Navigate to \"Settings\" > \"Devices\" > \"Link a Device\" on your other device and then enter your Session ID here to start the linking process." explanationLabel.text = "vc_enter_session_id_explanation"
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
// Link button // Link button
let linkButton = Button(style: .prominentOutline, size: .large) let linkButton = Button(style: .prominentOutline, size: .large)
linkButton.setTitle(NSLocalizedString("Continue", comment: ""), for: UIControl.State.normal) linkButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
linkButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) linkButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
linkButton.addTarget(self, action: #selector(requestDeviceLink), for: UIControl.Event.touchUpInside) linkButton.addTarget(self, action: #selector(requestDeviceLink), for: UIControl.Event.touchUpInside)
let linkButtonContainer = UIView() let linkButtonContainer = UIView()
@ -270,7 +270,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("Session needs camera access to scan QR codes", comment: "") explanationLabel.text = NSLocalizedString("vc_scan_qr_code_camera_access_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
@ -278,7 +278,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let callToActionButton = UIButton() let callToActionButton = UIButton()
callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
callToActionButton.setTitleColor(Colors.accent, for: UIControl.State.normal) callToActionButton.setTitleColor(Colors.accent, for: UIControl.State.normal)
callToActionButton.setTitle(NSLocalizedString("Enable Camera Access", comment: ""), for: UIControl.State.normal) callToActionButton.setTitle(NSLocalizedString("vc_scan_qr_code_grant_camera_access_button_title", comment: ""), for: UIControl.State.normal)
callToActionButton.addTarget(self, action: #selector(requestCameraAccess), for: UIControl.Event.touchUpInside) callToActionButton.addTarget(self, action: #selector(requestCameraAccess), for: UIControl.Event.touchUpInside)
// Set up stack view // Set up stack view
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ]) let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ])

View File

@ -24,7 +24,7 @@ class Modal : BaseVC {
result.backgroundColor = Colors.buttonBackground result.backgroundColor = Colors.buttonBackground
result.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) result.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
result.setTitleColor(Colors.text, for: UIControl.State.normal) result.setTitleColor(Colors.text, for: UIControl.State.normal)
result.setTitle(NSLocalizedString("Cancel", comment: ""), for: UIControl.State.normal) result.setTitle(NSLocalizedString("cancel", comment: ""), for: UIControl.State.normal)
return result return result
}() }()

View File

@ -30,7 +30,7 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
}() }()
// MARK: Components // MARK: Components
private lazy var nameTextField = TextField(placeholder: NSLocalizedString("Enter a group name", comment: "")) private lazy var nameTextField = TextField(placeholder: NSLocalizedString("vc_create_closed_group_text_field_hint", comment: ""))
private lazy var tableView: UITableView = { private lazy var tableView: UITableView = {
let result = UITableView() let result = UITableView()
@ -49,7 +49,7 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
setUpGradientBackground() setUpGradientBackground()
setUpNavBarStyle() setUpNavBarStyle()
let customTitleFontSize = Values.largeFontSize let customTitleFontSize = Values.largeFontSize
setNavBarTitle(NSLocalizedString("New Closed Group", comment: ""), customFontSize: customTitleFontSize) setNavBarTitle(NSLocalizedString("vc_create_closed_group_title", comment: ""), customFontSize: customTitleFontSize)
// Set up navigation bar buttons // Set up navigation bar buttons
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close)) let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text closeButton.tintColor = Colors.text
@ -89,9 +89,9 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.text = NSLocalizedString("You don't have any contacts yet", comment: "") explanationLabel.text = NSLocalizedString("vc_create_closed_group_empty_state_message", comment: "")
let createNewPrivateChatButton = Button(style: .prominentOutline, size: .large) let createNewPrivateChatButton = Button(style: .prominentOutline, size: .large)
createNewPrivateChatButton.setTitle(NSLocalizedString("Start a Session", comment: ""), for: UIControl.State.normal) createNewPrivateChatButton.setTitle(NSLocalizedString("vc_create_closed_group_empty_state_button_title", comment: ""), for: UIControl.State.normal)
createNewPrivateChatButton.addTarget(self, action: #selector(createNewPrivateChat), for: UIControl.Event.touchUpInside) createNewPrivateChatButton.addTarget(self, action: #selector(createNewPrivateChat), for: UIControl.Event.touchUpInside)
createNewPrivateChatButton.set(.width, to: 180) createNewPrivateChatButton.set(.width, to: 180)
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ]) let stackView = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ])
@ -120,7 +120,7 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
// MARK: Interaction // MARK: Interaction
func textFieldDidEndEditing(_ textField: UITextField) { func textFieldDidEndEditing(_ textField: UITextField) {
crossfadeLabel.text = textField.text!.isEmpty ? NSLocalizedString("New Closed Group", comment: "") : textField.text! crossfadeLabel.text = textField.text!.isEmpty ? NSLocalizedString("vc_create_closed_group_title", comment: "") : textField.text!
} }
func scrollViewDidScroll(_ scrollView: UIScrollView) { func scrollViewDidScroll(_ scrollView: UIScrollView) {
@ -166,16 +166,16 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
presentAlert(alert) presentAlert(alert)
} }
guard let name = nameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), name.count > 0 else { guard let name = nameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), name.count > 0 else {
return showError(title: NSLocalizedString("Please enter a group name", comment: "")) return showError(title: NSLocalizedString("vc_create_closed_group_group_name_missing_error", comment: ""))
} }
guard name.count < 64 else { guard name.count < 64 else {
return showError(title: NSLocalizedString("Please enter a shorter group name", comment: "")) return showError(title: NSLocalizedString("vc_create_closed_group_group_name_too_long_error", comment: ""))
} }
guard selectedContacts.count >= 2 else { guard selectedContacts.count >= 2 else {
return showError(title: NSLocalizedString("Please pick at least 2 group members", comment: "")) return showError(title: NSLocalizedString("vc_create_closed_group_not_enough_group_members_error", comment: ""))
} }
guard selectedContacts.count < 50 else { // Minus one because we're going to include self later guard selectedContacts.count < 50 else { // Minus one because we're going to include self later
return showError(title: NSLocalizedString("A closed group cannot have more than 50 members", comment: "")) return showError(title: NSLocalizedString("vc_create_closed_group_too_many_group_members_error", comment: ""))
} }
let selectedContacts = self.selectedContacts let selectedContacts = self.selectedContacts
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
@ -190,8 +190,8 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false) SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false)
}.catch(on: DispatchQueue.main) { _ in }.catch(on: DispatchQueue.main) { _ in
self?.dismiss(animated: true, completion: nil) // Dismiss the loader self?.dismiss(animated: true, completion: nil) // Dismiss the loader
let title = NSLocalizedString("Couldn't Create Group", comment: "") let title = "Couldn't Create Group"
let message = NSLocalizedString("Please check your internet connection and try again.", comment: "") let message = "Please check your internet connection and try again."
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
self?.presentAlert(alert) self?.presentAlert(alert)
@ -206,16 +206,16 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
presentAlert(alert) presentAlert(alert)
} }
guard let name = nameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), name.count > 0 else { guard let name = nameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), name.count > 0 else {
return showError(title: NSLocalizedString("Please enter a group name", comment: "")) return showError(title: NSLocalizedString("vc_create_closed_group_group_name_missing_error", comment: ""))
} }
guard name.count < 64 else { guard name.count < 64 else {
return showError(title: NSLocalizedString("Please enter a shorter group name", comment: "")) return showError(title: NSLocalizedString("vc_create_closed_group_group_name_too_long_error", comment: ""))
} }
guard selectedContacts.count >= 2 else { guard selectedContacts.count >= 2 else {
return showError(title: NSLocalizedString("Please pick at least 2 group members", comment: "")) return showError(title: NSLocalizedString("vc_create_closed_group_not_enough_group_members_error", comment: ""))
} }
guard selectedContacts.count < 10 else { // Minus one because we're going to include self later guard selectedContacts.count < 10 else { // Minus one because we're going to include self later
return showError(title: NSLocalizedString("A closed group cannot have more than 10 members", comment: "")) return showError(title: NSLocalizedString("vc_create_closed_group_too_many_group_members_error", comment: ""))
} }
let userPublicKey = getUserHexEncodedPublicKey() let userPublicKey = getUserHexEncodedPublicKey()
let storage = OWSPrimaryStorage.shared() let storage = OWSPrimaryStorage.shared()

View File

@ -7,11 +7,11 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
// MARK: Components // MARK: Components
private lazy var tabBar: TabBar = { private lazy var tabBar: TabBar = {
let tabs = [ let tabs = [
TabBar.Tab(title: NSLocalizedString("Enter Session ID", comment: "")) { [weak self] in TabBar.Tab(title: NSLocalizedString("vc_create_private_chat_enter_session_id_tab_title", comment: "")) { [weak self] in
guard let self = self else { return } guard let self = self else { return }
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil) self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
}, },
TabBar.Tab(title: NSLocalizedString("Scan QR Code", comment: "")) { [weak self] in TabBar.Tab(title: NSLocalizedString("vc_create_private_chat_scan_qr_code_tab_title", comment: "")) { [weak self] in
guard let self = self else { return } guard let self = self else { return }
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil) self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
} }
@ -32,7 +32,7 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
}() }()
private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = { private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = {
let message = NSLocalizedString("Scan a users QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.", comment: "") let message = NSLocalizedString("vc_create_private_chat_scan_qr_code_explanation", comment: "")
let result = ScanQRCodeWrapperVC(message: message) let result = ScanQRCodeWrapperVC(message: message)
result.delegate = self result.delegate = self
return result return result
@ -43,7 +43,7 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
super.viewDidLoad() super.viewDidLoad()
setUpGradientBackground() setUpGradientBackground()
setUpNavBarStyle() setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("New Session", comment: "")) setNavBarTitle(NSLocalizedString("vc_create_private_chat_title", comment: ""))
let navigationBar = navigationController!.navigationBar let navigationBar = navigationController!.navigationBar
// Set up navigation bar buttons // Set up navigation bar buttons
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close)) let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
@ -126,7 +126,7 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
fileprivate func startNewPrivateChatIfPossible(with hexEncodedPublicKey: String) { fileprivate func startNewPrivateChatIfPossible(with hexEncodedPublicKey: String) {
if !ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) { if !ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) {
let alert = UIAlertController(title: NSLocalizedString("Invalid Session ID", comment: ""), message: NSLocalizedString("Please check the Session ID and try again", comment: ""), preferredStyle: .alert) let alert = UIAlertController(title: NSLocalizedString("invalid_session_id", comment: ""), message: NSLocalizedString("Please check the Session ID and try again", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
presentAlert(alert) presentAlert(alert)
} else { } else {
@ -149,11 +149,11 @@ private final class EnterPublicKeyVC : UIViewController {
}() }()
// MARK: Components // MARK: Components
private lazy var publicKeyTextField = TextField(placeholder: NSLocalizedString("Enter a Session ID", comment: "")) private lazy var publicKeyTextField = TextField(placeholder: NSLocalizedString("vc_enter_public_key_text_field_hint", comment: ""))
private lazy var copyButton: Button = { private lazy var copyButton: Button = {
let result = Button(style: .unimportant, size: .medium) let result = Button(style: .unimportant, size: .medium)
result.setTitle(NSLocalizedString("Copy", comment: ""), for: UIControl.State.normal) result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside) result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
return result return result
}() }()
@ -166,12 +166,12 @@ private final class EnterPublicKeyVC : UIViewController {
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("Users can share their Session ID from their account settings, or by sharing their QR code.", comment: "") explanationLabel.text = NSLocalizedString("vc_enter_public_key_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
// Set up separator // Set up separator
let separator = Separator(title: NSLocalizedString("Your Session ID", comment: "")) let separator = Separator(title: NSLocalizedString("your_session_id", comment: ""))
// Set up user public key label // Set up user public key label
let userPublicKeyLabel = UILabel() let userPublicKeyLabel = UILabel()
userPublicKeyLabel.textColor = Colors.text userPublicKeyLabel.textColor = Colors.text
@ -182,7 +182,7 @@ private final class EnterPublicKeyVC : UIViewController {
userPublicKeyLabel.text = userHexEncodedPublicKey userPublicKeyLabel.text = userHexEncodedPublicKey
// Set up share button // Set up share button
let shareButton = Button(style: .unimportant, size: .medium) let shareButton = Button(style: .unimportant, size: .medium)
shareButton.setTitle(NSLocalizedString("Share", comment: ""), for: UIControl.State.normal) shareButton.setTitle(NSLocalizedString("share", comment: ""), for: UIControl.State.normal)
shareButton.addTarget(self, action: #selector(sharePublicKey), for: UIControl.Event.touchUpInside) shareButton.addTarget(self, action: #selector(sharePublicKey), for: UIControl.Event.touchUpInside)
// Set up button container // Set up button container
let buttonContainer = UIStackView(arrangedSubviews: [ copyButton, shareButton ]) let buttonContainer = UIStackView(arrangedSubviews: [ copyButton, shareButton ])
@ -191,7 +191,7 @@ private final class EnterPublicKeyVC : UIViewController {
buttonContainer.distribution = .fillEqually buttonContainer.distribution = .fillEqually
// Next button // Next button
let nextButton = Button(style: .prominentOutline, size: .large) let nextButton = Button(style: .prominentOutline, size: .large)
nextButton.setTitle(NSLocalizedString("Next", comment: ""), for: UIControl.State.normal) nextButton.setTitle(NSLocalizedString("next", comment: ""), for: UIControl.State.normal)
nextButton.addTarget(self, action: #selector(startNewPrivateChatIfPossible), for: UIControl.Event.touchUpInside) nextButton.addTarget(self, action: #selector(startNewPrivateChatIfPossible), for: UIControl.Event.touchUpInside)
let nextButtonContainer = UIView() let nextButtonContainer = UIView()
nextButtonContainer.addSubview(nextButton) nextButtonContainer.addSubview(nextButton)
@ -226,7 +226,7 @@ private final class EnterPublicKeyVC : UIViewController {
@objc private func enableCopyButton() { @objc private func enableCopyButton() {
copyButton.isUserInteractionEnabled = true copyButton.isUserInteractionEnabled = true
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyButton.setTitle(NSLocalizedString("Copy", comment: ""), for: UIControl.State.normal) self.copyButton.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
}, completion: nil) }, completion: nil)
} }
@ -235,7 +235,7 @@ private final class EnterPublicKeyVC : UIViewController {
UIPasteboard.general.string = userHexEncodedPublicKey UIPasteboard.general.string = userHexEncodedPublicKey
copyButton.isUserInteractionEnabled = false copyButton.isUserInteractionEnabled = false
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyButton.setTitle(NSLocalizedString("Copied", comment: ""), for: UIControl.State.normal) self.copyButton.setTitle("Copied", for: UIControl.State.normal)
}, completion: nil) }, completion: nil)
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false) Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false)
} }
@ -261,7 +261,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("Session needs camera access to scan QR codes", comment: "") explanationLabel.text = NSLocalizedString("vc_scan_qr_code_camera_access_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
@ -269,7 +269,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let callToActionButton = UIButton() let callToActionButton = UIButton()
callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
callToActionButton.setTitleColor(Colors.accent, for: UIControl.State.normal) callToActionButton.setTitleColor(Colors.accent, for: UIControl.State.normal)
callToActionButton.setTitle(NSLocalizedString("Enable Camera Access", comment: ""), for: UIControl.State.normal) callToActionButton.setTitle(NSLocalizedString("vc_scan_qr_code_grant_camera_access_button_title", comment: ""), for: UIControl.State.normal)
callToActionButton.addTarget(self, action: #selector(requestCameraAccess), for: UIControl.Event.touchUpInside) callToActionButton.addTarget(self, action: #selector(requestCameraAccess), for: UIControl.Event.touchUpInside)
// Set up stack view // Set up stack view
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ]) let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ])

View File

@ -8,7 +8,7 @@ final class NukeDataModal : Modal {
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize) titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize)
titleLabel.text = NSLocalizedString("Clear All Data", comment: "") titleLabel.text = NSLocalizedString("modal_clear_all_data_title", comment: "")
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
titleLabel.textAlignment = .center titleLabel.textAlignment = .center
@ -16,7 +16,7 @@ final class NukeDataModal : Modal {
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("This will permanently delete your messages, sessions, and contacts.", comment: "") explanationLabel.text = NSLocalizedString("modal_clear_all_data_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
@ -29,7 +29,7 @@ final class NukeDataModal : Modal {
} }
nukeDataButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) nukeDataButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
nukeDataButton.setTitleColor(isLightMode ? Colors.destructive : Colors.text, for: UIControl.State.normal) nukeDataButton.setTitleColor(isLightMode ? Colors.destructive : Colors.text, for: UIControl.State.normal)
nukeDataButton.setTitle(NSLocalizedString("Delete", comment: ""), for: UIControl.State.normal) nukeDataButton.setTitle(NSLocalizedString("TXT_DELETE_TITLE", comment: ""), for: UIControl.State.normal)
nukeDataButton.addTarget(self, action: #selector(nuke), for: UIControl.Event.touchUpInside) nukeDataButton.addTarget(self, action: #selector(nuke), for: UIControl.Event.touchUpInside)
// Set up button stack view // Set up button stack view
let buttonStackView = UIStackView(arrangedSubviews: [ cancelButton, nukeDataButton ]) let buttonStackView = UIStackView(arrangedSubviews: [ cancelButton, nukeDataButton ])

View File

@ -11,8 +11,8 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
} }
// MARK: Components // MARK: Components
private lazy var apnsOptionView = OptionView(title: NSLocalizedString("Apple Push Notification Service", comment: ""), explanation: NSLocalizedString("Session will use the Apple Push Notification service to receive push notifications. You'll be notified of new messages reliably and immediately. Using APNs means that your IP address and device token will be exposed to Apple. If you use push notifications for other apps, this will already be the case. Your IP address and device token will also be exposed to Loki, but your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private.", comment: ""), delegate: self, isRecommended: true) private lazy var apnsOptionView = OptionView(title: NSLocalizedString("vc_pn_mode_apns_option_title", comment: ""), explanation: NSLocalizedString("vc_pn_mode_apns_option_explanation", comment: ""), delegate: self, isRecommended: true)
private lazy var backgroundPollingOptionView = OptionView(title: NSLocalizedString("Background Polling", comment: ""), explanation: NSLocalizedString("Session will occasionally check for new messages in the background. This guarantees full metadata protection, but message notifications may be significantly delayed.", comment: ""), delegate: self) private lazy var backgroundPollingOptionView = OptionView(title: NSLocalizedString("vc_pn_mode_background_polling_option_title", comment: ""), explanation: NSLocalizedString("vc_pn_mode_background_polling_option_explanation", comment: ""), delegate: self)
// MARK: Lifecycle // MARK: Lifecycle
override func viewDidLoad() { override func viewDidLoad() {
@ -24,14 +24,14 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize) titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("Push Notifications", comment: "") titleLabel.text = NSLocalizedString("vc_pn_mode_title", comment: "")
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
// Set up explanation label // Set up explanation label
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose.", comment: "") explanationLabel.text = NSLocalizedString("vc_pn_mode_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
// Set up spacers // Set up spacers
@ -41,7 +41,7 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
registerButtonBottomOffsetSpacer.set(.height, to: isIPhone5OrSmaller ? CGFloat(16) : Values.onboardingButtonBottomOffset) registerButtonBottomOffsetSpacer.set(.height, to: isIPhone5OrSmaller ? CGFloat(16) : Values.onboardingButtonBottomOffset)
// Set up register button // Set up register button
let registerButton = Button(style: .prominentFilled, size: .large) let registerButton = Button(style: .prominentFilled, size: .large)
registerButton.setTitle(NSLocalizedString("Continue", comment: ""), for: UIControl.State.normal) registerButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside) registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
// Set up register button container // Set up register button container
@ -75,7 +75,7 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
@objc private func register() { @objc private func register() {
guard selectedOptionView != nil else { guard selectedOptionView != nil else {
let title = NSLocalizedString("Please Pick an Option", comment: "") let title = NSLocalizedString("vc_pn_mode_no_option_picked_dialog_title", comment: "")
let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert) let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
return present(alert, animated: true, completion: nil) return present(alert, animated: true, completion: nil)

View File

@ -19,7 +19,7 @@ final class PathVC : BaseVC {
private lazy var learnMoreButton: Button = { private lazy var learnMoreButton: Button = {
let result = Button(style: .prominentOutline, size: .large) let result = Button(style: .prominentOutline, size: .large)
result.setTitle(NSLocalizedString("Learn More", comment: ""), for: UIControl.State.normal) result.setTitle(NSLocalizedString("vc_path_learn_more_button_title", comment: ""), for: UIControl.State.normal)
result.addTarget(self, action: #selector(learnMore), for: UIControl.Event.touchUpInside) result.addTarget(self, action: #selector(learnMore), for: UIControl.Event.touchUpInside)
return result return result
}() }()
@ -35,7 +35,7 @@ final class PathVC : BaseVC {
private func setUpNavBar() { private func setUpNavBar() {
setUpNavBarStyle() setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Path", comment: "")) setNavBarTitle(NSLocalizedString("vc_path_title", comment: ""))
// Set up close button // Set up close button
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close)) let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text closeButton.tintColor = Colors.text
@ -47,7 +47,7 @@ final class PathVC : BaseVC {
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("Session hides your IP by bouncing your messages through several Service Nodes in Sessions decentralized network. These are the countries your connection is currently being bounced through:", comment: "") explanationLabel.text = NSLocalizedString("vc_path_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
@ -113,8 +113,8 @@ final class PathVC : BaseVC {
let isGuardSnode = (snode == pathToDisplay.first!) let isGuardSnode = (snode == pathToDisplay.first!)
return getPathRow(snode: snode, location: .middle, dotAnimationStartDelay: Double(index) + 2, dotAnimationRepeatInterval: dotAnimationRepeatInterval, isGuardSnode: isGuardSnode) return getPathRow(snode: snode, location: .middle, dotAnimationStartDelay: Double(index) + 2, dotAnimationRepeatInterval: dotAnimationRepeatInterval, isGuardSnode: isGuardSnode)
} }
let youRow = getPathRow(title: NSLocalizedString("You", comment: ""), subtitle: nil, location: .top, dotAnimationStartDelay: 1, dotAnimationRepeatInterval: dotAnimationRepeatInterval) let youRow = getPathRow(title: NSLocalizedString("vc_path_device_row_title", comment: ""), subtitle: nil, location: .top, dotAnimationStartDelay: 1, dotAnimationRepeatInterval: dotAnimationRepeatInterval)
let destinationRow = getPathRow(title: NSLocalizedString("Destination", comment: ""), subtitle: nil, location: .bottom, dotAnimationStartDelay: Double(pathToDisplay.count) + 2, dotAnimationRepeatInterval: dotAnimationRepeatInterval) let destinationRow = getPathRow(title: NSLocalizedString("vc_path_destination_row_title", comment: ""), subtitle: nil, location: .bottom, dotAnimationStartDelay: Double(pathToDisplay.count) + 2, dotAnimationRepeatInterval: dotAnimationRepeatInterval)
let rows = [ youRow ] + snodeRows + [ destinationRow ] let rows = [ youRow ] + snodeRows + [ destinationRow ]
rows.forEach { pathStackView.addArrangedSubview($0) } rows.forEach { pathStackView.addArrangedSubview($0) }
spinner.stopAnimating() spinner.stopAnimating()
@ -158,7 +158,7 @@ final class PathVC : BaseVC {
private func getPathRow(snode: Snode, location: LineView.Location, dotAnimationStartDelay: Double, dotAnimationRepeatInterval: Double, isGuardSnode: Bool) -> UIStackView { private func getPathRow(snode: Snode, location: LineView.Location, dotAnimationStartDelay: Double, dotAnimationRepeatInterval: Double, isGuardSnode: Bool) -> UIStackView {
let country = IP2Country.isInitialized ? (IP2Country.shared.countryNamesCache[snode.ip] ?? "Resolving...") : "Resolving..." let country = IP2Country.isInitialized ? (IP2Country.shared.countryNamesCache[snode.ip] ?? "Resolving...") : "Resolving..."
let title = isGuardSnode ? NSLocalizedString("Entry Node", comment: "") : NSLocalizedString("Service Node", comment: "") let title = isGuardSnode ? NSLocalizedString("vc_path_guard_node_row_title", comment: "") : NSLocalizedString("vc_path_service_node_row_title", comment: "")
return getPathRow(title: title, subtitle: country, location: location, dotAnimationStartDelay: dotAnimationStartDelay, dotAnimationRepeatInterval: dotAnimationRepeatInterval) return getPathRow(title: title, subtitle: country, location: location, dotAnimationStartDelay: dotAnimationStartDelay, dotAnimationRepeatInterval: dotAnimationRepeatInterval)
} }

View File

@ -8,11 +8,11 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
// MARK: Components // MARK: Components
private lazy var tabBar: TabBar = { private lazy var tabBar: TabBar = {
let tabs = [ let tabs = [
TabBar.Tab(title: NSLocalizedString("View My QR Code", comment: "")) { [weak self] in TabBar.Tab(title: NSLocalizedString("vc_qr_code_view_my_qr_code_tab_title", comment: "")) { [weak self] in
guard let self = self else { return } guard let self = self else { return }
self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil) self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil)
}, },
TabBar.Tab(title: NSLocalizedString("Scan QR Code", comment: "")) { [weak self] in TabBar.Tab(title: NSLocalizedString("vc_qr_code_view_scan_qr_code_tab_title", comment: "")) { [weak self] in
guard let self = self else { return } guard let self = self else { return }
self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil) self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil)
} }
@ -33,7 +33,7 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
}() }()
private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = { private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = {
let message = NSLocalizedString("Scan someone's QR code to start a conversation with them", comment: "") let message = NSLocalizedString("vc_qr_code_view_scan_qr_code_explanation", comment: "")
let result = ScanQRCodeWrapperVC(message: message) let result = ScanQRCodeWrapperVC(message: message)
result.delegate = self result.delegate = self
return result return result
@ -44,7 +44,7 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
super.viewDidLoad() super.viewDidLoad()
setUpGradientBackground() setUpGradientBackground()
setUpNavBarStyle() setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("QR Code", comment: "")) setNavBarTitle(NSLocalizedString("vc_qr_code_title", comment: ""))
let navigationBar = navigationController!.navigationBar let navigationBar = navigationController!.navigationBar
// Set up page VC // Set up page VC
let hasCameraAccess = (AVCaptureDevice.authorizationStatus(for: .video) == .authorized) let hasCameraAccess = (AVCaptureDevice.authorizationStatus(for: .video) == .authorized)
@ -122,7 +122,7 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
fileprivate func startNewPrivateChatIfPossible(with hexEncodedPublicKey: String) { fileprivate func startNewPrivateChatIfPossible(with hexEncodedPublicKey: String) {
if !ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) { if !ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) {
let alert = UIAlertController(title: NSLocalizedString("Invalid Session ID", comment: ""), message: NSLocalizedString("Please check the Session ID and try again.", comment: ""), preferredStyle: .alert) let alert = UIAlertController(title: NSLocalizedString("invalid_session_id", comment: ""), message: NSLocalizedString("Please check the Session ID and try again.", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
presentAlert(alert) presentAlert(alert)
} else { } else {
@ -153,7 +153,7 @@ private final class ViewMyQRCodeVC : UIViewController {
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? CGFloat(40) : Values.massiveFontSize) titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? CGFloat(40) : Values.massiveFontSize)
titleLabel.text = NSLocalizedString("Scan Me", comment: "") titleLabel.text = "Scan Me"
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.textAlignment = .center titleLabel.textAlignment = .center
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
@ -178,13 +178,13 @@ private final class ViewMyQRCodeVC : UIViewController {
// let attributedText = NSMutableAttributedString(string: text) // let attributedText = NSMutableAttributedString(string: text)
// attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.mediumFontSize), range: (text as NSString).range(of: "your unique public QR code")) // attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.mediumFontSize), range: (text as NSString).range(of: "your unique public QR code"))
// explanationLabel.attributedText = attributedText // explanationLabel.attributedText = attributedText
explanationLabel.text = NSLocalizedString("This is your QR code. Other users can scan it to start a session with you.", comment: "") explanationLabel.text = NSLocalizedString("vc_view_my_qr_code_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
// Set up share button // Set up share button
let shareButton = Button(style: .regular, size: .large) let shareButton = Button(style: .regular, size: .large)
shareButton.setTitle(NSLocalizedString("Share", comment: ""), for: UIControl.State.normal) shareButton.setTitle(NSLocalizedString("share", comment: ""), for: UIControl.State.normal)
shareButton.addTarget(self, action: #selector(shareQRCode), for: UIControl.Event.touchUpInside) shareButton.addTarget(self, action: #selector(shareQRCode), for: UIControl.Event.touchUpInside)
// Set up share button container // Set up share button container
let shareButtonContainer = UIView() let shareButtonContainer = UIView()
@ -232,7 +232,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("Session needs camera access to scan QR codes", comment: "") explanationLabel.text = NSLocalizedString("vc_scan_qr_code_camera_access_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
@ -240,7 +240,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let callToActionButton = UIButton() let callToActionButton = UIButton()
callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
callToActionButton.setTitleColor(Colors.accent, for: UIControl.State.normal) callToActionButton.setTitleColor(Colors.accent, for: UIControl.State.normal)
callToActionButton.setTitle(NSLocalizedString("Enable Camera Access", comment: ""), for: UIControl.State.normal) callToActionButton.setTitle(NSLocalizedString("vc_scan_qr_code_grant_camera_access_button_title", comment: ""), for: UIControl.State.normal)
callToActionButton.addTarget(self, action: #selector(requestCameraAccess), for: UIControl.Event.touchUpInside) callToActionButton.addTarget(self, action: #selector(requestCameraAccess), for: UIControl.Event.touchUpInside)
// Set up stack view // Set up stack view
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ]) let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ])

View File

@ -15,7 +15,7 @@ final class RegisterVC : BaseVC {
private lazy var copyPublicKeyButton: Button = { private lazy var copyPublicKeyButton: Button = {
let result = Button(style: .prominentOutline, size: .large) let result = Button(style: .prominentOutline, size: .large)
result.setTitle(NSLocalizedString("Copy", comment: ""), for: UIControl.State.normal) result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) result.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside) result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
return result return result
@ -47,14 +47,14 @@ final class RegisterVC : BaseVC {
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize) titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("Say hello to your Session ID", comment: "") titleLabel.text = NSLocalizedString("vc_register_title", comment: "")
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
// Set up explanation label // Set up explanation label
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("Your Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design.", comment: "") explanationLabel.text = NSLocalizedString("vc_register_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
// Set up public key label container // Set up public key label container
@ -69,7 +69,7 @@ final class RegisterVC : BaseVC {
let bottomSpacer = UIView.vStretchingSpacer() let bottomSpacer = UIView.vStretchingSpacer()
// Set up register button // Set up register button
let registerButton = Button(style: .prominentFilled, size: .large) let registerButton = Button(style: .prominentFilled, size: .large)
registerButton.setTitle(NSLocalizedString("Continue", comment: ""), for: UIControl.State.normal) registerButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside) registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
// Set up button stack view // Set up button stack view
@ -123,7 +123,7 @@ final class RegisterVC : BaseVC {
@objc private func enableCopyButton() { @objc private func enableCopyButton() {
copyPublicKeyButton.isUserInteractionEnabled = true copyPublicKeyButton.isUserInteractionEnabled = true
UIView.transition(with: copyPublicKeyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { UIView.transition(with: copyPublicKeyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyPublicKeyButton.setTitle(NSLocalizedString("Copy", comment: ""), for: UIControl.State.normal) self.copyPublicKeyButton.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
}, completion: nil) }, completion: nil)
} }
@ -180,7 +180,7 @@ final class RegisterVC : BaseVC {
UIPasteboard.general.string = keyPair.hexEncodedPublicKey UIPasteboard.general.string = keyPair.hexEncodedPublicKey
copyPublicKeyButton.isUserInteractionEnabled = false copyPublicKeyButton.isUserInteractionEnabled = false
UIView.transition(with: copyPublicKeyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { UIView.transition(with: copyPublicKeyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyPublicKeyButton.setTitle(NSLocalizedString("Copied", comment: ""), for: UIControl.State.normal) self.copyPublicKeyButton.setTitle("Copied", for: UIControl.State.normal)
}, completion: nil) }, completion: nil)
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false) Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false)
} }

View File

@ -8,7 +8,7 @@ final class RestoreVC : BaseVC {
// MARK: Components // MARK: Components
private lazy var mnemonicTextField: TextField = { private lazy var mnemonicTextField: TextField = {
let result = TextField(placeholder: NSLocalizedString("Enter your recovery phrase", comment: "")) let result = TextField(placeholder: NSLocalizedString("vc_restore_seed_text_field_hint", comment: ""))
result.layer.borderColor = Colors.text.cgColor result.layer.borderColor = Colors.text.cgColor
return result return result
}() }()
@ -39,14 +39,14 @@ final class RestoreVC : BaseVC {
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize) titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("Restore your account", comment: "") titleLabel.text = NSLocalizedString("vc_restore_title", comment: "")
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
// Set up explanation label // Set up explanation label
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = "Enter the recovery phrase that was given to you when you signed up to restore your account." explanationLabel.text = "vc_restore_explanation"
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
// Set up legal label // Set up legal label
@ -66,7 +66,7 @@ final class RestoreVC : BaseVC {
restoreButtonBottomOffsetConstraint = restoreButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset) restoreButtonBottomOffsetConstraint = restoreButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
// Set up restore button // Set up restore button
let restoreButton = Button(style: .prominentFilled, size: .large) let restoreButton = Button(style: .prominentFilled, size: .large)
restoreButton.setTitle(NSLocalizedString("Continue", comment: ""), for: UIControl.State.normal) restoreButton.setTitle(NSLocalizedString("continue_2", comment: ""), for: UIControl.State.normal)
restoreButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) restoreButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
restoreButton.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside) restoreButton.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside)
// Set up restore button container // Set up restore button container

View File

@ -18,7 +18,7 @@ final class SeedModal : Modal {
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize) titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize)
titleLabel.text = NSLocalizedString("Your Recovery Phrase", comment: "") titleLabel.text = NSLocalizedString("modal_seed_title", comment: "")
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
titleLabel.textAlignment = .center titleLabel.textAlignment = .center
@ -34,7 +34,7 @@ final class SeedModal : Modal {
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("This is your recovery phrase. With it, you can restore or migrate your Session ID to a new device.", comment: "") explanationLabel.text = NSLocalizedString("modal_seed_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
explanationLabel.textAlignment = .center explanationLabel.textAlignment = .center
@ -45,7 +45,7 @@ final class SeedModal : Modal {
copyButton.backgroundColor = Colors.buttonBackground copyButton.backgroundColor = Colors.buttonBackground
copyButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) copyButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
copyButton.setTitleColor(Colors.text, for: UIControl.State.normal) copyButton.setTitleColor(Colors.text, for: UIControl.State.normal)
copyButton.setTitle(NSLocalizedString("Copy", comment: ""), for: UIControl.State.normal) copyButton.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
copyButton.addTarget(self, action: #selector(copySeed), for: UIControl.Event.touchUpInside) copyButton.addTarget(self, action: #selector(copySeed), for: UIControl.Event.touchUpInside)
// Set up button stack view // Set up button stack view
let buttonStackView = UIStackView(arrangedSubviews: [ cancelButton, copyButton ]) let buttonStackView = UIStackView(arrangedSubviews: [ cancelButton, copyButton ])

View File

@ -30,7 +30,7 @@ final class SeedVC : BaseVC {
let attributedTitle = NSMutableAttributedString(string: title) let attributedTitle = NSMutableAttributedString(string: title)
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "90%")) attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "90%"))
result.title = attributedTitle result.title = attributedTitle
result.subtitle = NSLocalizedString("Tap and hold the redacted words to reveal your recovery phrase, then store it safely to secure your Session ID.", comment: "") result.subtitle = NSLocalizedString("view_seed_reminder_subtitle_2", comment: "")
result.setProgress(0.9, animated: false) result.setProgress(0.9, animated: false)
return result return result
}() }()
@ -47,7 +47,7 @@ final class SeedVC : BaseVC {
private lazy var copyButton: Button = { private lazy var copyButton: Button = {
let result = Button(style: .prominentOutline, size: .large) let result = Button(style: .prominentOutline, size: .large)
result.setTitle(NSLocalizedString("Copy", comment: ""), for: UIControl.State.normal) result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
result.addTarget(self, action: #selector(copyMnemonic), for: UIControl.Event.touchUpInside) result.addTarget(self, action: #selector(copyMnemonic), for: UIControl.Event.touchUpInside)
return result return result
}() }()
@ -57,7 +57,7 @@ final class SeedVC : BaseVC {
super.viewDidLoad() super.viewDidLoad()
setUpGradientBackground() setUpGradientBackground()
setUpNavBarStyle() setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Your Recovery Phrase", comment: "")) setNavBarTitle(NSLocalizedString("vc_seed_title", comment: ""))
// Set up navigation bar buttons // Set up navigation bar buttons
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close)) let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text closeButton.tintColor = Colors.text
@ -66,14 +66,14 @@ final class SeedVC : BaseVC {
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.textColor = Colors.text titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize) titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("Meet your recovery phrase", comment: "") titleLabel.text = NSLocalizedString("vc_seed_title_2", comment: "")
titleLabel.numberOfLines = 0 titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping titleLabel.lineBreakMode = .byWordWrapping
// Set up explanation label // Set up explanation label
let explanationLabel = UILabel() let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text explanationLabel.textColor = Colors.text
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
explanationLabel.text = NSLocalizedString("Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and dont give it to anyone.", comment: "") explanationLabel.text = NSLocalizedString("vc_seed_explanation", comment: "")
explanationLabel.numberOfLines = 0 explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping explanationLabel.lineBreakMode = .byWordWrapping
// Set up mnemonic label // Set up mnemonic label
@ -93,7 +93,7 @@ final class SeedVC : BaseVC {
let callToActionLabel = UILabel() let callToActionLabel = UILabel()
callToActionLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) callToActionLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
callToActionLabel.font = .systemFont(ofSize: isIPhone5OrSmaller ? Values.smallFontSize : Values.mediumFontSize) callToActionLabel.font = .systemFont(ofSize: isIPhone5OrSmaller ? Values.smallFontSize : Values.mediumFontSize)
callToActionLabel.text = NSLocalizedString("Hold to reveal", comment: "") callToActionLabel.text = NSLocalizedString("vc_seed_reveal_button_title", comment: "")
callToActionLabel.textAlignment = .center callToActionLabel.textAlignment = .center
let callToActionLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic)) let callToActionLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
callToActionLabel.addGestureRecognizer(callToActionLabelGestureRecognizer) callToActionLabel.addGestureRecognizer(callToActionLabelGestureRecognizer)
@ -144,7 +144,7 @@ final class SeedVC : BaseVC {
@objc private func enableCopyButton() { @objc private func enableCopyButton() {
copyButton.isUserInteractionEnabled = true copyButton.isUserInteractionEnabled = true
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyButton.setTitle(NSLocalizedString("Copy", comment: ""), for: UIControl.State.normal) self.copyButton.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
}, completion: nil) }, completion: nil)
} }
@ -164,7 +164,7 @@ final class SeedVC : BaseVC {
self.seedReminderView.title = attributedTitle self.seedReminderView.title = attributedTitle
}, completion: nil) }, completion: nil)
UIView.transition(with: seedReminderView.subtitleLabel, duration: 1, options: .transitionCrossDissolve, animations: { UIView.transition(with: seedReminderView.subtitleLabel, duration: 1, options: .transitionCrossDissolve, animations: {
self.seedReminderView.subtitle = NSLocalizedString("Make sure to store your recovery phrase in a safe place", comment: "") self.seedReminderView.subtitle = NSLocalizedString("view_seed_reminder_subtitle_3", comment: "")
}, completion: nil) }, completion: nil)
seedReminderView.setProgress(1, animated: true) seedReminderView.setProgress(1, animated: true)
UserDefaults.standard[.hasViewedSeed] = true UserDefaults.standard[.hasViewedSeed] = true
@ -176,7 +176,7 @@ final class SeedVC : BaseVC {
UIPasteboard.general.string = mnemonic UIPasteboard.general.string = mnemonic
copyButton.isUserInteractionEnabled = false copyButton.isUserInteractionEnabled = false
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyButton.setTitle(NSLocalizedString("Copied", comment: ""), for: UIControl.State.normal) self.copyButton.setTitle("Copied", for: UIControl.State.normal)
}, completion: nil) }, completion: nil)
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false) Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false)
} }

View File

@ -38,14 +38,14 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
}() }()
private lazy var displayNameTextField: TextField = { private lazy var displayNameTextField: TextField = {
let result = TextField(placeholder: NSLocalizedString("Enter a display name", comment: ""), usesDefaultHeight: false) let result = TextField(placeholder: NSLocalizedString("vc_settings_display_name_text_field_hint", comment: ""), usesDefaultHeight: false)
result.textAlignment = .center result.textAlignment = .center
return result return result
}() }()
private lazy var copyButton: Button = { private lazy var copyButton: Button = {
let result = Button(style: .prominentOutline, size: .medium) let result = Button(style: .prominentOutline, size: .medium)
result.setTitle(NSLocalizedString("Copy", comment: ""), for: UIControl.State.normal) result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside) result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
return result return result
}() }()
@ -55,9 +55,9 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
super.viewDidLoad() super.viewDidLoad()
setUpGradientBackground() setUpGradientBackground()
setUpNavBarStyle() setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Settings", comment: "")) setNavBarTitle(NSLocalizedString("vc_settings_title", comment: ""))
// Set up navigation bar buttons // Set up navigation bar buttons
let backButton = UIBarButtonItem(title: NSLocalizedString("Back", comment: ""), style: .plain, target: nil, action: nil) let backButton = UIBarButtonItem(title: "Back", style: .plain, target: nil, action: nil)
backButton.tintColor = Colors.text backButton.tintColor = Colors.text
navigationItem.backBarButtonItem = backButton navigationItem.backBarButtonItem = backButton
updateNavigationBarButtons() updateNavigationBarButtons()
@ -84,7 +84,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
headerStackView.spacing = Values.smallSpacing headerStackView.spacing = Values.smallSpacing
headerStackView.alignment = .center headerStackView.alignment = .center
// Set up separator // Set up separator
let separator = Separator(title: NSLocalizedString("Your Session ID", comment: "")) let separator = Separator(title: NSLocalizedString("your_session_id", comment: ""))
// Set up public key label // Set up public key label
let publicKeyLabel = UILabel() let publicKeyLabel = UILabel()
publicKeyLabel.textColor = Colors.text publicKeyLabel.textColor = Colors.text
@ -95,7 +95,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
publicKeyLabel.text = userHexEncodedPublicKey publicKeyLabel.text = userHexEncodedPublicKey
// Set up share button // Set up share button
let shareButton = Button(style: .regular, size: .medium) let shareButton = Button(style: .regular, size: .medium)
shareButton.setTitle(NSLocalizedString("Share", comment: ""), for: UIControl.State.normal) shareButton.setTitle(NSLocalizedString("share", comment: ""), for: UIControl.State.normal)
shareButton.addTarget(self, action: #selector(sharePublicKey), for: UIControl.Event.touchUpInside) shareButton.addTarget(self, action: #selector(sharePublicKey), for: UIControl.Event.touchUpInside)
// Set up button container // Set up button container
let buttonContainer = UIStackView(arrangedSubviews: [ copyButton, shareButton ]) let buttonContainer = UIStackView(arrangedSubviews: [ copyButton, shareButton ])
@ -161,19 +161,19 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
} }
var result = [ var result = [
getSeparator(), getSeparator(),
getSettingButton(withTitle: NSLocalizedString("Privacy", comment: ""), color: Colors.text, action: #selector(showPrivacySettings)), getSettingButton(withTitle: NSLocalizedString("vc_settings_privacy_button_title", comment: ""), color: Colors.text, action: #selector(showPrivacySettings)),
getSeparator(), getSeparator(),
getSettingButton(withTitle: NSLocalizedString("Notifications", comment: ""), color: Colors.text, action: #selector(showNotificationSettings)) getSettingButton(withTitle: NSLocalizedString("vc_settings_notifications_button_title", comment: ""), color: Colors.text, action: #selector(showNotificationSettings))
] ]
let isMasterDevice = UserDefaults.standard.isMasterDevice let isMasterDevice = UserDefaults.standard.isMasterDevice
if isMasterDevice { if isMasterDevice {
result.append(getSeparator()) result.append(getSeparator())
result.append(getSettingButton(withTitle: NSLocalizedString("Devices", comment: ""), color: Colors.text, action: #selector(showLinkedDevices))) result.append(getSettingButton(withTitle: NSLocalizedString("vc_settings_devices_button_title", comment: ""), color: Colors.text, action: #selector(showLinkedDevices)))
result.append(getSeparator()) result.append(getSeparator())
result.append(getSettingButton(withTitle: NSLocalizedString("Recovery Phrase", comment: ""), color: Colors.text, action: #selector(showSeed))) result.append(getSettingButton(withTitle: NSLocalizedString("vc_settings_recovery_phrase_button_title", comment: ""), color: Colors.text, action: #selector(showSeed)))
} }
result.append(getSeparator()) result.append(getSeparator())
result.append(getSettingButton(withTitle: NSLocalizedString("Clear All Data", comment: ""), color: Colors.destructive, action: #selector(clearAllData))) result.append(getSettingButton(withTitle: NSLocalizedString("vc_settings_clear_all_data_button_title", comment: ""), color: Colors.destructive, action: #selector(clearAllData)))
result.append(getSeparator()) result.append(getSeparator())
return result return result
} }
@ -182,12 +182,12 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
@objc private func enableCopyButton() { @objc private func enableCopyButton() {
copyButton.isUserInteractionEnabled = true copyButton.isUserInteractionEnabled = true
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyButton.setTitle(NSLocalizedString("Copy", comment: ""), for: UIControl.State.normal) self.copyButton.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
}, completion: nil) }, completion: nil)
} }
func avatarActionSheetTitle() -> String? { func avatarActionSheetTitle() -> String? {
return NSLocalizedString("Update Profile Picture", comment: "") return "Update Profile Picture"
} }
func fromViewController() -> UIViewController { func fromViewController() -> UIViewController {
@ -199,7 +199,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
} }
func clearAvatarActionLabel() -> String { func clearAvatarActionLabel() -> String {
return NSLocalizedString("Clear", comment: "") return "Clear"
} }
// MARK: Updating // MARK: Updating
@ -266,8 +266,8 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
if let error = error as? DotNetAPI.DotNetAPIError { if let error = error as? DotNetAPI.DotNetAPIError {
isMaxFileSizeExceeded = (error == .maxFileSizeExceeded) isMaxFileSizeExceeded = (error == .maxFileSizeExceeded)
} }
let title = isMaxFileSizeExceeded ? "Maximum File Size Exceeded" : NSLocalizedString("Couldn't Update Profile", comment: "") let title = isMaxFileSizeExceeded ? "Maximum File Size Exceeded" : "Couldn't Update Profile"
let message = isMaxFileSizeExceeded ? "Please select a smaller photo and try again" : NSLocalizedString("Please check your internet connection and try again", comment: "") let message = isMaxFileSizeExceeded ? "Please select a smaller photo and try again" : "Please check your internet connection and try again"
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil) self?.present(alert, animated: true, completion: nil)
@ -299,15 +299,15 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
} }
let displayName = displayNameTextField.text!.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) let displayName = displayNameTextField.text!.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
guard !displayName.isEmpty else { guard !displayName.isEmpty else {
return showError(title: NSLocalizedString("Please pick a display name", comment: "")) return showError(title: NSLocalizedString("vc_settings_display_name_missing_error", comment: ""))
} }
let allowedCharacters = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_ ") let allowedCharacters = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_ ")
let hasInvalidCharacters = !displayName.allSatisfy { $0.unicodeScalars.allSatisfy { allowedCharacters.contains($0) } } let hasInvalidCharacters = !displayName.allSatisfy { $0.unicodeScalars.allSatisfy { allowedCharacters.contains($0) } }
guard !hasInvalidCharacters else { guard !hasInvalidCharacters else {
return showError(title: NSLocalizedString("Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters", comment: "")) return showError(title: NSLocalizedString("vc_settings_invalid_display_name_error", comment: ""))
} }
guard !OWSProfileManager.shared().isProfileNameTooLong(displayName) else { guard !OWSProfileManager.shared().isProfileNameTooLong(displayName) else {
return showError(title: NSLocalizedString("Please pick a shorter display name", comment: "")) return showError(title: NSLocalizedString("vc_settings_display_name_too_long_error", comment: ""))
} }
isEditingDisplayName = false isEditingDisplayName = false
displayNameToBeUploaded = displayName displayNameToBeUploaded = displayName
@ -326,7 +326,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
UIPasteboard.general.string = userHexEncodedPublicKey UIPasteboard.general.string = userHexEncodedPublicKey
copyButton.isUserInteractionEnabled = false copyButton.isUserInteractionEnabled = false
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyButton.setTitle(NSLocalizedString("Copied", comment: ""), for: UIControl.State.normal) self.copyButton.setTitle("Copied", for: UIControl.State.normal)
}, completion: nil) }, completion: nil)
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false) Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false)
} }

View File

@ -36,13 +36,13 @@
// Loki: Customize title // Loki: Customize title
UILabel *titleLabel = [UILabel new]; UILabel *titleLabel = [UILabel new];
titleLabel.text = NSLocalizedString(@"Notifications", @""); titleLabel.text = NSLocalizedString(@"vc_notification_settings_title", @"");
titleLabel.textColor = LKColors.text; titleLabel.textColor = LKColors.text;
titleLabel.font = [UIFont boldSystemFontOfSize:LKValues.veryLargeFontSize]; titleLabel.font = [UIFont boldSystemFontOfSize:LKValues.veryLargeFontSize];
self.navigationItem.titleView = titleLabel; self.navigationItem.titleView = titleLabel;
// Loki: Set up back button // Loki: Set up back button
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Back", "") style:UIBarButtonItemStylePlain target:nil action:nil]; UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStylePlain target:nil action:nil];
backButton.tintColor = LKColors.text; backButton.tintColor = LKColors.text;
self.navigationItem.backBarButtonItem = backButton; self.navigationItem.backBarButtonItem = backButton;
} }
@ -65,8 +65,8 @@
OWSPreferences *prefs = Environment.shared.preferences; OWSPreferences *prefs = Environment.shared.preferences;
OWSTableSection *strategySection = [OWSTableSection new]; OWSTableSection *strategySection = [OWSTableSection new];
strategySection.headerTitle = NSLocalizedString(@"Notification Strategy", @""); strategySection.headerTitle = NSLocalizedString(@"preferences_notifications_strategy_category_title", @"");
[strategySection addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"Use APNs", @"") [strategySection addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"preferences_notifications_use_apns_option_title", @"")
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"push_notification_strategy") accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"push_notification_strategy")
isOnBlock:^{ isOnBlock:^{
return [NSUserDefaults.standardUserDefaults boolForKey:@"isUsingFullAPNs"]; return [NSUserDefaults.standardUserDefaults boolForKey:@"isUsingFullAPNs"];
@ -76,7 +76,7 @@
} }
target:weakSelf target:weakSelf
selector:@selector(didToggleAPNsSwitch:)]]; selector:@selector(didToggleAPNsSwitch:)]];
strategySection.footerTitle = NSLocalizedString(@"Session will use the Apple Push Notification service to receive push notifications. You'll be notified of new messages reliably and immediately. Using APNs means that your IP address and device token will be exposed to Apple. If you use push notifications for other apps, this will already be the case. Your IP address and device token will also be exposed to Loki, but your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private.", @""); strategySection.footerTitle = NSLocalizedString(@"preferences_notifications_use_apns_option_explanation", @"");
[contents addSection:strategySection]; [contents addSection:strategySection];
// Sounds section. // Sounds section.

View File

@ -43,7 +43,7 @@ static NSString *const kSealedSenderInfoURL = @"https://signal.org/blog/sealed-s
// Loki: Customize title // Loki: Customize title
UILabel *titleLabel = [UILabel new]; UILabel *titleLabel = [UILabel new];
titleLabel.text = NSLocalizedString(@"Privacy", @""); titleLabel.text = NSLocalizedString(@"vc_privacy_settings_title", @"");
titleLabel.textColor = LKColors.text; titleLabel.textColor = LKColors.text;
titleLabel.font = [UIFont boldSystemFontOfSize:LKValues.veryLargeFontSize]; titleLabel.font = [UIFont boldSystemFontOfSize:LKValues.veryLargeFontSize];
self.navigationItem.titleView = titleLabel; self.navigationItem.titleView = titleLabel;
@ -538,11 +538,11 @@ static NSString *const kSealedSenderInfoURL = @"https://signal.org/blog/sealed-s
{ {
BOOL isOn = sender.isOn; BOOL isOn = sender.isOn;
if (isOn) { if (isOn) {
NSString *title = NSLocalizedString(@"Enable Link Previews?", @""); NSString *title = @"Enable Link Previews?";
NSString *message = NSLocalizedString(@"You will not have full metadata protection when sending or receiving link previews.", @""); NSString *message = @"You will not have full metadata protection when sending or receiving link previews.";
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"") style:UIAlertActionStyleDefault handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"") style:UIAlertActionStyleDefault handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[sender setOn:NO animated:YES]; [sender setOn:NO animated:YES];
SSKPreferences.areLinkPreviewsEnabled = NO; SSKPreferences.areLinkPreviewsEnabled = NO;
}]]; }]];

View File

@ -2554,296 +2554,208 @@
// MARK: - Loki Messenger:
"Session can let you know when you get a message (and who it is from)" = "Session can let you know when you get a message (and who it is from)";
"Create Your Session Account" = "Create Your Session Account";
"Enter a name to be shown to your contacts" = "Enter a name to be shown to your contacts";
"Display Name" = "Display Name";
"Type an optional password for added security" = "Type an optional password for added security";
"Password (Optional)" = "Password (Optional)";
"Next" = "Next";
"Add" = "Add";
"Please save the seed below in a safe location. It can be used to restore your account if you lose access, or to migrate your account to a new device." = "Please save the seed below in a safe location. It can be used to restore your account if you lose access, or to migrate your account to a new device.";
"Restore your account by entering your seed below." = "Restore your account by entering your seed below.";
"Copy" = "Copy";
"Copied ✓" = "Copied ✓";
"Restore Using Seed" = "Restore Using Seed";
"Register" = "Register";
"Enter Your Seed" = "Enter Your Seed";
"Register a New Account" = "Register a New Account";
"Restore" = "Restore";
"Something went wrong. Please check your seed and try again." = "Something went wrong. Please check your seed and try again.";
"Looks like you didn't enter enough words. Please check your seed and try again." = "Looks like you didn't enter enough words. Please check your seed and try again.";
"You seem to be missing the last word of your seed. Please check what you entered and try again." = "You seem to be missing the last word of your seed. Please check what you entered and try again.";
"There appears to be an invalid word in your seed. Please check what you entered and try again." = "There appears to be an invalid word in your seed. Please check what you entered and try again.";
"Your seed couldn't be verified. Please check what you entered and try again." = "Your seed couldn't be verified. Please check what you entered and try again.";
"Search by public key" = "Search by public key";
"Start a Conversation" = "Start a Conversation";
"Invalid Session ID" = "Invalid Session ID";
"No search results" = "No search results";
"Calculating proof of work" = "Calculating proof of work";
"Failed to calculate proof of work." = "Failed to calculate proof of work.";
"Share Public Key" = "Share Public Key";
"%@ sent you a session request" = "%@ sent you a session request";
"Accept" = "Accept";
"Decline" = "Decline";
"Pending session request" = "Pending session request";
"New Message" = "New Message";
"Secure session reset in progress" = "Secure session reset in progress";
"Secure session reset done" = "Secure session reset done";
"Session" = "Session";
"You've sent %@ a session request" = "You've sent %@ a session request";
"You've declined %@'s session request" = "You've declined %@'s session request";
"You've accepted %@'s session request" = "You've accepted %@'s session request";
"%@ accepted your session request" = "%@ accepted your session request";
"%@'s session request has expired" = "%@'s session request has expired";
"Your session request to %@ has expired" = "Your session request to %@ has expired";
"Show Seed" = "Show Seed";
"Your Seed" = "Your Seed";
"Require Touch ID, Face ID or your device passcode to unlock Sessions screen. You can still receive notifications when Screen Lock is enabled. Use Sessions notification settings to customise the information displayed in notifications." = "Require Touch ID, Face ID or your device passcode to unlock Sessions screen. You can still receive notifications when Screen Lock is enabled. Use Sessions notification settings to customise the information displayed in notifications.";
"Prevent Session previews from appearing in the app switcher." = "Prevent Session previews from appearing in the app switcher.";
"Session" = "Session";
"Privacy Policy" = "Privacy Policy";
"New Session" = "New Session";
"Add Public Chat Server" = "Add Public Chat Server";
"Enter a Public Key" = "Enter a Public Key";
"Enter a Server URL" = "Enter a Server URL";
"For example: 059abcf223aa8c10e3dc2d623688b75dd25896794717e4a9c486772664fc95e41e." = "For example: 059abcf223aa8c10e3dc2d623688b75dd25896794717e4a9c486772664fc95e41e.";
"Invalid Session ID" = "Invalid Session ID";
"Please check the Session ID and try again" = "Please check the Session ID and try again";
"Looks like you don't have any conversations yet. Get started by messaging a friend." = "Looks like you don't have any conversations yet. Get started by messaging a friend.";
"Enter the public key of the person you'd like to securely message. They can share their public key with you by going into Session's in-app settings and clicking \"Share Public Key\"." = "Enter the public key of the person you'd like to securely message. They can share their public key with you by going into Session's in-app settings and clicking \"Share Public Key\".";
"Unlock Session" = "Unlock Session";
"Clear All Data" = "Clear All Data";
"Are you sure you want to clear all your data? This will delete your entire account, including all conversations and your personal key pair." = "Are you sure you want to clear all your data? This will delete your entire account, including all conversations and your personal key pair.";
"Cancel" = "Cancel";
"Update Required" = "Update Required";
"This version of Session is no longer supported. Please press OK to reset your account and migrate to the latest version." = "This version of Session is no longer supported. Please press OK to reset your account and migrate to the latest version.";
"Loki Public Chat" = "Loki Public Chat";
"Loki News" = "Loki News";
"Session Updates" = "Session Updates";
"Show QR Code" = "Show QR Code";
"This is your QR code. Other people can scan it to start a secure conversation with you." = "This is your QR code. Other people can scan it to start a secure conversation with you.";
"Scan a QR Code Instead" = "Scan a QR Code Instead";
"Session needs camera access to scan QR codes." = "Session needs camera access to scan QR codes.";
"You can enable camera access in your device settings." = "You can enable camera access in your device settings.";
"Scan QR Code" = "Scan QR Code";
"Loki" = "Loki";
"Can't Start Conversation" = "Can't Start Conversation";
"Please enter the public key of the person you'd like to message." = "Please enter the public key of the person you'd like to message.";
"Session is currently in beta. For development purposes the beta version collects basic usage statistics and crash logs. In addition, the beta version doesn't yet provide full privacy and shouldn't be used to transmit sensitive information." = "Session is currently in beta. For development purposes the beta version collects basic usage statistics and crash logs. In addition, the beta version doesn't yet provide full privacy and shouldn't be used to transmit sensitive information.";
"Copy Public Key" = "Copy Public Key";
"Link Device" = "Link Device";
"Waiting for Device" = "Waiting for Device";
"Waiting for Authorization" = "Waiting for Authorization";
"Create a new account on your other device and click \"Link Device\" when you're at the \"Create Your Session Account\" step to start the linking process" = "Create a new account on your other device and click \"Link Device\" when you're at the \"Create Your Session Account\" step to start the linking process";
"Linking Request Received" = "Linking Request Received";
"Invalid Session ID" = "Invalid Session ID";
"Please check that the words below match those shown on your other device" = "Please check that the words below match those shown on your other device";
"Link to an existing device by going into its in-app settings and clicking \"Link Device\"." = "Link to an existing device by going into its in-app settings and clicking \"Link Device\".";
"Authorize" = "Authorize";
"Enter the Other Device's Public Key" = "Enter the Other Device's Public Key";
"This is your personal secret. It can be used to restore your account if you lose access, or to migrate your account to a new device." = "This is your personal secret. It can be used to restore your account if you lose access, or to migrate your account to a new device.";
"Device Link Authorized" = "Device Link Authorized";
"Your device has been linked successfully" = "Your device has been linked successfully";
"Link" = "Link";
"Anonymous" = "Anonymous";
"Invalid URL" = "Invalid URL";
"Please check the URL you entered and try again." = "Please check the URL you entered and try again.";
"Please pick a shorter display name" = "Please pick a shorter display name";
"Please pick a display name" = "Please pick a display name";
"Add Public Chat" = "Add Public Chat";
"Enter a URL" = "Enter a URL";
"Enter the URL of the public chat you'd like to join. The Loki Public Chat URL is https://chat.lokinet.org." = "Enter the URL of the public chat you'd like to join. The Loki Public Chat URL is https://chat.lokinet.org.";
"Connecting..." = "Connecting...";
"Couldn't Connect" = "Couldn't Connect";
"Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters" = "Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters";
"Multi Device Limit Reached" = "Multi Device Limit Reached";
"It's currently not allowed to link more than one device." = "It's currently not allowed to link more than one device.";
"Profile Picture" = "Profile Picture";
"Set Profile Picture" = "Set Profile Picture";
"Clear Profile Picture" = "Clear Profile Picture";
"Invalid QR Code" = "Invalid QR Code";
"Please make sure the QR code you scanned is correct and try again." = "Please make sure the QR code you scanned is correct and try again.";
"Devices" = "Devices";
"You haven't linked any devices yet" = "You haven't linked any devices yet";
"Link a Device (Beta)" = "Link a Device (Beta)";
"Unlink" = "Unlink";
"Change Name" = "Change Name";
"Change Device Name" = "Change Device Name";
"Enter the new display name for your device below" = "Enter the new display name for your device below";
"Enter a Name" = "Enter a Name";
"Error" = "Error";
"Please pick a name" = "Please pick a name";
"Couldn't Link Device" = "Couldn't Link Device";
"Couldn't Unlink Device" = "Couldn't Unlink Device";
"Please check your internet connection and try again" = "Please check your internet connection and try again";
"Device Unlinked" = "Device Unlinked";
"Your device was unlinked successfully" = "Your device was unlinked successfully";
"Unnamed Device" = "Unnamed Device";
"Linked device (%@)" = "Linked device (%@)";
"Restore session" = "Restore session";
"Would you like to start a new session with %@?" = "Would you like to start a new session with %@?";
// MARK: - Session // MARK: - Session
"Messages" = "Messages"; "continue_2" = "Continue";
"Note to Self" = "Note to Self"; "copy" = "Copy";
"New Group" = "New Group"; "invalid_url" = "Invalid URL";
"Delete" = "Delete"; "copied_to_clipboard" = "Copied to clipboard";
"Search" = "Search"; "device_linking_failed" = "Couldn't link device.";
"New Session" = "New Session"; "next" = "Next";
"Enter a Session ID" = "Enter a Session ID"; "share" = "Share";
"Users can share their Session ID from their account settings, or by sharing their QR code." = "Users can share their Session ID from their account settings, or by sharing their QR code."; "invalid_session_id" = "Invalid Session ID";
"Scan a users QR code to start a session. QR codes can be found by tapping the QR code icon in account settings." = "Scan a users QR code to start a session. QR codes can be found by tapping the QR code icon in account settings."; "cancel" = "Cancel";
"Your Session ID" = "Your Session ID"; "your_session_id" = "Your Session ID";
"Copy" = "Copy";
"Copied" = "Copied"; "vc_landing_title_2" = "Your Session begins here...";
"Share" = "Share"; "vc_landing_register_button_title" = "Create Session ID";
"Next" = "Next"; "vc_landing_restore_button_title" = "Continue Your Session";
"Session needs camera access to scan QR codes" = "Session needs camera access to scan QR codes"; "vc_landing_link_button_title" = "Link to an existing account";
"Enable Camera Access" = "Enable Camera Access"; "vc_landing_device_unlinked_modal_title" = "Your device was unlinked successfully";
"Scan the QR code of the person you'd like to securely message. They can find their QR code by going into Session's in-app settings and tapping \"Show QR Code\"." = "Scan the QR code of the person you'd like to securely message. They can find their QR code by going into Session's in-app settings and tapping \"Show QR Code\".";
"Enter Session ID" = "Enter Session ID"; "view_fake_chat_bubble_1" = "What's Session?";
"Open Group URL" = "Open Group URL"; "view_fake_chat_bubble_2" = "It's a decentralized, encrypted messaging app";
"Scan QR Code" = "Scan QR Code"; "view_fake_chat_bubble_3" = "So it doesn't collect my personal information or my conversation metadata? How does it work?";
"Scan the QR code of the open group you'd like to join" = "Scan the QR code of the open group you'd like to join"; "view_fake_chat_bubble_4" = "Using a combination of advanced anonymous routing and end-to-end encryption technologies.";
"Join Open Group" = "Join Open Group"; "view_fake_chat_bubble_5" = "Friends don't let friends use compromised messengers. You're welcome.";
"Enter an open group URL" = "Enter an open group URL";
"Invalid URL" = "Invalid URL"; "vc_register_title" = "Say hello to your Session ID";
"Please check the URL you entered and try again" = "Please check the URL you entered and try again"; "vc_register_explanation" = "Your Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design.";
"Couldn't Join" = "Couldn't Join"; "vc_register_public_key_copied_message" = "Copied to clipboard";
"Settings" = "Settings";
"Privacy" = "Privacy"; "vc_restore_title" = "Restore your account";
"Notifications" = "Notifications"; "vc_restore_explanation" = "Enter the recovery phrase that was given to you when you signed up to restore your account.";
"Devices" = "Devices"; "vc_restore_seed_text_field_hint" = "Enter your recovery phrase";
"Recovery Phrase" = "Recovery Phrase";
"Clear All Data" = "Clear All Data"; "vc_link_device_title" = "Link Device";
"This will permanently delete your messages, sessions, and contacts." = "This will permanently delete your messages, sessions, and contacts."; "vc_link_device_enter_session_id_tab_title" = "Enter Session ID";
"Delete" = "Delete"; "vc_link_device_scan_qr_code_tab_title" = "Scan QR Code";
"This is your recovery phrase. With it, you can restore or migrate your Session ID to a new device." = "This is your recovery phrase. With it, you can restore or migrate your Session ID to a new device."; "vc_link_device_scan_qr_code_explanation" = "Navigate to \"Settings\" > \"Devices\" > \"Link a Device\" on your other device and then scan the QR code that comes up to start the linking process.";
"The information shown in notifications when your phone is locked." = "The information shown in notifications when your phone is locked.";
"Notifications" = "Notifications"; "vc_enter_session_id_title" = "Link your device";
"Back" = "Back"; "vc_enter_session_id_explanation" = "Navigate to \"Settings\" > \"Devices\" > \"Link a Device\" on your other device and then enter your Session ID here to start the linking process.";
"View My QR Code" = "View My QR Code"; "vc_enter_session_id_text_field_hint" = "Enter your Session ID";
"Scan someone's QR code to start a conversation with them" = "Scan someone's QR code to start a conversation with them";
"QR Code" = "QR Code"; "vc_display_name_title_2" = "Pick your display name";
"Scan Me" = "Scan Me"; "vc_display_name_explanation" = "This will be your name when you use Session. It can be your real name, an alias, or anything else you like.";
"This is your QR code. Other users can scan it to start a session with you." = "This is your QR code. Other users can scan it to start a session with you."; "vc_display_name_text_field_hint" = "Enter a display name";
"Privacy" = "Privacy"; "vc_display_name_display_name_missing_error" = "Please pick a display name";
"Unlock Session's screen using Touch ID, Face ID, or your iOS device passcode. You can still receive message notifications while Screen Lock is enabled. Session's notification settings allow you to customize the information that is displayed." = "Unlock Session's screen using Touch ID, Face ID, or your iOS device passcode. You can still receive message notifications while Screen Lock is enabled. Session's notification settings allow you to customize the information that is displayed."; "vc_display_name_display_name_invalid_error" = "Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters";
"Sound" = "Sound"; "vc_display_name_display_name_too_long_error" = "Please pick a shorter display name";
"Content" = "Content";
"Update Profile Picture" = "Update Profile Picture"; "vc_pn_mode_title" = "Push Notifications";
"Couldn't Update Profile Picture" = "Couldn't Update Profile Picture"; "vc_pn_mode_explanation" = "There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose.";
"Clear" = "Clear"; "vc_pn_mode_apns_option_title" = "Apple Push Notification Service";
"Enter a display name" = "Enter a display name"; "vc_pn_mode_apns_option_explanation" = "Session will use the Apple Push Notification Service service to receive push notifications. You'll be notified of new messages reliably and immediately. Using APNs means that your IP address and device token will be exposed to Apple. If you use push notifications for other apps, this will already be the case. Your IP address and device token will also be exposed to Loki, but your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private.";
"Your Session begins here..." = "Your Session begins here..."; "vc_pn_mode_background_polling_option_title" = "Background Polling";
"What's Session?" = "What's Session?"; "vc_pn_mode_background_polling_option_explanation" = "Session will occasionally check for new messages in the background. This guarantees full metadata protection, but message notifications may be significantly delayed.";
"It's a decentralized, encrypted messaging app." = "It's a decentralized, encrypted messaging app."; "vc_pn_mode_recommended_option_tag" = "Recommended";
"So it doesn't collect my personal information or my conversation metadata? How does it work?" = "So it doesn't collect my personal information or my conversation metadata? How does it work?"; "vc_pn_mode_no_option_picked_modal_title" = "Please Pick an Option";
"Using a combination of advanced anonymous routing and end-to-end encryption technologies." = "Using a combination of advanced anonymous routing and end-to-end encryption technologies.";
"Friends don't let friends use compromised messengers. You're welcome." = "Friends don't let friends use compromised messengers. You're welcome."; "vc_home_empty_state_message" = "You don't have any contacts yet";
"Create Session ID" = "Create Session ID"; "vc_home_empty_state_button_title" = "Start a Session";
"Continue your Session" = "Continue your Session"; "vc_home_leave_group_modal_message" = "Are you sure you want to leave this group?";
"Say hello to your Session ID" = "Say hello to your Session ID"; "vc_home_leaving_group_failed_message" = "Couldn't leave group";
"Continue" = "Continue"; "vc_home_delete_conversation_modal_message" = "Are you sure you want to delete this conversation?";
"Copy Session ID" = "Copy Session ID"; "vc_home_conversation_deleted_message" = "Conversation deleted";
"Pick your display name" = "Pick your display name";
"Enter a display name" = "Enter a display name"; "sheet_pn_mode_title" = "Push Notifications";
"Restore your account" = "Restore your account"; "sheet_pn_mode_explanation" = "Session now features two ways to handle push notifications. Make sure to read the descriptions carefully before you choose.";
"Enter your recovery phrase" = "Enter your recovery phrase"; "sheet_pn_mode_apns_option_title" = "Apple Push Notification Service";
"Message" = "Message"; "sheet_pn_mode_apns_option_explanation" = "Session will use the Apple Push Notification Service service to receive push notifications. You'll be notified of new messages reliably and immediately. Using APNs means that your IP address and device token will be exposed to Apple. If you use push notifications for other apps, this will already be the case. Your IP address and device token will also be exposed to Loki, but your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private.";
"You" = "You"; "sheet_pn_mode_background_polling_option_title" = "Background Polling";
"Encrypting message" = "Encrypting message"; "sheet_pn_mode_background_polling_option_explanation" = "Session will occasionally check for new messages in the background. This guarantees full metadata protection, but message notifications may be significantly delayed.";
"Tracing a path" = "Tracing a path"; "sheet_pn_mode_recommended_option_tag" = "Recommended";
"Sending message" = "Sending message"; "sheet_pn_mode_no_option_picked_modal_title" = "Please Pick an Option";
"Message sent securely" = "Message sent securely"; "sheet_pn_mode_confirm_button_title" = "Confirm";
"Message failed to send" = "Message failed to send"; "sheet_pn_mode_skip_button_title" = "Skip";
"Secure your account by saving your recovery phrase" = "Secure your account by saving your recovery phrase";
"Continue" = "Continue"; "vc_seed_title" = "Your Recovery Phrase";
"Your Recovery Phrase" = "Your Recovery Phrase"; "vc_seed_title_2" = "Meet your recovery phrase";
"Meet your recovery phrase" = "Meet your recovery phrase"; "vc_seed_explanation" = "Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and dont give it to anyone.";
"Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and dont give it to anyone." = "Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and dont give it to anyone."; "vc_seed_reveal_button_title" = "Hold to reveal";
"Tap and hold the redacted words to reveal your recovery phrase, then store it safely to secure your Session ID." = "Tap and hold the redacted words to reveal your recovery phrase, then store it safely to secure your Session ID.";
"Hold to reveal" = "Hold to reveal"; "view_seed_reminder_subtitle_1" = "Secure your account by saving your recovery phrase";
"Make sure to store your recovery phrase in a safe place" = "Make sure to store your recovery phrase in a safe place"; "view_seed_reminder_subtitle_2" = "Tap and hold the redacted words to reveal your recovery phrase, then store it safely to secure your Session ID.";
"Link to an existing account" = "Link to an existing account"; "view_seed_reminder_subtitle_3" = "Make sure to store your recovery phrase in a safe place";
"Enter your public key" = "Enter your public key";
"Link to your existing account by going into your in-app settings and clicking \"Devices\"." = "Link to your existing account by going into your in-app settings and clicking \"Devices\"."; "vc_path_title" = "Path";
"Download Session on your other device and tap \"Link to an existing account\" at the bottom of the landing screen. If you have an existing account on your other device already you will have to delete that account first." = "Download Session on your other device and tap \"Link to an existing account\" at the bottom of the landing screen. If you have an existing account on your other device already you will have to delete that account first."; "vc_path_explanation" = "Session hides your IP by bouncing your messages through several Service Nodes in Session's decentralized network. These are the countries your connection is currently being bounced through:";
"Group Settings" = "Group Settings"; "vc_path_device_row_title" = "You";
"Your Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design." = "Your Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design."; "vc_path_guard_node_row_title" = "Entry Node";
"Enter the recovery phrase that was given to you when you signed up to restore your account." = "Enter the recovery phrase that was given to you when you signed up to restore your account."; "vc_path_service_node_row_title" = "Service Node";
"Enter Session ID" = "Enter Session ID"; "vc_path_destination_row_title" = "Destination";
"Link your device" = "Link your device"; "vc_path_learn_more_button_title" = "Learn More";
"Enter your Session ID to start the linking process." = "Enter your Session ID to start the linking process.";
"Enter your Session ID" = "Enter your Session ID"; "vc_create_private_chat_title" = "New Session";
"Recent Chats" = "Recent Chats"; "vc_create_private_chat_enter_session_id_tab_title" = "Enter Session ID";
"Other Chats" = "Other Chats"; "vc_create_private_chat_scan_qr_code_tab_title" = "Scan QR Code";
"See and share when messages are being typed (applies to all sessions)." = "See and share when messages are being typed (applies to all sessions)."; "vc_create_private_chat_scan_qr_code_explanation" = "Scan a users QR code to start a session. QR codes can be found by tapping the QR code icon in account settings.";
"Disable Preview in App Switcher" = "Disable Preview in App Switcher";
"Are you sure? This cannot be undone." = "Are you sure? This cannot be undone."; "vc_enter_public_key_text_field_hint" = "Enter Session ID of recipient";
"When enabled, messages between you and %@ will disappear after they have been seen." = "When enabled, messages between you and %@ will disappear after they have been seen."; "vc_enter_public_key_explanation" = "Users can share their Session ID by going into their account settings and tapping \"Share Session ID\", or by sharing their QR code.";
"This will be your name when you use Session. It can be your real name, an alias, or anything else you like." = "This will be your name when you use Session. It can be your real name, an alias, or anything else you like.";
"Session Out of Sync" = "Session Out of Sync"; "vc_scan_qr_code_camera_access_explanation" = "Session needs camera access to scan QR codes";
"Would you like to restore your session? This can help resolve issues. Your messages will be preserved." = "Would you like to restore your session? This can help resolve issues. Your messages will be preserved."; "vc_scan_qr_code_grant_camera_access_button_title" = "Grant Camera Access";
"Would you like to restore your session with %@? This can help resolve issues. Your messages will be preserved." = "Would you like to restore your session with %@? This can help resolve issues. Your messages will be preserved.";
"Restore" = "Restore"; "vc_create_closed_group_title" = "New Closed Group";
"Dismiss" = "Dismiss"; "vc_create_closed_group_text_field_hint" = "Enter a group name";
"New Closed Group" = "New Closed Group"; "vc_create_closed_group_explanation" = "Closed groups support up to 10 members and provide the same privacy protections as one-on-one sessions.";
"Group Members" = "Group Members"; "vc_create_closed_group_empty_state_message" = "You don't have any contacts yet";
"You don't have any contacts yet" = "You don't have any contacts yet"; "vc_create_closed_group_empty_state_button_title" = "Start a Session";
"Start a Session" = "Start a Session"; "vc_create_closed_group_group_name_missing_error" = "Please enter a group name";
"Enter a group name" = "Enter a group name"; "vc_create_closed_group_group_name_too_long_error" = "Please enter a shorter group name";
"Please enter a group name" = "Please enter a group name"; "vc_create_closed_group_not_enough_group_members_error" = "Please pick at least 2 group members";
"Please enter a shorter group name" = "Please enter a shorter group name"; "vc_create_closed_group_too_many_group_members_error" = "A closed group cannot have more than 10 members";
"Please pick at least 2 group members" = "Please pick at least 2 group members"; "vc_create_closed_group_invalid_session_id_error" = "One of the members of your group has an invalid Session ID";
"Enable Link Previews?" = "Enable Link Previews?";
"You will not have full metadata protection when sending or receiving link previews." = "You will not have full metadata protection when sending or receiving link previews."; "vc_join_public_chat_title" = "Join Open Group";
"Open groups can be joined by anyone and do not provide full privacy protection" = "Open groups can be joined by anyone and do not provide full privacy protection"; "vc_join_public_chat_error" = "Couldn't join group";
"Search GIFs?" = "Search GIFs?"; "vc_join_public_chat_enter_group_url_tab_title" = "Open Group URL";
"You will not have full metadata protection when sending GIFs." = "You will not have full metadata protection when sending GIFs."; "vc_join_public_chat_scan_qr_code_tab_title" = "Scan QR Code";
"The ability to add members to a closed group is coming soon." = "The ability to add members to a closed group is coming soon."; "vc_join_public_chat_scan_qr_code_explanation" = "Scan the QR code of the open group you'd like to join";
"A closed group cannot have more than 10 members" = "A closed group cannot have more than 10 members";
"A closed group cannot have more than 50 members" = "A closed group cannot have more than 50 members"; "vc_enter_chat_url_text_field_hint" = "Enter an open group URL";
"Closed groups support up to 10 members" = "Closed groups support up to 10 members"; "vc_enter_chat_url_privacy_warning" = "Open groups can be joined by anyone and do not provide full privacy protection";
"Closed groups support up to 50 members" = "Closed groups support up to 50 members";
"No messages yet" = "No messages yet"; "vc_settings_title" = "Settings";
"Would you like to join the Session Public Chat?" = "Would you like to join the Session Public Chat?"; "vc_settings_display_name_text_field_hint" = "Enter a display name";
"Join Public Chat" = "Join Public Chat"; "vc_settings_display_name_missing_error" = "Please pick a display name";
"No, thank you" = "No, thank you"; "vc_settings_invalid_display_name_error" = "Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters";
"Report" = "Report"; "vc_settings_display_name_too_long_error" = "Please pick a shorter display name";
"Please Pick an Option" = "Please Pick an Option"; "vc_settings_privacy_button_title" = "Privacy";
"There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose." = "There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose."; "vc_settings_notifications_button_title" = "Notifications";
"Apple Push Notification Service" = "Apple Push Notification Service"; "vc_settings_chats_button_title" = "Chats";
"Session will use the Apple Push Notification service to receive push notifications. You'll be notified of new messages reliably and immediately. Using APNs means that your IP address and device token will be exposed to Apple. If you use push notifications for other apps, this will already be the case. Your IP address and device token will also be exposed to Loki, but your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private." = "Session will use the Apple Push Notification service to receive push notifications. You'll be notified of new messages reliably and immediately. Using APNs means that your IP address and device token will be exposed to Apple. If you use push notifications for other apps, this will already be the case. Your IP address and device token will also be exposed to Loki, but your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private."; "vc_settings_devices_button_title" = "Devices";
"Background Polling" = "Background Polling"; "vc_settings_recovery_phrase_button_title" = "Recovery Phrase";
"Session will occasionally check for new messages in the background. This guarantees full metadata protection, but message notifications may be significantly delayed." = "Session will occasionally check for new messages in the background. This guarantees full metadata protection, but message notifications may be significantly delayed."; "vc_settings_clear_all_data_button_title" = "Clear Data";
"Use APNs" = "Use APNs";
"Recommended" = "Recommended"; "vc_notification_settings_title" = "Notifications";
"Notification Strategy" = "Notification Strategy"; "vc_notification_settings_style_section_title" = "Notification Style";
"Session now features two ways to handle push notifications. Make sure to read the descriptions carefully before you choose." = "Session now features two ways to handle push notifications. Make sure to read the descriptions carefully before you choose."; "vc_notification_settings_content_section_title" = "Notification Content";
"Push Notifications" = "Push Notifications";
"Confirm" = "Confirm"; "vc_privacy_settings_title" = "Privacy";
"Skip" = "Skip";
"Link Previews" = "Link Previews"; "vc_chat_settings_title" = "Chats";
"Invalid Session ID" = "Invalid Session ID";
"Please make sure the Session ID you entered is correct and try again." = "Please make sure the Session ID you entered is correct and try again."; "vc_linked_devices_title" = "Devices";
"Device Linking Failed" = "Device Linking Failed"; "vc_linked_devices_multi_device_limit_reached_modal_title" = "Device Limit Reached";
"Please check your internet connection and try again" = "Please check your internet connection and try again"; "vc_linked_devices_multi_device_limit_reached_modal_explanation" = "It's currently not allowed to link more than one device.";
"Authorizing Device Link" = "Authorizing Device Link"; "vc_linked_devices_unlinking_failed_message" = "Couldn't unlink device.";
"Please wait while the device link is created. This can take up to a minute." = "Please wait while the device link is created. This can take up to a minute."; "vc_linked_devices_unlinking_successful_message" = "Your device was unlinked successfully";
"Path" = "Path"; "vc_linked_devices_linking_failed_message" = "Couldn't link device.";
"Session hides your IP by bouncing your messages through several Service Nodes in Sessions decentralized network. These are the countries your connection is currently being bounced through:" = "Session hides your IP by bouncing your messages through several Service Nodes in Sessions decentralized network. These are the countries your connection is currently being bounced through:"; "vc_linked_devices_empty_state_message" = "You haven't linked any devices yet";
"Entry Node" = "Entry Node"; "vc_linked_devices_empty_state_button_title" = "Link a Device (Beta)";
"Service Node" = "Service Node";
"You" = "You"; "preferences_notifications_strategy_category_title" = "Notification Strategy";
"Destination" = "Destination"; "preferences_notifications_use_apns_option_title" = "Use APNs";
"Learn More" = "Learn More"; "preferences_notifications_use_apns_option_explanation" = "Using Apple Push Notification Service allows for more reliable push notifications, but exposes your IP and device token to Apple and Loki.";
"Please ask the open group operator to add you to the group." = "Please ask the open group operator to add you to the group.";
"Unauthorized" = "Unauthorized"; "modal_link_device_slave_mode_title_1" = "Waiting for Authorization";
"Closed group created" = "Closed group created"; "modal_link_device_slave_mode_title_2" = "Device Link Authorized";
"Couldn't Create Group" = "Couldn't Create Group"; "modal_link_device_slave_mode_explanation_1" = "Please check that the words below match those shown on your other device.";
"Please check your internet connection and try again." = "Please check your internet connection and try again."; "modal_link_device_slave_mode_explanation_2" = "Your device has been linked successfully";
"modal_link_device_master_mode_title_1" = "Waiting for Device";
"modal_link_device_master_mode_title_2" = "Linking Request Received";
"modal_link_device_master_mode_title_3" = "Authorizing Device Link";
"modal_link_device_master_mode_explanation_1" = "Download Session on your other device and tap \"Link to an existing account\" at the bottom of the landing screen. If you have an existing account on your other device already you will have to delete that account first.";
"modal_link_device_master_mode_explanation_2" = "Please check that the words below match those shown on your other device.";
"modal_link_device_master_mode_explanation_3" = "Please wait while the device link is created. This can take up to a minute.";
"modal_link_device_master_mode_authorize_button_title" = "Authorize";
"vc_device_list_bottom_sheet_change_name_button_title" = "Change name";
"vc_device_list_bottom_sheet_unlink_device_button_title" = "Unlink device";
"modal_edit_device_name_text_field_hint" = "Enter a name";
"modal_seed_title" = "Your Recovery Phrase";
"modal_seed_explanation" = "This is your recovery phrase. With it, you can restore or migrate your Session ID to a new device.";
"modal_clear_all_data_title" = "Clear All Data";
"modal_clear_all_data_explanation" = "This will permanently delete your messages, sessions, and contacts.";
"vc_qr_code_title" = "QR Code";
"vc_qr_code_view_my_qr_code_tab_title" = "View My QR Code";
"vc_qr_code_view_scan_qr_code_tab_title" = "Scan QR Code";
"vc_qr_code_view_scan_qr_code_explanation" = "Scan someone's QR code to start a conversation with them";
"vc_view_my_qr_code_explanation" = "This is your QR code. Other users can scan it to start a session with you.";
"vc_view_my_qr_code_share_title" = "Share QR Code";
"view_friend_request_accept_button_title" = "Accept";
"view_friend_request_reject_button_title" = "Decline";
"view_friend_request_incoming_pending_message" = "%@ sent you a session request";
"view_friend_request_incoming_accepted_message" = "You've accepted %@'s session request";
"view_friend_request_incoming_declined_message" = "You've declined %@'s session request";
"view_friend_request_incoming_expired_message" = "%@'s session request has expired";
"view_friend_request_outgoing_pending_message" = "You've sent %@ a session request";
"view_friend_request_outgoing_accepted_message" = "%@ accepted your session request";
"view_friend_request_outgoing_expired_message" = "Your session request to %@ has expired";
"session_reset_banner_message" = "Would you like to restore your session with %@?";
"session_reset_banner_dismiss_button_title" = "Dismiss";
"session_reset_banner_restore_button_title" = "Restore";
"vc_contact_selection_contacts_title" = "Contacts";
"vc_contact_selection_closed_groups_title" = "Closed Groups";
"vc_contact_selection_open_groups_title" = "Open Groups";

View File

@ -201,7 +201,7 @@ NS_ASSUME_NONNULL_BEGIN
// Existing threads are listed first, ordered by most recently active // Existing threads are listed first, ordered by most recently active
OWSTableSection *recentChatsSection = [OWSTableSection new]; OWSTableSection *recentChatsSection = [OWSTableSection new];
recentChatsSection.headerTitle = NSLocalizedString(@"Recent Chats", @""); recentChatsSection.headerTitle = NSLocalizedString(@"SELECT_THREAD_TABLE_RECENT_CHATS_TITLE", @"");
for (TSThread *thread in [self filteredThreadsWithSearchText]) { for (TSThread *thread in [self filteredThreadsWithSearchText]) {
[recentChatsSection [recentChatsSection
addItem:[OWSTableItem addItem:[OWSTableItem
@ -277,7 +277,7 @@ NS_ASSUME_NONNULL_BEGIN
// Contacts who don't yet have a thread are listed last // Contacts who don't yet have a thread are listed last
OWSTableSection *otherContactsSection = [OWSTableSection new]; OWSTableSection *otherContactsSection = [OWSTableSection new];
otherContactsSection.headerTitle = NSLocalizedString(@"Other Chats", @""); otherContactsSection.headerTitle = @"Other Chats";
NSArray<SignalAccount *> *filteredSignalAccounts = [self filteredSignalAccountsWithSearchText]; NSArray<SignalAccount *> *filteredSignalAccounts = [self filteredSignalAccountsWithSearchText];
for (SignalAccount *signalAccount in filteredSignalAccounts) { for (SignalAccount *signalAccount in filteredSignalAccounts) {
[otherContactsSection [otherContactsSection