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 {
if threadViewModel.threadRecord.isNoteToSelf() {
return NSLocalizedString("Note to Self", comment: "")
return NSLocalizedString("NOTE_TO_SELF", comment: "")
} else {
let hexEncodedPublicKey = threadViewModel.contactIdentifier!
return UserDisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey) ?? hexEncodedPublicKey

View File

@ -8,11 +8,11 @@ final class FakeChatView : UIView {
}
private lazy var chatBubbles = [
getChatBubble(withText: NSLocalizedString("What's Session?", comment: ""), wasSentByCurrentUser: true),
getChatBubble(withText: NSLocalizedString("It's a decentralized, encrypted messaging app.", 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("Using a combination of advanced anonymous routing and end-to-end encryption technologies.", 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_1", comment: ""), wasSentByCurrentUser: true),
getChatBubble(withText: NSLocalizedString("view_fake_chat_bubble_2", comment: ""), wasSentByCurrentUser: false),
getChatBubble(withText: NSLocalizedString("view_fake_chat_bubble_3", comment: ""), wasSentByCurrentUser: true),
getChatBubble(withText: NSLocalizedString("view_fake_chat_bubble_4", comment: ""), wasSentByCurrentUser: false),
getChatBubble(withText: NSLocalizedString("view_fake_chat_bubble_5", comment: ""), wasSentByCurrentUser: false)
]
private lazy var scrollView: UIScrollView = {

View File

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

View File

@ -56,7 +56,7 @@ final class SeedReminderView : UIView {
labelStackView.spacing = 4
// Set up button
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.addTarget(self, action: #selector(handleContinueButtonTapped), for: UIControl.Event.touchUpInside)
// Set up content stack view

View File

@ -29,7 +29,7 @@ final class SessionRestorationView : UIView {
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize)
titleLabel.text = NSLocalizedString("Session Out of Sync", comment: "")
titleLabel.text = "Session Out of Sync"
titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.textAlignment = .center
@ -37,7 +37,7 @@ final class SessionRestorationView : UIView {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
@ -48,7 +48,7 @@ final class SessionRestorationView : UIView {
restoreButton.backgroundColor = Colors.accent
restoreButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
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)
// Set up dismiss button
let dismissButton = UIButton()
@ -57,7 +57,7 @@ final class SessionRestorationView : UIView {
dismissButton.backgroundColor = Colors.buttonBackground
dismissButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
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)
// Set up button stack view
let buttonStackView = UIStackView(arrangedSubviews: [ dismissButton, restoreButton ])
@ -73,7 +73,7 @@ final class SessionRestorationView : UIView {
// Update explanation label if possible
if let contactID = thread.contactIdentifier() {
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 }
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)
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)
}
}

View File

@ -76,7 +76,7 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
result.backgroundColor = Colors.accent
result.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
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
}()
@ -130,14 +130,14 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
}
titleLabel.text = {
switch mode {
case .master: return NSLocalizedString("Waiting for Device", comment: "")
case .slave: return NSLocalizedString("Waiting for Authorization", comment: "")
case .master: return NSLocalizedString("modal_link_device_master_mode_title_1", comment: "")
case .slave: return NSLocalizedString("modal_link_device_slave_mode_title_1", comment: "")
}
}()
subtitleLabel.text = {
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 .slave: return NSLocalizedString("Please check that the words below match those shown on your other device", comment: "")
case .master: return NSLocalizedString("modal_link_device_master_mode_explanation_1", comment: "")
case .slave: return NSLocalizedString("modal_link_device_slave_mode_explanation_1", comment: "")
}
}()
mnemonicLabel.isHidden = (mode == .master)
@ -157,8 +157,8 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
func requestUserAuthorization(for deviceLink: DeviceLink) {
self.deviceLink = deviceLink
qrCodeImageViewContainer.isHidden = true
titleLabel.text = NSLocalizedString("Linking Request Received", comment: "")
subtitleLabel.text = NSLocalizedString("Please check that the words below match those shown on your other device", comment: "")
titleLabel.text = NSLocalizedString("modal_link_device_master_mode_title_2", comment: "")
subtitleLabel.text = NSLocalizedString("modal_link_device_master_mode_explanation_2", comment: "")
let hexEncodedPublicKey = deviceLink.slave.publicKey.removing05PrefixIfNeeded()
mnemonicLabel.text = Mnemonic.hash(hexEncodedString: hexEncodedPublicKey)
mnemonicLabel.isHidden = false
@ -172,8 +172,8 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
mainStackView.insertArrangedSubview(spinner, at: 0)
spinner.set(.height, to: 64)
spinner.startAnimating()
titleLabel.text = NSLocalizedString("Authorizing Device Link", comment: "")
subtitleLabel.text = NSLocalizedString("Please wait while the device link is created. This can take up to a minute.", comment: "")
titleLabel.text = NSLocalizedString("modal_link_device_master_mode_title_3", comment: "")
subtitleLabel.text = NSLocalizedString("modal_link_device_master_mode_explanation_3", comment: "")
mnemonicLabel.isHidden = true
buttonStackView.isHidden = true
let deviceLink = self.deviceLink!
@ -204,7 +204,7 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
let _ = FileServerAPI.removeDeviceLink(signedDeviceLink) // Attempt to roll back
DispatchQueue.main.async {
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))
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).")
DispatchQueue.main.async {
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))
self?.presentingViewController?.present(alert, animated: true, completion: nil)
}
@ -225,8 +225,8 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
session.stopListeningForLinkingAuthorization()
spinner.stopAnimating()
spinner.isHidden = true
titleLabel.text = NSLocalizedString("Device Link Authorized", comment: "")
subtitleLabel.text = NSLocalizedString("Your device has been linked successfully", comment: "")
titleLabel.text = NSLocalizedString("modal_link_device_slave_mode_title_2", comment: "")
subtitleLabel.text = NSLocalizedString("modal_link_device_slave_mode_explanation_2", comment: "")
mnemonicLabel.isHidden = true
buttonStackView.isHidden = true
FileServerAPI.addDeviceLink(deviceLink).catch { error in

View File

@ -23,9 +23,9 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping
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)
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.set(.width, to: 180)
let result = UIStackView(arrangedSubviews: [ explanationLabel, linkNewDeviceButton ])
@ -40,7 +40,7 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Devices", comment: ""))
setNavBarTitle(NSLocalizedString("vc_linked_devices_title", comment: ""))
// Set up link new device button
let linkNewDeviceButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(linkNewDevice))
linkNewDeviceButton.tintColor = Colors.text
@ -108,7 +108,7 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
deviceLinkingModal.modalTransitionStyle = .crossDissolve
present(deviceLinkingModal, animated: true, completion: nil)
} 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))
present(alert, animated: true, completion: nil)
}
@ -118,7 +118,7 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
defer { tableView.deselectRow(at: indexPath, animated: true) }
let deviceLink = deviceLinks[indexPath.row]
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 }
let deviceNameModal = DeviceNameModal()
deviceNameModal.device = deviceLink.other
@ -127,10 +127,10 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
deviceNameModal.modalTransitionStyle = .crossDissolve
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)
})
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)
}
@ -166,7 +166,7 @@ final class DeviceLinksVC : BaseVC, UITableViewDataSource, UITableViewDelegate,
})
self?.updateDeviceLinks()
}.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))
self?.present(alert, animated: true, completion: nil)
}

View File

@ -10,7 +10,7 @@ final class DeviceNameModal : Modal {
result.textColor = Colors.text
result.font = .systemFont(ofSize: Values.mediumFontSize)
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))
result.attributedPlaceholder = placeholder
result.tintColor = Colors.accent
@ -31,7 +31,7 @@ final class DeviceNameModal : Modal {
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize)
titleLabel.text = NSLocalizedString("Change Device Name", comment: "")
titleLabel.text = "Change Device Name"
titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.textAlignment = .center
@ -39,7 +39,7 @@ final class DeviceNameModal : Modal {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
@ -95,7 +95,7 @@ final class DeviceNameModal : Modal {
UserDefaults.standard[.slaveDeviceName(device.publicKey)] = name
delegate?.handleDeviceNameChanged(to: name, for: device)
} 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))
present(alert, animated: true, completion: nil)
}

View File

@ -7,7 +7,7 @@ final class DisplayNameVC : BaseVC {
// MARK: Components
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
return result
}()
@ -22,14 +22,14 @@ final class DisplayNameVC : BaseVC {
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Set up spacers
@ -43,7 +43,7 @@ final class DisplayNameVC : BaseVC {
registerButtonBottomOffsetConstraint = registerButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
// Set up register button
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.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
// Set up register button container
@ -128,15 +128,15 @@ final class DisplayNameVC : BaseVC {
}
let displayName = displayNameTextField.text!.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
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 hasInvalidCharacters = !displayName.allSatisfy { $0.unicodeScalars.allSatisfy { allowedCharacters.contains($0) } }
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 {
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
let pnModeVC = PNModeVC()

View File

@ -35,12 +35,12 @@ final class GroupMembersVC : BaseVC, UITableViewDataSource {
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Group Members", comment: ""))
setNavBarTitle("Group Members")
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping

View File

@ -30,7 +30,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
let attributedTitle = NSMutableAttributedString(string: title)
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "80%"))
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.delegate = self
return result
@ -70,9 +70,9 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping
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)
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.set(.width, to: 180)
let result = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ])
@ -91,7 +91,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
setUpNavBarStyle()
}
updateNavigationBarButtons()
setNavBarTitle(NSLocalizedString("Messages", comment: ""))
setNavBarTitle("Messages")
// Set up seed reminder view if needed
let userDefaults = UserDefaults.standard
let hasViewedSeed = userDefaults[.hasViewedSeed]
@ -372,7 +372,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
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)
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_DELETE_TITLE", comment: ""), style: .destructive) { _ in
try! Storage.writeSync { transaction in

View File

@ -8,11 +8,11 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
// MARK: Components
private lazy var tabBar: TabBar = {
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 }
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 }
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 = {
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)
result.delegate = self
return result
@ -44,7 +44,7 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Join Open Group", comment: ""))
setNavBarTitle(NSLocalizedString("vc_join_public_chat_title", comment: ""))
let navigationBar = navigationController!.navigationBar
// Set up navigation bar buttons
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) {
guard !isJoining else { return }
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
let channelID: UInt64 = 1
@ -154,11 +154,11 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
}
.catch(on: DispatchQueue.main) { [weak self] error in
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
var title = NSLocalizedString("Couldn't Join", comment: "")
var title = "Couldn't Join"
var message = ""
if case HTTP.Error.httpRequestFailed(let statusCode, _) = error, statusCode == 401 || statusCode == 403 {
title = NSLocalizedString("Unauthorized", comment: "")
message = NSLocalizedString("Please ask the open group operator to add you to the group.", comment: "")
title = "Unauthorized"
message = "Please ask the open group operator to add you to the group."
}
self?.isJoining = false
self?.showError(title: title, message: message)
@ -180,7 +180,7 @@ private final class EnterChatURLVC : UIViewController {
// MARK: Components
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.autocapitalizationType = .none
return result
@ -194,13 +194,13 @@ private final class EnterChatURLVC : UIViewController {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
// Next button
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)
let nextButtonContainer = UIView()
nextButtonContainer.addSubview(nextButton)
@ -280,7 +280,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
@ -288,7 +288,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let callToActionButton = UIButton()
callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
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)
// Set up stack view
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ])

View File

@ -11,7 +11,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
private lazy var registerButton: Button = {
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.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
return result
@ -19,7 +19,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
private lazy var restoreButton: Button = {
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.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside)
return result
@ -27,7 +27,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
private lazy var linkButton: Button = {
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.addTarget(self, action: #selector(linkDevice), for: UIControl.Event.touchUpInside)
return result
@ -43,7 +43,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// 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
// Show device unlinked alert if needed
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))
present(alert, animated: true, completion: nil)
UserDefaults.removeAll()
@ -128,7 +128,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
// MARK: Device Linking
func requestDeviceLink(with hexEncodedPublicKey: String) {
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))
return present(alert, animated: true, completion: nil)
}

View File

@ -8,11 +8,11 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon
// MARK: Components
private lazy var tabBar: TabBar = {
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 }
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 }
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 = {
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)
result.delegate = self
return result
@ -44,7 +44,7 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Link Device", comment: ""))
setNavBarTitle(NSLocalizedString("vc_link_device_title", comment: ""))
let navigationBar = navigationController!.navigationBar
// Set up navigation bar buttons
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
private lazy var publicKeyTextField: TextField = {
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 {
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()
titleLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Link button
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.addTarget(self, action: #selector(requestDeviceLink), for: UIControl.Event.touchUpInside)
let linkButtonContainer = UIView()
@ -270,7 +270,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
@ -278,7 +278,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let callToActionButton = UIButton()
callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
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)
// Set up stack view
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ])

View File

@ -24,7 +24,7 @@ class Modal : BaseVC {
result.backgroundColor = Colors.buttonBackground
result.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
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
}()

View File

@ -30,7 +30,7 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
}()
// 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 = {
let result = UITableView()
@ -49,7 +49,7 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
setUpGradientBackground()
setUpNavBarStyle()
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
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text
@ -89,9 +89,9 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping
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)
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.set(.width, to: 180)
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, createNewPrivateChatButton ])
@ -120,7 +120,7 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
// MARK: Interaction
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) {
@ -166,16 +166,16 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
presentAlert(alert)
}
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 {
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 {
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
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
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)
}.catch(on: DispatchQueue.main) { _ in
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
let title = NSLocalizedString("Couldn't Create Group", comment: "")
let message = NSLocalizedString("Please check your internet connection and try again.", comment: "")
let title = "Couldn't Create Group"
let message = "Please check your internet connection and try again."
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
self?.presentAlert(alert)
@ -206,16 +206,16 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
presentAlert(alert)
}
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 {
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 {
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
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 storage = OWSPrimaryStorage.shared()

View File

@ -7,11 +7,11 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
// MARK: Components
private lazy var tabBar: TabBar = {
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 }
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 }
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 = {
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)
result.delegate = self
return result
@ -43,7 +43,7 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("New Session", comment: ""))
setNavBarTitle(NSLocalizedString("vc_create_private_chat_title", comment: ""))
let navigationBar = navigationController!.navigationBar
// Set up navigation bar buttons
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) {
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))
presentAlert(alert)
} else {
@ -149,11 +149,11 @@ private final class EnterPublicKeyVC : UIViewController {
}()
// 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 = {
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)
return result
}()
@ -166,12 +166,12 @@ private final class EnterPublicKeyVC : UIViewController {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
// 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
let userPublicKeyLabel = UILabel()
userPublicKeyLabel.textColor = Colors.text
@ -182,7 +182,7 @@ private final class EnterPublicKeyVC : UIViewController {
userPublicKeyLabel.text = userHexEncodedPublicKey
// Set up share button
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)
// Set up button container
let buttonContainer = UIStackView(arrangedSubviews: [ copyButton, shareButton ])
@ -191,7 +191,7 @@ private final class EnterPublicKeyVC : UIViewController {
buttonContainer.distribution = .fillEqually
// Next button
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)
let nextButtonContainer = UIView()
nextButtonContainer.addSubview(nextButton)
@ -226,7 +226,7 @@ private final class EnterPublicKeyVC : UIViewController {
@objc private func enableCopyButton() {
copyButton.isUserInteractionEnabled = true
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)
}
@ -235,7 +235,7 @@ private final class EnterPublicKeyVC : UIViewController {
UIPasteboard.general.string = userHexEncodedPublicKey
copyButton.isUserInteractionEnabled = false
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)
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()
explanationLabel.textColor = Colors.text
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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
@ -269,7 +269,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let callToActionButton = UIButton()
callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
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)
// Set up stack view
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ])

View File

@ -8,7 +8,7 @@ final class NukeDataModal : Modal {
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
titleLabel.textAlignment = .center
@ -16,7 +16,7 @@ final class NukeDataModal : Modal {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
@ -29,7 +29,7 @@ final class NukeDataModal : Modal {
}
nukeDataButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
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)
// Set up button stack view
let buttonStackView = UIStackView(arrangedSubviews: [ cancelButton, nukeDataButton ])

View File

@ -11,8 +11,8 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
}
// 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 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 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("vc_pn_mode_background_polling_option_title", comment: ""), explanation: NSLocalizedString("vc_pn_mode_background_polling_option_explanation", comment: ""), delegate: self)
// MARK: Lifecycle
override func viewDidLoad() {
@ -24,14 +24,14 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Set up spacers
@ -41,7 +41,7 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
registerButtonBottomOffsetSpacer.set(.height, to: isIPhone5OrSmaller ? CGFloat(16) : Values.onboardingButtonBottomOffset)
// Set up register button
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.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
// Set up register button container
@ -75,7 +75,7 @@ final class PNModeVC : BaseVC, OptionViewDelegate {
@objc private func register() {
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)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
return present(alert, animated: true, completion: nil)

View File

@ -19,7 +19,7 @@ final class PathVC : BaseVC {
private lazy var learnMoreButton: Button = {
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)
return result
}()
@ -35,7 +35,7 @@ final class PathVC : BaseVC {
private func setUpNavBar() {
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Path", comment: ""))
setNavBarTitle(NSLocalizedString("vc_path_title", comment: ""))
// Set up close button
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text
@ -47,7 +47,7 @@ final class PathVC : BaseVC {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
@ -113,8 +113,8 @@ final class PathVC : BaseVC {
let isGuardSnode = (snode == pathToDisplay.first!)
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 destinationRow = getPathRow(title: NSLocalizedString("Destination", comment: ""), subtitle: nil, location: .bottom, dotAnimationStartDelay: Double(pathToDisplay.count) + 2, 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("vc_path_destination_row_title", comment: ""), subtitle: nil, location: .bottom, dotAnimationStartDelay: Double(pathToDisplay.count) + 2, dotAnimationRepeatInterval: dotAnimationRepeatInterval)
let rows = [ youRow ] + snodeRows + [ destinationRow ]
rows.forEach { pathStackView.addArrangedSubview($0) }
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 {
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)
}

View File

@ -8,11 +8,11 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
// MARK: Components
private lazy var tabBar: TabBar = {
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 }
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 }
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 = {
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)
result.delegate = self
return result
@ -44,7 +44,7 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("QR Code", comment: ""))
setNavBarTitle(NSLocalizedString("vc_qr_code_title", comment: ""))
let navigationBar = navigationController!.navigationBar
// Set up page VC
let hasCameraAccess = (AVCaptureDevice.authorizationStatus(for: .video) == .authorized)
@ -122,7 +122,7 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
fileprivate func startNewPrivateChatIfPossible(with hexEncodedPublicKey: String) {
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))
presentAlert(alert)
} else {
@ -153,7 +153,7 @@ private final class ViewMyQRCodeVC : UIViewController {
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? CGFloat(40) : Values.massiveFontSize)
titleLabel.text = NSLocalizedString("Scan Me", comment: "")
titleLabel.text = "Scan Me"
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .center
titleLabel.lineBreakMode = .byWordWrapping
@ -178,13 +178,13 @@ private final class ViewMyQRCodeVC : UIViewController {
// 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"))
// 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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
// Set up share button
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)
// Set up share button container
let shareButtonContainer = UIView()
@ -232,7 +232,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
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.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
@ -240,7 +240,7 @@ private final class ScanQRCodePlaceholderVC : UIViewController {
let callToActionButton = UIButton()
callToActionButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
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)
// Set up stack view
let stackView = UIStackView(arrangedSubviews: [ explanationLabel, callToActionButton ])

View File

@ -15,7 +15,7 @@ final class RegisterVC : BaseVC {
private lazy var copyPublicKeyButton: Button = {
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.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
return result
@ -47,14 +47,14 @@ final class RegisterVC : BaseVC {
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Set up public key label container
@ -69,7 +69,7 @@ final class RegisterVC : BaseVC {
let bottomSpacer = UIView.vStretchingSpacer()
// Set up register button
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.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
// Set up button stack view
@ -123,7 +123,7 @@ final class RegisterVC : BaseVC {
@objc private func enableCopyButton() {
copyPublicKeyButton.isUserInteractionEnabled = true
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)
}
@ -180,7 +180,7 @@ final class RegisterVC : BaseVC {
UIPasteboard.general.string = keyPair.hexEncodedPublicKey
copyPublicKeyButton.isUserInteractionEnabled = false
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)
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
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
return result
}()
@ -39,14 +39,14 @@ final class RestoreVC : BaseVC {
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Set up legal label
@ -66,7 +66,7 @@ final class RestoreVC : BaseVC {
restoreButtonBottomOffsetConstraint = restoreButtonBottomOffsetSpacer.set(.height, to: Values.onboardingButtonBottomOffset)
// Set up restore button
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.addTarget(self, action: #selector(restore), for: UIControl.Event.touchUpInside)
// Set up restore button container

View File

@ -18,7 +18,7 @@ final class SeedModal : Modal {
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.mediumFontSize)
titleLabel.text = NSLocalizedString("Your Recovery Phrase", comment: "")
titleLabel.text = NSLocalizedString("modal_seed_title", comment: "")
titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.textAlignment = .center
@ -34,7 +34,7 @@ final class SeedModal : Modal {
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
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.lineBreakMode = .byWordWrapping
explanationLabel.textAlignment = .center
@ -45,7 +45,7 @@ final class SeedModal : Modal {
copyButton.backgroundColor = Colors.buttonBackground
copyButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
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)
// Set up button stack view
let buttonStackView = UIStackView(arrangedSubviews: [ cancelButton, copyButton ])

View File

@ -30,7 +30,7 @@ final class SeedVC : BaseVC {
let attributedTitle = NSMutableAttributedString(string: title)
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "90%"))
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)
return result
}()
@ -47,7 +47,7 @@ final class SeedVC : BaseVC {
private lazy var copyButton: Button = {
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)
return result
}()
@ -57,7 +57,7 @@ final class SeedVC : BaseVC {
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Your Recovery Phrase", comment: ""))
setNavBarTitle(NSLocalizedString("vc_seed_title", comment: ""))
// Set up navigation bar buttons
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text
@ -66,14 +66,14 @@ final class SeedVC : BaseVC {
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
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.lineBreakMode = .byWordWrapping
// Set up mnemonic label
@ -93,7 +93,7 @@ final class SeedVC : BaseVC {
let callToActionLabel = UILabel()
callToActionLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
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
let callToActionLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
callToActionLabel.addGestureRecognizer(callToActionLabelGestureRecognizer)
@ -144,7 +144,7 @@ final class SeedVC : BaseVC {
@objc private func enableCopyButton() {
copyButton.isUserInteractionEnabled = true
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)
}
@ -164,7 +164,7 @@ final class SeedVC : BaseVC {
self.seedReminderView.title = attributedTitle
}, completion: nil)
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)
seedReminderView.setProgress(1, animated: true)
UserDefaults.standard[.hasViewedSeed] = true
@ -176,7 +176,7 @@ final class SeedVC : BaseVC {
UIPasteboard.general.string = mnemonic
copyButton.isUserInteractionEnabled = false
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)
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 = {
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
return result
}()
private lazy var copyButton: Button = {
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)
return result
}()
@ -55,9 +55,9 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("Settings", comment: ""))
setNavBarTitle(NSLocalizedString("vc_settings_title", comment: ""))
// 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
navigationItem.backBarButtonItem = backButton
updateNavigationBarButtons()
@ -84,7 +84,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
headerStackView.spacing = Values.smallSpacing
headerStackView.alignment = .center
// 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
let publicKeyLabel = UILabel()
publicKeyLabel.textColor = Colors.text
@ -95,7 +95,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
publicKeyLabel.text = userHexEncodedPublicKey
// Set up share button
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)
// Set up button container
let buttonContainer = UIStackView(arrangedSubviews: [ copyButton, shareButton ])
@ -161,19 +161,19 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
}
var result = [
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(),
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
if isMasterDevice {
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(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(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())
return result
}
@ -182,12 +182,12 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
@objc private func enableCopyButton() {
copyButton.isUserInteractionEnabled = true
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)
}
func avatarActionSheetTitle() -> String? {
return NSLocalizedString("Update Profile Picture", comment: "")
return "Update Profile Picture"
}
func fromViewController() -> UIViewController {
@ -199,7 +199,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
}
func clearAvatarActionLabel() -> String {
return NSLocalizedString("Clear", comment: "")
return "Clear"
}
// MARK: Updating
@ -266,8 +266,8 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
if let error = error as? DotNetAPI.DotNetAPIError {
isMaxFileSizeExceeded = (error == .maxFileSizeExceeded)
}
let title = isMaxFileSizeExceeded ? "Maximum File Size Exceeded" : NSLocalizedString("Couldn't Update Profile", comment: "")
let message = isMaxFileSizeExceeded ? "Please select a smaller photo and try again" : NSLocalizedString("Please check your internet connection and try again", comment: "")
let title = isMaxFileSizeExceeded ? "Maximum File Size Exceeded" : "Couldn't Update Profile"
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)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: 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)
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 hasInvalidCharacters = !displayName.allSatisfy { $0.unicodeScalars.allSatisfy { allowedCharacters.contains($0) } }
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 {
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
displayNameToBeUploaded = displayName
@ -326,7 +326,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
UIPasteboard.general.string = userHexEncodedPublicKey
copyButton.isUserInteractionEnabled = false
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)
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false)
}

