diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 4fdbe29f..050d2e2f 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -25,8 +25,8 @@ if platform == 'win32' and getattr(sys, 'frozen', False): import tempfile sys.stderr = open(join(tempfile.gettempdir(), '%s.log' % appname), 'wt') -import l10n -l10n.Translations().install() +from l10n import Translations +Translations().install(config.get('language') or None) import companion import bpc @@ -85,9 +85,13 @@ class AppWindow: frame.grid(sticky=tk.NSEW) frame.columnconfigure(1, weight=1) - tk.Label(frame, text=_('Cmdr')+':').grid(row=1, column=0, sticky=tk.W) # Main window - tk.Label(frame, text=_('System')+':').grid(row=2, column=0, sticky=tk.W) # Main window - tk.Label(frame, text=_('Station')+':').grid(row=3, column=0, sticky=tk.W) # Main window + self.cmdr_label = tk.Label(frame) + self.system_label = tk.Label(frame) + self.station_label = tk.Label(frame) + + self.cmdr_label.grid(row=1, column=0, sticky=tk.W) + self.system_label.grid(row=2, column=0, sticky=tk.W) + self.station_label.grid(row=3, column=0, sticky=tk.W) self.cmdr = tk.Label(frame, anchor=tk.W) self.system = HyperlinkLabel(frame, compound=tk.RIGHT, url = self.system_url, popup_copy = True) @@ -103,8 +107,9 @@ class AppWindow: appitem.grid(columnspan=2, sticky=tk.W) self.button = ttk.Button(frame, text=_('Update'), width=28, command=self.getandsend, default=tk.ACTIVE, state=tk.DISABLED) # Update button in main window - self.theme_button = tk.Label(frame, text=_('Update'), width = platform == 'darwin' and 32 or 28, state=tk.DISABLED) # Update button in main window + self.theme_button = tk.Label(frame, width = platform == 'darwin' and 32 or 28, state=tk.DISABLED) self.status = tk.Label(frame, name='status', anchor=tk.W) + row = frame.grid_size()[1] self.button.grid(row=row, columnspan=2, sticky=tk.NSEW) self.theme_button.grid(row=row, columnspan=2, sticky=tk.NSEW) @@ -118,83 +123,86 @@ class AppWindow: for child in frame.winfo_children(): child.grid_configure(padx=5, pady=(platform=='win32' and 1 or 3)) - menubar = tk.Menu() + self.menubar = tk.Menu() if platform=='darwin': # Can't handle (de)iconify if topmost is set, so suppress iconify button # http://wiki.tcl.tk/13428 and p15 of https://developer.apple.com/legacy/library/documentation/Carbon/Conceptual/HandlingWindowsControls/windowscontrols.pdf root.call('tk::unsupported::MacWindowStyle', 'style', root, 'document', 'closeBox horizontalZoom resizable') # https://www.tcl.tk/man/tcl/TkCmd/menu.htm - apple_menu = tk.Menu(menubar, name='apple') - apple_menu.add_command(label=_("About {APP}").format(APP=applongname), command=lambda:self.w.call('tk::mac::standardAboutPanel')) # App menu entry on OSX - apple_menu.add_command(label=_("Check for Updates..."), command=lambda:self.updater.checkForUpdates()) - menubar.add_cascade(menu=apple_menu) - self.edit_menu = tk.Menu(menubar, name='edit') - self.edit_menu.add_command(label=_('Copy'), accelerator='Command-c', state=tk.DISABLED, command=self.copy) # As in Copy and Paste - menubar.add_cascade(label=_('Edit'), menu=self.edit_menu) # Menu title + self.file_menu = tk.Menu(self.menubar, name='apple') + self.file_menu.add_command(command=lambda:self.w.call('tk::mac::standardAboutPanel')) + self.file_menu.add_command(command=lambda:self.updater.checkForUpdates()) + self.menubar.add_cascade(menu=self.file_menu) + self.edit_menu = tk.Menu(self.menubar, name='edit') + self.edit_menu.add_command(accelerator='Command-c', state=tk.DISABLED, command=self.copy) + self.menubar.add_cascade(menu=self.edit_menu) self.w.bind('', self.copy) - self.view_menu = tk.Menu(menubar, name='view') - self.view_menu.add_command(label=_('Status'), state=tk.DISABLED, command=lambda:stats.StatsDialog(self.w, self.session)) # Menu item - menubar.add_cascade(label=_('View'), menu=self.view_menu) # Menu title on OSX - window_menu = tk.Menu(menubar, name='window') - menubar.add_cascade(label=_('Window'), menu=window_menu) # Menu title on OSX - self.w['menu'] = menubar + self.view_menu = tk.Menu(self.menubar, name='view') + self.view_menu.add_command(state=tk.DISABLED, command=lambda:stats.StatsDialog(self.w, self.session)) + self.menubar.add_cascade(menu=self.view_menu) + window_menu = tk.Menu(self.menubar, name='window') + self.menubar.add_cascade(menu=window_menu) + self.w['menu'] = self.menubar # https://www.tcl.tk/man/tcl/TkCmd/tk_mac.htm self.w.call('set', 'tk::mac::useCompatibilityMetrics', '0') self.w.createcommand('tkAboutDialog', lambda:self.w.call('tk::mac::standardAboutPanel')) self.w.createcommand("::tk::mac::Quit", self.onexit) - self.w.createcommand("::tk::mac::ShowPreferences", lambda:prefs.PreferencesDialog(self.w, self.login)) + self.w.createcommand("::tk::mac::ShowPreferences", lambda:prefs.PreferencesDialog(self.w, self.postprefs)) self.w.createcommand("::tk::mac::ReopenApplication", self.w.deiconify) # click on app in dock = restore self.w.protocol("WM_DELETE_WINDOW", self.w.withdraw) # close button shouldn't quit app else: - file_menu = self.view_menu = tk.Menu(menubar, tearoff=tk.FALSE) - file_menu.add_command(label=_('Status'), state=tk.DISABLED, command=lambda:stats.StatsDialog(self.w, self.session)) # Menu item - file_menu.add_command(label=_("Check for Updates..."), command=lambda:self.updater.checkForUpdates()) - file_menu.add_command(label=_("Settings"), command=lambda:prefs.PreferencesDialog(self.w, self.login)) # Item in the File menu on Windows - file_menu.add_separator() - file_menu.add_command(label=_("Exit"), command=self.onexit) # Item in the File menu on Windows - menubar.add_cascade(label=_("File"), menu=file_menu) # Menu title on Windows - self.edit_menu = tk.Menu(menubar, tearoff=tk.FALSE) - self.edit_menu.add_command(label=_('Copy'), accelerator='Ctrl+C', state=tk.DISABLED, command=self.copy) # As in Copy and Paste - menubar.add_cascade(label=_('Edit'), menu=self.edit_menu) # Menu title + self.file_menu = self.view_menu = tk.Menu(self.menubar, tearoff=tk.FALSE) + self.file_menu.add_command(state=tk.DISABLED, command=lambda:stats.StatsDialog(self.w, self.session)) + self.file_menu.add_command(command=lambda:self.updater.checkForUpdates()) + self.file_menu.add_command(command=lambda:prefs.PreferencesDialog(self.w, self.postprefs)) + self.file_menu.add_separator() + self.file_menu.add_command(command=self.onexit) + self.menubar.add_cascade(menu=self.file_menu) + self.edit_menu = tk.Menu(self.menubar, tearoff=tk.FALSE) + self.edit_menu.add_command(accelerator='Ctrl+C', state=tk.DISABLED, command=self.copy) + self.menubar.add_cascade(menu=self.edit_menu) if platform == 'win32': + # Must be added after at least one "real" menu entry self.always_ontop = tk.BooleanVar(value = config.getint('always_ontop')) - system_menu = tk.Menu(menubar, name='system', tearoff=tk.FALSE) + system_menu = tk.Menu(self.menubar, name='system', tearoff=tk.FALSE) system_menu.add_separator() system_menu.add_checkbutton(label=_('Always on top'), variable = self.always_ontop, command=self.ontop_changed) # Appearance setting - menubar.add_cascade(menu=system_menu) + self.menubar.add_cascade(menu=system_menu) # Gets index 0 self.w.bind('', self.copy) self.w.protocol("WM_DELETE_WINDOW", self.onexit) - theme.register(menubar) # menus and children aren't automatically registered - theme.register(file_menu) + theme.register(self.menubar) # menus and children aren't automatically registered + theme.register(self.file_menu) theme.register(self.edit_menu) # Alternate title bar and menu for dark theme - theme_menubar = tk.Frame(frame) - theme_menubar.columnconfigure(2, weight=1) - theme_titlebar = tk.Label(theme_menubar, text=applongname, image=self.theme_icon, anchor=tk.W, compound=tk.LEFT) + self.theme_menubar = tk.Frame(frame) + self.theme_menubar.columnconfigure(2, weight=1) + theme_titlebar = tk.Label(self.theme_menubar, text=applongname, image=self.theme_icon, anchor=tk.W, compound=tk.LEFT) theme_titlebar.grid(columnspan=3, sticky=tk.NSEW) self.drag_offset = None theme_titlebar.bind('', self.drag_start) theme_titlebar.bind('', self.drag_continue) theme_titlebar.bind('', self.drag_end) if platform == 'win32': # Can't work out how to deiconify on Linux - theme_minimize = tk.Label(theme_menubar, image=self.theme_minimize) + theme_minimize = tk.Label(self.theme_menubar, image=self.theme_minimize) theme_minimize.grid(row=0, column=3) theme.button_bind(theme_minimize, self.oniconify, image=self.theme_minimize) - theme_close = tk.Label(theme_menubar, image=self.theme_close) + theme_close = tk.Label(self.theme_menubar, image=self.theme_close) theme_close.grid(row=0, column=4) theme.button_bind(theme_close, self.onexit, image=self.theme_close) - theme_file_menu = tk.Label(theme_menubar, text=_('File'), anchor=tk.W) # Menu title on Windows - theme_file_menu.grid(row=1, column=0, padx=5, sticky=tk.W) - theme.button_bind(theme_file_menu, lambda e: file_menu.tk_popup(e.widget.winfo_rootx(), e.widget.winfo_rooty() + e.widget.winfo_height())) - theme_edit_menu = tk.Label(theme_menubar, text=_('Edit'), anchor=tk.W) # Menu title - theme_edit_menu.grid(row=1, column=1, sticky=tk.W) - theme.button_bind(theme_edit_menu, lambda e: self.edit_menu.tk_popup(e.widget.winfo_rootx(), e.widget.winfo_rooty() + e.widget.winfo_height())) + self.theme_file_menu = tk.Label(self.theme_menubar, anchor=tk.W) + self.theme_file_menu.grid(row=1, column=0, padx=5, sticky=tk.W) + theme.button_bind(self.theme_file_menu, lambda e: self.file_menu.tk_popup(e.widget.winfo_rootx(), e.widget.winfo_rooty() + e.widget.winfo_height())) + self.theme_edit_menu = tk.Label(self.theme_menubar, anchor=tk.W) # Menu title + self.theme_edit_menu.grid(row=1, column=1, sticky=tk.W) + theme.button_bind(self.theme_edit_menu, lambda e: self.edit_menu.tk_popup(e.widget.winfo_rootx(), e.widget.winfo_rooty() + e.widget.winfo_height())) theme.register_highlight(theme_titlebar) theme.register(self.theme_minimize) # images aren't automatically registered theme.register(self.theme_close) - theme.register_alternate((menubar, theme_menubar), {'row':0, 'columnspan':2, 'sticky':tk.NSEW}) + theme.register_alternate((self.menubar, self.theme_menubar), {'row':0, 'columnspan':2, 'sticky':tk.NSEW}) + + self.set_labels() # update geometry if config.get('geometry'): @@ -228,18 +236,44 @@ class AppWindow: # First run if not config.get('username') or not config.get('password'): - prefs.PreferencesDialog(self.w, self.login) + prefs.PreferencesDialog(self.w, self.postprefs) else: self.login() - # call after credentials have changed + # callback after the Preferences dialog is applied + def postprefs(self): + self.set_labels() # in case language has changed + self.login() # in case credentials gave changed + + # set main window labels, e.g. after language change + def set_labels(self): + self.cmdr_label['text'] = _('Cmdr') + ':' # Main window + self.system_label['text'] = _('System') + ':' # Main window + self.station_label['text'] = _('Station') + ':' # Main window + self.button['text'] = self.theme_button['text'] = _('Update') # Update button in main window + self.edit_menu.entryconfigure(0, label=_('Copy')) # As in Copy and Paste + self.view_menu.entryconfigure(0, label=_('Status')) # Menu item + self.file_menu.entryconfigure(1, label=_("Check for Updates...")) # Menu item + if platform == 'darwin': + self.file_menu.entryconfigure(0, label=_("About {APP}").format(APP=applongname)) # App menu entry on OSX + self.menubar.entryconfigure(1, label=_('View')) # Menu title on OSX + self.menubar.entryconfigure(2, label=_('Edit')) # Menu title + self.menubar.entryconfigure(3, label=_('Window')) # Menu title on OSX + else: + self.file_menu.entryconfigure(2, label=_("Settings")) # Item in the File menu on Windows + self.file_menu.entryconfigure(4, label=_("Exit")) # Item in the File menu on Windows + self.menubar.entryconfigure(self.menubar.index('end')-2, label=_('File')) # Menu title on Windows + self.menubar.entryconfigure(self.menubar.index('end')-1, label=_('Edit')) # Menu title + self.theme_file_menu['text'] = _('File') # Menu title on Windows + self.theme_edit_menu['text'] = _('Edit') # Menu title + def login(self): self.status['text'] = _('Logging in...') self.button['state'] = self.theme_button['state'] = tk.DISABLED self.w.update_idletasks() try: self.session.login(config.get('username'), config.get('password')) - self.view_menu.entryconfigure(_('Status'), state=tk.NORMAL) + self.view_menu.entryconfigure(0, state=tk.NORMAL) # Status self.status['text'] = '' except companion.VerificationRequired: # don't worry about authentication now - prompt on query @@ -294,7 +328,7 @@ class AppWindow: self.system['image'] = '' self.status['text'] = _('Fetching data...') self.theme_button['state'] = tk.DISABLED - self.edit_menu.entryconfigure(_('Copy'), state=tk.DISABLED) + self.edit_menu.entryconfigure(0, state=tk.DISABLED) # Copy self.w.update_idletasks() try: @@ -320,8 +354,8 @@ class AppWindow: self.system['text'] = data.get('lastSystem') and data.get('lastSystem').get('name') or '' self.station['text'] = data.get('commander') and data.get('commander').get('docked') and data.get('lastStarport') and data.get('lastStarport').get('name') or (EDDB.system(self.system['text']) and self.STATION_UNDOCKED or '') self.status['text'] = '' - self.edit_menu.entryconfigure(_('Copy'), state=tk.NORMAL) - self.view_menu.entryconfigure(_('Status'), state=tk.NORMAL) + self.edit_menu.entryconfigure(0, state=tk.NORMAL) # Copy + self.view_menu.entryconfigure(0, state=tk.NORMAL) # Status if data['lastStarport'].get('commodities'): # Fixup anomalies in the commodity data diff --git a/L10n/cs.strings b/L10n/cs.strings old mode 100644 new mode 100755 index 74754cb0..ed26f0a6 --- a/L10n/cs.strings +++ b/L10n/cs.strings @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "Čeština"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "Ověřovací kód byl odeslán na{CR}e-mailovou adresu spojenou s vašim Elite účtem."; @@ -49,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Změnit..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Zkontrolovat aktualizace..."; /* Federation rank. [stats.py] */ @@ -97,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Dealer"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "Výchozí"; /* Empire rank. [stats.py] */ @@ -205,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Knight"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Jazyk"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Naposledy aktualizováno {HH}:{MM}:{SS}"; diff --git a/L10n/de.strings b/L10n/de.strings old mode 100644 new mode 100755 index 26176d25..1303dcf0 --- a/L10n/de.strings +++ b/L10n/de.strings @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "Deutsch"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "Ein Verifizierungscode wurde an die mit{CR}Elite verknüpfte Email Adresse gesendet."; @@ -49,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Ordner..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Auf Aktualisierungen überprüfen..."; /* Federation rank. [stats.py] */ @@ -97,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Kleinhändler"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "Standard"; /* Empire rank. [stats.py] */ @@ -205,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Ritter"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Sprache"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Zuletzt aktualisiert um {HH}:{MM}:{SS}"; diff --git a/L10n/en.template b/L10n/en.template index bb5a5ba4..eff3fcbf 100644 --- a/L10n/en.template +++ b/L10n/en.template @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "English"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "A verification code has now been sent to the{CR}email address associated with your Elite account."; @@ -49,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Change..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Check for Updates..."; /* Federation rank. [stats.py] */ @@ -97,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Dealer"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "Default"; /* Empire rank. [stats.py] */ @@ -205,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Knight"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Language"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Last updated at {HH}:{MM}:{SS}"; diff --git a/L10n/es.strings b/L10n/es.strings old mode 100644 new mode 100755 index fae90dd4..05114d98 --- a/L10n/es.strings +++ b/L10n/es.strings @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "Español"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "Se ha enviado el código de verificación a la{CR}dirección de correo electrónico asociada a tu cuenta de Frontier."; @@ -49,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Cambiar..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Buscar Actualizaciones..."; /* Federation rank. [stats.py] */ @@ -97,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Distribuidor"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "Por defecto"; /* Empire rank. [stats.py] */ @@ -205,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Caballero"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Idioma"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Última actualización: {HH}:{MM}:{SS}"; diff --git a/L10n/fr.strings b/L10n/fr.strings old mode 100644 new mode 100755 index 4105e632..338046c3 --- a/L10n/fr.strings +++ b/L10n/fr.strings @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "Français"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "Le code de vérification a été envoyé à l'adresse{CR}e-mail associée à votre compte Elite: Dangerous."; @@ -49,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Spécifier..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Chercher une mise à jour..."; /* Federation rank. [stats.py] */ @@ -67,6 +70,9 @@ /* Combat rank. [stats.py] */ "Competent" = "Compétent"; +/* Output settings. [prefs.py] */ +"Connected to {EDPROXY} at {ADDR}" = "Connecté à {EDPROXY} sur {ADDR}"; + /* Update button in main window. [EDMarketConnector.py] */ "cooldown {SS}s" = "Temps de recharge {SS}s"; @@ -94,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Revendeur"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "Par défaut"; /* Empire rank. [stats.py] */ @@ -142,6 +148,9 @@ /* [companion.py] */ "Error: Server is down" = "Erreur : Le serveur est indisponible"; +/* [companion.py] */ +"Error: Verification failed" = "Erreur : La vérification a échouée"; + /* Item in the File menu on Windows. [EDMarketConnector.py] */ "Exit" = "Quitter"; @@ -178,6 +187,9 @@ /* CQC rank. [stats.py] */ "Hero" = "Hero"; +/* Dark theme color setting. [prefs.py] */ +"Highlighted text" = "Texte en surbrillance"; + /* Tab heading in settings on Windows. [prefs.py] */ "Hotkey" = "Raccourci clavier"; @@ -196,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Chevalier"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Langue"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Dernière mise à jour à {HH}:{MM}:{SS}"; @@ -250,6 +265,9 @@ /* No hotkey/shortcut currently defined. [prefs.py] */ "None" = "Aucun"; +/* Dark theme color setting. [prefs.py] */ +"Normal text" = "Texte normal"; + /* Combat rank. [stats.py] */ "Novice" = "Novice"; diff --git a/L10n/it.strings b/L10n/it.strings old mode 100644 new mode 100755 index 609b1ffc..52105feb --- a/L10n/it.strings +++ b/L10n/it.strings @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "Italiano"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "Un codice di verifica è stato inviato alla email{CR}associata al tuo account di Elite Dangerous."; @@ -49,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Cambia..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Controlla aggiornamenti..."; /* Federation rank. [stats.py] */ @@ -67,6 +70,9 @@ /* Combat rank. [stats.py] */ "Competent" = "Competent"; +/* Output settings. [prefs.py] */ +"Connected to {EDPROXY} at {ADDR}" = "Connesso a {EDPROXY} all'indirizzo {ADDR}"; + /* Update button in main window. [EDMarketConnector.py] */ "cooldown {SS}s" = "disponibile tra {SS} secondi"; @@ -94,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Dealer"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "Predefinito"; /* Empire rank. [stats.py] */ @@ -142,6 +148,9 @@ /* [companion.py] */ "Error: Server is down" = "Errore: Il Server è offline"; +/* [companion.py] */ +"Error: Verification failed" = "Errore: fallita Verifica"; + /* Item in the File menu on Windows. [EDMarketConnector.py] */ "Exit" = "Esci"; @@ -178,6 +187,9 @@ /* CQC rank. [stats.py] */ "Hero" = "Hero"; +/* Dark theme color setting. [prefs.py] */ +"Highlighted text" = "Testo evidenziato"; + /* Tab heading in settings on Windows. [prefs.py] */ "Hotkey" = "Hotkey"; @@ -196,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Knight"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Lingua"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Ultimo aggiornamento alle {HH}:{MM}:{SS}"; @@ -250,6 +265,9 @@ /* No hotkey/shortcut currently defined. [prefs.py] */ "None" = "Nessuna"; +/* Dark theme color setting. [prefs.py] */ +"Normal text" = "Testo normale"; + /* Combat rank. [stats.py] */ "Novice" = "Novice"; diff --git a/L10n/lv.strings b/L10n/lv.strings old mode 100644 new mode 100755 index 6c39abf0..ba7fcd01 --- a/L10n/lv.strings +++ b/L10n/lv.strings @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "Latviešu"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "Apstiprinājuma kods ir nosūtīts uz {CR} e-pasta adresi, kas ir saistīts ar Jūsu Elite profilu."; @@ -49,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Mainīt..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Pārbaudīt atjauninājumus..."; /* Federation rank. [stats.py] */ @@ -67,6 +70,9 @@ /* Combat rank. [stats.py] */ "Competent" = "Competent"; +/* Output settings. [prefs.py] */ +"Connected to {EDPROXY} at {ADDR}" = "Connected to {EDPROXY} at {ADDR}"; + /* Update button in main window. [EDMarketConnector.py] */ "cooldown {SS}s" = "Atskaite {SS}"; @@ -94,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Dealer"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "Noklusējuma"; /* Empire rank. [stats.py] */ @@ -142,6 +148,9 @@ /* [companion.py] */ "Error: Server is down" = "Kļūda: Serveris nav aktīvs"; +/* [companion.py] */ +"Error: Verification failed" = "Error: Verification failed"; + /* Item in the File menu on Windows. [EDMarketConnector.py] */ "Exit" = "Iziet"; @@ -178,6 +187,9 @@ /* CQC rank. [stats.py] */ "Hero" = "Varonis"; +/* Dark theme color setting. [prefs.py] */ +"Highlighted text" = "Highlighted text"; + /* Tab heading in settings on Windows. [prefs.py] */ "Hotkey" = "Ātrais taustiņš"; @@ -196,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Bruņinieks"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Valoda"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Pēdējo reizi atjaunināts {HH}:{MM}:{SS}"; @@ -250,6 +265,9 @@ /* No hotkey/shortcut currently defined. [prefs.py] */ "None" = "Neviens"; +/* Dark theme color setting. [prefs.py] */ +"Normal text" = "Normal text"; + /* Combat rank. [stats.py] */ "Novice" = "Novice"; diff --git a/L10n/nl.strings b/L10n/nl.strings old mode 100644 new mode 100755 index f9e323b4..e0fefc35 --- a/L10n/nl.strings +++ b/L10n/nl.strings @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "Nederlands"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "Een verificatie code is zojuist verstuurd naar het{CR}e-mail adres behorende bij je Elite account"; @@ -49,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Wijzigen..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Controleren op updates..."; /* Federation rank. [stats.py] */ @@ -97,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Dealer"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "Standaard"; /* Empire rank. [stats.py] */ @@ -184,6 +187,9 @@ /* CQC rank. [stats.py] */ "Hero" = "Hero"; +/* Dark theme color setting. [prefs.py] */ +"Highlighted text" = "Highlighted text"; + /* Tab heading in settings on Windows. [prefs.py] */ "Hotkey" = "Hotkey"; @@ -202,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Knight"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Taal"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Voor het laatst ververst om {HH}:{MM}:{SS}"; @@ -256,6 +265,9 @@ /* No hotkey/shortcut currently defined. [prefs.py] */ "None" = "Geen"; +/* Dark theme color setting. [prefs.py] */ +"Normal text" = "Normal text"; + /* Combat rank. [stats.py] */ "Novice" = "Novice"; diff --git a/L10n/pl.strings b/L10n/pl.strings old mode 100644 new mode 100755 index b09703ab..d7b91686 --- a/L10n/pl.strings +++ b/L10n/pl.strings @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "Polski"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "Kod weryfikacyjny został wysłany na adres{CR}e-mail powiązany z Twoim kontem w Elite."; @@ -49,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Zmień..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Sprawdź aktualizacje..."; /* Federation rank. [stats.py] */ @@ -97,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Dealer"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "Domyślne"; /* Empire rank. [stats.py] */ @@ -205,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Knight"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Język"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Ostatnia aktualizacja {HH}:{MM}:{SS}"; diff --git a/L10n/ru.strings b/L10n/ru.strings old mode 100644 new mode 100755 index 4d901e2b..f2f3a799 --- a/L10n/ru.strings +++ b/L10n/ru.strings @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "Русский"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "Проверочный код был выслан на адрес электронной почты,{CR}указанный в вашей учетной записи Elite: Dangerous."; @@ -49,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Обзор..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Поиск обновлений..."; /* Federation rank. [stats.py] */ @@ -67,6 +70,9 @@ /* Combat rank. [stats.py] */ "Competent" = "Специалист"; +/* Output settings. [prefs.py] */ +"Connected to {EDPROXY} at {ADDR}" = "Connected to {EDPROXY} at {ADDR}"; + /* Update button in main window. [EDMarketConnector.py] */ "cooldown {SS}s" = "пауза {SS} сек"; @@ -94,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Агент"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "По умолчанию"; /* Empire rank. [stats.py] */ @@ -142,6 +148,9 @@ /* [companion.py] */ "Error: Server is down" = "Ошибка: сервер не отвечает"; +/* [companion.py] */ +"Error: Verification failed" = "Error: Verification failed"; + /* Item in the File menu on Windows. [EDMarketConnector.py] */ "Exit" = "Выход"; @@ -178,6 +187,9 @@ /* CQC rank. [stats.py] */ "Hero" = "Герой"; +/* Dark theme color setting. [prefs.py] */ +"Highlighted text" = "Highlighted text"; + /* Tab heading in settings on Windows. [prefs.py] */ "Hotkey" = "Горячая клавиша"; @@ -196,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Рыцарь"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Язык"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Последнее обновление {HH}:{MM}:{SS}"; @@ -250,6 +265,9 @@ /* No hotkey/shortcut currently defined. [prefs.py] */ "None" = "Не назначена"; +/* Dark theme color setting. [prefs.py] */ +"Normal text" = "Normal text"; + /* Combat rank. [stats.py] */ "Novice" = "Новичок"; diff --git a/L10n/sl.strings b/L10n/sl.strings old mode 100644 new mode 100755 index daf35b08..b677f7d2 --- a/L10n/sl.strings +++ b/L10n/sl.strings @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "Slovenščina"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "Koda za preverjanje istovetnosti{CR}je bila poslana na e-poštni naslov."; @@ -25,6 +28,9 @@ /* Output setting. [prefs.py] */ "Automatically make a log entry on entering a system" = "Shrani podatke v dnevnik leta, ko prispeš v sistem"; +/* [prefs.py] */ +"Automatically open uncharted systems’ EDSM pages" = "Automatically open uncharted systems’ EDSM pages"; + /* Cmdr stats. [stats.py] */ "Balance" = "Ravnotežje"; @@ -46,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Spremeni..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Preveri posodobitve..."; /* Federation rank. [stats.py] */ @@ -94,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Dealer"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "Privzeta"; /* Empire rank. [stats.py] */ @@ -142,6 +148,9 @@ /* [companion.py] */ "Error: Server is down" = "Napaka: Strežnik ni na voljo"; +/* [companion.py] */ +"Error: Verification failed" = "Error: Verification failed"; + /* Item in the File menu on Windows. [EDMarketConnector.py] */ "Exit" = "Izhod"; @@ -178,6 +187,9 @@ /* CQC rank. [stats.py] */ "Hero" = "Hero"; +/* Dark theme color setting. [prefs.py] */ +"Highlighted text" = "Highlighted text"; + /* Tab heading in settings on Windows. [prefs.py] */ "Hotkey" = "Bližnjica"; @@ -196,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Knight"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Jezik"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Zadnja osvežitev podatkov {HH}:{MM}:{SS} "; @@ -250,6 +265,9 @@ /* No hotkey/shortcut currently defined. [prefs.py] */ "None" = "Brez"; +/* Dark theme color setting. [prefs.py] */ +"Normal text" = "Normal text"; + /* Combat rank. [stats.py] */ "Novice" = "Novice"; diff --git a/L10n/uk.strings b/L10n/uk.strings old mode 100644 new mode 100755 index 29459134..ae5e877b --- a/L10n/uk.strings +++ b/L10n/uk.strings @@ -1,3 +1,6 @@ +/* Language name */ +"!Language" = "Українська"; + /* Use same text as E:D Launcher's verification dialog. [prefs.py] */ "A verification code has now been sent to the{CR}email address associated with your Elite account." = "Код підтвердження був відправлений на адресу {CR}, що пов'язана з Вашим обліковим записом Elite."; @@ -49,7 +52,7 @@ /* Folder selection button on OSX. [prefs.py] */ "Change..." = "Внесення змін..."; -/* [EDMarketConnector.py] */ +/* Menu item. [EDMarketConnector.py] */ "Check for Updates..." = "Перевірка оновлень..."; /* Federation rank. [stats.py] */ @@ -97,7 +100,7 @@ /* Trade rank. [stats.py] */ "Dealer" = "Дилер"; -/* Appearance theme setting. [prefs.py] */ +/* Appearance theme and language setting. [l10n.py] */ "Default" = "Стандарт"; /* Empire rank. [stats.py] */ @@ -205,6 +208,9 @@ /* Empire rank. [stats.py] */ "Knight" = "Лицар"; +/* Appearance setting prompt. [prefs.py] */ +"Language" = "Мова"; + /* [EDMarketConnector.py] */ "Last updated at {HH}:{MM}:{SS}" = "Останнє оновлення було {HH}:{MM}:{SS}"; diff --git a/companion.py b/companion.py index a250bf96..b2c5b6e5 100644 --- a/companion.py +++ b/companion.py @@ -169,12 +169,18 @@ class Session: pass def login(self, username=None, password=None): - self.state = Session.STATE_INIT if (not username or not password): if not self.credentials: raise CredentialsError() + elif self.state == Session.STATE_OK: + return # already logged in else: - self.credentials = { 'email' : username, 'password' : password } + credentials = { 'email' : username, 'password' : password } + if self.credentials == credentials and self.state == Session.STATE_OK: + return # already logged in + else: + self.credentials = credentials + self.state = Session.STATE_INIT try: r = self.session.post(URL_LOGIN, data = self.credentials, timeout=timeout) except: diff --git a/l10n.py b/l10n.py index 532175d3..fce95b40 100755 --- a/l10n.py +++ b/l10n.py @@ -4,6 +4,7 @@ # import codecs +from collections import OrderedDict import os from os.path import dirname, isfile, join, normpath import re @@ -12,49 +13,64 @@ from sys import platform import __builtin__ +# Language name +LANGUAGE_ID = '!Language' + + class Translations: FALLBACK = 'en' # strings in this code are in English + FALLBACK_NAME = 'English' + + TRANS_RE = re.compile(r'\s*"([^"]+)"\s*=\s*"([^"]+)"\s*;\s*$') + COMMENT_RE = re.compile(r'\s*/\*.*\*/\s*$') + def __init__(self): self.translations = {} def install_dummy(self): # For when translation is not desired or not available + self.translations = {} # not used __builtin__.__dict__['_'] = lambda x: unicode(x).replace(u'{CR}', u'\n') # Promote strings to Unicode for consistency - def install(self): + def install(self, lang=None): available = self.available() available.add(Translations.FALLBACK) - for preferred in self.preferred(): - if preferred in available: - lang = preferred - break - else: + if not lang: for preferred in self.preferred(): - preferred = preferred.split('-',1)[0] # just base language if preferred in available: lang = preferred break else: - lang = Translations.FALLBACK + for preferred in self.preferred(): + preferred = preferred.split('-',1)[0] # just base language + if preferred in available: + lang = preferred + break if lang not in self.available(): self.install_dummy() else: - regexp = re.compile(r'\s*"([^"]+)"\s*=\s*"([^"]+)"\s*;\s*$') - comment= re.compile(r'\s*/\*.*\*/\s*$') - with self.file(lang) as h: - for line in h: - if line.strip(): - match = regexp.match(line) - if match: - self.translations[match.group(1)] = match.group(2).replace(u'{CR}', u'\n') - elif __debug__ and not comment.match(line): - print 'Bad translation: %s' % line.strip() + self.translations = self.contents(lang) __builtin__.__dict__['_'] = self.translate + def contents(self, lang): + assert lang in self.available() + translations = {} + with self.file(lang) as h: + for line in h: + if line.strip(): + match = Translations.TRANS_RE.match(line) + if match: + translations[match.group(1)] = match.group(2).replace(u'{CR}', u'\n') + elif __debug__ and not Translations.COMMENT_RE.match(line): + print 'Bad translation: %s' % line.strip() + if translations.get(LANGUAGE_ID, LANGUAGE_ID) == LANGUAGE_ID: + translations[LANGUAGE_ID] = unicode(lang) # Replace language name with code if missing + return translations + if __debug__: def translate(self, x): if not self.translations.get(x): @@ -75,6 +91,16 @@ class Translations: available = set([x[:-len('.strings')] for x in os.listdir(path) if x.endswith('.strings')]) return available + # Available language names by code + def available_names(self): + names = OrderedDict([ + (None, _('Default')), # Appearance theme and language setting + ]) + names.update(sorted([(lang, self.contents(lang).get(LANGUAGE_ID, lang)) for lang in self.available()] + + [(Translations.FALLBACK, Translations.FALLBACK_NAME)], + key=lambda x: x[1])) # Sort by name + return names + # Returns list of preferred language codes in lowercase RFC4646 format. # Typically "lang[-script][-region]" where lang is a 2 alpha ISO 639-1 or 3 alpha ISO 639-2 code, # script is a 4 alpha ISO 15924 code and region is a 2 alpha ISO 3166 code @@ -149,6 +175,7 @@ if __name__ == "__main__": seen[match.group(2)] = (match.group(4) and (match.group(4)[1:].strip()) + '. ' or '') + '[%s]' % f if seen: template = codecs.open('L10n/en.template', 'w', 'utf-8') + template.write('/* Language name */\n"%s" = "%s";\n\n' % (LANGUAGE_ID, 'English')) for thing in sorted(seen, key=unicode.lower): if seen[thing]: template.write('/* %s */\n' % (seen[thing])) diff --git a/prefs.py b/prefs.py index bba7f4ff..e2dddf98 100644 --- a/prefs.py +++ b/prefs.py @@ -13,6 +13,7 @@ import myNotebook as nb from config import applongname, config from edproxy import edproxy from hotkey import hotkeymgr +from l10n import Translations from monitor import monitor from theme import theme @@ -218,6 +219,8 @@ class PreferencesDialog(tk.Toplevel): _('Keyboard shortcut') or # Tab heading in settings on OSX _('Hotkey')) # Tab heading in settings on Windows + self.languages = Translations().available_names() + self.lang = tk.StringVar(value = self.languages.get(config.get('language'), _('Default'))) # Appearance theme and language setting self.always_ontop = tk.BooleanVar(value = config.getint('always_ontop')) self.theme = tk.IntVar(value = config.getint('theme') and 1 or 0) self.theme_colors = [config.get('dark_text'), config.get('dark_highlight')] @@ -227,21 +230,24 @@ class PreferencesDialog(tk.Toplevel): ] themeframe = nb.Frame(notebook) themeframe.columnconfigure(2, weight=1) - nb.Label(themeframe).grid(sticky=tk.W) # big spacer - self.ontop_button = nb.Checkbutton(themeframe, text=_('Always on top'), variable=self.always_ontop, command=self.themevarchanged) - self.ontop_button.grid(columnspan=3, padx=BUTTONX, sticky=tk.W) # Appearance setting - ttk.Separator(themeframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY*8, sticky=tk.EW) + nb.Label(themeframe, text=_('Language')).grid(row=10, padx=PADX, sticky=tk.W) # Appearance setting prompt + self.lang_button = nb.OptionMenu(themeframe, self.lang, self.lang.get(), *self.languages.values()) + self.lang_button.grid(row=10, column=1, columnspan=2, padx=PADX, sticky=tk.W) + ttk.Separator(themeframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY*6, sticky=tk.EW) nb.Label(themeframe, text=_('Theme')).grid(columnspan=3, padx=PADX, sticky=tk.W) # Appearance setting - nb.Radiobutton(themeframe, text=_('Default'), variable=self.theme, value=0, command=self.themevarchanged).grid(columnspan=3, padx=BUTTONX, sticky=tk.W) # Appearance theme setting + nb.Radiobutton(themeframe, text=_('Default'), variable=self.theme, value=0, command=self.themevarchanged).grid(columnspan=3, padx=BUTTONX, sticky=tk.W) # Appearance theme and language setting nb.Radiobutton(themeframe, text=_('Dark'), variable=self.theme, value=1, command=self.themevarchanged).grid(columnspan=3, padx=BUTTONX, sticky=tk.W) # Appearance theme setting self.theme_label_0 = nb.Label(themeframe, text=self.theme_prompts[0]) - self.theme_label_0.grid(row=10, padx=PADX, sticky=tk.W) + self.theme_label_0.grid(row=20, padx=PADX, sticky=tk.W) self.theme_button_0 = nb.ColoredButton(themeframe, text=_('Station'), background='black', command=lambda:self.themecolorbrowse(0)) # Main window - self.theme_button_0.grid(row=10, column=1, padx=PADX, pady=PADY, sticky=tk.NSEW) + self.theme_button_0.grid(row=20, column=1, padx=PADX, pady=PADY, sticky=tk.NSEW) self.theme_label_1 = nb.Label(themeframe, text=self.theme_prompts[1]) - self.theme_label_1.grid(row=11, padx=PADX, sticky=tk.W) + self.theme_label_1.grid(row=21, padx=PADX, sticky=tk.W) self.theme_button_1 = nb.ColoredButton(themeframe, text=' Hutton Orbital ', background='black', command=lambda:self.themecolorbrowse(1)) # Do not translate - self.theme_button_1.grid(row=11, column=1, padx=PADX, pady=PADY, sticky=tk.NSEW) + self.theme_button_1.grid(row=21, column=1, padx=PADX, pady=PADY, sticky=tk.NSEW) + ttk.Separator(themeframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY*6, sticky=tk.EW) + self.ontop_button = nb.Checkbutton(themeframe, text=_('Always on top'), variable=self.always_ontop, command=self.themevarchanged) + self.ontop_button.grid(columnspan=3, padx=BUTTONX, sticky=tk.W) # Appearance setting notebook.add(themeframe, text=_('Appearance')) # Tab heading in settings @@ -445,6 +451,10 @@ class PreferencesDialog(tk.Toplevel): config.set('hotkey_always', int(not self.hotkey_only.get())) config.set('hotkey_mute', int(not self.hotkey_play.get())) + lang_codes = { v: k for k, v in self.languages.iteritems() } # Codes by name + config.set('language', lang_codes.get(self.lang.get()) or '') + Translations().install(config.get('language') or None) + config.set('always_ontop', self.always_ontop.get()) config.set('theme', self.theme.get()) config.set('dark_text', self.theme_colors[0]) @@ -454,7 +464,7 @@ class PreferencesDialog(tk.Toplevel): config.set('anonymous', self.out_anon.get()) self._destroy() - if credentials != (config.get('username'), config.get('password')) and self.callback: + if self.callback: self.callback() def _destroy(self):