From f6d133804b7fdfab5a00d95a1985c3e220935ac1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 5 Feb 2022 10:27:29 +1100 Subject: Provide nationalization (#893) * Nationalization * Add _ as builtins to flake8 * Removing conflict hash tag Co-authored-by: Daniel Girtler Co-authored-by: Anton Hvornum --- archinstall/lib/menu/selection_menu.py | 50 +++++---- archinstall/lib/translation.py | 82 +++++++++++++++ archinstall/lib/user_interaction.py | 184 ++++++++++++++++++++------------- 3 files changed, 224 insertions(+), 92 deletions(-) create mode 100644 archinstall/lib/translation.py (limited to 'archinstall/lib') diff --git a/archinstall/lib/menu/selection_menu.py b/archinstall/lib/menu/selection_menu.py index 940941be..f3fd3b4c 100644 --- a/archinstall/lib/menu/selection_menu.py +++ b/archinstall/lib/menu/selection_menu.py @@ -27,6 +27,8 @@ from ..user_interaction import select_kernel from ..user_interaction import select_encrypted_partitions from ..user_interaction import select_harddrives from ..user_interaction import select_profile +from ..user_interaction import select_archinstall_language +from ..translation import Translation class Selector: def __init__( @@ -128,29 +130,36 @@ class Selector: class GlobalMenu: def __init__(self): + self._translation = Translation.load_nationalization() self._menu_options = {} self._setup_selection_menu_options() def _setup_selection_menu_options(self): + self._menu_options['archinstall-language'] = \ + Selector( + _('Select Archinstall language'), + lambda: self._select_archinstall_language('English'), + default='English', + enabled=True) self._menu_options['keyboard-layout'] = \ - Selector('Select keyboard layout', lambda: select_language('us'), default='us') + Selector(_('Select keyboard layout'), lambda: select_language('us'), default='us') self._menu_options['mirror-region'] = \ Selector( - 'Select mirror region', + _('Select mirror region'), lambda: select_mirror_regions(), display_func=lambda x: list(x.keys()) if x else '[]', default={}) self._menu_options['sys-language'] = \ - Selector('Select locale language', lambda: select_locale_lang('en_US'), default='en_US') + Selector(_('Select locale language'), lambda: select_locale_lang('en_US'), default='en_US') self._menu_options['sys-encoding'] = \ - Selector('Select locale encoding', lambda: select_locale_enc('utf-8'), default='utf-8') + Selector(_('Select locale encoding'), lambda: select_locale_enc('utf-8'), default='utf-8') self._menu_options['harddrives'] = \ Selector( - 'Select harddrives', + _('Select harddrives'), lambda: self._select_harddrives()) self._menu_options['disk_layouts'] = \ Selector( - 'Select disk layout', + _('Select disk layout'), lambda: select_disk_layout( storage['arguments'].get('harddrives', []), storage['arguments'].get('advanced', False) @@ -158,60 +167,60 @@ class GlobalMenu: dependencies=['harddrives']) self._menu_options['!encryption-password'] = \ Selector( - 'Set encryption password', + _('Set encryption password'), lambda: get_password(prompt='Enter disk encryption password (leave blank for no encryption): '), display_func=lambda x: self._secret(x) if x else 'None', dependencies=['harddrives']) self._menu_options['swap'] = \ Selector( - 'Use swap', + _('Use swap'), lambda: ask_for_swap(), default=True) self._menu_options['bootloader'] = \ Selector( - 'Select bootloader', + _('Select bootloader'), lambda: ask_for_bootloader(storage['arguments'].get('advanced', False)),) self._menu_options['hostname'] = \ Selector('Specify hostname', lambda: ask_hostname()) self._menu_options['!root-password'] = \ Selector( - 'Set root password', + _('Set root password'), lambda: self._set_root_password(), display_func=lambda x: self._secret(x) if x else 'None') self._menu_options['!superusers'] = \ Selector( - 'Specify superuser account', + _('Specify superuser account'), lambda: self._create_superuser_account(), dependencies_not=['!root-password'], display_func=lambda x: list(x.keys()) if x else '') self._menu_options['!users'] = \ Selector( - 'Specify user account', + _('Specify user account'), lambda: self._create_user_account(), default={}, display_func=lambda x: list(x.keys()) if x else '[]') self._menu_options['profile'] = \ Selector( - 'Specify profile', + _('Specify profile'), lambda: self._select_profile(), display_func=lambda x: x if x else 'None') self._menu_options['audio'] = \ Selector( - 'Select audio', + _('Select audio'), lambda: ask_for_audio_selection(is_desktop_profile(storage['arguments'].get('profile', None)))) self._menu_options['kernels'] = \ Selector( - 'Select kernels', + _('Select kernels'), lambda: select_kernel(), default=['linux']) self._menu_options['packages'] = \ Selector( - 'Additional packages to install', + _('Additional packages to install'), lambda: ask_additional_packages_to_install(storage['arguments'].get('packages', None)), default=[]) self._menu_options['nic'] = \ Selector( - 'Configure network', + _('Configure network'), lambda: ask_to_configure_network(), display_func=lambda x: x if x else 'Not configured, unavailable unless setup manually', default={}) @@ -219,7 +228,7 @@ class GlobalMenu: Selector('Select timezone', lambda: ask_for_a_timezone()) self._menu_options['ntp'] = \ Selector( - 'Set automatic time sync (NTP)', + _('Set automatic time sync (NTP)'), lambda: self._select_ntp(), default=True) self._menu_options['install'] = \ @@ -323,6 +332,11 @@ class GlobalMenu: return missing + def _select_archinstall_language(self, default_lang): + language = select_archinstall_language(default_lang) + self._translation.activate(language) + return language + def _set_root_password(self): prompt = 'Enter root password (leave blank to disable root & create superuser): ' password = get_password(prompt=prompt) diff --git a/archinstall/lib/translation.py b/archinstall/lib/translation.py new file mode 100644 index 00000000..1ae6989d --- /dev/null +++ b/archinstall/lib/translation.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +import json +import os +import gettext + +from pathlib import Path +from typing import List, Dict + + +class Languages: + def __init__(self): + self._mappings = self._get_language_mappings() + + def _get_language_mappings(self) -> List[Dict[str, str]]: + locales_dir = Translation.get_locales_dir() + languages = Path.joinpath(locales_dir, 'languages.json') + + with open(languages, 'r') as fp: + return json.load(fp) + + def get_language(self, abbr: str) -> str: + for entry in self._mappings: + if entry['abbr'] == abbr: + return entry['lang'] + + raise ValueError(f'No language with abbrevation "{abbr}" found') + + +class DeferredTranslation: + def __init__(self, message): + self.message = message + + def __len__(self) -> int: + return len(self.message) + + def __str__(self) -> str: + translate = _ + if translate is DeferredTranslation: + return self.message + return translate(self.message) + + @classmethod + def install(cls): + import builtins + builtins._ = cls + + +class Translation: + def __init__(self, locales_dir): + DeferredTranslation.install() + + self._languages = {} + + for name in self.get_all_names(): + self._languages[name] = gettext.translation('base', localedir=locales_dir, languages=[name]) + + def activate(self, name): + if language := self._languages.get(name, None): + language.install() + else: + raise ValueError(f'Language not supported: {name}') + + @classmethod + def load_nationalization(cls) -> Translation: + locales_dir = cls.get_locales_dir() + return Translation(locales_dir) + + @classmethod + def get_locales_dir(cls) -> Path: + cur_path = Path(__file__).parent.parent + locales_dir = Path.joinpath(cur_path, 'locales') + return locales_dir + + @classmethod + def get_all_names(cls) -> List[str]: + locales_dir = cls.get_locales_dir() + filenames = os.listdir(locales_dir) + def_languages = filter(lambda x: len(x) == 2, filenames) + + languages = Languages() + return [languages.get_language(lang) for lang in def_languages] diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 8cc7de0a..b1560ea1 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -28,6 +28,7 @@ from .mirrors import list_mirrors # TODO: Some inconsistencies between the selection processes. # Some return the keys from the options, some the values? +from .translation import Translation from .disk.validators import fs_types from .packages.packages import validate_package_list @@ -79,7 +80,7 @@ def do_countdown() -> bool: print(".", end='') if SIG_TRIGGER: - prompt = 'Do you really want to abort' + prompt = _('Do you really want to abort?') choice = Menu(prompt, ['yes', 'no'], skip=False).run() if choice == 'yes': exit(0) @@ -97,7 +98,7 @@ def do_countdown() -> bool: def get_password(prompt :str = "Enter a password: ") -> Optional[str]: while passwd := getpass.getpass(prompt): - passwd_verification = getpass.getpass(prompt='And one more time for verification: ') + passwd_verification = getpass.getpass(prompt=_('And one more time for verification: ')) if passwd != passwd_verification: log(' * Passwords did not match * ', fg='red') continue @@ -267,24 +268,26 @@ class MiniCurses: return response -def ask_for_swap(prompt='Would you like to use swap on zram?', forced=False): +def ask_for_swap(): + prompt = _('Would you like to use swap on zram?') choice = Menu(prompt, ['yes', 'no'], default_option='yes').run() return False if choice == 'no' else True def ask_ntp() -> bool: - prompt = 'Would you like to use automatic time synchronization (NTP) with the default time servers?' - prompt += 'Hardware time and other post-configuration steps might be required in order for NTP to work. For more information, please check the Arch wiki' + prompt = _('Would you like to use automatic time synchronization (NTP) with the default time servers?') + prompt += _('Hardware time and other post-configuration steps might be required in order for NTP to work. For more information, please check the Arch wiki') choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='yes').run() return False if choice == 'no' else True def ask_hostname(): - hostname = input('Desired hostname for the installation: ').strip(' ') + hostname = input(_('Desired hostname for the installation: ').strip(' ')) return hostname -def ask_for_superuser_account(prompt :str = 'Username for required superuser with sudo privileges: ', forced :bool = False) -> Dict[str, Dict[str, str]]: +def ask_for_superuser_account(prompt :str = '', forced :bool = False) -> Dict[str, Dict[str, str]]: + prompt = prompt if prompt else _('Username for required superuser with sudo privileges: ') while 1: new_user = input(prompt).strip(' ') @@ -298,11 +301,13 @@ def ask_for_superuser_account(prompt :str = 'Username for required superuser wit elif not check_for_correct_username(new_user): continue - password = get_password(prompt=f'Password for user {new_user}: ') + prompt = _('Password for user "{}"').format(new_user) + password = get_password(prompt=prompt) return {new_user: {"!password": password}} -def ask_for_additional_users(prompt :str = 'Any additional users to install (leave blank for no users): ') -> List[Dict[str, Dict[str, str]]]: +def ask_for_additional_users(prompt :str = '') -> tuple[dict[str, dict[str, str | None]], dict[str, dict[str, str | None]]]: + prompt = prompt if prompt else _('Any additional users to install (leave blank for no users): ') users = {} superusers = {} @@ -312,9 +317,14 @@ def ask_for_additional_users(prompt :str = 'Any additional users to install (lea break if not check_for_correct_username(new_user): continue - password = get_password(prompt=f'Password for user {new_user}: ') - if input("Should this user be a superuser (sudoer) [y/N]: ").strip(' ').lower() in ('y', 'yes'): + prompt = _('Password for user "{}"').format(new_user) + password = get_password(prompt=prompt) + + prompt = _('Should this user be a superuser (sudoer)?') + choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='no').run() + + if choice == 'yes': superusers[new_user] = {"!password": password} else: users[new_user] = {"!password": password} @@ -322,12 +332,12 @@ def ask_for_additional_users(prompt :str = 'Any additional users to install (lea return users, superusers -def ask_for_a_timezone() -> str: +def ask_timezone() -> str: timezones = list_timezones() default = 'UTC' selected_tz = Menu( - f'Select a timezone or leave blank to use default "{default}"', + _('Select a timezone'), list(timezones), skip=False, default_option=default @@ -340,13 +350,18 @@ def ask_for_bootloader(advanced_options :bool = False) -> str: bootloader = "systemd-bootctl" if has_uefi() else "grub-install" if has_uefi(): if not advanced_options: - bootloader_choice = Menu('Would you like to use GRUB as a bootloader instead of systemd-boot?', ['yes', 'no'], default_option='no').run() + bootloader_choice = Menu( + _('Would you like to use GRUB as a bootloader instead of systemd-boot?'), + ['yes', 'no'], + default_option='no' + ).run() + if bootloader_choice == "yes": bootloader = "grub-install" else: # We use the common names for the bootloader as the selection, and map it back to the expected values. choices = ['systemd-boot', 'grub', 'efistub'] - selection = Menu('Choose a bootloader or leave blank to use systemd-boot', choices).run() + selection = Menu(_('Choose a bootloader'), choices).run() if selection != "": if selection == 'systemd-boot': bootloader = 'systemd-bootctl' @@ -362,7 +377,7 @@ def ask_for_audio_selection(desktop :bool = True) -> str: audio = 'pipewire' if desktop else 'none' choices = ['pipewire', 'pulseaudio'] if desktop else ['pipewire', 'pulseaudio', 'none'] selected_audio = Menu( - f'Choose an audio server', + _('Choose an audio server'), choices, default_option=audio, skip=False @@ -373,14 +388,13 @@ def ask_for_audio_selection(desktop :bool = True) -> str: # TODO: Remove? Moved? def ask_additional_packages_to_install(packages :List[str] = None) -> List[str]: # Additional packages (with some light weight error handling for invalid package names) - print( - "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.") - print("If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.") + print(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.')) + print(_('If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.')) + while True: - if packages is None: - packages = [p for p in input( - 'Write additional packages to install (space separated, leave blank to skip): ' - ).split(' ') if len(p)] + packages = [p for p in input( + _('Write additional packages to install (space separated, leave blank to skip): ') + ).split(' ') if len(p)] if len(packages): # Verify packages that were given @@ -401,16 +415,20 @@ def ask_to_configure_network() -> Dict[str, Any]: # Optionally configure one network interface. # while 1: # {MAC: Ifname} + + iso_config = _('Copy ISO network configuration to installation') + network_manager = _('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)') + interfaces = { - 'ISO-CONFIG': 'Copy ISO network configuration to installation', - 'NetworkManager': 'Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)', + 'ISO-CONFIG': iso_config, + 'NetworkManager': network_manager, **list_interfaces() } - nic = Menu('Select one network interface to configure', list(interfaces.values())).run() + nic = Menu(_('Select one network interface to configure'), list(interfaces.values())).run() - if nic and nic != 'Copy ISO network configuration to installation': - if nic == 'Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)': + if nic and nic != iso_config: + if nic == network_manager: return {'nic': nic, 'NetworkManager': True} # Current workaround: @@ -420,15 +438,13 @@ def ask_to_configure_network() -> Dict[str, Any]: modes = ['DHCP (auto detect)', 'IP (static)'] default_mode = 'DHCP (auto detect)' - mode = Menu( - f'Select which mode to configure for "{nic}" or leave blank for default "{default_mode}"', - modes, - default_option=default_mode - ).run() + prompt = _('Select which mode to configure for "{}" or skip to use default mode "{}"').format(nic, default_mode) + mode = Menu(prompt, modes, default_option=default_mode).run() if mode == 'IP (static)': while 1: - ip = input(f"Enter the IP and subnet for {nic} (example: 192.168.0.5/24): ").strip() + prompt = _('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(nic) + ip = input(prompt).strip() # Implemented new check for correct IP/subnet input try: ipaddress.ip_interface(ip) @@ -442,7 +458,7 @@ def ask_to_configure_network() -> Dict[str, Any]: # Implemented new check for correct gateway IP address while 1: - gateway = input('Enter your gateway (router) IP address or leave blank for none: ').strip() + gateway = input(_('Enter your gateway (router) IP address or leave blank for none: ')).strip() try: if len(gateway) == 0: gateway = None @@ -457,7 +473,9 @@ def ask_to_configure_network() -> Dict[str, Any]: ) dns = None - if len(dns_input := input('Enter your DNS servers (space separated, blank for none): ').strip()): + dns_input = input(_('Enter your DNS servers (space separated, blank for none): ')).strip() + + if len(dns_input): dns = dns_input.split(' ') return {'nic': nic, 'dhcp': False, 'ip': ip, 'gateway': gateway, 'dns': dns} @@ -489,7 +507,9 @@ def ask_for_main_filesystem_format(advanced_options=False): if advanced_options: options.update(advanced) - return Menu('Select which filesystem your main partition should use', options, skip=False).run() + prompt = _('Select which filesystem your main partition should use') + choice = Menu(prompt, options, skip=False).run() + return choice def current_partition_layout(partitions :List[Partition], with_idx :bool = False) -> Dict[str, Any]: @@ -533,7 +553,8 @@ def current_partition_layout(partitions :List[Partition], with_idx :bool = False current_layout += f'{row[:-1]}\n' - return f'\n\nCurrent partition layout:\n\n{current_layout}' + title = _('Current partition layout') + return f'\n\n{title}:\n\n{current_layout}' def select_partition(title :str, partitions :List[Partition], multiple :bool = False) -> Union[int, List[int], None]: @@ -583,7 +604,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A "Set desired filesystem for a partition", ] - title = f'Select what to do with \n{block_device}' + title = _('Select what to do with\n{}').format(block_device) # show current partition layout: if len(block_device_struct["partitions"]): @@ -600,16 +621,19 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A # # https://www.gnu.org/software/parted/manual/html_node/mklabel.html # name = input("Enter a desired name for the partition: ").strip() - fstype = Menu('Enter a desired filesystem type for the partition', fs_types(), skip=False).run() + fstype = Menu(_('Enter a desired filesystem type for the partition'), fs_types(), skip=False).run() + + prompt = _('Enter the start sector (percentage or block number, default: {}): ').format(block_device.first_free_sector) + start = input(prompt).strip() - start = input(f"Enter the start sector (percentage or block number, default: {block_device.first_free_sector}): ").strip() if not start.strip(): start = block_device.first_free_sector end_suggested = block_device.first_end_sector else: end_suggested = '100%' - end = input(f"Enter the end sector of the partition (percentage or block number, ex: {end_suggested}): ").strip() + prompt = _('Enter the end sector of the partition (percentage or block number, ex: {}): "').format(end_suggested) + end = input(prompt).strip() if not end.strip(): end = end_suggested @@ -634,7 +658,10 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A continue elif task[:len("Suggest partition layout")] == "Suggest partition layout": if len(block_device_struct["partitions"]): - if input(f"{block_device} contains queued partitions, this will remove those, are you sure? y/N: ").strip().lower() in ('', 'n'): + prompt = _('{} contains queued partitions, this will remove those, are you sure?').format(block_device) + choice = Menu(prompt, ['yes', 'no'], default_option='no').run() + + if choice == 'no': continue block_device_struct.update(suggest_single_disk_layout(block_device)[block_device.path]) @@ -644,7 +671,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A current_layout = current_partition_layout(block_device_struct['partitions'], with_idx=True) if task == "Delete a partition": - title = f'{current_layout}\n\nSelect by index which partitions to delete' + title = _('{}\n\nSelect by index which partitions to delete').format(current_layout) to_delete = select_partition(title, block_device_struct["partitions"], multiple=True) if to_delete: @@ -652,12 +679,12 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A elif task == "Clear/Delete all partitions": block_device_struct["partitions"] = [] elif task == "Assign mount-point for a partition": - title = f'{current_layout}\n\nSelect by index which partition to mount where' + title = _('{}\n\nSelect by index which partition to mount where').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) if partition is not None: - print(' * Partition mount-points are relative to inside the installation, the boot would be /boot as an example.') - mountpoint = input('Select where to mount partition (leave blank to remove mountpoint): ').strip() + print(_(' * Partition mount-points are relative to inside the installation, the boot would be /boot as an example.')) + mountpoint = input(_('Select where to mount partition (leave blank to remove mountpoint): ')).strip() if len(mountpoint): block_device_struct["partitions"][partition]['mountpoint'] = mountpoint @@ -668,7 +695,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A del(block_device_struct["partitions"][partition]['mountpoint']) elif task == "Mark/Unmark a partition to be formatted (wipes data)": - title = f'{current_layout}\n\nSelect which partition to mask for formatting' + title = _('{}\n\nSelect which partition to mask for formatting').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) if partition is not None: @@ -679,7 +706,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A if not block_device_struct["partitions"][partition].get('filesystem', None): block_device_struct["partitions"][partition]['filesystem'] = {} - fstype = Menu('Enter a desired filesystem type for the partition', fs_types(), skip=False).run() + fstype = Menu(_('Enter a desired filesystem type for the partition'), fs_types(), skip=False).run() block_device_struct["partitions"][partition]['filesystem']['format'] = fstype @@ -687,7 +714,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A block_device_struct["partitions"][partition]['wipe'] = not block_device_struct["partitions"][partition].get('wipe', False) elif task == "Mark/Unmark a partition as encrypted": - title = f'{current_layout}\n\nSelect which partition to mark as encrypted' + title = _('{}\n\nSelect which partition to mark as encrypted').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) if partition is not None: @@ -695,21 +722,21 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A block_device_struct["partitions"][partition]['encrypted'] = not block_device_struct["partitions"][partition].get('encrypted', False) elif task == "Mark/Unmark a partition as bootable (automatic for /boot)": - title = f'{current_layout}\n\nSelect which partition to mark as bootable' + title = _('{}\n\nSelect which partition to mark as bootable').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) if partition is not None: block_device_struct["partitions"][partition]['boot'] = not block_device_struct["partitions"][partition].get('boot', False) elif task == "Set desired filesystem for a partition": - title = f'{current_layout}\n\nSelect which partition to set a filesystem on' + title = _('{}\n\nSelect which partition to set a filesystem on').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) if partition is not None: if not block_device_struct["partitions"][partition].get('filesystem', None): block_device_struct["partitions"][partition]['filesystem'] = {} - fstype_title = 'Enter a desired filesystem type for the partition: ' + fstype_title = _('Enter a desired filesystem type for the partition: ') fstype = Menu(fstype_title, fs_types(), skip=False).run() block_device_struct["partitions"][partition]['filesystem']['format'] = fstype @@ -728,15 +755,20 @@ def select_individual_blockdevice_usage(block_devices: list) -> Dict[str, Any]: return result +def select_archinstall_language(default='English'): + languages = Translation.get_all_names() + language = Menu(_('Select Archinstall language'), languages, default_option=default).run() + return language + + def select_disk_layout(block_devices :list, advanced_options=False) -> Dict[str, Any]: - modes = [ - "Wipe all selected drives and use a best-effort default partition layout", - "Select what to do with each individual drive (followed by partition usage)" - ] + wipe_mode = _('Wipe all selected drives and use a best-effort default partition layout') + custome_mode = _('Select what to do with each individual drive (followed by partition usage)') + modes = [wipe_mode, custome_mode] - mode = Menu('Select what you wish to do with the selected block devices', modes, skip=False).run() + mode = Menu(_('Select what you wish to do with the selected block devices'), modes, skip=False).run() - if mode == 'Wipe all selected drives and use a best-effort default partition layout': + if mode == wipe_mode: return get_default_partition_layout(block_devices, advanced_options) else: return select_individual_blockdevice_usage(block_devices) @@ -787,8 +819,7 @@ def select_profile() -> Optional[str]: option = f'{profile.profile}: {description}' options[option] = profile - title = 'This is a list of pre-programmed profiles, ' \ - 'they might make it easier to install things like desktop environments' + title = _('This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments') selection = Menu(title=title, p_options=list(options.keys())).run() @@ -812,7 +843,7 @@ def select_language(default_value :str) -> str: # allows for searching anyways sorted_kb_lang = sorted(sorted(list(kb_lang)), key=len) - selected_lang = Menu('Select Keyboard layout', sorted_kb_lang, default_option=default_value, sort=False).run() + selected_lang = Menu(_('Select Keyboard layout'), sorted_kb_lang, default_option=default_value, sort=False).run() return selected_lang @@ -827,7 +858,7 @@ def select_mirror_regions() -> Dict[str, Any]: mirrors = list_mirrors() selected_mirror = Menu( - 'Select one of the regions to download packages from', + _('Select one of the regions to download packages from'), list(mirrors.keys()), multi=True ).run() @@ -849,7 +880,7 @@ def select_harddrives() -> Optional[str]: options = {f'{option}': option for option in hard_drives} selected_harddrive = Menu( - 'Select one or more hard drives to use and configure', + _('Select one or more hard drives to use and configure'), list(options.keys()), multi=True ).run() @@ -876,18 +907,18 @@ def select_driver(options :Dict[str, Any] = AVAILABLE_GFX_DRIVERS, force_ask :bo title = '' if has_amd_graphics(): - title += 'For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options.\n' + title += _('For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options.') + '\n' if has_intel_graphics(): - title += 'For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n' + title += _('For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n') if has_nvidia_graphics(): - title += 'For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n' + title += _('For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n') if not arguments.get('gfx_driver', None) or force_ask: - title += '\n\nSelect a graphics driver or leave blank to install all open-source drivers' + title += _('\n\nSelect a graphics driver or leave blank to install all open-source drivers') arguments['gfx_driver'] = Menu(title, drivers).run() if arguments.get('gfx_driver', None) is None: - arguments['gfx_driver'] = "All open-source (default)" + arguments['gfx_driver'] = _("All open-source (default)") return options.get(arguments.get('gfx_driver')) @@ -906,7 +937,7 @@ def select_kernel() -> List[str]: default_kernel = "linux" selected_kernels = Menu( - f'Choose which kernels to use or leave blank for default "{default_kernel}"', + _('Choose which kernels to use or leave blank for default "{}"').format(default_kernel), kernels, sort=True, multi=True, @@ -920,7 +951,7 @@ def select_locale_lang(default): locale_lang = set([locale.split()[0] for locale in locales]) selected_locale = Menu( - f'Choose which locale language to use', + _('Choose which locale language to use'), locale_lang, sort=True, default_option=default @@ -934,7 +965,7 @@ def select_locale_enc(default): locale_enc = set([locale.split()[1] for locale in locales]) selected_locale = Menu( - f'Choose which locale encoding to use', + _('Choose which locale encoding to use'), locale_enc, sort=True, default_option=default @@ -942,8 +973,9 @@ def select_locale_enc(default): return selected_locale + def generic_select(p_options :Union[list,dict], - input_text :str = "Select one of the values shown below: ", + input_text :str = '', allow_empty_input :bool = True, options_output :bool = True, # function not available sort :bool = False, @@ -974,6 +1006,8 @@ def generic_select(p_options :Union[list,dict], log(f"invalid parameter at Menu() call was at <{sys._getframe(1).f_code.co_name}>",level=logging.WARNING) raise RequirementError("generic_select() requires an iterable as option.") + input_text = input_text if input_text else _('Select one of the values shown below: ') + if isinstance(p_options,dict): options = list(p_options.values()) else: @@ -1008,11 +1042,13 @@ def generic_select(p_options :Union[list,dict], def generic_multi_select(p_options :Union[list,dict], - text :str = "Select one or more of the options below: ", + text :str = '', sort :bool = False, default :Any = None, allow_empty :bool = False) -> Any: + text = text if text else _("Select one or more of the options below: ") + return generic_select(p_options, input_text=text, allow_empty_input=allow_empty, -- cgit v1.2.3-54-g00ecf