View File

@ -36,13 +36,13 @@
// Loki: Customize title
UILabel *titleLabel = [UILabel new];
titleLabel.text = NSLocalizedString(@"Notifications", @"");
titleLabel.text = NSLocalizedString(@"vc_notification_settings_title", @"");
titleLabel.textColor = LKColors.text;
titleLabel.font = [UIFont boldSystemFontOfSize:LKValues.veryLargeFontSize];
self.navigationItem.titleView = titleLabel;
// 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;
self.navigationItem.backBarButtonItem = backButton;
}
@ -65,8 +65,8 @@
OWSPreferences *prefs = Environment.shared.preferences;
OWSTableSection *strategySection = [OWSTableSection new];
strategySection.headerTitle = NSLocalizedString(@"Notification Strategy", @"");
[strategySection addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"Use APNs", @"")
strategySection.headerTitle = NSLocalizedString(@"preferences_notifications_strategy_category_title", @"");
[strategySection addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"preferences_notifications_use_apns_option_title", @"")
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"push_notification_strategy")
isOnBlock:^{
return [NSUserDefaults.standardUserDefaults boolForKey:@"isUsingFullAPNs"];
@ -76,7 +76,7 @@
}
target:weakSelf
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];
// Sounds section.

View File

@ -43,7 +43,7 @@ static NSString *const kSealedSenderInfoURL = @"https://signal.org/blog/sealed-s
// Loki: Customize title
UILabel *titleLabel = [UILabel new];
titleLabel.text = NSLocalizedString(@"Privacy", @"");
titleLabel.text = NSLocalizedString(@"vc_privacy_settings_title", @"");
titleLabel.textColor = LKColors.text;
titleLabel.font = [UIFont boldSystemFontOfSize:LKValues.veryLargeFontSize];
self.navigationItem.titleView = titleLabel;
@ -538,11 +538,11 @@ static NSString *const kSealedSenderInfoURL = @"https://signal.org/blog/sealed-s
{
BOOL isOn = sender.isOn;
if (isOn) {
NSString *title = NSLocalizedString(@"Enable Link Previews?", @"");
NSString *message = NSLocalizedString(@"You will not have full metadata protection when sending or receiving link previews.", @"");
NSString *title = @"Enable 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];
[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];
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
"Messages" = "Messages";
"Note to Self" = "Note to Self";
"New Group" = "New Group";
"Delete" = "Delete";
"Search" = "Search";
"New Session" = "New Session";
"Enter a Session ID" = "Enter a Session ID";
"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.";
"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.";
"Your Session ID" = "Your Session ID";
"Copy" = "Copy";
"Copied" = "Copied";
"Share" = "Share";
"Next" = "Next";
"Session needs camera access to scan QR codes" = "Session needs camera access to scan QR codes";
"Enable Camera Access" = "Enable Camera Access";
"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";
"Open Group URL" = "Open Group URL";
"Scan QR Code" = "Scan QR Code";
"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";
"Join Open Group" = "Join Open Group";
"Enter an open group URL" = "Enter an open group URL";
"Invalid URL" = "Invalid URL";
"Please check the URL you entered and try again" = "Please check the URL you entered and try again";
"Couldn't Join" = "Couldn't Join";
"Settings" = "Settings";
"Privacy" = "Privacy";
"Notifications" = "Notifications";
"Devices" = "Devices";
"Recovery Phrase" = "Recovery Phrase";
"Clear All Data" = "Clear All Data";
"This will permanently delete your messages, sessions, and contacts." = "This will permanently delete your messages, sessions, and contacts.";
"Delete" = "Delete";
"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.";
"The information shown in notifications when your phone is locked." = "The information shown in notifications when your phone is locked.";
"Notifications" = "Notifications";
"Back" = "Back";
"View My QR Code" = "View My QR Code";
"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";
"Scan Me" = "Scan Me";
"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.";
"Privacy" = "Privacy";
"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.";
"Sound" = "Sound";
"Content" = "Content";
"Update Profile Picture" = "Update Profile Picture";
"Couldn't Update Profile Picture" = "Couldn't Update Profile Picture";
"Clear" = "Clear";
"Enter a display name" = "Enter a display name";
"Your Session begins here..." = "Your Session begins here...";
"What's Session?" = "What's Session?";
"It's a decentralized, encrypted messaging app." = "It's a decentralized, encrypted messaging app.";
"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?";
"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.";
"Create Session ID" = "Create Session ID";
"Continue your Session" = "Continue your Session";
"Say hello to your Session ID" = "Say hello to your Session ID";
"Continue" = "Continue";
"Copy Session ID" = "Copy Session ID";
"Pick your display name" = "Pick your display name";
"Enter a display name" = "Enter a display name";
"Restore your account" = "Restore your account";
"Enter your recovery phrase" = "Enter your recovery phrase";
"Message" = "Message";
"You" = "You";
"Encrypting message" = "Encrypting message";
"Tracing a path" = "Tracing a path";
"Sending message" = "Sending message";
"Message sent securely" = "Message sent securely";
"Message failed to send" = "Message failed to send";
"Secure your account by saving your recovery phrase" = "Secure your account by saving your recovery phrase";
"Continue" = "Continue";
"Your Recovery Phrase" = "Your Recovery Phrase";
"Meet your recovery phrase" = "Meet your recovery phrase";
"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.";
"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";
"Make sure to store your recovery phrase in a safe place" = "Make sure to store your recovery phrase in a safe place";
"Link to an existing account" = "Link to an existing account";
"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\".";
"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.";
"Group Settings" = "Group Settings";
"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.";
"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.";
"Enter Session ID" = "Enter Session ID";
"Link your device" = "Link your device";
"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";
"Recent Chats" = "Recent Chats";
"Other Chats" = "Other Chats";
"See and share when messages are being typed (applies to all sessions)." = "See and share when messages are being typed (applies to all sessions).";
"Disable Preview in App Switcher" = "Disable Preview in App Switcher";
"Are you sure? This cannot be undone." = "Are you sure? This cannot be undone.";
"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.";
"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";
"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.";
"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";
"Dismiss" = "Dismiss";
"New Closed Group" = "New Closed Group";
"Group Members" = "Group Members";
"You don't have any contacts yet" = "You don't have any contacts yet";
"Start a Session" = "Start a Session";
"Enter a group name" = "Enter a group name";
"Please enter a group name" = "Please enter a group name";
"Please enter a shorter group name" = "Please enter a shorter group name";
"Please pick at least 2 group members" = "Please pick at least 2 group members";
"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.";
"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";
"Search GIFs?" = "Search GIFs?";
"You will not have full metadata protection when sending GIFs." = "You will not have full metadata protection when sending GIFs.";
"The ability to add members to a closed group is coming soon." = "The ability to add members to a closed group is coming soon.";
"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";
"Closed groups support up to 10 members" = "Closed groups support up to 10 members";
"Closed groups support up to 50 members" = "Closed groups support up to 50 members";
"No messages yet" = "No messages yet";
"Would you like to join the Session Public Chat?" = "Would you like to join the Session Public Chat?";
"Join Public Chat" = "Join Public Chat";
"No, thank you" = "No, thank you";
"Report" = "Report";
"Please Pick an Option" = "Please Pick an Option";
"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.";
"Apple Push Notification Service" = "Apple Push Notification Service";
"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.";
"Background Polling" = "Background Polling";
"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.";
"Use APNs" = "Use APNs";
"Recommended" = "Recommended";
"Notification Strategy" = "Notification Strategy";
"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.";
"Push Notifications" = "Push Notifications";
"Confirm" = "Confirm";
"Skip" = "Skip";
"Link Previews" = "Link Previews";
"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.";
"Device Linking Failed" = "Device Linking Failed";
"Please check your internet connection and try again" = "Please check your internet connection and try again";
"Authorizing Device Link" = "Authorizing Device Link";
"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.";
"Path" = "Path";
"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:";
"Entry Node" = "Entry Node";
"Service Node" = "Service Node";
"You" = "You";
"Destination" = "Destination";
"Learn More" = "Learn More";
"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";
"Closed group created" = "Closed group created";
"Couldn't Create Group" = "Couldn't Create Group";
"Please check your internet connection and try again." = "Please check your internet connection and try again.";
"continue_2" = "Continue";
"copy" = "Copy";
"invalid_url" = "Invalid URL";
"copied_to_clipboard" = "Copied to clipboard";
"device_linking_failed" = "Couldn't link device.";
"next" = "Next";
"share" = "Share";
"invalid_session_id" = "Invalid Session ID";
"cancel" = "Cancel";
"your_session_id" = "Your Session ID";
"vc_landing_title_2" = "Your Session begins here...";
"vc_landing_register_button_title" = "Create Session ID";
"vc_landing_restore_button_title" = "Continue Your Session";
"vc_landing_link_button_title" = "Link to an existing account";
"vc_landing_device_unlinked_modal_title" = "Your device was unlinked successfully";
"view_fake_chat_bubble_1" = "What's Session?";
"view_fake_chat_bubble_2" = "It's a decentralized, encrypted messaging app";
"view_fake_chat_bubble_3" = "So it doesn't collect my personal information or my conversation metadata? How does it work?";
"view_fake_chat_bubble_4" = "Using a combination of advanced anonymous routing and end-to-end encryption technologies.";
"view_fake_chat_bubble_5" = "Friends don't let friends use compromised messengers. You're welcome.";
"vc_register_title" = "Say hello to your Session ID";
"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.";
"vc_register_public_key_copied_message" = "Copied to clipboard";
"vc_restore_title" = "Restore your account";
"vc_restore_explanation" = "Enter the recovery phrase that was given to you when you signed up to restore your account.";
"vc_restore_seed_text_field_hint" = "Enter your recovery phrase";
"vc_link_device_title" = "Link Device";
"vc_link_device_enter_session_id_tab_title" = "Enter Session ID";
"vc_link_device_scan_qr_code_tab_title" = "Scan QR Code";
"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.";
"vc_enter_session_id_title" = "Link your device";
"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.";
"vc_enter_session_id_text_field_hint" = "Enter your Session ID";
"vc_display_name_title_2" = "Pick your display name";
"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.";
"vc_display_name_text_field_hint" = "Enter a display name";
"vc_display_name_display_name_missing_error" = "Please pick a display name";
"vc_display_name_display_name_invalid_error" = "Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters";
"vc_display_name_display_name_too_long_error" = "Please pick a shorter display name";
"vc_pn_mode_title" = "Push Notifications";
"vc_pn_mode_explanation" = "There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose.";
"vc_pn_mode_apns_option_title" = "Apple Push Notification Service";
"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.";
"vc_pn_mode_background_polling_option_title" = "Background Polling";
"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.";
"vc_pn_mode_recommended_option_tag" = "Recommended";
"vc_pn_mode_no_option_picked_modal_title" = "Please Pick an Option";
"vc_home_empty_state_message" = "You don't have any contacts yet";
"vc_home_empty_state_button_title" = "Start a Session";
"vc_home_leave_group_modal_message" = "Are you sure you want to leave this group?";
"vc_home_leaving_group_failed_message" = "Couldn't leave group";
"vc_home_delete_conversation_modal_message" = "Are you sure you want to delete this conversation?";
"vc_home_conversation_deleted_message" = "Conversation deleted";
"sheet_pn_mode_title" = "Push Notifications";
"sheet_pn_mode_explanation" = "Session now features two ways to handle push notifications. Make sure to read the descriptions carefully before you choose.";
"sheet_pn_mode_apns_option_title" = "Apple Push Notification Service";
"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.";
"sheet_pn_mode_background_polling_option_title" = "Background Polling";
"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.";
"sheet_pn_mode_recommended_option_tag" = "Recommended";
"sheet_pn_mode_no_option_picked_modal_title" = "Please Pick an Option";
"sheet_pn_mode_confirm_button_title" = "Confirm";
"sheet_pn_mode_skip_button_title" = "Skip";
"vc_seed_title" = "Your Recovery Phrase";
"vc_seed_title_2" = "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.";
"vc_seed_reveal_button_title" = "Hold to reveal";
"view_seed_reminder_subtitle_1" = "Secure your account by saving your recovery phrase";
"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.";
"view_seed_reminder_subtitle_3" = "Make sure to store your recovery phrase in a safe place";
"vc_path_title" = "Path";
"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:";
"vc_path_device_row_title" = "You";
"vc_path_guard_node_row_title" = "Entry Node";
"vc_path_service_node_row_title" = "Service Node";
"vc_path_destination_row_title" = "Destination";
"vc_path_learn_more_button_title" = "Learn More";
"vc_create_private_chat_title" = "New Session";
"vc_create_private_chat_enter_session_id_tab_title" = "Enter Session ID";
"vc_create_private_chat_scan_qr_code_tab_title" = "Scan QR Code";
"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.";
"vc_enter_public_key_text_field_hint" = "Enter Session ID of recipient";
"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.";
"vc_scan_qr_code_camera_access_explanation" = "Session needs camera access to scan QR codes";
"vc_scan_qr_code_grant_camera_access_button_title" = "Grant Camera Access";
"vc_create_closed_group_title" = "New Closed Group";
"vc_create_closed_group_text_field_hint" = "Enter a group name";
"vc_create_closed_group_explanation" = "Closed groups support up to 10 members and provide the same privacy protections as one-on-one sessions.";
"vc_create_closed_group_empty_state_message" = "You don't have any contacts yet";
"vc_create_closed_group_empty_state_button_title" = "Start a Session";
"vc_create_closed_group_group_name_missing_error" = "Please enter a group name";
"vc_create_closed_group_group_name_too_long_error" = "Please enter a shorter group name";
"vc_create_closed_group_not_enough_group_members_error" = "Please pick at least 2 group members";
"vc_create_closed_group_too_many_group_members_error" = "A closed group cannot have more than 10 members";
"vc_create_closed_group_invalid_session_id_error" = "One of the members of your group has an invalid Session ID";
"vc_join_public_chat_title" = "Join Open Group";
"vc_join_public_chat_error" = "Couldn't join group";
"vc_join_public_chat_enter_group_url_tab_title" = "Open Group URL";
"vc_join_public_chat_scan_qr_code_tab_title" = "Scan QR Code";
"vc_join_public_chat_scan_qr_code_explanation" = "Scan the QR code of the open group you'd like to join";
"vc_enter_chat_url_text_field_hint" = "Enter an open group URL";
"vc_enter_chat_url_privacy_warning" = "Open groups can be joined by anyone and do not provide full privacy protection";
"vc_settings_title" = "Settings";
"vc_settings_display_name_text_field_hint" = "Enter a display name";
"vc_settings_display_name_missing_error" = "Please pick a display name";
"vc_settings_invalid_display_name_error" = "Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters";
"vc_settings_display_name_too_long_error" = "Please pick a shorter display name";
"vc_settings_privacy_button_title" = "Privacy";
"vc_settings_notifications_button_title" = "Notifications";
"vc_settings_chats_button_title" = "Chats";
"vc_settings_devices_button_title" = "Devices";
"vc_settings_recovery_phrase_button_title" = "Recovery Phrase";
"vc_settings_clear_all_data_button_title" = "Clear Data";
"vc_notification_settings_title" = "Notifications";
"vc_notification_settings_style_section_title" = "Notification Style";
"vc_notification_settings_content_section_title" = "Notification Content";
"vc_privacy_settings_title" = "Privacy";
"vc_chat_settings_title" = "Chats";
"vc_linked_devices_title" = "Devices";
"vc_linked_devices_multi_device_limit_reached_modal_title" = "Device Limit Reached";
"vc_linked_devices_multi_device_limit_reached_modal_explanation" = "It's currently not allowed to link more than one device.";
"vc_linked_devices_unlinking_failed_message" = "Couldn't unlink device.";
"vc_linked_devices_unlinking_successful_message" = "Your device was unlinked successfully";
"vc_linked_devices_linking_failed_message" = "Couldn't link device.";
"vc_linked_devices_empty_state_message" = "You haven't linked any devices yet";
"vc_linked_devices_empty_state_button_title" = "Link a Device (Beta)";
"preferences_notifications_strategy_category_title" = "Notification Strategy";
"preferences_notifications_use_apns_option_title" = "Use APNs";
"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.";
"modal_link_device_slave_mode_title_1" = "Waiting for Authorization";
"modal_link_device_slave_mode_title_2" = "Device Link Authorized";
"modal_link_device_slave_mode_explanation_1" = "Please check that the words below match those shown on your other device.";
"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
OWSTableSection *recentChatsSection = [OWSTableSection new];
recentChatsSection.headerTitle = NSLocalizedString(@"Recent Chats", @"");
recentChatsSection.headerTitle = NSLocalizedString(@"SELECT_THREAD_TABLE_RECENT_CHATS_TITLE", @"");
for (TSThread *thread in [self filteredThreadsWithSearchText]) {
[recentChatsSection
addItem:[OWSTableItem
@ -277,7 +277,7 @@ NS_ASSUME_NONNULL_BEGIN
// Contacts who don't yet have a thread are listed last
OWSTableSection *otherContactsSection = [OWSTableSection new];
otherContactsSection.headerTitle = NSLocalizedString(@"Other Chats", @"");
otherContactsSection.headerTitle = @"Other Chats";
NSArray<SignalAccount *> *filteredSignalAccounts = [self filteredSignalAccountsWithSearchText];
for (SignalAccount *signalAccount in filteredSignalAccounts) {
[otherContactsSection