diff --git a/electrum/gui/default_lang.py b/electrum/gui/default_lang.py new file mode 100644 index 000000000..ceb030ca8 --- /dev/null +++ b/electrum/gui/default_lang.py @@ -0,0 +1,40 @@ +# Copyright (C) 2023 The Electrum developers +# Distributed under the MIT software license, see the accompanying +# file LICENCE or http://www.opensource.org/licenses/mit-license.php +# +# Note: try not to import modules from electrum, or at least from GUIs. +# This is to avoid evaluating module-level string-translations before we get +# a chance to set the default language. + +import os +from typing import Optional + +from electrum.i18n import languages + + +jLocale = None +if "ANDROID_DATA" in os.environ: + from jnius import autoclass, cast + jLocale = autoclass("java.util.Locale") + + +def get_default_language(*, gui_name: Optional[str] = None) -> str: + if gui_name == "qt": + from PyQt5.QtCore import QLocale + name = QLocale.system().name() + return name if name in languages else "en_UK" + elif gui_name == "qml": + from PyQt5.QtCore import QLocale + # On Android QLocale does not return the system locale + try: + name = str(jLocale.getDefault().toString()) + except Exception: + name = QLocale.system().name() + return name if name in languages else "en_GB" + elif gui_name == "kivy": + if "ANDROID_DATA" not in os.environ: + return "en_UK" + # FIXME: CJK/Arabic/etc languages do not work at all with kivy due to font issues, + # so it is easiest to just default to English... (see #2032) + return "en_UK" + return "" diff --git a/electrum/gui/kivy/i18n.py b/electrum/gui/kivy/i18n.py index 76cf2a09e..5b33afee2 100644 --- a/electrum/gui/kivy/i18n.py +++ b/electrum/gui/kivy/i18n.py @@ -1,5 +1,10 @@ import gettext +from electrum.logging import get_logger + + +_logger = get_logger(__name__) + class _(str): @@ -35,6 +40,7 @@ class _(str): @staticmethod def switch_lang(lang): + _logger.info(f"switch_lang() called with {lang=!r}") # get the right locales directory, and instantiate a gettext from electrum.i18n import LOCALE_DIR, set_language locales = gettext.translation('electrum', LOCALE_DIR, languages=[lang], fallback=True) diff --git a/electrum/gui/kivy/util.py b/electrum/gui/kivy/util.py index 7320a1fae..60681025a 100644 --- a/electrum/gui/kivy/util.py +++ b/electrum/gui/kivy/util.py @@ -1,4 +1,6 @@ -from kivy.utils import get_color_from_hex, platform +from kivy.utils import get_color_from_hex + +from electrum.gui.default_lang import get_default_language as _get_default_language def address_colors(wallet, addr): @@ -24,13 +26,4 @@ def address_colors(wallet, addr): def get_default_language() -> str: - if platform != 'android': - return 'en_UK' - # FIXME: CJK/Arabic/etc languages do not work at all with kivy due to font issues, - # so it is easiest to just default to English... (see #2032) - return 'en_UK' - # # try getting the language of the Android OS - # from jnius import autoclass - # Locale = autoclass("java.util.Locale") - # lang = str(Locale.getDefault().toString()) - # return lang if lang else 'en_UK' + return _get_default_language(gui_name="kivy") diff --git a/electrum/gui/qml/__init__.py b/electrum/gui/qml/__init__.py index 5d5bf5045..9d36f369b 100644 --- a/electrum/gui/qml/__init__.py +++ b/electrum/gui/qml/__init__.py @@ -33,9 +33,6 @@ if TYPE_CHECKING: from .qeapp import ElectrumQmlApplication, Exception_Hook -if 'ANDROID_DATA' in os.environ: - from jnius import autoclass, cast - jLocale = autoclass("java.util.Locale") class ElectrumTranslator(QTranslator): def __init__(self, parent=None): @@ -51,12 +48,6 @@ class ElectrumGui(BaseElectrumGui, Logger): BaseElectrumGui.__init__(self, config=config, daemon=daemon, plugins=plugins) Logger.__init__(self) - lang = config.get('language','') - if not lang: - lang = self.get_default_language() - self.logger.info(f'setting language {lang}') - set_language(lang) - # uncomment to debug plugin and import tracing # os.environ['QML_IMPORT_TRACE'] = '1' # os.environ['QT_DEBUG_PLUGINS'] = '1' @@ -119,12 +110,3 @@ class ElectrumGui(BaseElectrumGui, Logger): def stop(self): self.logger.info('closing GUI') self.app.quit() - - def get_default_language(self): - # On Android QLocale does not return the system locale - try: - name = str(jLocale.getDefault().toString()) - except Exception: - name = QLocale.system().name() - self.logger.info(f'System default locale: {name}') - return name if name in languages else 'en_GB' diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py index 890475fe0..326d70bd7 100644 --- a/electrum/gui/qt/__init__.py +++ b/electrum/gui/qt/__init__.py @@ -65,7 +65,7 @@ from electrum.logging import Logger from electrum.gui import BaseElectrumGui from .installwizard import InstallWizard, WalletAlreadyOpenInMemory -from .util import get_default_language, read_QIcon, ColorScheme, custom_message_box, MessageBoxMixin +from .util import read_QIcon, ColorScheme, custom_message_box, MessageBoxMixin from .main_window import ElectrumWindow from .network_dialog import NetworkDialog from .stylesheet_patcher import patch_qt_stylesheet @@ -111,7 +111,6 @@ class ElectrumGui(BaseElectrumGui, Logger): @profiler def __init__(self, *, config: 'SimpleConfig', daemon: 'Daemon', plugins: 'Plugins'): - set_language(config.get('language', get_default_language())) BaseElectrumGui.__init__(self, config=config, daemon=daemon, plugins=plugins) Logger.__init__(self) self.logger.info(f"Qt GUI starting up... Qt={QtCore.QT_VERSION_STR}, PyQt={QtCore.PYQT_VERSION_STR}") diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py index 3ba3bcebc..653d35288 100644 --- a/electrum/gui/qt/util.py +++ b/electrum/gui/qt/util.py @@ -1117,10 +1117,6 @@ class IconLabel(QWidget): self.icon.setPixmap(icon.pixmap(self.icon_size)) self.icon.repaint() # macOS hack for #6269 -def get_default_language(): - name = QLocale.system().name() - return name if name in languages else 'en_UK' - def char_width_in_lineedit() -> int: char_width = QFontMetrics(QLineEdit().font()).averageCharWidth() diff --git a/run_electrum b/run_electrum index 7c363d539..aaf6ea161 100755 --- a/run_electrum +++ b/run_electrum @@ -384,14 +384,22 @@ def main(): sys.exit(1) config = SimpleConfig(config_options) + cmdname = config.get('cmd') # set language as early as possible # Note: we are already too late for strings that are declared in the global scope # of an already imported module. However, the GUI and the plugins at least have - # not been imported yet. - # Note: it is ok to call set_language() again later. E.g. the Qt GUI has additional - # tools to figure out the default language so it will get called again there. - set_language(config.get('language')) + # not been imported yet. (see #4621) + # Note: it is ok to call set_language() again later, but note that any call only applies + # to not-yet-evaluated strings. + if cmdname == 'gui': + from electrum.gui.default_lang import get_default_language + gui_name = config.get('gui', 'qt') + lang = config.get('language') + if not lang: + lang = get_default_language(gui_name=gui_name) + _logger.info(f"get_default_language: detected default as {lang=!r}") + set_language(lang) if config.get('testnet'): constants.set_testnet() @@ -402,8 +410,6 @@ def main(): elif config.get('signet'): constants.set_signet() - cmdname = config.get('cmd') - if cmdname == 'daemon' and config.get("detach"): # detect lockfile. # This is not as good as get_file_descriptor, but that would require the asyncio loop