From efd8ae21f39141171aefdca6484c722e22ab0d3f Mon Sep 17 00:00:00 2001 From: Adam Kankovsky Date: Tue, 14 Jan 2025 10:28:32 +0100 Subject: [PATCH] Creating a dbus interface to get local keyboard layouts Adding dbus interface for loading locale keyboards Remove duplicate function and migrate to localization one --- pyanaconda/localization.py | 31 ++++++++++++++++++- .../modules/localization/localization.py | 26 ++++++++++++++++ .../localization/localization_interface.py | 13 ++++++++ pyanaconda/ui/gui/xkl_wrapper.py | 23 ++------------ 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/pyanaconda/localization.py b/pyanaconda/localization.py index 3e44ebb462a..0c1fd42bf26 100644 --- a/pyanaconda/localization.py +++ b/pyanaconda/localization.py @@ -25,7 +25,9 @@ import re from collections import namedtuple +import iso639 import langtable +from xkbregistry import rxkb from pyanaconda.anaconda_loggers import get_module_logger from pyanaconda.core import constants @@ -36,7 +38,7 @@ log = get_module_logger(__name__) SCRIPTS_SUPPORTED_BY_CONSOLE = {'Latn', 'Cyrl', 'Grek'} - +LayoutInfo = namedtuple("LayoutInfo", ["langs", "desc"]) class LocalizationConfigError(Exception): """Exception class for localization configuration related problems""" @@ -390,6 +392,33 @@ def get_territory_locales(territory): return langtable.list_locales(territoryId=territory) +def _build_layout_infos(): + """Build localized information for keyboard layouts. + + :param rxkb_context: RXKB context (e.g., rxkb.Context()) + :return: Dictionary with layouts and their descriptions + """ + rxkb_context = rxkb.Context() + layout_infos = {} + + for layout in rxkb_context.layouts.values(): + name = layout.name + if layout.variant: + name += f" ({layout.variant})" + + langs = [] + for lang in layout.iso639_codes: + if iso639.find(iso639_2=lang): + langs.append(iso639.to_name(lang)) + + if name not in layout_infos: + layout_infos[name] = LayoutInfo(langs, layout.description) + else: + layout_infos[name].langs.extend(langs) + + return layout_infos + + def get_locale_keyboards(locale): """Function returning preferred keyboard layouts for the given locale. diff --git a/pyanaconda/modules/localization/localization.py b/pyanaconda/modules/localization/localization.py index 627f39dc2f8..84fd531de1e 100644 --- a/pyanaconda/modules/localization/localization.py +++ b/pyanaconda/modules/localization/localization.py @@ -17,6 +17,7 @@ # License and may only be used or replicated with the express permission of # Red Hat, Inc. # +import gettext import langtable from pyanaconda.anaconda_loggers import get_module_logger @@ -24,6 +25,7 @@ from pyanaconda.core.dbus import DBus from pyanaconda.core.signal import Signal from pyanaconda.localization import ( + _build_layout_infos, get_available_translations, get_common_languages, get_english_name, @@ -50,6 +52,8 @@ log = get_module_logger(__name__) +Xkb_ = lambda x: gettext.translation("xkeyboard-config", fallback=True).gettext(x) +iso_ = lambda x: gettext.translation("iso_639", fallback=True).gettext(x) class LocalizationService(KickstartService): """The Localization service.""" @@ -80,6 +84,8 @@ def __init__(self): self.compositor_selected_layout_changed = Signal() self.compositor_layouts_changed = Signal() + self._layout_infos = _build_layout_infos() + self._localed_wrapper = None def publish(self): @@ -177,6 +183,26 @@ def get_locale_data(self, locale_id): return tdata + def get_locale_keyboard_layouts(self, lang): + """Get localized keyboard layouts for a given locale. + + :param lang: locale string (e.g., "cs_CZ.UTF-8") + :return: list of dictionaries with keyboard layout information + """ + language_id = lang.split("_")[0].lower() + english_name = get_english_name(language_id) + + return [ + { + "layoutId": name, + "description": f"{Xkb_(info.desc)}", + "langs": info.langs, + } + # Include only the layouts of the current language in this structure + for name, info in self._layout_infos.items() + if english_name in info.langs + ] + @property def language(self): """Return the language.""" diff --git a/pyanaconda/modules/localization/localization_interface.py b/pyanaconda/modules/localization/localization_interface.py index 0b46702583c..8ae9f21c358 100644 --- a/pyanaconda/modules/localization/localization_interface.py +++ b/pyanaconda/modules/localization/localization_interface.py @@ -91,6 +91,19 @@ def GetLocaleData(self, locale_id: Str) -> Structure: locale_data = self.implementation.get_locale_data(locale_id) return LocaleData.to_structure(locale_data) + def GetLocaleKeyboardLayouts(self, lang: Str) -> List[Structure]: + """Get keyboard layouts for the specified language. + + For example: [ + {"description": "US layout", "layoutId": "us", "variantId": ""}, + {"description": "Czech QWERTY", "layoutId": "cz", "variantId": "qwerty"} + ] + + :param lang: Language code string (e.g., "en_US.UTF-8") + :return: List of keyboard layout dictionaries + """ + return self.implementation.get_locale_keyboard_layouts(lang) + @property def Language(self) -> Str: """The language the system will use.""" diff --git a/pyanaconda/ui/gui/xkl_wrapper.py b/pyanaconda/ui/gui/xkl_wrapper.py index c2261667759..11a2997304f 100644 --- a/pyanaconda/ui/gui/xkl_wrapper.py +++ b/pyanaconda/ui/gui/xkl_wrapper.py @@ -18,23 +18,19 @@ import gettext import threading -from collections import namedtuple -import iso639 from xkbregistry import rxkb from pyanaconda import localization from pyanaconda.core.async_utils import async_action_wait from pyanaconda.core.string import upcase_first_letter from pyanaconda.keyboard import normalize_layout_variant +from pyanaconda.localization import _build_layout_infos from pyanaconda.modules.common.constants.services import LOCALIZATION Xkb_ = lambda x: gettext.translation("xkeyboard-config", fallback=True).gettext(x) iso_ = lambda x: gettext.translation("iso_639", fallback=True).gettext(x) -# namedtuple for information about a keyboard layout (its language and description) -LayoutInfo = namedtuple("LayoutInfo", ["langs", "desc"]) - class XklWrapper: """ Class that used to wrap libxklavier functionality. @@ -62,26 +58,11 @@ def __init__(self): self._rxkb = rxkb.Context() self._layout_infos = {} - self._build_layout_infos() + self._layout_infos = _build_layout_infos() self._switch_opt_infos = {} self._build_switch_opt_infos() - def _build_layout_infos(self): - for layout in self._rxkb.layouts.values(): - name = layout.name - if layout.variant: - name += ' (' + layout.variant + ')' - - langs = [] - for lang in layout.iso639_codes: - if iso639.find(iso639_2=lang): - langs.append(iso639.to_name(lang)) - - if name not in self._layout_infos: - self._layout_infos[name] = LayoutInfo(langs, layout.description) - else: - self._layout_infos[name].langs.extend(langs) def _build_switch_opt_infos(self): for group in self._rxkb.option_groups: