Commit 5d0363b6 authored by Tijmen de Mes's avatar Tijmen de Mes

Allow python strings to be translated

parent d0a4a70d
......@@ -3,7 +3,7 @@ from PyQt5 import uic
from blink import __date__, __version__
from blink.resources import Resources
from blink.util import QSingleton
from blink.util import QSingleton, translate
__all__ = ['AboutPanel']
......@@ -41,7 +41,7 @@ class AboutPanel(base_class, ui_class, metaclass=QSingleton):
with Resources.directory:
self.setupUi(self)
self.version.setText('Version %s\n%s' % (__version__, __date__))
self.version.setText(translate('about_panel', 'Version %s\n%s') % (__version__, __date__))
credits_width = self.credits_text.fontMetrics().width("NLnet Foundation" + "http://sipsimpleclient.org") + 40
self.credits_text.setFixedWidth(credits_width)
......
......@@ -32,7 +32,7 @@ from blink.contacts import URIUtils
from blink.resources import ApplicationData, IconManager, Resources
from blink.sessions import SessionManager, StreamDescription
from blink.widgets.labels import Status
from blink.util import QSingleton, call_in_gui_thread, run_in_gui_thread
from blink.util import QSingleton, call_in_gui_thread, run_in_gui_thread, translate
__all__ = ['AccountModel', 'ActiveAccountModel', 'AccountSelector', 'AddAccountDialog', 'ServerToolsAccountModel', 'ServerToolsWindow']
......@@ -379,7 +379,7 @@ class AddAccountDialog(base_class, ui_class, metaclass=QSingleton):
self.accept()
else:
self.setEnabled(False)
self.create_status_label.value = Status('Creating account on server...')
self.create_status_label.value = Status(translate('add_account_dialog', 'Creating account on server...'))
self._create_sip_account(self.username, self.password, self.email_address, self.display_name)
def _SH_PanelChangeRequest(self, index):
......@@ -397,28 +397,28 @@ class AddAccountDialog(base_class, ui_class, metaclass=QSingleton):
red = '#cc0000'
# validate the add panel
if not self.display_name_editor.text_valid:
self.add_status_label.value = Status("Display name cannot be empty", color=red)
self.add_status_label.value = Status(translate('add_account_dialog', "Display name cannot be empty"), color=red)
elif not self.sip_address_editor.text_correct:
self.add_status_label.value = Status("SIP address should be specified as user@domain", color=red)
self.add_status_label.value = Status(translate('add_account_dialog', "SIP address should be specified as user@domain"), color=red)
elif not self.sip_address_editor.text_allowed:
self.add_status_label.value = Status("An account with this SIP address was already added", color=red)
self.add_status_label.value = Status(translate('add_account_dialog', "An account with this SIP address was already added"), color=red)
elif not self.password_editor.text_valid:
self.add_status_label.value = Status("Password cannot be empty", color=red)
self.add_status_label.value = Status(translate('add_account_dialog', "Password cannot be empty"), color=red)
else:
self.add_status_label.value = None
# validate the create panel
if not self.name_editor.text_valid:
self.create_status_label.value = Status("Name cannot be empty", color=red)
self.create_status_label.value = Status(translate('add_account_dialog', "Name cannot be empty"), color=red)
elif not self.username_editor.text_correct:
self.create_status_label.value = Status("Username should have 5 to 32 characters, start with a letter or non-zero digit, contain only letters, digits or .-_ and end with a letter or digit", color=red)
self.create_status_label.value = Status(translate('add_account_dialog', "Username should have 5 to 32 characters, start with a letter or non-zero digit, contain only letters, digits or .-_ and end with a letter or digit"), color=red)
elif not self.username_editor.text_allowed:
self.create_status_label.value = Status("The username you requested is already taken. Please choose another one and try again.", color=red)
self.create_status_label.value = Status(translate('add_account_dialog', "The username you requested is already taken. Please choose another one and try again."), color=red)
elif not self.new_password_editor.text_valid:
self.create_status_label.value = Status("Password should contain at least 8 characters", color=red)
self.create_status_label.value = Status(translate('add_account_dialog', "Password should contain at least 8 characters"), color=red)
elif not self.verify_password_editor.text_valid:
self.create_status_label.value = Status("Passwords do not match", color=red)
self.create_status_label.value = Status(translate('add_account_dialog', "Passwords do not match"), color=red)
elif not self.email_address_editor.text_valid:
self.create_status_label.value = Status("E-mail address should be specified as user@domain", color=red)
self.create_status_label.value = Status(translate('add_account_dialog', "E-mail address should be specified as user@domain"), color=red)
else:
self.create_status_label.value = None
# enable the accept button if everything is valid in the current panel
......@@ -488,9 +488,9 @@ class AddAccountDialog(base_class, ui_class, metaclass=QSingleton):
else:
call_in_gui_thread(setattr, self.create_status_label, 'value', Status(response_data['error_message'], color=red))
except (json.decoder.JSONDecodeError, KeyError):
call_in_gui_thread(setattr, self.create_status_label, 'value', Status('Illegal server response', color=red))
call_in_gui_thread(setattr, self.create_status_label, 'value', Status(translate('add_account_dialog', 'Illegal server response'), color=red))
except urllib.error.URLError as e:
call_in_gui_thread(setattr, self.create_status_label, 'value', Status('Failed to contact server: %s' % e.reason, color=red))
call_in_gui_thread(setattr, self.create_status_label, 'value', Status(translate('add_account_dialog', 'Failed to contact server: %s') % e.reason, color=red))
finally:
call_in_gui_thread(self.setEnabled, True)
......@@ -783,7 +783,7 @@ class ServerToolsWindow(base_class, ui_class, metaclass=QSingleton):
self._update_navigation_buttons()
def _SH_WebViewTitleChanged(self, title):
self.window().setWindowTitle('Blink Server Tools: {}'.format(title))
self.window().setWindowTitle(translate('server_window', 'Blink Server Tools: {}').format(title))
def _SH_ModelChanged(self, parent_index, start, end):
menu = self.account_button.menu()
......
......@@ -44,7 +44,7 @@ from blink.history import HistoryManager
from blink.messages import MessageManager, BlinkMessage
from blink.resources import IconManager, Resources
from blink.sessions import ChatSessionModel, ChatSessionListView, SessionManager, StreamDescription
from blink.util import run_in_gui_thread, call_later
from blink.util import run_in_gui_thread, call_later, translate
from blink.widgets.color import ColorHelperMixin
from blink.widgets.graph import Graph
from blink.widgets.otr import OTRWidget
......@@ -894,7 +894,7 @@ class ChatWidget(base_class, ui_class):
image_data = base64.b64encode(data).decode()
self.send_message(image_data, content_type=match.group('type'))
except Exception as e:
self.add_message(ChatStatus('Error sending image: %s' % str(e)))
self.add_message(ChatStatus(translate('chat_window', 'Error sending image: %s') % str(e)))
else:
account = self.session.blink_session.account
content = '''<img src="{}" class="scaled-to-fit" />'''.format(text)
......@@ -964,7 +964,7 @@ class ChatWidget(base_class, ui_class):
try:
msg_id = self.send_message(text, content_type='text/html', id=id)
except Exception as e:
self.add_message(ChatStatus('Error sending message: %s' % e)) # decide what type to use here. -Dan
self.add_message(ChatStatus(translate('chat_window', 'Error sending message: %s') % e)) # decide what type to use here. -Dan
else:
if msg_id is not None:
id = msg_id
......@@ -996,7 +996,7 @@ class ChatWidget(base_class, ui_class):
def _SH_OTRTimerTimeout(self):
self.otr_timer.stop()
self.add_message(ChatStatus('Timeout in enabling OTR, recipient did not answer to OTR encryption request')) # decide what type to use here. -Dan
self.add_message(ChatStatus(translate('chat_window', 'Timeout in enabling OTR, recipient did not answer to OTR encryption request'))) # decide what type to use here. -Dan
NotificationCenter().post_notification('ChatStreamOTRTimeout', self.session.blink_session)
@run_in_gui_thread
......@@ -1146,7 +1146,7 @@ class VideoWidget(VideoSurface, ui_class):
self.close_button.setIcon(close_icon)
self.screenshot_button_menu = QMenu(self)
self.screenshot_button_menu.addAction('Open screenshots folder', self._SH_ScreenshotsFolderActionTriggered)
self.screenshot_button_menu.addAction(translate('chat_window', 'Open screenshots folder'), self._SH_ScreenshotsFolderActionTriggered)
@property
def interactive(self):
......@@ -1572,7 +1572,7 @@ class NoSessionsLabel(QLabel):
self.setFont(font)
self.setAlignment(Qt.AlignCenter)
self.setStyleSheet("""QLabel { border: 1px inset palette(dark); border-radius: 3px; background-color: white; color: #545454; }""")
self.setText("No Sessions")
self.setText(translate('chat_window', "No Sessions"))
chat_window.session_panel.installEventFilter(self)
def eventFilter(self, watched, event):
......@@ -1735,23 +1735,23 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.control_menu = QMenu(self.control_button)
self.control_button.setMenu(self.control_menu)
self.control_button.actions = ContextMenuActions()
self.control_button.actions.connect = QAction("Start real time chat session", self, triggered=self._AH_Connect)
self.control_button.actions.connect_with_audio = QAction("Start audio call", self, triggered=self._AH_ConnectWithAudio)
self.control_button.actions.connect_with_video = QAction("Start video call", self, triggered=self._AH_ConnectWithVideo)
self.control_button.actions.disconnect = QAction("Disconnect", self, triggered=self._AH_Disconnect)
self.control_button.actions.add_audio = QAction("Add audio", self, triggered=self._AH_AddAudio)
self.control_button.actions.remove_audio = QAction("Remove audio", self, triggered=self._AH_RemoveAudio)
self.control_button.actions.add_video = QAction("Add video", self, triggered=self._AH_AddVideo)
self.control_button.actions.remove_video = QAction("Remove video", self, triggered=self._AH_RemoveVideo)
self.control_button.actions.add_chat = QAction("Add real time chat", self, triggered=self._AH_AddChat)
self.control_button.actions.remove_chat = QAction("Remove real time chat", self, triggered=self._AH_RemoveChat)
self.control_button.actions.share_my_screen = QAction("Share my screen", self, triggered=self._AH_ShareMyScreen)
self.control_button.actions.request_screen = QAction("Request screen", self, triggered=self._AH_RequestScreen)
self.control_button.actions.end_screen_sharing = QAction("End screen sharing", self, triggered=self._AH_EndScreenSharing)
self.control_button.actions.enable_otr = QAction("Enable OTR for messaging", self, triggered=self._AH_EnableOTR)
self.control_button.actions.enable_otr_progress = QAction("Enabling OTR for messaging", self, enabled=False)
self.control_button.actions.disable_otr = QAction("Disable OTR for messaging", self, triggered=self._AH_DisableOTR)
self.control_button.actions.main_window = QAction("Main Window", self, triggered=self._AH_MainWindow, shortcut='Ctrl+B', shortcutContext=Qt.ApplicationShortcut)
self.control_button.actions.connect = QAction(translate('chat_window', "Start real time chat session"), self, triggered=self._AH_Connect)
self.control_button.actions.connect_with_audio = QAction(translate('chat_window', "Start audio call"), self, triggered=self._AH_ConnectWithAudio)
self.control_button.actions.connect_with_video = QAction(translate('chat_window', "Start video call"), self, triggered=self._AH_ConnectWithVideo)
self.control_button.actions.disconnect = QAction(translate('chat_window', "Disconnect"), self, triggered=self._AH_Disconnect)
self.control_button.actions.add_audio = QAction(translate('chat_window', "Add audio"), self, triggered=self._AH_AddAudio)
self.control_button.actions.remove_audio = QAction(translate('chat_window', "Remove audio"), self, triggered=self._AH_RemoveAudio)
self.control_button.actions.add_video = QAction(translate('chat_window', "Add video"), self, triggered=self._AH_AddVideo)
self.control_button.actions.remove_video = QAction(translate('chat_window', "Remove video"), self, triggered=self._AH_RemoveVideo)
self.control_button.actions.add_chat = QAction(translate('chat_window', "Add real time chat"), self, triggered=self._AH_AddChat)
self.control_button.actions.remove_chat = QAction(translate('chat_window', "Remove real time chat"), self, triggered=self._AH_RemoveChat)
self.control_button.actions.share_my_screen = QAction(translate('chat_window', "Share my screen"), self, triggered=self._AH_ShareMyScreen)
self.control_button.actions.request_screen = QAction(translate('chat_window', "Request screen"), self, triggered=self._AH_RequestScreen)
self.control_button.actions.end_screen_sharing = QAction(translate('chat_window', "End screen sharing"), self, triggered=self._AH_EndScreenSharing)
self.control_button.actions.enable_otr = QAction(translate('chat_window', "Enable OTR for messaging"), self, triggered=self._AH_EnableOTR)
self.control_button.actions.enable_otr_progress = QAction(translate('chat_window', "Enabling OTR for messaging"), self, enabled=False)
self.control_button.actions.disable_otr = QAction(translate('chat_window', "Disable OTR for messaging"), self, triggered=self._AH_DisableOTR)
self.control_button.actions.main_window = QAction(translate('chat_window', "Main Window"), self, triggered=self._AH_MainWindow, shortcut='Ctrl+B', shortcutContext=Qt.ApplicationShortcut)
self.addAction(self.control_button.actions.main_window) # make this active even when it's not in the control_button's menu
......@@ -1941,19 +1941,19 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
state = "%s" % blink_session.state
if 'status' in elements and blink_session.state in ('initialized', 'connecting/*', 'connected/*', 'ended'):
state_map = {'initialized': 'Disconnected',
'connecting/dns_lookup': 'Finding destination...',
'connecting': 'Connecting...',
'connecting/ringing': 'Ringing',
'connecting/starting': 'Starting media...',
'connected': 'Connected'}
state_map = {'initialized': translate('chat_window', 'Disconnected'),
'connecting/dns_lookup': translate('chat_window', "Finding destination..."),
'connecting': translate('chat_window', "Connecting..."),
'connecting/ringing': translate('chat_window', "Ringing"),
'connecting/starting': translate('chat_window', "Starting media..."),
'connected': translate('chat_window', "Connected")}
if blink_session.state == 'ended':
self.status_value_label.setForegroundRole(QPalette.AlternateBase if blink_session.state.error else QPalette.WindowText)
self.status_value_label.setText(blink_session.state.reason)
self.chat_value_widget.setEnabled(True)
self.chat_value_label.setText('Using SIP Message')
self.chat_value_label.setText(translate('chat_window', "Using SIP Message"))
if blink_session.chat_type is not None:
self.chat_encryption_label.setVisible(False)
self.chat_connection_label.setVisible(False)
......@@ -1976,31 +1976,31 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
if audio_info.ice_status == 'succeeded':
if 'relay' in {candidate.type.lower() for candidate in (audio_info.local_rtp_candidate, audio_info.remote_rtp_candidate)}:
self.audio_connection_label.setPixmap(self.pixmaps.relay_connection)
self.audio_connection_label.setToolTip('Using relay')
self.audio_connection_label.setToolTip(translate('chat_window', "Using relay"))
else:
self.audio_connection_label.setPixmap(self.pixmaps.direct_connection)
self.audio_connection_label.setToolTip('Peer to peer')
self.audio_connection_label.setToolTip(translate('chat_window', "Peer to peer"))
elif audio_info.ice_status == 'failed':
self.audio_connection_label.setPixmap(self.pixmaps.unknown_connection)
self.audio_connection_label.setToolTip("Couldn't negotiate ICE")
self.audio_connection_label.setToolTip(translate('chat_window', "Couldn't negotiate ICE"))
elif audio_info.ice_status == 'disabled':
if blink_session.contact.type == 'bonjour':
self.audio_connection_label.setPixmap(self.pixmaps.direct_connection)
self.audio_connection_label.setToolTip('Peer to peer')
self.audio_connection_label.setToolTip(translate('chat_window', "Peer to peer"))
else:
self.audio_connection_label.setPixmap(self.pixmaps.unknown_connection)
self.audio_connection_label.setToolTip('ICE is disabled')
self.audio_connection_label.setToolTip(translate('chat_window', "ICE is disabled"))
elif audio_info.ice_status is None:
self.audio_connection_label.setPixmap(self.pixmaps.unknown_connection)
self.audio_connection_label.setToolTip('ICE is unavailable')
self.audio_connection_label.setToolTip(translate('chat_window', "ICE is unavailable"))
else:
self.audio_connection_label.setPixmap(self.pixmaps.unknown_connection)
self.audio_connection_label.setToolTip('Negotiating ICE')
self.audio_connection_label.setToolTip(translate('chat_window', "Negotiating ICE"))
if audio_info.encryption is not None:
self.audio_encryption_label.setToolTip('Media is encrypted using %s (%s)' % (audio_info.encryption, audio_info.encryption_cipher))
self.audio_encryption_label.setToolTip(translate('chat_window', "Media is encrypted using %s (%s)") % (audio_info.encryption, audio_info.encryption_cipher))
else:
self.audio_encryption_label.setToolTip('Media is not encrypted')
self.audio_encryption_label.setToolTip(translate('chat_window', "Media is not encrypted"))
self._update_rtp_encryption_icon(self.audio_encryption_label)
self.audio_connection_label.setVisible(audio_info.remote_address is not None)
......@@ -2010,31 +2010,31 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
if video_info.ice_status == 'succeeded':
if 'relay' in {candidate.type.lower() for candidate in (video_info.local_rtp_candidate, video_info.remote_rtp_candidate)}:
self.video_connection_label.setPixmap(self.pixmaps.relay_connection)
self.video_connection_label.setToolTip('Using relay')
self.video_connection_label.setToolTip(translate('chat_window', "Using relay"))
else:
self.video_connection_label.setPixmap(self.pixmaps.direct_connection)
self.video_connection_label.setToolTip('Peer to peer')
self.video_connection_label.setToolTip(translate('chat_window', "Peer to peer"))
elif video_info.ice_status == 'failed':
self.video_connection_label.setPixmap(self.pixmaps.unknown_connection)
self.video_connection_label.setToolTip("Couldn't negotiate ICE")
elif video_info.ice_status == 'disabled':
if blink_session.contact.type == 'bonjour':
self.video_connection_label.setPixmap(self.pixmaps.direct_connection)
self.video_connection_label.setToolTip('Peer to peer')
self.video_connection_label.setToolTip(translate('chat_window', "Peer to peer"))
else:
self.video_connection_label.setPixmap(self.pixmaps.unknown_connection)
self.video_connection_label.setToolTip('ICE is disabled')
self.video_connection_label.setToolTip(translate('chat_window', "ICE is disabled"))
elif video_info.ice_status is None:
self.video_connection_label.setPixmap(self.pixmaps.unknown_connection)
self.video_connection_label.setToolTip('ICE is unavailable')
self.video_connection_label.setToolTip(translate('chat_window', "ICE is unavailable"))
else:
self.video_connection_label.setPixmap(self.pixmaps.unknown_connection)
self.video_connection_label.setToolTip('Negotiating ICE')
self.video_connection_label.setToolTip(translate('chat_window', "Negotiating ICE"))
if video_info.encryption is not None:
self.video_encryption_label.setToolTip('Media is encrypted using %s (%s)' % (video_info.encryption, video_info.encryption_cipher))
self.video_encryption_label.setToolTip(translate('chat_window', "Media is encrypted using %s (%s)") % (video_info.encryption, video_info.encryption_cipher))
else:
self.video_encryption_label.setToolTip('Media is not encrypted')
self.video_encryption_label.setToolTip(translate('chat_window', "Media is not encrypted"))
self._update_rtp_encryption_icon(self.video_encryption_label)
self.video_connection_label.setVisible(video_info.remote_address is not None)
......@@ -2050,33 +2050,33 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.zrtp_widget.show()
if any(len(path) > 1 for path in (chat_info.full_local_path, chat_info.full_remote_path)):
self.chat_value_label.setText('Using relay')
self.chat_value_label.setText(translate('chat_window', "Using relay"))
self.chat_connection_label.setPixmap(self.pixmaps.relay_connection)
self.chat_connection_label.setToolTip('Using relay')
self.chat_connection_label.setToolTip(translate('chat_window', "Using relay"))
elif chat_info.full_local_path and chat_info.full_remote_path:
self.chat_value_label.setText('Peer to peer')
self.chat_value_label.setText(translate('chat_window', "Peer to peer"))
self.chat_connection_label.setPixmap(self.pixmaps.direct_connection)
self.chat_connection_label.setToolTip('Peer to peer')
self.chat_connection_label.setToolTip(translate('chat_window', "Peer to peer"))
elif blink_session.chat_type is None:
self.chat_value_widget.setEnabled(True)
self.chat_value_label.setText('Using SIP Message')
self.chat_connection_label.setToolTip('Using SIP Message')
self.chat_value_label.setText(translate('chat_window', "Using SIP Message"))
self.chat_connection_label.setToolTip(translate('chat_window', "Using SIP Message"))
else:
self.chat_value_label.setText('N/A')
self.chat_value_label.setText(translate('chat_window', "N/A"))
if chat_info.encryption is not None and chat_info.transport == 'tls':
self.chat_encryption_label.setToolTip('Media is encrypted using TLS and {0.encryption} ({0.encryption_cipher})'.format(chat_info))
self.chat_encryption_label.setToolTip(translate('chat_window', "Media is encrypted using TLS and {0.encryption} ({0.encryption_cipher})").format(chat_info))
elif chat_info.encryption is not None:
self.chat_encryption_label.setToolTip('Media is encrypted using {0.encryption} ({0.encryption_cipher})'.format(chat_info))
self.chat_encryption_label.setToolTip(translate('chat_window', "Media is encrypted using {0.encryption} ({0.encryption_cipher})").format(chat_info))
elif chat_info.transport == 'tls':
self.chat_encryption_label.setToolTip('Media is encrypted using TLS')
self.chat_encryption_label.setToolTip(translate('chat_window', "Media is encrypted using TLS"))
else:
self.chat_encryption_label.setToolTip('Media is not encrypted')
self.chat_encryption_label.setToolTip(translate('chat_window', "Media is not encrypted"))
if messages_info.encryption is not None:
if messages_info.encryption == 'OpenPGP':
self.chat_encryption_label.setToolTip('Media is encrypted using {0.encryption}'.format(messages_info))
self.chat_encryption_label.setToolTip(translate('chat_window', "Media is encrypted using {0.encryption}").format(messages_info))
else:
self.chat_encryption_label.setToolTip('Media is encrypted using {0.encryption} ({0.encryption_cipher})'.format(messages_info))
self.chat_encryption_label.setToolTip(translate('chat_window', "Media is encrypted using {0.encryption} ({0.encryption_cipher})").format(messages_info))
self._update_chat_encryption_icon()
......@@ -2095,20 +2095,20 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.otr_widget.show()
if screen_info.remote_address is not None and screen_info.mode == 'active':
self.screen_value_label.setText('Viewing remote')
self.screen_value_label.setText(translate('chat_window', "Viewing remote"))
elif screen_info.remote_address is not None and screen_info.mode == 'passive':
self.screen_value_label.setText('Sharing local')
self.screen_value_label.setText(translate('chat_window', "Sharing local"))
else:
self.screen_value_label.setText('N/A')
self.screen_value_label.setText(translate('chat_window', "N/A"))
if any(len(path) > 1 for path in (screen_info.full_local_path, screen_info.full_remote_path)):
self.screen_connection_label.setPixmap(self.pixmaps.relay_connection)
self.screen_connection_label.setToolTip('Using relay')
self.screen_connection_label.setToolTip(translate('chat_window', "Using relay"))
elif screen_info.full_local_path and screen_info.full_remote_path:
self.screen_connection_label.setPixmap(self.pixmaps.direct_connection)
self.screen_connection_label.setToolTip('Peer to peer')
self.screen_connection_label.setToolTip(translate('chat_window', "Peer to peer"))
self.screen_encryption_label.setToolTip('Media is encrypted using TLS')
self.screen_encryption_label.setToolTip(translate('chat_window', "Media is encrypted using TLS"))
self.screen_connection_label.setVisible(screen_info.remote_address is not None)
self.screen_encryption_label.setVisible(screen_info.remote_address is not None and screen_info.transport == 'tls')
......@@ -2535,7 +2535,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
session = blink_session.items.chat
if session is None:
return
session.chat_widget.add_message(ChatStatus(f'Delivery failed: {notification.data.data.code} - {notification.data.data.reason}'))
session.chat_widget.add_message(ChatStatus(translate('chat_window', f'Delivery failed: {notification.data.data.code} - {notification.data.data.reason}')))
call_later(.5, session.chat_widget.update_message_status, id=notification.data.id, status='failed')
def _NH_BlinkMessageWillRemove(self, notification):
......@@ -2582,7 +2582,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
session = blink_session.items.chat
if session is None:
return
session.chat_widget.add_message(ChatStatus(f'Decryption failed: {notification.data.error}'))
session.chat_widget.add_message(ChatStatus(translate('chat_window', f'Decryption failed: {notification.data.error}')))
def _NH_MessageStreamPGPKeysDidLoad(self, notification):
stream = notification.sender
......@@ -2761,9 +2761,9 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
return
if notification.data.new_state is OTRState.Encrypted:
session.chat_widget.stop_otr_timer()
session.chat_widget.add_message(ChatStatus('Encryption enabled'))
session.chat_widget.add_message(ChatStatus(translate('chat_window','Encryption enabled')))
elif notification.data.old_state is OTRState.Encrypted:
session.chat_widget.add_message(ChatStatus('Encryption disabled'))
session.chat_widget.add_message(ChatStatus(translate('chat_window', 'Encryption disabled')))
self.otr_widget.hide()
if notification.data.new_state is OTRState.Finished:
session.chat_widget.chat_input.lock(EncryptionLock)
......@@ -2793,7 +2793,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
session = notification.sender.blink_session.items.chat
if session is None:
return
session.chat_widget.add_message(ChatStatus('Failed to initialize chat: %s' % notification.data.reason))
session.chat_widget.add_message(ChatStatus(translate('chat_window', 'Failed to initialize chat: %s') % notification.data.reason))
def _NH_MediaStreamDidStart(self, notification):
if notification.sender.type != 'chat':
......@@ -2810,9 +2810,9 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
if session is None:
return
if notification.data.error is not None:
session.chat_widget.add_message(ChatStatus('Disconnected: %s' % notification.data.error))
session.chat_widget.add_message(ChatStatus(translate('chat_window', 'Disconnected: %s') % notification.data.error))
else:
session.chat_widget.add_message(ChatStatus('Disconnected'))
session.chat_widget.add_message(ChatStatus(translate('chat_window', 'Disconnected')))
# Set type back for message as the stream ended cleanly -- Tijmen
notification.sender.blink_session.chat_type = None
......@@ -2848,10 +2848,10 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.selected_session.active_panel = self.participants_panel
def _SH_LatencyGraphUpdated(self):
self.latency_label.setText('Network Latency: %dms, max=%dms' % (max(self.audio_latency_graph.last_value, self.video_latency_graph.last_value), self.latency_graph.max_value))
self.latency_label.setText(translate('chat_window', 'Network Latency: %dms, max=%dms') % (max(self.audio_latency_graph.last_value, self.video_latency_graph.last_value), self.latency_graph.max_value))
def _SH_PacketLossGraphUpdated(self):
self.packet_loss_label.setText('Packet Loss: %.1f%%, max=%.1f%%' % (max(self.audio_packet_loss_graph.last_value, self.video_packet_loss_graph.last_value), self.packet_loss_graph.max_value))
self.packet_loss_label.setText(translate('chat_window', 'Packet Loss: %.1f%%, max=%.1f%%') % (max(self.audio_packet_loss_graph.last_value, self.video_packet_loss_graph.last_value), self.packet_loss_graph.max_value))
def _SH_TrafficGraphUpdated(self):
blink_settings = BlinkSettings()
......@@ -2861,7 +2861,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
else:
incoming_traffic = TrafficNormalizer.normalize(self.incoming_traffic_graph.last_value * 8, bits_per_second=True)
outgoing_traffic = TrafficNormalizer.normalize(self.outgoing_traffic_graph.last_value * 8, bits_per_second=True)
self.traffic_label.setText("""<p>Traffic: <span style="font-family: sans-serif; color: #d70000;">\u2193</span> %s <span style="font-family: sans-serif; color: #0064d7;">\u2191</span> %s</p>""" % (incoming_traffic, outgoing_traffic))
self.traffic_label.setText(translate('chat_window', """<p>Traffic: <span style="font-family: sans-serif; color: #d70000;">\u2193</span> %s <span style="font-family: sans-serif; color: #0064d7;">\u2191</span> %s</p>""") % (incoming_traffic, outgoing_traffic))
def _SH_MuteButtonClicked(self, checked):
settings = SIPSimpleSettings()
......
......@@ -8,7 +8,7 @@ import sys
from PyQt5 import uic
from PyQt5.QtCore import Qt, QAbstractListModel, QAbstractTableModel, QEasingCurve, QModelIndex, QPropertyAnimation, QSortFilterProxyModel
from PyQt5.QtCore import QByteArray, QEvent, QMimeData, QPointF, QRectF, QRect, QSize, QTimer, QUrl, pyqtSignal
from PyQt5.QtCore import QByteArray, QEvent, QMimeData, QPointF, QRectF, QRect, QSize, QTimer, QUrl, pyqtSignal, QT_TRANSLATE_NOOP
from PyQt5.QtGui import QBrush, QColor, QIcon, QKeyEvent, QLinearGradient, QMouseEvent, QPainter, QPainterPath, QPalette, QPen, QPixmap, QPolygonF
from PyQt5.QtWebKitWidgets import QWebView
from PyQt5.QtWidgets import QAction, QApplication, QItemDelegate, QStyledItemDelegate, QStyle
......@@ -48,7 +48,7 @@ from blink.configuration.datatypes import IconDescriptor, FileURL
from blink.resources import ApplicationData, Resources, IconManager
from blink.sessions import SessionManager, StreamDescription
from blink.messages import MessageManager
from blink.util import call_in_gui_thread, run_in_gui_thread
from blink.util import call_in_gui_thread, run_in_gui_thread, translate
from blink.widgets.buttons import SwitchViewButton
from blink.widgets.color import ColorHelperMixin
from blink.widgets.util import ContextMenuActions
......@@ -643,7 +643,7 @@ class GoogleContact(object):
organization = next((entry.get('name') for entry in contact_data.get('organizations', Null)), None)
icon_url, icon_metadata = next(((entry['url'], entry['metadata']) for entry in contact_data.get('photos', Null)), (None, None))
name = name.strip() if name is not None else 'Unknown'
name = name.strip() if name is not None else translate('contact_list', 'Unknown')
organization = organization.strip() if organization is not None else organization
uris = [GoogleContactURI.from_number(number) for number in contact_data.get('phoneNumbers', Null)]
......@@ -2288,7 +2288,7 @@ class ContactModel(QAbstractListModel):
# The MIME types we accept in drop operations, in the order they should be handled
accepted_mime_types = ['application/x-blink-group-list', 'application/x-blink-contact-list', 'text/uri-list']
# TODO: Maybe translate? -Tijmen
test_contacts = (dict(id='test_call', name='Test Call', preferred_media='audio+chat', uri='echo@conference.sip2sip.info', icon=Resources.get('icons/test-call.png')),
dict(id='test_conference', name='Test Conference', preferred_media='audio+chat', uri='test@conference.sip2sip.info', icon=Resources.get('icons/test-conference.png')))
......@@ -3136,20 +3136,20 @@ class ContactListView(QListView):
self.detail_view.hide()
self.context_menu = QMenu(self)
self.actions = ContextMenuActions()
self.actions.add_group = QAction("Add Group", self, triggered=self._AH_AddGroup)
self.actions.add_contact = QAction("Add Contact", self, triggered=self._AH_AddContact)
self.actions.edit_item = QAction("Edit", self, triggered=self._AH_EditItem)
self.actions.delete_item = QAction("Delete", self, triggered=self._AH_DeleteSelection)
self.actions.delete_selection = QAction("Delete Selection", self, triggered=self._AH_DeleteSelection)
self.actions.undo_last_delete = QAction("Undo Last Delete", self, triggered=self._AH_UndoLastDelete)
self.actions.start_audio_call = QAction("Start Audio Call", self, triggered=self._AH_StartAudioCall)
self.actions.start_video_call = QAction("Start Video Call", self, triggered=self._AH_StartVideoCall)
self.actions.start_chat_session = QAction("Start Real Time Chat Session", self, triggered=self._AH_StartChatSession)
self.actions.send_sms = QAction("Send Messages", self, triggered=self._AH_SendSMS)
self.actions.send_files = QAction("Send File(s)...", self, triggered=self._AH_SendFiles)
self.actions.request_screen = QAction("Request Screen", self, triggered=self._AH_RequestScreen)
self.actions.share_my_screen = QAction("Share My Screen", self, triggered=self._AH_ShareMyScreen)
self.actions.transfer_call = QAction("Transfer Active Call", self, triggered=self._AH_TransferCall)
self.actions.add_group = QAction(translate("contact_list", "Add Group"), self, triggered=self._AH_AddGroup)
self.actions.add_contact = QAction(translate("contact_list", "Add Contact"), self, triggered=self._AH_AddContact)
self.actions.edit_item = QAction(translate("contact_list", "Edit"), self, triggered=self._AH_EditItem)
self.actions.delete_item = QAction(translate("contact_list", "Delete"), self, triggered=self._AH_DeleteSelection)
self.actions.delete_selection = QAction(translate("contact_list", "Delete Selection"), self, triggered=self._AH_DeleteSelection)
self.actions.undo_last_delete = QAction(translate("contact_list", "Undo Last Delete"), self, triggered=self._AH_UndoLastDelete)
self.actions.start_audio_call = QAction(translate("contact_list", "Start Audio Call"), self, triggered=self._AH_StartAudioCall)
self.actions.start_video_call = QAction(translate("contact_list", "Start Video Call"), self, triggered=self._AH_StartVideoCall)
self.actions.start_chat_session = QAction(translate("contact_list", "Start Real Time Chat Session"), self, triggered=self._AH_StartChatSession)
self.actions.send_sms = QAction(translate("contact_list", "Send Messages"), self, triggered=self._AH_SendSMS)
self.actions.send_files = QAction(translate("contact_list", "Send File(s)..."), self, triggered=self._AH_SendFiles)
self.actions.request_screen = QAction(translate("contact_list", "Request Screen"), self, triggered=self._AH_RequestScreen)
self.actions.share_my_screen = QAction(translate("contact_list", "Share My Screen"), self, triggered=self._AH_ShareMyScreen)
self.actions.transfer_call = QAction(translate("contact_list", "Transfer Active Call"), self, triggered=self._AH_TransferCall)
self.drop_indicator_index = QModelIndex()
self.needs_restore = False
self.doubleClicked.connect(self._SH_DoubleClicked) # activated is emitted on single click
......@@ -3171,7 +3171,7 @@ class ContactListView(QListView):
model = self.model()
selected_items = [index.data(Qt.UserRole) for index in self.selectionModel().selectedIndexes()]
if not model.deleted_items:
undo_delete_text = "Undo Delete"
undo_delete_text = translate("contact_list", "Undo Delete")
elif len(model.deleted_items[-1]) == 1:
operation = model.deleted_items[-1][0]
if type(operation) is AddContactOperation:
......@@ -3185,12 +3185,12 @@ class ContactListView(QListView):
try:
contact = addressbook_manager.get_contact(operation.contact_id)
except KeyError:
name = 'Contact'
name = translate('contact_list', 'Contact')
else:
name = contact.name or 'Contact'
undo_delete_text = 'Undo Delete "%s"' % name
name = contact.name or translate('contact_list', 'Contact')
undo_delete_text = translate('contact_list', 'Undo Delete "%s"') % name
else:
undo_delete_text = "Undo Delete (%d items)" % len(model.deleted_items[-1])
undo_delete_text = translate('contact_list', "Undo Delete (%d items)") % len(model.deleted_items[-1])
menu = self.context_menu
menu.clear()
if not selected_items:
......@@ -3225,50 +3225,49 @@ class ContactListView(QListView):
can_transfer = contact.uri is not None and session_manager.active_session is not None and session_manager.active_session.state == 'connected'
if len(contact.uris) > 1 and can_call:
call_submenu = menu.addMenu('Start Audio Call')
call_submenu = menu.addMenu(translate('contact_list', 'Start Audio Call'))
for uri in contact.uris:
uri_text = '%s (%s)' % (uri.uri, uri.type) if uri.type not in ('SIP', 'Other') else uri.uri
call_item = QAction(uri_text, self)
call_item.triggered.connect(partial(self._AH_StartAudioCall, uri))
call_submenu.addAction(call_item)
call_submenu = menu.addMenu('Start Video Call')
call_submenu = menu.addMenu(translate('contact_list', 'Start Video Call'))
for uri in contact.uris:
uri_text = '%s (%s)' % (uri.uri, uri.type) if uri.type not in ('SIP', 'Other') else uri.uri
call_item = QAction(uri_text, self)
call_item.triggered.connect(partial(self._AH_StartVideoCall, uri))
call_submenu.addAction(call_item)
call_submenu = menu.addMenu('Send Messages')
call_submenu = menu.addMenu(translate('contact_list', 'Send Messages'))
for uri in contact.uris:
uri_text = '%s (%s)' % (uri.uri, uri.type) if uri.type not in ('SIP', 'Other') else uri.uri
call_item = QAction(uri_text, self)
call_item.triggered.connect(partial(self._AH_SendSMS, uri))
call_submenu.addAction(call_item)
call_submenu = menu.addMenu('Start Real Time Chat Session')
call_submenu = menu.addMenu(translate('contact_list', 'Start Real Time Chat Session'))
for uri in contact.uris:
uri_text = '%s (%s)' % (uri.uri, uri.type) if uri.type not in ('SIP', 'Other') else uri.uri
call_item = QAction(uri_text, self)
call_item.triggered.connect(partial(self._AH_StartChatSession, uri))
call_submenu.addAction(call_item)
call_submenu = menu.addMenu('Send File(s)...')
call_submenu = menu.addMenu(translate('contact_list', 'Send File(s)...'))
for uri in contact.uris:
uri_text = '%s (%s)' % (uri.uri, uri.type) if uri.type not in ('SIP', 'Other') else uri.uri
call_item = QAction(uri_text, self)
call_item.triggered.connect(partial(self._AH_SendFiles, uri))
call_submenu.addAction(call_item)
call_submenu = menu.addMenu('Request Screen')
call_submenu = menu.addMenu(translate('contact_list', 'Request Screen'))
for uri in contact.uris:
uri_text = '%s (%s)' % (uri.uri, uri.type) if uri.type not in ('SIP', 'Other') else uri.uri
call_item = QAction(uri_text, self)
call_item.triggered.connect(partial(self._AH_RequestScreen, uri))
call_submenu.addAction(call_item)
call_submenu = menu.addMenu('Share My Screen')
call_submenu = menu.addMenu(translate('contact_list', 'Share My Screen'))
for uri in contact.uris:
uri_text = '%s (%s)' % (uri.uri, uri.type) if uri.type not in ('SIP', 'Other') else uri.uri
call_item = QAction(uri_text, self)
......@@ -3293,7 +3292,7 @@ class ContactListView(QListView):
self.actions.share_my_screen.setEnabled(can_call)
if len(contact.uris) > 1 and can_transfer:
call_submenu = menu.addMenu('Transfer Call')
call_submenu = menu.addMenu(translate('contact_list', 'Transfer Call'))
for uri in contact.uris:
uri_text = '%s (%s)' % (uri.uri, uri.type) if uri.type not in ('SIP', 'Other') else uri.uri
call_item = QAction(uri_text, self)
......@@ -3680,18 +3679,18 @@ class ContactSearchListView(QListView):
self.detail_view.hide()
self.context_menu = QMenu(self)
self.actions = ContextMenuActions()
self.actions.edit_item = QAction("Edit", self, triggered=self._AH_EditItem)
self.actions.delete_item = QAction("Delete", self, triggered=self._AH_DeleteSelection)
self.actions.delete_selection = QAction("Delete Selection", self, triggered=self._AH_DeleteSelection)
self.actions.undo_last_delete = QAction("Undo Last Delete", self, triggered=self._AH_UndoLastDelete)
self.actions.start_audio_call = QAction("Start Audio Call", self, triggered=self._AH_StartAudioCall)
self.actions.start_video_call = QAction("Start Video Call", self, triggered=self._AH_StartVideoCall)
self.actions.start_chat_session = QAction("Start Real Time Chat Session", self, triggered=self._AH_StartChatSession)
self.actions.send_sms = QAction("Send Messages", self, triggered=self._AH_SendSMS)
self.actions.send_files = QAction("Send File(s)...", self, triggered=self._AH_SendFiles)
self.actions.request_screen = QAction("Request Screen", self, triggered=self._AH_RequestScreen)
self.actions.share_my_screen = QAction("Share My Screen", self, triggered=self._AH_ShareMyScreen)
self.actions.transfer_call = QAction("Transfer Active Call", self, triggered=self._AH_TransferCall)
self.actions.edit_item = QAction(translate("contact_list", "Edit"), self, triggered=self._AH_EditItem)
self.actions.delete_item = QAction(translate("contact_list", "Delete"), self, triggered=self._AH_DeleteSelection)
self.actions.delete_selection = QAction(translate("contact_list", "Delete Selection"), self, triggered=self._AH_DeleteSelection)
self.actions.undo_last_delete = QAction(translate("contact_list", "Undo Last Delete"), self, triggered=self._AH_UndoLastDelete)
self.actions.start_audio_call = QAction(translate("contact_list", "Start Audio Call"), self, triggered=self._AH_StartAudioCall)
self.actions.start_video_call = QAction(translate("contact_list", "Start Video Call"), self, triggered=self._AH_StartVideoCall)
self.actions.start_chat_session = QAction(translate("contact_list", "Start Real Time Chat Session"), self, triggered=self._AH_StartChatSession)
self.actions.send_sms = QAction(translate("contact_list", "Send Messages"), self, triggered=self._AH_SendSMS)
self.actions.send_files = QAction(translate("contact_list", "Send File(s)..."), self, triggered=self._AH_SendFiles)
self.actions.request_screen = QAction(translate("contact_list", "Request Screen"), self, triggered=self._AH_RequestScreen)
self.actions.share_my_screen = QAction(translate("contact_list", "Share My Screen"), self, triggered=self._AH_ShareMyScreen)
self.actions.transfer_call = QAction(translate("contact_list", "Transfer Active Call"), self, triggered=self._AH_TransferCall)
self.drop_indicator_index = QModelIndex()
self.doubleClicked.connect(self._SH_DoubleClicked) # activated is emitted on single click
notification_center = NotificationCenter()
......@@ -3727,12 +3726,12 @@ class ContactSearchListView(QListView):
try:
contact = addressbook_manager.get_contact(operation.contact_id)
except KeyError:
name = 'Contact'
name = translate('contact_list', 'Contact')
else:
name = contact.name or 'Contact'
undo_delete_text = 'Undo Delete "%s"' % name
name = contact.name or translate('contact_list', 'Contact')
undo_delete_text = translate('contact_list', 'Undo Delete "%s"') % name
else:
undo_delete_text = "Undo Delete (%d items)" % len(source_model.deleted_items[-1])
undo_delete_text = translate('contact_list', "Undo Delete (%d items)") % len(source_model.deleted_items[-1])
menu = self.context_menu
menu.clear()
if not selected_items:
......@@ -3941,7 +3940,7 @@ class ContactSearchListView(QListView):
def _AH_SendFiles(self, uri=None):
session_manager = SessionManager()
contact = self.selectionModel().selectedIndexes()[0].data(Qt.UserRole)
for filename in QFileDialog.getOpenFileNames(self, 'Select File(s)', session_manager.send_file_directory, 'Any file (*.*)')[0]:
for filename in QFileDialog.getOpenFileNames(self, translate('contact_list', 'Select File(s)'), session_manager.send_file_directory, 'Any file (*.*)')[0]:
session_manager.send_file(contact, uri or contact.uri, filename)
def _AH_RequestScreen(self, uri=None):
......@@ -4028,17 +4027,17 @@ class ContactDetailView(QListView):
self.animation.finished.connect(self._SH_AnimationFinished)
self.context_menu = QMenu(self)
self.actions = ContextMenuActions()
self.actions.delete_contact = QAction("Delete Contact", self, triggered=self._AH_DeleteContact)
self.actions.edit_contact = QAction("Edit Contact", self, triggered=self._AH_EditContact)
self.actions.make_uri_default = QAction("Set Address As Default", self, triggered=self._AH_MakeURIDefault)
self.actions.start_audio_call = QAction("Start Audio Call", self, triggered=self._AH_StartAudioCall)
self.actions.start_video_call = QAction("Start Video Call", self, triggered=self._AH_StartVideoCall)
self.actions.start_chat_session = QAction("Start Real Time Chat Session", self, triggered=self._AH_StartChatSession)
self.actions.send_sms = QAction("Send Messages", self, triggered=self._AH_SendSMS)
self.actions.send_files = QAction("Send File(s)...", self, triggered=self._AH_SendFiles)
self.actions.request_screen = QAction("Request Screen", self, triggered=self._AH_RequestScreen)
self.actions.share_my_screen = QAction("Share My Screen", self, triggered=self._AH_ShareMyScreen)
self.actions.transfer_call = QAction("Transfer Active Call", self, triggered=self._AH_TransferCall)
self.actions.delete_contact = QAction(translate("contact_list", "Delete Contact"), self, triggered=self._AH_DeleteContact)
self.actions.edit_contact = QAction(translate("contact_list", "Edit Contact"), self, triggered=self._AH_EditContact)
self.actions.make_uri_default = QAction(translate("contact_list", "Set Address As Default"), self, triggered=self._AH_MakeURIDefault)
self.actions.start_audio_call = QAction(translate("contact_list", "Start Audio Call"), self, triggered=self._AH_StartAudioCall)
self.actions.start_video_call = QAction(translate("contact_list", "Start Video Call"), self, triggered=self._AH_StartVideoCall)
self.actions.start_chat_session = QAction(translate("contact_list", "Start Real Time Chat Session"), self, triggered=self._AH_StartChatSession)
self.actions.send_sms = QAction(translate("contact_list", "Send Messages"), self, triggered=self._AH_SendSMS)
self.actions.send_files = QAction(translate("contact_list", "Send File(s)..."), self, triggered=self._AH_SendFiles)
self.actions.request_screen = QAction(translate("contact_list", "Request Screen"), self, triggered=self._AH_RequestScreen)
self.actions.share_my_screen = QAction(translate("contact_list", "Share My Screen"), self, triggered=self._AH_ShareMyScreen)
self.actions.transfer_call = QAction(translate("contact_list", "Transfer Active Call"), self, triggered=self._AH_TransferCall)
self.drop_indicator_index = QModelIndex()
self.doubleClicked.connect(self._SH_DoubleClicked) # activated is emitted on single click
contact_list.installEventFilter(self)
......@@ -4257,7 +4256,7 @@ class ContactDetailView(QListView):
selected_uri = item.uri
else:
selected_uri = uri or contact.uri
for filename in QFileDialog.getOpenFileNames(self, 'Select File(s)', session_manager.send_file_directory, 'Any file (*.*)')[0]:
for filename in QFileDialog.getOpenFileNames(self, translate('contact_list', 'Select File(s)'), session_manager.send_file_directory, 'Any file (*.*)')[0]:
session_manager.send_file(contact, selected_uri, filename)
def _AH_RequestScreen(self, uri=None):
......@@ -4373,12 +4372,18 @@ class ContactURIItem(object):
class URITypeComboBox(QComboBox):
builtin_types = (None, "Mobile", "Home", "Work", "SIP", "XMPP", "Other")
builtin_types = (None,
QT_TRANSLATE_NOOP('contact_editor', "Mobile"),
QT_TRANSLATE_NOOP('contact_editor', "Home"),
QT_TRANSLATE_NOOP('contact_editor', "Work"),
QT_TRANSLATE_NOOP('contact_editor', "SIP"),
QT_TRANSLATE_NOOP('contact_editor', "XMPP"),
QT_TRANSLATE_NOOP('contact_editor', "Other"))
def __init__(self, parent=None, types=()):
super(URITypeComboBox, self).__init__(parent)
self.setEditable(True)
self.addItems(self.builtin_types)
self.addItems((translate('contact_editor', item) for item in self.builtin_types))
self.addItems(sorted(set(types) - set(self.builtin_types)))
......@@ -4471,7 +4476,9 @@ class ContactURIDelegate(QItemDelegate):
class ContactURIModel(QAbstractTableModel):
columns = ('Address', 'Type', 'Default')
columns = (QT_TRANSLATE_NOOP('contact_editor', 'Address'),
QT_TRANSLATE_NOOP('contact_editor', 'Type'),
QT_TRANSLATE_NOOP('contact_editor', 'Default'))
AddressColumn = 0
TypeColumn = 1
......@@ -4507,7 +4514,7 @@ class ContactURIModel(QAbstractTableModel):
return item
elif role == Qt.DisplayRole:
if column == ContactURIModel.AddressColumn:
return 'Edit to add address' if item.ghost else str(item.uri or '')
return translate('contact_list', 'Edit to add address') if item.ghost else str(item.uri or '')
elif role == Qt.EditRole:
if column == ContactURIModel.AddressColumn:
return str(item.uri or '')
......@@ -4544,7 +4551,7 @@ class ContactURIModel(QAbstractTableModel):
def headerData(self, section, orientation, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.columns[section]
return translate('contact_editor', self.columns[section])
return super(ContactURIModel, self).headerData(section, orientation, role)
def init_with_address(self, address=None):
......@@ -4622,7 +4629,7 @@ class ContactURITableView(QTableView):
super(ContactURITableView, self).__init__(parent)
self.setItemDelegate(ContactURIDelegate(self))
self.context_menu = QMenu(self)
self.context_menu.addAction("Delete", self._AH_DeleteSelection)
self.context_menu.addAction(translate('contact_editor', "Delete"), self._AH_DeleteSelection)
self.horizontalHeader().setSectionResizeMode(self.horizontalHeader().ResizeToContents)
def selectionChanged(self, selected, deselected):
......@@ -4673,11 +4680,11 @@ class ContactEditorDialog(base_class, ui_class):
def setupUi(self, contact_editor):
super(ContactEditorDialog, self).setupUi(contact_editor)
self.preferred_media.setItemData(0, 'audio')
self.preferred_media.setItemData(1, 'video')
self.preferred_media.setItemData(2, 'chat')
self.preferred_media.setItemData(3, 'audio+chat')
self.preferred_media.setItemData(4, 'messages')
self.preferred_media.setItemData(0, translate('contact_editor', 'audio'))
self.preferred_media.setItemData(1, translate('contact_editor', 'video'))
self.preferred_media.setItemData(2, translate('contact_editor', 'chat'))
self.preferred_media.setItemData(3, translate('contact_editor', 'audio+chat'))
self.preferred_media.setItemData(4, translate('contact_editor', 'messages'))
self.addresses_table.verticalHeader().setDefaultSectionSize(URITypeComboBox().sizeHint().height())
def open_for_add(self, sip_address='', target_group=None):
......@@ -4688,7 +4695,7 @@ class ContactEditorDialog(base_class, ui_class):
self.icon_selector.init_with_contact(None)
self.presence.setChecked(True)
self.preferred_media.setCurrentIndex(0)
self.accept_button.setText('Add')
self.accept_button.setText(translate('contact_editor', 'Add'))
self.accept_button.setEnabled(False)
self.show()
......@@ -4702,7 +4709,7 @@ class ContactEditorDialog(base_class, ui_class):
self.presence.setChecked(contact.presence.subscribe)
self.auto_answer.setChecked(contact.auto_answer)
self.preferred_media.setCurrentIndex(self.preferred_media.findData(contact.preferred_media))
self.accept_button.setText('Ok')
self.accept_button.setText(translate('contact_editor', 'Ok'))
self.accept_button.setEnabled(True)
self.show()
......
......@@ -14,6 +14,7 @@ from zope.interface import implementer
from blink.configuration.settings import BlinkSettings
from blink.resources import Resources
from blink.sessions import FileTransferDelegate, FileTransferModel
from blink.util import translate
from blink.widgets.util import ContextMenuActions
......@@ -38,13 +39,13 @@ class FileTransferWindow(base_class, ui_class):
self.context_menu = QMenu(self.listview)
self.actions = ContextMenuActions()
self.actions.open_file = QAction("Open", self, triggered=self._AH_OpenFile)
self.actions.open_file_folder = QAction("Open File Folder", self, triggered=self._AH_OpenFileFolder)
self.actions.cancel_transfer = QAction("Cancel", self, triggered=self._AH_CancelTransfer)
self.actions.retry_transfer = QAction("Retry", self, triggered=self._AH_RetryTransfer)
self.actions.remove_entry = QAction("Remove From List", self, triggered=self._AH_RemoveEntry)
self.actions.open_downloads_folder = QAction("Open Transfers Folder", self, triggered=self._AH_OpenTransfersFolder)
self.actions.clear_list = QAction("Clear List", self, triggered=self._AH_ClearList)
self.actions.open_file = QAction(translate('filetransfer_window', "Open"), self, triggered=self._AH_OpenFile)
self.actions.open_file_folder = QAction(translate('filetransfer_window', "Open File Folder"), self, triggered=self._AH_OpenFileFolder)
self.actions.cancel_transfer = QAction(translate('filetransfer_window', "Cancel"), self, triggered=self._AH_CancelTransfer)
self.actions.retry_transfer = QAction(translate('filetransfer_window', "Retry"), self, triggered=self._AH_RetryTransfer)
self.actions.remove_entry = QAction(translate('filetransfer_window', "Remove From List"), self, triggered=self._AH_RemoveEntry)
self.actions.open_downloads_folder = QAction(translate('filetransfer_window', "Open Transfers Folder"), self, triggered=self._AH_OpenTransfersFolder)
self.actions.clear_list = QAction(translate('filetransfer_window', "Clear List"), self, triggered=self._AH_ClearList)
self.model.itemAdded.connect(self.update_status)
self.model.itemRemoved.connect(self.update_status)
......@@ -66,9 +67,9 @@ class FileTransferWindow(base_class, ui_class):
def update_status(self):
total = len(self.model.items)
active = len([item for item in self.model.items if not item.ended])
text = '%d %s' % (total, 'transfer' if total == 1 else 'transfers')
text = '%d %s' % (total, translate('filetransfer_window', 'transfer') if total == 1 else translate('filetransfer_window', 'transfers'))
if active > 0:
text += ' (%d active)' % active
text += translate('filetransfer_window', ' (%d active)') % active
self.status_label.setText(text)
def handle_notification(self, notification):
......
......@@ -24,7 +24,7 @@ from blink.configuration.settings import BlinkSettings
from blink.logging import MessagingTrace as log
from blink.messages import BlinkMessage
from blink.resources import ApplicationData, Resources
from blink.util import run_in_gui_thread
from blink.util import run_in_gui_thread, translate
import traceback
from sqlobject import SQLObject, StringCol, DateTimeCol, IntCol, UnicodeCol, DatabaseIndex
......@@ -587,15 +587,15 @@ class HistoryEntry(object):
today = date.today()
days = (today - call_date).days
if call_date == today:
result += call_time.strftime(" at %H:%M")
result += call_time.strftime(translate("history", " at %H:%M"))
elif days == 1:
result += call_time.strftime(" Yesterday at %H:%M")
result += call_time.strftime(translate("history", " Yesterday at %H:%M"))
elif days < 7:
result += call_time.strftime(" on %A")
result += call_time.strftime(translate("history", " on %A"))
elif call_date.year == today.year:
result += call_time.strftime(" on %B %d")
result += call_time.strftime(translate("history", " on %B %d"))
else:
result += call_time.strftime(" on %Y-%m-%d")
result += call_time.strftime(translate("history", " on %Y-%m-%d"))
if self.duration:
seconds = int(self.duration.total_seconds())
if seconds >= 3600:
......
......@@ -31,7 +31,7 @@ from blink.configuration.datatypes import IconDescriptor, FileURL, PresenceState
from blink.configuration.settings import BlinkSettings
from blink.presence import PendingWatcherDialog
from blink.resources import ApplicationData, IconManager, Resources
from blink.util import run_in_gui_thread
from blink.util import run_in_gui_thread, translate
from blink.widgets.buttons import AccountState, SwitchViewButton
......@@ -97,8 +97,8 @@ class MainWindow(base_class, ui_class):
self.system_tray_icon = QSystemTrayIcon(QIcon(Resources.get('icons/blink.png')), self)
self.system_tray_icon.activated.connect(self._SH_SystemTrayIconActivated)
menu = QMenu(self)
menu.addAction("Show", self._AH_SystemTrayShowWindow)
menu.addAction(QIcon(Resources.get('icons/application-exit.png')), "Quit", self._AH_QuitActionTriggered)
menu.addAction(translate("main_window", "Show"), self._AH_SystemTrayShowWindow)
menu.addAction(QIcon(Resources.get('icons/application-exit.png')), translate("main_window", "Quit"), self._AH_QuitActionTriggered)
self.system_tray_icon.setContextMenu(menu)
self.system_tray_icon.show()
else:
......@@ -225,8 +225,8 @@ class MainWindow(base_class, ui_class):
self.alert_devices_group = QActionGroup(self)
self.video_devices_group = QActionGroup(self)
self.screen_sharing_button.addAction(QAction('Request screen', self.screen_sharing_button, triggered=self._AH_RequestScreenActionTriggered))
self.screen_sharing_button.addAction(QAction('Share my screen', self.screen_sharing_button, triggered=self._AH_ShareMyScreenActionTriggered))
self.screen_sharing_button.addAction(QAction(translate('main_window', 'Request screen'), self.screen_sharing_button, triggered=self._AH_RequestScreenActionTriggered))
self.screen_sharing_button.addAction(QAction(translate('main_window', 'Share my screen'), self.screen_sharing_button, triggered=self._AH_ShareMyScreenActionTriggered))
# adjust search box height depending on theme as the value set in designer isn't suited for all themes
search_box = self.search_box
......@@ -270,7 +270,7 @@ class MainWindow(base_class, ui_class):
action_map = {}
action = action_map['system_default'] = self.output_device_menu.addAction('System default')
action = action_map['system_default'] = self.output_device_menu.addAction(translate('main_window', 'System default'))
action.setData('system_default')
action.setCheckable(True)
self.output_devices_group.addAction(action)
......@@ -283,7 +283,7 @@ class MainWindow(base_class, ui_class):
action.setCheckable(True)
self.output_devices_group.addAction(action)
action = action_map[None] = self.output_device_menu.addAction('None')
action = action_map[None] = self.output_device_menu.addAction(translate('main_window', 'None'))
action.setData(None)
action.setCheckable(True)
self.output_devices_group.addAction(action)
......@@ -293,7 +293,7 @@ class MainWindow(base_class, ui_class):
action_map = {}
action = action_map['system_default'] = self.input_device_menu.addAction('System default')
action = action_map['system_default'] = self.input_device_menu.addAction(translate('main_window', 'System default'))
action.setData('system_default')
action.setCheckable(True)
self.input_devices_group.addAction(action)
......@@ -306,7 +306,7 @@ class MainWindow(base_class, ui_class):
action.setCheckable(True)
self.input_devices_group.addAction(action)
action = action_map[None] = self.input_device_menu.addAction('None')
action = action_map[None] = self.input_device_menu.addAction(translate('main_window', 'None'))
action.setData(None)
action.setCheckable(True)
self.input_devices_group.addAction(action)
......@@ -316,7 +316,7 @@ class MainWindow(base_class, ui_class):
action_map = {}
action = action_map['system_default'] = self.alert_device_menu.addAction('System default')
action = action_map['system_default'] = self.alert_device_menu.addAction(translate('main_window', 'System default'))
action.setData('system_default')
action.setCheckable(True)
self.alert_devices_group.addAction(action)
......@@ -329,7 +329,7 @@ class MainWindow(base_class, ui_class):
action.setCheckable(True)
self.alert_devices_group.addAction(action)
action = action_map[None] = self.alert_device_menu.addAction('None')
action = action_map[None] = self.alert_device_menu.addAction(translate('main_window', 'None'))
action.setData(None)
action.setCheckable(True)
self.alert_devices_group.addAction(action)
......@@ -342,7 +342,7 @@ class MainWindow(base_class, ui_class):
action_map = {}
action = action_map['system_default'] = self.video_camera_menu.addAction('System default')
action = action_map['system_default'] = self.video_camera_menu.addAction(translate('main_window', 'System default'))
action.setData('system_default')
action.setCheckable(True)
self.video_devices_group.addAction(action)
......@@ -355,7 +355,7 @@ class MainWindow(base_class, ui_class):
action.setCheckable(True)
self.video_devices_group.addAction(action)
action = action_map[None] = self.video_camera_menu.addAction('None')
action = action_map[None] = self.video_camera_menu.addAction(translate('main_window', 'None'))
action.setData(None)
action.setCheckable(True)
self.video_devices_group.addAction(action)
......@@ -476,7 +476,7 @@ class MainWindow(base_class, ui_class):
action.entry = entry
action.setToolTip(entry.uri)
else:
action = self.history_menu.addAction("Call history is empty")
action = self.history_menu.addAction(translate("main_window", "Call history is empty"))
action.setEnabled(False)
def _AH_HistoryMenuTriggered(self, action):
......@@ -502,11 +502,11 @@ class MainWindow(base_class, ui_class):
def _SH_AccountStateChanged(self):
self.activity_note.setText(self.account_state.note)
if self.account_state.state is AccountState.Invisible:
self.activity_note.inactiveText = '(invisible)'
self.activity_note.inactiveText = translate('main_window', '(invisible)')
self.activity_note.setEnabled(False)
else:
if not self.activity_note.isEnabled():
self.activity_note.inactiveText = 'Add an activity note here'
self.activity_note.inactiveText = translate('main_window', 'Add an activity note here')
self.activity_note.setEnabled(True)
if not self.account_state.state.internal:
self.saved_account_state = None
......@@ -516,7 +516,7 @@ class MainWindow(base_class, ui_class):
blink_settings.save()
def _SH_AccountStateClicked(self, checked):
filename = QFileDialog.getOpenFileName(self, 'Select Icon', self.last_icon_directory, "Images (*.png *.tiff *.jpg *.xmp *.svg)")[0]
filename = QFileDialog.getOpenFileName(self, translate('main_window', 'Select Icon'), self.last_icon_directory, "Images (*.png *.tiff *.jpg *.xmp *.svg)")[0]
if filename:
self.last_icon_directory = os.path.dirname(filename)
filename = filename if os.path.realpath(filename) != os.path.realpath(self.default_icon_path) else None
......@@ -700,7 +700,7 @@ class MainWindow(base_class, ui_class):
self.contacts_view.setCurrentWidget(self.search_panel)
self.search_view.setCurrentWidget(self.search_list_panel if self.contact_search_model.rowCount() else self.not_found_panel)
selected_items = self.search_list.selectionModel().selectedIndexes()
self.enable_call_buttons(account_manager.default_account is not None and len(selected_items)<=1)
self.enable_call_buttons(account_manager.default_account is not None and len(selected_items) <= 1)
else:
self.contacts_view.setCurrentWidget(self.contact_list_panel)
selected_items = self.contact_list.selectionModel().selectedIndexes()
......@@ -739,7 +739,7 @@ class MainWindow(base_class, ui_class):
def _SH_AudioSessionModelChangedStructure(self):
active_sessions = self.session_model.active_sessions
self.active_sessions_label.setText('There is 1 active call' if len(active_sessions) == 1 else 'There are %d active calls' % len(active_sessions))
self.active_sessions_label.setText(translate('main_window', 'There is 1 active call') if len(active_sessions) == 1 else translate('main_window', 'There are %d active calls') % len(active_sessions))
self.active_sessions_label.setVisible(any(active_sessions))
self.hangup_all_button.setEnabled(any(active_sessions))
selected_indexes = self.session_list.selectionModel().selectedIndexes()
......@@ -754,7 +754,7 @@ class MainWindow(base_class, ui_class):
if self.account_state.state is not AccountState.Invisible:
if self.saved_account_state is None:
self.saved_account_state = self.account_state.state, self.activity_note.text()
self.account_state.setState(AccountState.Busy.Internal, note='On the phone')
self.account_state.setState(AccountState.Busy.Internal, note=translate('main_window', 'On the phone'))
elif self.saved_account_state is not None:
state, note = self.saved_account_state
self.saved_account_state = None
......@@ -797,9 +797,9 @@ class MainWindow(base_class, ui_class):
self.auto_accept_chat_action.setChecked(settings.chat.auto_accept)
self.received_messages_sound_action.setChecked(settings.sounds.play_message_alerts)
if settings.google_contacts.enabled:
self.google_contacts_action.setText('Disable &Google Contacts')
self.google_contacts_action.setText(translate('main_window', 'Disable &Google Contacts'))
else:
self.google_contacts_action.setText('Enable &Google Contacts...')
self.google_contacts_action.setText(translate('main_window', 'Enable &Google Contacts...'))
if not any(account.enabled for account in account_manager.iter_accounts()):
self.display_name.setEnabled(False)
self.activity_note.setEnabled(False)
......@@ -874,9 +874,9 @@ class MainWindow(base_class, ui_class):
self.received_messages_sound_action.setChecked(settings.sounds.play_message_alerts)
if 'google_contacts.enabled' in notification.data.modified:
if notification.sender.google_contacts.enabled:
self.google_contacts_action.setText('Disable &Google Contacts')
self.google_contacts_action.setText(translate('main_window', 'Disable &Google Contacts'))
else:
self.google_contacts_action.setText('Enable &Google Contacts...')
self.google_contacts_action.setText(translate('main_window', 'Enable &Google Contacts...'))
elif notification.sender is blink_settings:
if 'presence.current_state' in notification.data.modified:
state = getattr(AccountState, blink_settings.presence.current_state.state, AccountState.Available)
......
......@@ -41,7 +41,7 @@ from sipsimple.util import ISOTimestamp
from blink.logging import MessagingTrace as log
from blink.resources import Resources
from blink.sessions import SessionManager, StreamDescription, IncomingDialogBase
from blink.util import run_in_gui_thread
from blink.util import run_in_gui_thread, translate
__all__ = ['MessageManager', 'BlinkMessage']
......@@ -60,7 +60,7 @@ class GeneratePGPKeyDialog(IncomingDialogBase, ui_class):
self.setupUi(self)
self.slot = None
self.generate_button = self.dialog_button_box.addButton("Generate", QDialogButtonBox.AcceptRole)
self.generate_button = self.dialog_button_box.addButton(translate("generate_pgp_key_dialog", "Generate"), QDialogButtonBox.AcceptRole)
self.generate_button.setIcon(QApplication.style().standardIcon(QStyle.SP_DialogApplyButton))
def show(self, activate=True):
......@@ -136,7 +136,7 @@ class ImportDialog(IncomingDialogBase, ui_class):
self.setupUi(self)
self.slot = None
self.import_button = self.dialog_button_box.addButton("Import", QDialogButtonBox.AcceptRole)
self.import_button = self.dialog_button_box.addButton(translate("import_key_dialog", "Import"), QDialogButtonBox.AcceptRole)
self.import_button.setIcon(QApplication.style().standardIcon(QStyle.SP_DialogApplyButton))
self.import_button.setEnabled(False)
......@@ -232,7 +232,7 @@ class ExportDialog(IncomingDialogBase, ui_class):
self.setupUi(self)
self.slot = None
self.export_button = self.dialog_button_box.addButton("Export", QDialogButtonBox.AcceptRole)
self.export_button = self.dialog_button_box.addButton(translate("export_key_dialog", "Export"), QDialogButtonBox.AcceptRole)
self.export_button.setIcon(QApplication.style().standardIcon(QStyle.SP_DialogApplyButton))
self.export_button.setEnabled(False)
......
......@@ -28,7 +28,7 @@ from blink.configuration.datatypes import FileURL
from blink.configuration.settings import BlinkSettings
from blink.resources import ApplicationData, Resources
from blink.logging import LogManager
from blink.util import QSingleton, call_in_gui_thread, run_in_gui_thread
from blink.util import QSingleton, call_in_gui_thread, run_in_gui_thread, translate
__all__ = ['PreferencesWindow', 'AccountListView', 'SIPPortEditor']
......@@ -217,7 +217,7 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
with Resources.directory:
self.setupUi()
self.setWindowTitle('Blink Preferences')
self.setWindowTitle(translate('preferences_window', 'Blink Preferences'))
self.account_list.setModel(account_model)
self.delete_account_button.setEnabled(False)
......@@ -395,10 +395,10 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
# Accounts
self.key_negotiation_button.clear()
self.key_negotiation_button.addItem('Opportunistic', 'opportunistic')
self.key_negotiation_button.addItem('ZRTP', 'zrtp')
self.key_negotiation_button.addItem('SDES optional', 'sdes_optional')
self.key_negotiation_button.addItem('SDES mandatory', 'sdes_mandatory')
self.key_negotiation_button.addItem(translate('preferences_window', 'Opportunistic'), 'opportunistic')
self.key_negotiation_button.addItem(translate('preferences_window', 'ZRTP'), 'zrtp')
self.key_negotiation_button.addItem(translate('preferences_window', 'SDES optional'), 'sdes_optional')
self.key_negotiation_button.addItem(translate('preferences_window', 'SDES mandatory'), 'sdes_mandatory')
# Audio
......@@ -427,7 +427,7 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
self.video_framerate_button.addItem('%d fps' % rate, rate)
self.video_codec_bitrate_button.clear()
self.video_codec_bitrate_button.addItem('automatic', None)
self.video_codec_bitrate_button.addItem(translate('preferences_window', 'automatic'), None)
for bitrate in (1.0, 2.0, 4.0):
self.video_codec_bitrate_button.addItem('%g Mbps' % bitrate, bitrate)
......@@ -473,7 +473,7 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
# Languages
self.language_button.clear()
languages_path = Resources.get('i18n')
self.language_button.addItem('System Default', Language('default'))
self.language_button.addItem(translate('preferences_window', 'System Default'), Language('default'))
self.language_button.addItem('English', Language('en'))
for language_file in os.listdir(languages_path):
try:
......@@ -660,30 +660,30 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
class Separator: pass
self.audio_input_device_button.clear()
self.audio_input_device_button.addItem('System Default', 'system_default')
self.audio_input_device_button.addItem(translate('preferences_window', 'System Default'), 'system_default')
self.audio_input_device_button.insertSeparator(1)
self.audio_input_device_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
for device in SIPApplication.engine.input_devices:
self.audio_input_device_button.addItem(device, device)
self.audio_input_device_button.addItem('None', None)
self.audio_input_device_button.addItem(translate('preferences_window', 'None'), None)
self.audio_input_device_button.setCurrentIndex(self.audio_input_device_button.findData(settings.audio.input_device))
self.audio_output_device_button.clear()
self.audio_output_device_button.addItem('System Default', 'system_default')
self.audio_output_device_button.addItem(translate('preferences_window', 'System Default'), 'system_default')
self.audio_output_device_button.insertSeparator(1)
self.audio_output_device_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
for device in SIPApplication.engine.output_devices:
self.audio_output_device_button.addItem(device, device)
self.audio_output_device_button.addItem('None', None)
self.audio_output_device_button.addItem(translate('preferences_window', 'None'), None)
self.audio_output_device_button.setCurrentIndex(self.audio_output_device_button.findData(settings.audio.output_device))
self.audio_alert_device_button.clear()
self.audio_alert_device_button.addItem('System Default', 'system_default')
self.audio_alert_device_button.addItem(translate('preferences_window', 'System Default'), 'system_default')
self.audio_alert_device_button.insertSeparator(1)
self.audio_alert_device_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
for device in SIPApplication.engine.output_devices:
self.audio_alert_device_button.addItem(device, device)
self.audio_alert_device_button.addItem('None', None)
self.audio_alert_device_button.addItem(translate('preferences_window', 'None'), None)
self.audio_alert_device_button.setCurrentIndex(self.audio_alert_device_button.findData(settings.audio.alert_device))
def load_video_devices(self):
......@@ -692,12 +692,12 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
class Separator: pass
self.video_camera_button.clear()
self.video_camera_button.addItem('System Default', 'system_default')
self.video_camera_button.addItem(translate('preferences_window', 'System Default'), 'system_default')
self.video_camera_button.insertSeparator(1)
self.video_camera_button.setItemData(1, Separator) # prevent the separator from being selected (must have different itemData than the None device)
for device in SIPApplication.engine.video_devices:
self.video_camera_button.addItem(device, device)
self.video_camera_button.addItem('None', None)
self.video_camera_button.addItem(translate('preferences_window', 'None'), None)
self.video_camera_button.setCurrentIndex(self.video_camera_button.findData(settings.video.device))
def load_settings(self):
......@@ -859,11 +859,11 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
if selected_account_info.registration_state:
if selected_account_info.registration_state == 'succeeded' and selected_account_info.registrar is not None:
self.account_registration_label.setText('Registered at %s' % selected_account_info.registrar)
self.account_registration_label.setText(translate('preferences_window', 'Registered at %s') % selected_account_info.registrar)
else:
self.account_registration_label.setText('Registration %s' % selected_account_info.registration_state.title())
self.account_registration_label.setText(translate('preferences_window', 'Registration %s') % selected_account_info.registration_state.title())
else:
self.account_registration_label.setText('Not Registered')
self.account_registration_label.setText(translate('preferences_window', 'Not Registered'))
else:
self.account_registration_label.setText('')
......@@ -903,7 +903,7 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
self.message_pgp_enabled_button.setChecked(account.sms.enable_pgp)
if account is not bonjour_account:
self.account_auto_answer.setText('Auto answer from allowed contacts')
self.account_auto_answer.setText(translate('preferences_window', 'Auto answer from allowed contacts'))
# Server settings tab
self.always_use_my_proxy_button.setChecked(account.sip.always_use_my_proxy)
outbound_proxy = account.sip.outbound_proxy or UnspecifiedOutboundProxy
......@@ -952,7 +952,7 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
self.idd_prefix_button.addItem(item_text)
self.idd_prefix_button.setCurrentIndex(self.idd_prefix_button.findText(item_text))
item_text = account.pstn.prefix or 'None'
item_text = account.pstn.prefix or translate('preferences_window', 'None')
index = self.prefix_button.findText(item_text)
if index == -1:
self.prefix_button.addItem(item_text)
......@@ -975,7 +975,7 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
self.last_id_editor.setEnabled(account.sms.enable_history_synchronization)
self.last_id_editor.setText(account.sms.history_synchronization_id)
else:
self.account_auto_answer.setText('Auto answer from all neighbours')
self.account_auto_answer.setText(translate('preferences_window', 'Auto answer from all neighbours'))
self.message_replication_button.hide()
self.message_synchronization_button.hide()
......@@ -1079,7 +1079,7 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
logs_size += os.stat(os.path.join(path, name)).st_size
except (OSError, IOError):
pass
self.log_files_size_label.setText("There are currently %s of log files" % self._normalize_binary_size(logs_size))
self.log_files_size_label.setText(translate('preferences_window', "There are currently %s of log files") % self._normalize_binary_size(logs_size))
def _update_pstn_example_label(self):
prefix = self.prefix_button.currentText()
......@@ -1142,15 +1142,15 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
self.password_editor.hide()
else:
if tab_widget.indexOf(self.server_settings_tab) == -1:
tab_widget.addTab(self.server_settings_tab, "Server Settings")
tab_widget.addTab(self.server_settings_tab, translate('preferences_window', "Server Settings"))
if tab_widget.indexOf(self.network_tab) == -1:
tab_widget.addTab(self.network_tab, "NAT Traversal")
tab_widget.addTab(self.network_tab, translate('preferences_window', "NAT Traversal"))
if tab_widget.indexOf(self.advanced_tab) == -1:
tab_widget.addTab(self.advanced_tab, "Advanced")
tab_widget.addTab(self.advanced_tab, translate('preferences_window', "Advanced"))
self.password_label.show()
self.password_editor.show()
self.voicemail_uri_editor.inactiveText = "Discovered by subscribing to %s" % selected_account.id
self.xcap_root_editor.inactiveText = "Taken from the DNS TXT record for xcap.%s" % selected_account.id.domain
self.voicemail_uri_editor.inactiveText = translate('preferences_window', "Discovered by subscribing to %s") % selected_account.id
self.xcap_root_editor.inactiveText = translate('preferences_window', "Taken from the DNS TXT record for xcap.%s") % selected_account.id.domain
self.load_account_settings(selected_account)
def _SH_AccountListDataChanged(self, topLeft, bottomRight):
......@@ -1163,9 +1163,9 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
selected_account_info = self.account_list.model().data(selected_index, Qt.UserRole)
if selected_account_info is account_info:
if account_info.registration_state:
self.account_registration_label.setText('Registration %s' % account_info.registration_state.title())
self.account_registration_label.setText(translate('preferences_window', 'Registration %s') % account_info.registration_state.title())
else:
self.account_registration_label.setText('Not Registered')
self.account_registration_label.setText(translate('preferences_window', 'Not Registered'))
def _SH_DeleteAccountButtonClicked(self):
model = self.account_list.model()
......@@ -1173,7 +1173,7 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
selected_index = self.account_list.selectionModel().selectedIndexes()[0]
selected_account = selected_index.data(Qt.UserRole).account
title, message = "Remove Account", "Permanently remove account %s?" % selected_account.id
title, message = translate('preferences_window', "Remove Account"), translate('preferences_window', "Permanently remove account %s?") % selected_account.id
if QMessageBox.question(self, title, message, QMessageBox.Ok | QMessageBox.Cancel) == QMessageBox.Cancel:
return
......@@ -1498,9 +1498,9 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
X509Certificate(contents)
X509PrivateKey(contents)
except (OSError, IOError) as e:
QMessageBox.critical(self, "TLS Certificate Error", "The certificate file could not be opened: %s" % e.strerror)
QMessageBox.critical(self, translate('preferences_window', "TLS Certificate Error"), translate('preferences_window', "The certificate file could not be opened: %s") % e.strerror)
except GNUTLSError as e:
QMessageBox.critical(self, "TLS Certificate Error", "The certificate file is invalid: %s" % e)
QMessageBox.critical(self, translate('preferences_window', "TLS Certificate Error"), translate('preferences_window', "The certificate file is invalid: %s") % e)
else:
self.tls_cert_file_editor.setText(cert_path)
settings.tls.certificate = cert_path
......@@ -1619,16 +1619,16 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
if value == 0:
self.answer_delay_seconds_label.setText('')
elif value == 1:
self.answer_delay_seconds_label.setText('second')
self.answer_delay_seconds_label.setText(translate('preferences_window', 'second'))
else:
self.answer_delay_seconds_label.setText('seconds')
self.answer_delay_seconds_label.setText(translate('preferences_window', 'seconds'))
settings = SIPSimpleSettings()
if settings.answering_machine.answer_delay != value:
settings.answering_machine.answer_delay = value
settings.save()
def _SH_MaxRecordingValueChanged(self, value):
self.max_recording_minutes_label.setText('minute' if value == 1 else 'minutes')
self.max_recording_minutes_label.setText(translate('preferences_window', 'minute') if value == 1 else translate('preferences_window', 'minutes'))
settings = SIPSimpleSettings()
if settings.answering_machine.max_recording != value:
settings.answering_machine.max_recording = value
......@@ -1762,7 +1762,7 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
def _SH_ScreenshotsDirectoryBrowseButtonClicked(self, checked):
# TODO: open the file selection dialog in non-modal mode. Same for the one for TLS CA list and the IconSelector from contacts. -Dan
settings = BlinkSettings()
directory = QFileDialog.getExistingDirectory(self, 'Select Screenshots Directory', settings.screenshots_directory.normalized) or None
directory = QFileDialog.getExistingDirectory(self, translate('preferences_window', 'Select Screenshots Directory'), settings.screenshots_directory.normalized) or None
if directory is not None:
directory = os.path.normpath(directory)
if directory != settings.screenshots_directory:
......@@ -1789,7 +1789,7 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
def _SH_TransfersDirectoryBrowseButtonClicked(self, checked):
# TODO: open the file selection dialog in non-modal mode. Same for the one for TLS CA list and the IconSelector from contacts. -Dan
settings = BlinkSettings()
directory = QFileDialog.getExistingDirectory(self, 'Select Transfers Directory', settings.transfers_directory.normalized) or None
directory = QFileDialog.getExistingDirectory(self, translate('preferences_window', 'Select Transfers Directory'), settings.transfers_directory.normalized) or None
if directory is not None:
directory = os.path.normpath(directory)
if directory != settings.transfers_directory:
......@@ -1909,9 +1909,9 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
try:
X509Certificate(open(ca_path).read())
except (OSError, IOError) as e:
QMessageBox.critical(self, "TLS Certificate Error", "The certificate authority file could not be opened: %s" % e.strerror)
QMessageBox.critical(self, translate('preferences_window', "TLS Certificate Error"), translate('preferences_window', "The certificate authority file could not be opened: %s") % e.strerror)
except GNUTLSError as e:
QMessageBox.critical(self, "TLS Certificate Error", "The certificate authority file is invalid: %s" % e)
QMessageBox.critical(self, translate('preferences_window', "TLS Certificate Error"), translate('preferences_window', "The certificate authority file is invalid: %s") % e)
else:
self.tls_ca_file_editor.setText(ca_path)
settings.tls.ca_list = ca_path
......@@ -1928,8 +1928,8 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
if data.language_code != settings.interface.language:
settings.interface.language = data.language_code
settings.save()
title = "Restart required"
question = "The application language was changed. A restart is required to apply the change. Would you like to restart now?"
title = translate('preferences_window', "Restart required")
question = translate('preferences_window', "The application language was changed. A restart is required to apply the change. Would you like to restart now?")
if QMessageBox.question(self, title, question) == QMessageBox.No:
return
......@@ -1983,13 +1983,13 @@ class PreferencesWindow(base_class, ui_class, metaclass=QSingleton):
if 'sounds.play_message_alerts' in notification.data.modified:
self.chat_message_alert_button.setChecked(settings.sounds.play_message_alerts)
if 'sip.auto_answer_interval' in notification.data.modified:
self.auto_answer_interval.setValue(settings.sip.auto_answer_interval)
self.auto_answer_interval.setValue(settings.sip.auto_answer_interval)
if 'video.device' in notification.data.modified:
self.video_camera_button.setCurrentIndex(self.video_camera_button.findData(settings.video.device))
elif notification.sender is self.selected_account is not None:
account = notification.sender
if 'sip.auto_answer' in notification.data.modified:
self.account_auto_answer.setChecked(account.sip.auto_answer)
self.account_auto_answer.setChecked(account.sip.auto_answer)
if 'enabled' in notification.data.modified:
self.account_enabled_button.setChecked(account.enabled)
if not account.enabled:
......
......@@ -23,6 +23,7 @@ from sipsimple.threading import run_in_thread
from blink.configuration.settings import BlinkSettings
from blink.resources import Resources
from blink.screensharing.vncclient import ServerDefault, TrueColor, HighColor, LowColor
from blink.util import translate
__all__ = ['ScreensharingWindow', 'VNCViewer']
......@@ -401,7 +402,7 @@ class ScreensharingDialog(base_class, ui_class):
return False
def get_credentials(self):
self.message_label.setText('Screen sharing requires authentication')
self.message_label.setText(translate('vnc_viewer', 'Screen sharing requires authentication'))
self.username_label.show()
self.username_editor.show()
self.username_editor.clear()
......@@ -413,7 +414,7 @@ class ScreensharingDialog(base_class, ui_class):
return (self.username_editor.text(), self.password_editor.text()) if result == self.Accepted else (None, None)
def get_password(self):
self.message_label.setText('Screen sharing requires a password')
self.message_label.setText(translate('vnc_viewer', 'Screen sharing requires a password'))
self.username_label.hide()
self.username_editor.hide()
self.username_editor.clear()
......@@ -493,7 +494,7 @@ class ScreensharingToolbox(base_class, ui_class):
self.close_button.setDefaultAction(self.close_action)
self.color_depth_button.clear()
self.color_depth_button.addItem('Default Color Depth', ServerDefault)
self.color_depth_button.addItem(translate('vnc_viewer', 'Default Color Depth'), ServerDefault)
self.color_depth_button.addItem('TrueColor (24 bits)', TrueColor)
self.color_depth_button.addItem('HighColor (16 bits)', HighColor)
self.color_depth_button.addItem('LowColor (8 bits)', LowColor)
......@@ -593,13 +594,13 @@ class ScreensharingWindow(base_class, ui_class):
self.fullscreen_button.setDefaultAction(self.fullscreen_action)
self.color_depth_button.clear()
self.color_depth_button.addItem('Default Color Depth', ServerDefault)
self.color_depth_button.addItem(translate('vnc_viewer', 'Default Color Depth'), ServerDefault)
self.color_depth_button.addItem('TrueColor (24 bits)', TrueColor)
self.color_depth_button.addItem('HighColor (16 bits)', HighColor)
self.color_depth_button.addItem('LowColor (8 bits)', LowColor)
self.screenshot_button_menu = QMenu(self)
self.screenshot_button_menu.addAction('Open screenshots folder', self._SH_ScreenshotsFolderActionTriggered)
self.screenshot_button_menu.addAction(translate('vnc_viewer', 'Open screenshots folder'), self._SH_ScreenshotsFolderActionTriggered)
def closeEvent(self, event):
super(ScreensharingWindow, self).closeEvent(event)
......
......@@ -46,7 +46,7 @@ from sipsimple.threading import run_in_thread, run_in_twisted_thread
from blink.configuration.settings import BlinkSettings
from blink.resources import ApplicationData, Resources
from blink.screensharing import ScreensharingWindow, VNCClient, ServerDefault
from blink.util import call_later, run_in_gui_thread
from blink.util import call_later, run_in_gui_thread, translate
from blink.widgets.buttons import LeftSegment, MiddleSegment, RightSegment
from blink.widgets.labels import Status
from blink.widgets.color import ColorHelperMixin, ColorUtils, cache_result, background_color_key
......@@ -1275,7 +1275,7 @@ class SMPVerification(Enum):
@implementer(IObserver)
class SMPVerificationHandler(object):
question = 'What is the ZRTP authentication string?'.encode('utf-8')
question = translate('zrtp_widget', 'What is the ZRTP authentication string?').encode('utf-8')
def __init__(self, blink_session):
"""@type blink_session: BlinkSession"""
......@@ -1874,9 +1874,9 @@ class DraggedAudioSessionWidget(base_class, ui_class):
self.address_label.setText(session_widget.address_label.text())
if self.in_conference:
self.note_label.setText('Drop outside the conference to detach')
self.note_label.setText(translate('sessions', 'Drop outside the conference to detach'))
else:
self.note_label.setText('<p><b>Drop</b>:&nbsp;Conference&nbsp; <b>Alt+Drop</b>:&nbsp;Transfer</p>')
self.note_label.setText(translate('sessions', '<p><b>Drop</b>:&nbsp;Conference&nbsp; <b>Alt+Drop</b>:&nbsp;Transfer</p>'))
def paintEvent(self, event):
painter = QPainter(self)
......@@ -1949,11 +1949,11 @@ class AudioSessionItem(object):
def __unicode__(self):
if self.status is not None:
return '{0.type} call with {0.name} ({0.status})'.format(self)
return translate('sessions', '{0.type} call with {0.name} ({0.status})').format(self)
elif self.codec_info:
return '{0.type} call with {0.name} using {0.codec_info} ({0.duration!s})'.format(self)
return translate('sessions', '{0.type} call with {0.name} using {0.codec_info} ({0.duration!s})').format(self)
else:
return '{0.type} call with {0.name}'.format(self)
return translate('sessions', '{0.type} call with {0.name}').format(self)
@property
def audio_stream(self):
......@@ -2172,30 +2172,30 @@ class AudioSessionItem(object):
def _NH_BlinkSessionConnectionProgress(self, notification):
stage = notification.data.stage
if stage == 'initializing':
self.status = Status('Initializing...')
self.status = Status(translate('sessions', 'Initializing...'))
elif stage == 'connecting/dns_lookup':
self.status = Status('Looking up destination...')
self.status = Status(translate('sessions', 'Looking up destination...'))
elif stage == 'connecting' and self.blink_session.routes:
self.tls = self.blink_session.transport == 'tls'
uri = self.blink_session.routes[0].uri
destination = '%s:%s' % (self.blink_session.transport, uri.host.decode())
self.status = Status('Trying %s' % destination)
self.status = Status(translate('sessions', 'Trying %s') % destination)
elif stage == 'ringing':
self.status = Status('Ringing...')
self.status = Status(translate('sessions', 'Ringing...'))
elif stage == 'starting':
self.status = Status('Starting media...')
self.status = Status(translate('sessions', 'Starting media...'))
else:
self.status = None
def _NH_BlinkSessionInfoUpdated(self, notification):
if 'media' in notification.data.elements:
audio_info = self.blink_session.info.streams.audio
self.type = 'HD Audio' if audio_info.sample_rate and audio_info.sample_rate >= 16000 else 'Audio'
self.type = translate('sessions', 'HD Audio') if audio_info.sample_rate and audio_info.sample_rate >= 16000 else translate('sessions', 'Audio')
self.codec_info = audio_info.codec
if audio_info.encryption is not None:
self.widget.srtp_label.setToolTip('Media is encrypted using %s (%s)' % (audio_info.encryption, audio_info.encryption_cipher))
self.widget.srtp_label.setToolTip(translate('sessions', 'Media is encrypted using %s (%s)') % (audio_info.encryption, audio_info.encryption_cipher))
else:
self.widget.srtp_label.setToolTip('Media is not encrypted')
self.widget.srtp_label.setToolTip(translate('sessions', 'Media is not encrypted'))
self.widget.update_rtp_encryption_icon()
self.srtp = audio_info.encryption is not None
if 'statistics' in notification.data.elements:
......@@ -2206,9 +2206,9 @@ class AudioSessionItem(object):
self.widget.hold_button.setChecked(notification.data.local_hold)
if self.blink_session.state == 'connected':
if notification.data.local_hold:
self.status = Status('On hold', color='#000090')
self.status = Status(translate('sessions', 'On hold'), color='#000090')
elif notification.data.remote_hold:
self.status = Status('Hold by remote', color='#000090')
self.status = Status(translate('sessions', 'Hold by remote'), color='#000090')
else:
self.status = None
......@@ -2226,7 +2226,7 @@ class AudioSessionItem(object):
self.status = Status('Connected')
call_later(3, self._reset_status, self.status) # reset status 3 seconds later if it hasn't changed until then
else:
self.status = Status('Audio refused', color='#900000')
self.status = Status(translate('sessions', 'Audio refused'), color='#900000')
self._cleanup()
def _NH_BlinkSessionDidAddStream(self, notification):
......@@ -2240,7 +2240,7 @@ class AudioSessionItem(object):
def _NH_BlinkSessionDidNotAddStream(self, notification):
if notification.data.stream.type == 'audio':
self.status = Status('Audio refused', color='#900000') # where can we get the reason from? (rejected, cancelled, failed, ...) -Dan
self.status = Status(translate('sessions', 'Audio refused'), color='#900000') # where can we get the reason from? (rejected, cancelled, failed, ...) -Dan
self._cleanup()
def _NH_BlinkSessionWillRemoveStream(self, notification):
......@@ -2273,24 +2273,25 @@ class AudioSessionItem(object):
def _NH_BlinkSessionTransferNewOutgoing(self, notification):
self.status_context = 'transfer'
self.status = Status('Transfer: Trying', context='transfer')
self.status = Status(translate('sessions', 'Transfer: Trying'), context='transfer')
def _NH_BlinkSessionTransferDidEnd(self, notification):
if self.blink_session.transfer_direction == 'outgoing':
self.status = Status('Transfer: Succeeded', context='transfer')
self.status = Status(translate('sessions', 'Transfer: Succeeded'), context='transfer')
def _NH_BlinkSessionTransferDidFail(self, notification):
if self.blink_session.transfer_direction == 'outgoing':
# TODO: maybe translate? -Tijmen
reason_map = {403: 'Forbidden', 404: 'Not Found', 408: 'Timeout', 480: 'Unavailable', 486: 'Busy',
487: 'Cancelled', 488: 'Not Acceptable', 600: 'Busy', 603: 'Declined'}
reason = reason_map.get(notification.data.code, 'Failed')
self.status = Status("Transfer: {}".format(reason), context='transfer')
reason = reason_map.get(notification.data.code, translate('sessions', 'Failed'))
self.status = Status(translate('sessions', "Transfer: {}").format(reason), context='transfer')
call_later(3, self._reset_status, self.status)
self.status_context = None
def _NH_BlinkSessionTransferGotProgress(self, notification):
if notification.data.code < 200: # final answers are handled in DidEnd and DiDFail
self.status = Status("Transfer: {}".format(notification.data.reason), context='transfer')
self.status = Status(translate('sessions', "Transfer: {}").format(notification.data.reason), context='transfer')
def _NH_MediaStreamWillEnd(self, notification):
stream = notification.sender
......@@ -4121,7 +4122,7 @@ class BlinkFileTransfer(BlinkSessionBase):
if failure_reason is not None:
end_reason = failure_reason
else:
end_reason = 'Completed (%s)' % FileSizeFormatter.format(self.file_selector.size)
end_reason = translate('sessions', 'Completed (%s)') % FileSizeFormatter.format(self.file_selector.size)
state = SessionState('ended')
state.reason = end_reason
state.error = failure_reason is not None
......@@ -4142,7 +4143,7 @@ class BlinkFileTransfer(BlinkSessionBase):
self.state = 'connecting'
routes = notification.data.result
if not routes:
self._terminate(failure_reason='Destination not found')
self._terminate(failure_reason=translate('sessions', 'Destination not found'))
self.routes = None
return
self.routes = routes
......@@ -4155,7 +4156,7 @@ class BlinkFileTransfer(BlinkSessionBase):
notification.center.remove_observer(self, sender=notification.sender)
if self.state in ('ending', 'ended'):
return
self._terminate(failure_reason='DNS Lookup failed')
self._terminate(failure_reason=translate('sessions', 'DNS Lookup failed'))
def _NH_SIPSessionNewOutgoing(self, notification):
self.state = 'initializing'
......@@ -4479,17 +4480,17 @@ class FileTransferItem(object):
def _NH_BlinkFileTransferDidChangeState(self, notification):
state = notification.data.new_state
if state == 'connecting/dns_lookup':
self.status = 'Looking up destination...'
self.status = translate('sessions', 'Looking up destination...')
elif state == 'connecting':
self.status = 'Connecting...'
self.status = translate('sessions', 'Connecting...')
elif state == 'connecting/ringing':
self.status = 'Ringing...'
self.status = translate('sessions', 'Ringing...')
elif state == 'connecting/starting':
self.status = 'Starting...'
self.status = translate('sessions', 'Starting...')
elif state == 'connected':
self.status = 'Connected'
self.status = translate('sessions', 'Connected')
elif state == 'ending':
self.status = 'Ending...'
self.status = translate('sessions', 'Ending...')
else:
self.status = None
self.progress = None
......@@ -4498,7 +4499,7 @@ class FileTransferItem(object):
def _NH_BlinkFileTransferDidInitialize(self, notification):
self.progress = None
self.status = 'Connecting...'
self.status = translate('sessions', 'Connecting...')
self.widget.update_content(self)
notification.center.post_notification('FileTransferItemDidChange', sender=self)
......@@ -4506,7 +4507,7 @@ class FileTransferItem(object):
progress = notification.data.progress
if self.progress is None or progress > self.progress:
self.progress = progress
self.status = 'Computing hash (%s%%)' % notification.data.progress
self.status = translate('sessions', 'Computing hash (%s%%)') % notification.data.progress
self.widget.update_content(self)
notification.center.post_notification('FileTransferItemDidChange', sender=self)
......@@ -4514,7 +4515,7 @@ class FileTransferItem(object):
self.bytes = notification.data.bytes
self.total_bytes = notification.data.total_bytes
progress = int(self.bytes * 100 / self.total_bytes)
status = 'Transferring: %s/%s (%s%%)' % (FileSizeFormatter.format(self.bytes), FileSizeFormatter.format(self.total_bytes), progress)
status = translate('sessions', 'Transferring: %s/%s (%s%%)') % (FileSizeFormatter.format(self.bytes), FileSizeFormatter.format(self.total_bytes), progress)
if self.progress is None or progress > self.progress or status != self.status:
self.progress = progress
self.status = status
......@@ -5233,7 +5234,7 @@ class IncomingDialog(IncomingDialogBase, ui_class):
self.reject_button.released.connect(self._set_reject_mode)
self.screensharing_stream.hidden.connect(self.screensharing_label.hide)
self.screensharing_stream.shown.connect(self.screensharing_label.show)
self.auto_answer_label.setText('Auto-answer is inactive')
self.auto_answer_label.setText(translate('incoming_dialog', 'Auto-answer is inactive'))
self.auto_answer_interval = None
self._auto_answer_timer = None
self.passed_time = 0
......@@ -5269,12 +5270,12 @@ class IncomingDialog(IncomingDialogBase, ui_class):
self._auto_answer_timer.setInterval(1000)
self._auto_answer_timer.timeout.connect(self._update_auto_answer)
self._auto_answer_timer.start()
self.auto_answer_label.setText('Auto answer in %d seconds' % interval)
self.auto_answer_label.setText(translate('incoming_dialog', 'Auto answer in %d seconds') % interval)
def _update_auto_answer(self):
self.passed_time = self.passed_time + 1
remaining_time = self.auto_answer_interval - self.passed_time
self.auto_answer_label.setText('Auto answer in %d seconds' % remaining_time)
self.auto_answer_label.setText(translate('incoming_dialog', 'Auto answer in %d seconds') % remaining_time)
if remaining_time == 0:
self._auto_answer_timer.stop()
self.hide()
......@@ -5285,20 +5286,20 @@ class IncomingDialog(IncomingDialogBase, ui_class):
self.chat_stream.active = True
self.screensharing_stream.active = True
self.video_stream.active = True
self.note_label.setText('To refuse a media type click its icon')
self.note_label.setText(translate('incoming_dialog', 'To refuse a media type click its icon'))
else:
self.audio_stream.active = False
self.chat_stream.active = False
self.screensharing_stream.active = False
self.video_stream.active = False
if self.audio_stream.in_use:
self.note_label.setText('Audio call')
self.note_label.setText(translate('incoming_dialog', 'Audio call'))
elif self.chat_stream.in_use:
self.note_label.setText('Chat session')
self.note_label.setText(translate('incoming_dialog', 'Chat session'))
elif self.video_stream.in_use:
self.note_label.setText('Video call')
self.note_label.setText(translate('incoming_dialog', 'Video call'))
elif self.screensharing_stream.in_use:
self.note_label.setText('Screen sharing request')
self.note_label.setText(translate('incoming_dialog', 'Screen sharing request'))
else:
self.note_label.setText('')
self._update_accept_button()
......@@ -5326,10 +5327,10 @@ class IncomingRequest(QObject):
self._auto_answer_timer = None
if proposal:
self.dialog.setWindowTitle('Incoming Session Update')
self.dialog.setWindowTitle(translate('incoming_dialog', 'Incoming Session Update'))
self.dialog.busy_button.hide()
else:
self.dialog.setWindowTitle('Incoming Session Request')
self.dialog.setWindowTitle(translate('incoming_dialog', 'Incoming Session Request'))
address = '%s@%s' % (session.remote_identity.uri.user, session.remote_identity.uri.host)
self.dialog.uri_label.setText(address)
self.dialog.username_label.setText(contact.name or session.remote_identity.display_name or address)
......@@ -5360,14 +5361,14 @@ class IncomingRequest(QObject):
self.dialog.screensharing_stream.setVisible(self.screensharing_stream is not None)
if self.screensharing_stream is not None:
if self.screensharing_stream.handler.type == 'active':
self.dialog.screensharing_label.setText('is offering screen sharing')
self.dialog.screensharing_label.setText(translate('incoming_dialog', 'is offering screen sharing'))
else:
self.dialog.screensharing_label.setText('is asking to share your screen')
self.dialog.screensharing_label.setText(translate('incoming_dialog', 'is asking to share your screen'))
# self.dialog.screensharing_stream.accepted = bool(proposal)
self.dialog.finished.connect(self._SH_DialogFinished)
def __eq__(self, other):
return self is other
......@@ -5472,6 +5473,7 @@ class IncomingFileTransferDialog(IncomingDialogBase, ui_class):
def _set_reject_mode(self):
self.reject_mode = 'reject'
del ui_class, base_class
......@@ -5497,9 +5499,9 @@ class IncomingFileTransferRequest(QObject):
filename = os.path.basename(self.stream.file_selector.name)
size = self.stream.file_selector.size
if size:
self.dialog.file_label.setText('File: %s (%s)' % (filename, FileSizeFormatter.format(size)))
self.dialog.file_label.setText(translate('incoming_filetransfer_dialog', 'File: %s (%s)') % (filename, FileSizeFormatter.format(size)))
else:
self.dialog.file_label.setText('File: %s' % filename)
self.dialog.file_label.setText(translate('incoming_filetransfer_dialog', 'File: %s') % filename)
self.dialog.finished.connect(self._SH_DialogFinished)
......@@ -5572,7 +5574,7 @@ class IncomingCallTransferRequest(QObject):
self.dialog.uri_label.setText(contact_uri.uri)
self.dialog.username_label.setText(contact.name)
self.dialog.user_icon.setPixmap(contact.icon.pixmap(48))
self.dialog.transfer_label.setText('transfer requested by {}'.format(blink_session.contact.name or blink_session.contact_uri.uri))
self.dialog.transfer_label.setText(translate('incoming_calltransfer_dialog', 'transfer requested by {}').format(blink_session.contact.name or blink_session.contact_uri.uri))
self.dialog.finished.connect(self._SH_DialogFinished)
......@@ -5652,6 +5654,7 @@ class ConferenceDialog(base_class, ui_class):
streams.append(StreamDescription('chat'))
session_manager.create_session(contact, contact_uri, streams, account=account)
del ui_class, base_class
......
from PyQt5.QtCore import Qt, QLineF, QPointF, QRectF, QSize, QTimer, pyqtSignal
from PyQt5.QtCore import Qt, QCoreApplication, QLineF, QPointF, QRectF, QSize, QTimer, pyqtSignal, QT_TRANSLATE_NOOP
from PyQt5.QtGui import QBrush, QColor, QLinearGradient, QIcon, QPainter, QPainterPath, QPalette, QPen, QPixmap, QPolygonF
from PyQt5.QtWidgets import QAction, QCommonStyle, QMenu, QPushButton, QStyle, QStyleOptionToolButton, QStylePainter, QToolButton
......@@ -10,6 +10,8 @@ from blink.widgets.color import ColorScheme, ColorUtils, ColorHelperMixin
__all__ = ['ToolButton', 'ConferenceButton', 'StreamButton', 'SegmentButton', 'SingleSegment', 'LeftSegment', 'MiddleSegment', 'RightSegment',
'RecordButton', 'SwitchViewButton', 'StateButton', 'AccountState']
translate = QCoreApplication.translate
class ToolButton(QToolButton):
"""A custom QToolButton that doesn't show a menu indicator arrow"""
......@@ -28,8 +30,8 @@ class ConferenceButton(ToolButton):
def __init__(self, parent=None):
super(ConferenceButton, self).__init__(parent)
self.make_conference_action = QAction('Conference all single sessions', self, triggered=self.makeConference.emit)
self.break_conference_action = QAction('Break selected conference', self, triggered=self.breakConference.emit)
self.make_conference_action = QAction(translate('conference_button', 'Conference all single sessions'), self, triggered=self.makeConference.emit)
self.break_conference_action = QAction(translate('conference_button', 'Break selected conference'), self, triggered=self.breakConference.emit)
self.toggled.connect(self._SH_Toggled)
self.addAction(self.make_conference_action)
......@@ -259,8 +261,8 @@ class SwitchViewButton(QPushButton):
viewChanged = pyqtSignal(int)
button_text = {ContactView: 'Switch to Calls', SessionView: 'Switch to Contacts'}
button_dnd_text = {ContactView: 'Drag here to add to a conference', SessionView: 'Drag here to go back to contacts'}
button_text = {ContactView: QT_TRANSLATE_NOOP('switch_view_button', 'Switch to Calls'), SessionView: QT_TRANSLATE_NOOP('switch_view_button', 'Switch to Contacts')}
button_dnd_text = {ContactView: QT_TRANSLATE_NOOP('switch_view_button', 'Drag here to add to a conference'), SessionView: QT_TRANSLATE_NOOP('switch_view_button', 'Drag here to go back to contacts')}
dnd_style_sheet1 = """
QPushButton {
......@@ -307,7 +309,7 @@ class SwitchViewButton(QPushButton):
text = self.button_dnd_text[value]
else:
text = self.button_text[value]
self.setText(text)
self.setText(translate('switch_view_button', text))
self.viewChanged.emit(value)
view = property(_get_view, _set_view)
......@@ -324,11 +326,11 @@ class SwitchViewButton(QPushButton):
self.dnd_timer.phase = 0
self.original_height = self.height()
self.setStyleSheet(self.dnd_style_sheet1)
self.setText(self.button_dnd_text[self.view])
self.setText(translate('switch_view_button', self.button_dnd_text[self.view]))
self.setFixedHeight(40)
else:
self.setStyleSheet('')
self.setText(self.button_text[self.view])
self.setText(translate('switch_view_button', self.button_text[self.view]))
self.setFixedHeight(self.original_height)
dnd_active = property(_get_dnd_active, _set_dnd_active)
......@@ -662,10 +664,10 @@ class PresenceState(object):
class AccountState(StateButton):
Invisible = PresenceState('Invisible', '#efedeb', Resources.get('icons/state-invisible.svg'))
Available = PresenceState('Available', '#00ff00', Resources.get('icons/state-available.svg'))
Away = PresenceState('Away', '#ffff00', Resources.get('icons/state-away.svg'))
Busy = PresenceState('Busy', '#ff0000', Resources.get('icons/state-busy.svg'))
Invisible = PresenceState(QT_TRANSLATE_NOOP('presence_state', 'Invisible'), '#efedeb', Resources.get('icons/state-invisible.svg'))
Available = PresenceState(QT_TRANSLATE_NOOP('presence_state', 'Available'), '#00ff00', Resources.get('icons/state-available.svg'))
Away = PresenceState(QT_TRANSLATE_NOOP('presence_state', 'Away'), '#ffff00', Resources.get('icons/state-away.svg'))
Busy = PresenceState(QT_TRANSLATE_NOOP('presence_state', 'Busy'), '#ff0000', Resources.get('icons/state-busy.svg'))
stateChanged = pyqtSignal()
......@@ -675,7 +677,7 @@ class AccountState(StateButton):
super(AccountState, self).__init__(parent)
menu = QMenu(self)
for state in (self.Available, self.Away, self.Busy, self.Invisible):
action = menu.addAction(QIcon(state.icon), state.name)
action = menu.addAction(QIcon(state.icon), translate('presence_state', state.name))
action.state = state
action.note = None
menu.addSeparator()
......
......@@ -11,6 +11,7 @@ from application.python.types import MarkerType
from sipsimple.configuration.datatypes import Path
from blink.resources import IconManager
from blink.util import translate
from blink.widgets.color import ColorHelperMixin
from blink.widgets.util import QtDynamicProperty, ContextMenuActions
......@@ -27,8 +28,8 @@ class IconSelector(QLabel):
def __init__(self, parent=None):
super(IconSelector, self).__init__(parent)
self.actions = ContextMenuActions()
self.actions.select_icon = QAction('Select icon...', self, triggered=self._SH_ChangeIconActionTriggered)
self.actions.remove_icon = QAction('Use contact provided icon', self, triggered=self._SH_RemoveIconActionTriggered)
self.actions.select_icon = QAction(translate('icon_selector', 'Select icon...'), self, triggered=self._SH_ChangeIconActionTriggered)
self.actions.remove_icon = QAction(translate('icon_selector', 'Use contact provided icon'), self, triggered=self._SH_RemoveIconActionTriggered)
self.icon_size = 48
self.default_icon = None
self.contact_icon = None
......@@ -107,7 +108,7 @@ class IconSelector(QLabel):
super(IconSelector, self).mouseReleaseEvent(event)
def _SH_ChangeIconActionTriggered(self):
filename = QFileDialog.getOpenFileName(self, 'Select Icon', self.last_icon_directory, "Images (*.png *.tiff *.jpg *.xmp *.svg)")[0]
filename = QFileDialog.getOpenFileName(self, translate('icon_selector', 'Select Icon'), self.last_icon_directory, "Images (*.png *.tiff *.jpg *.xmp *.svg)")[0]
if filename:
self.filename = filename
......
......@@ -6,6 +6,7 @@ from PyQt5.QtGui import QPainter, QPalette, QPixmap
from PyQt5.QtWidgets import QAbstractButton, QLineEdit, QBoxLayout, QHBoxLayout, QLabel, QLayout, QSizePolicy, QSpacerItem, QStyle, QStyleOptionFrame, QWidget
from blink.resources import Resources
from blink.util import translate
from blink.widgets.util import QtDynamicProperty
......@@ -274,7 +275,7 @@ class SearchBox(LineEdit):
self.clear_button.hide()
self.clear_button.clicked.connect(self.clear)
self.textChanged.connect(self._SH_TextChanged)
self.inactiveText = "Search"
self.inactiveText = translate('search_box', "Search")
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape:
......
......@@ -6,6 +6,7 @@ from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QStyle, QStyleOption, QStylePainter
from blink.resources import Resources
from blink.util import translate
from blink.sessions import SMPVerification
......@@ -56,7 +57,7 @@ class OTRWidget(base_class, ui_class):
@peer_verified.setter
def peer_verified(self, verified):
self.__dict__['peer_verified'] = verified
self.validate_button.setText('Invalidate' if verified else 'Validate')
self.validate_button.setText(translate('otr_widget', 'Invalidate') if verified else translate('otr_widget', 'Validate'))
self.validate_button.setChecked(verified)
self.validate_button.setEnabled(verified or self.verification_stack.currentWidget() is not self.smp_panel or self.smp_status is SMPVerification.Succeeded)
self.peer_fingerprint_value.setStyleSheet('QLabel {{ color: {}; }}'.format(self.color_table['green'] if verified else self.color_table['orange']))
......@@ -93,11 +94,11 @@ class OTRWidget(base_class, ui_class):
@property
def smp_status_text(self):
if self.peer_verified:
return '<span style="color: {[green]};">Verified</span>'.format(self.color_table)
return translate('otr_widget', '<span style="color: {[green]};">Verified</span>').format(self.color_table)
elif self.smp_status is SMPVerification.Succeeded:
return '<span style="color: {[green]};">Succeeded</span>'.format(self.color_table)
return translate('otr_widget', '<span style="color: {[green]};">Succeeded</span>').format(self.color_table)
elif self.smp_status is SMPVerification.Failed:
return '<span style="color: {[orange]};">Failed</span>'.format(self.color_table)
return translate('otr_widget', '<span style="color: {[orange]};">Failed</span>').format(self.color_table)
else:
return '{}'.format(self.smp_status.value)
......
......@@ -4,6 +4,7 @@ from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QStyle, QStyleOption, QStylePainter
from blink.resources import Resources
from blink.util import translate
__all__ = ['ZRTPWidget']
......@@ -44,10 +45,10 @@ class ZRTPWidget(base_class, ui_class):
self.__dict__['peer_verified'] = verified
if verified:
self.validate_button.setText('Invalidate')
self.status_value.setText('<span style="color: hsv(100, 85%, 100%);">Verified</span>')
self.status_value.setText(translate('zrtp_widget', '<span style="color: hsv(100, 85%, 100%);">Verified</span>'))
else:
self.validate_button.setText('Validate')
self.status_value.setText('<span style="color: hsv(20, 85%, 100%);">Not verified</span>')
self.status_value.setText(translate('zrtp_widget', '<span style="color: hsv(20, 85%, 100%);">Not verified</span>'))
self.validate_button.setChecked(verified)
peer_verified = property(_get_peer_verified, _set_peer_verified)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment