From 9fb8d3164ce07e6cd08fe60f2e6f1203ccb8991a Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 6 Feb 2022 21:30:26 +1100 Subject: Update nationalization (#944) * Update nationalization * Update translations * Finish german translation * Fix errors #943 * Add remaining translations * Fix alignment in menu * Update README * Update translations: * Fix flake8 * Update tz function Co-authored-by: Daniel Girtler --- archinstall/lib/disk/filesystem.py | 10 +++-- archinstall/lib/disk/user_guides.py | 1 + archinstall/lib/installer.py | 6 +-- archinstall/lib/menu/menu.py | 2 +- archinstall/lib/menu/selection_menu.py | 58 +++++++++++++----------- archinstall/lib/profiles.py | 4 +- archinstall/lib/translation.py | 13 ++++-- archinstall/lib/user_interaction.py | 82 ++++++++++++++++++++-------------- 8 files changed, 103 insertions(+), 73 deletions(-) (limited to 'archinstall/lib') diff --git a/archinstall/lib/disk/filesystem.py b/archinstall/lib/disk/filesystem.py index c8a74e3f..4f1b4217 100644 --- a/archinstall/lib/disk/filesystem.py +++ b/archinstall/lib/disk/filesystem.py @@ -83,7 +83,7 @@ class Filesystem: for partition in layout.get('partitions', []): # We don't want to re-add an existing partition (those containing a UUID already) if partition.get('wipe', False) and not partition.get('PARTUUID', None): - print("Adding partition....") + print(_("Adding partition....")) start = partition.get('start') or ( prev_partition and f'{prev_partition["device_instance"].end_sectors}s' or DEFAULT_PARTITION_START) partition['device_instance'] = self.add_partition(partition.get('type', 'primary'), @@ -94,7 +94,7 @@ class Filesystem: # print('Device instance:', partition['device_instance']) elif (partition_uuid := partition.get('PARTUUID')) and (partition_instance := self.blockdevice.get_partition(uuid=partition_uuid)): - print("Re-using partition_instance:", partition_instance) + print(_("Re-using partition instance: {}").format(partition_instance)) partition['device_instance'] = partition_instance else: raise ValueError(f"{self}.load_layout() doesn't know how to continue without a new partition definition or a UUID ({partition.get('PARTUUID')}) on the device ({self.blockdevice.get_partition(uuid=partition.get('PARTUUID'))}).") @@ -113,7 +113,9 @@ class Filesystem: raise ValueError(f"Missing encryption password for {partition['device_instance']}") from ..user_interaction import get_password - storage['arguments']['!encryption-password'] = get_password(f"Enter a encryption password for {partition['device_instance']}") + + prompt = str(_('Enter a encryption password for {}').format(partition['device_instance'])) + storage['arguments']['!encryption-password'] = get_password(prompt) partition['!password'] = storage['arguments']['!encryption-password'] @@ -136,7 +138,7 @@ class Filesystem: while True: partition['filesystem']['format'] = input(f"Enter a valid fs-type for newly encrypted partition {partition['filesystem']['format']}: ").strip() if not partition['filesystem']['format'] or valid_fs_type(partition['filesystem']['format']) is False: - print("You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's.") + print(_("You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's.")) continue break diff --git a/archinstall/lib/disk/user_guides.py b/archinstall/lib/disk/user_guides.py index 8acb8cd2..90d323c7 100644 --- a/archinstall/lib/disk/user_guides.py +++ b/archinstall/lib/disk/user_guides.py @@ -11,6 +11,7 @@ from ..hardware import has_uefi from ..output import log from ..menu import Menu + def suggest_single_disk_layout(block_device :BlockDevice, default_filesystem :Optional[str] = None, advanced_options :bool = False) -> Dict[str, Any]: diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index cde1ec1d..daac340b 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -141,8 +141,8 @@ class Installer: # We avoid printing /mnt/ because that might confuse people if they note it down # and then reboot, and a identical log file will be found in the ISO medium anyway. - print(f"[!] A log file has been created here: {os.path.join(storage['LOG_PATH'], storage['LOG_FILE'])}") - print(" Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues") + print(_("[!] A log file has been created here: {} {}").format(os.path.join(storage['LOG_PATH'], storage['LOG_FILE']))) + print(_(" Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues")) raise args[1] self.genfstab() @@ -282,7 +282,7 @@ class Installer: partition.mount(f'{self.target}{mountpoint}') - def post_install_check(self, *args :str, **kwargs :str) -> List[bool]: + def post_install_check(self, *args :str, **kwargs :str) -> List[str]: return [step for step, flag in self.helper_flags.items() if flag is False] def pacstrap(self, *packages :str, **kwargs :str) -> bool: diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py index dfd47a7a..1811482d 100644 --- a/archinstall/lib/menu/menu.py +++ b/archinstall/lib/menu/menu.py @@ -65,7 +65,7 @@ class Menu(TerminalMenu): menu_title = f'\n{title}\n\n' if skip: - menu_title += "Use ESC to skip\n\n" + menu_title += str(_("Use ESC to skip\n\n")) if default_option: # if a default value was specified we move that one diff --git a/archinstall/lib/menu/selection_menu.py b/archinstall/lib/menu/selection_menu.py index f3fd3b4c..f094c6dc 100644 --- a/archinstall/lib/menu/selection_menu.py +++ b/archinstall/lib/menu/selection_menu.py @@ -1,4 +1,5 @@ import sys +from typing import Dict from .menu import Menu from ..general import SysCommand @@ -7,7 +8,7 @@ from ..output import log from ..profiles import is_desktop_profile from ..disk import encrypted_partitions from ..locale_helpers import set_keyboard_language -from ..user_interaction import get_password +from ..user_interaction import get_password, ask_for_a_timezone from ..user_interaction import ask_ntp from ..user_interaction import ask_for_swap from ..user_interaction import ask_for_bootloader @@ -15,7 +16,6 @@ from ..user_interaction import ask_hostname from ..user_interaction import ask_for_audio_selection from ..user_interaction import ask_additional_packages_to_install from ..user_interaction import ask_to_configure_network -from ..user_interaction import ask_for_a_timezone from ..user_interaction import ask_for_superuser_account from ..user_interaction import ask_for_additional_users from ..user_interaction import select_language @@ -30,6 +30,7 @@ from ..user_interaction import select_profile from ..user_interaction import select_archinstall_language from ..translation import Translation + class Selector: def __init__( self, @@ -76,7 +77,6 @@ class Selector: self._display_func = display_func self._current_selection = default self.enabled = enabled - self.text = self.menu_text() self._dependencies = dependencies self._dependencies_not = dependencies_not @@ -88,12 +88,15 @@ class Selector: def dependencies_not(self): return self._dependencies_not + @property + def current_selection(self): + return self._current_selection + def set_enabled(self): self.enabled = True def update_description(self, description): self._description = description - self.text = self.menu_text() def menu_text(self): current = '' @@ -105,14 +108,13 @@ class Selector: current = str(self._current_selection) if current: - padding = 35 - len(self._description) + padding = 35 - len(str(self._description)) current = ' ' * padding + f'SET: {current}' return f'{self._description} {current}' def set_current_selection(self, current): self._current_selection = current - self.text = self.menu_text() def has_selection(self): if self._current_selection is None: @@ -168,7 +170,7 @@ class GlobalMenu: self._menu_options['!encryption-password'] = \ Selector( _('Set encryption password'), - lambda: get_password(prompt='Enter disk encryption password (leave blank for no encryption): '), + lambda: get_password(prompt=str(_('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'] = \ @@ -181,7 +183,7 @@ class GlobalMenu: _('Select bootloader'), lambda: ask_for_bootloader(storage['arguments'].get('advanced', False)),) self._menu_options['hostname'] = \ - Selector('Specify hostname', lambda: ask_hostname()) + Selector(_('Specify hostname'), lambda: ask_hostname()) self._menu_options['!root-password'] = \ Selector( _('Set root password'), @@ -222,10 +224,10 @@ class GlobalMenu: Selector( _('Configure network'), lambda: ask_to_configure_network(), - display_func=lambda x: x if x else 'Not configured, unavailable unless setup manually', + display_func=lambda x: x if x else _('Not configured, unavailable unless setup manually'), default={}) self._menu_options['timezone'] = \ - Selector('Select timezone', lambda: ask_for_a_timezone()) + Selector(_('Select timezone'), lambda: ask_for_a_timezone()) self._menu_options['ntp'] = \ Selector( _('Set automatic time sync (NTP)'), @@ -235,7 +237,7 @@ class GlobalMenu: Selector( self._install_text(), enabled=True) - self._menu_options['abort'] = Selector('Abort', enabled=True) + self._menu_options['abort'] = Selector(_('Abort'), enabled=True) def enable(self, selector_name, omit_if_set=False): arg = storage['arguments'].get(selector_name, None) @@ -259,26 +261,29 @@ class GlobalMenu: self._set_kb_language() enabled_menus = self._menus_to_enable() - menu_text = [m.text for m in enabled_menus.values()] - selection = Menu('Set/Modify the below options', menu_text, sort=False).run() + menu_text = [m.menu_text() for m in enabled_menus.values()] + selection = Menu(_('Set/Modify the below options'), menu_text, sort=False).run() + if selection: selection = selection.strip() - if 'Abort' in selection: + if str(_('Abort')) in selection: exit(0) - elif 'Install' in selection: + elif str(_('Install')) in selection: if self._missing_configs() == 0: break else: self._process_selection(selection) + for key in self._menu_options: sel = self._menu_options[key] if key not in storage['arguments']: - storage['arguments'][key] = sel._current_selection + storage['arguments'][key] = sel.current_selection + self._post_processing() def _process_selection(self, selection): # find the selected option in our option list - option = [[k, v] for k, v in self._menu_options.items() if v.text.strip() == selection] + option = [[k, v] for k, v in self._menu_options.items() if v.menu_text().strip() == selection] if len(option) != 1: raise ValueError(f'Selection not found: {selection}') @@ -306,7 +311,7 @@ class GlobalMenu: def _install_text(self): missing = self._missing_configs() if missing > 0: - return f'Install ({missing} config(s) missing)' + return _('Install ({} config(s) missing)').format(missing) return 'Install' def _missing_configs(self): @@ -338,7 +343,7 @@ class GlobalMenu: return language def _set_root_password(self): - prompt = 'Enter root password (leave blank to disable root & create superuser): ' + prompt = str(_('Enter root password (leave blank to disable root): ')) password = get_password(prompt=prompt) # TODO: Do we really wanna wipe the !superusers and !users if root password is set? @@ -368,11 +373,12 @@ class GlobalMenu: storage['arguments']['disk_layouts'] = {} if not harddrives: - prompt = 'You decided to skip harddrive selection\n' - prompt += f"and will use whatever drive-setup is mounted at {storage['MOUNT_POINT']} (experimental)\n" - prompt += "WARNING: Archinstall won't check the suitability of this setup\n" + prompt = _( + "You decided to skip harddrive selection\nand will use whatever drive-setup is mounted at {} (experimental)\n" + "WARNING: Archinstall won't check the suitability of this setup\n" + "Do you wish to continue?" + ).format(storage['MOUNT_POINT']) - prompt += 'Do you wish to continue?' choice = Menu(prompt, ['yes', 'no'], default_option='yes').run() if choice == 'no': @@ -397,11 +403,11 @@ class GlobalMenu: return profile def _create_superuser_account(self): - superuser = ask_for_superuser_account('Create a required super-user with sudo privileges: ', forced=True) + superuser = ask_for_superuser_account(str(_('Create a required super-user with sudo privileges: ')), forced=True) return superuser def _create_user_account(self): - users, superusers = ask_for_additional_users('Enter a username to create an additional user: ') + users, superusers = ask_for_additional_users(str(_('Enter a username to create an additional user (leave blank to skip): '))) storage['arguments']['!superusers'] = {**storage['arguments'].get('!superusers', {}), **superusers} return users @@ -431,7 +437,7 @@ class GlobalMenu: raise ValueError(f'No selection found: {selection_name}') - def _menus_to_enable(self): + def _menus_to_enable(self) -> Dict[str, Selector]: enabled_menus = {} for name, selection in self._menu_options.items(): diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py index 9befd3d5..4295fa8f 100644 --- a/archinstall/lib/profiles.py +++ b/archinstall/lib/profiles.py @@ -86,10 +86,10 @@ def list_profiles( try: profile_list = json.loads(grab_url_data(profiles_url)) except urllib.error.HTTPError as err: - print(f'Error: Listing profiles on URL "{profiles_url}" resulted in:', err) + print(_('Error: Listing profiles on URL "{}" resulted in:').format(profiles_url), err) return cache except json.decoder.JSONDecodeError as err: - print(f'Error: Could not decode "{profiles_url}" result as JSON:', err) + print(_('Error: Could not decode "{}" result as JSON:').format(profiles_url), err) return cache for profile in profile_list: diff --git a/archinstall/lib/translation.py b/archinstall/lib/translation.py index 1ae6989d..16556fbe 100644 --- a/archinstall/lib/translation.py +++ b/archinstall/lib/translation.py @@ -28,7 +28,7 @@ class Languages: class DeferredTranslation: - def __init__(self, message): + def __init__(self, message: str): self.message = message def __len__(self) -> int: @@ -40,6 +40,15 @@ class DeferredTranslation: return self.message return translate(self.message) + def __lt__(self, other) -> bool: + return self.message < other + + def __gt__(self, other) -> bool: + return self.message > other + + def format(self, *args) -> str: + return self.message.format(*args) + @classmethod def install(cls): import builtins @@ -48,8 +57,6 @@ class DeferredTranslation: class Translation: def __init__(self, locales_dir): - DeferredTranslation.install() - self._languages = {} for name in self.get_all_names(): diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 3db74e9b..badf4cb4 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -96,7 +96,10 @@ def do_countdown() -> bool: return True -def get_password(prompt :str = "Enter a password: ") -> Optional[str]: +def get_password(prompt :str = '') -> Optional[str]: + if not prompt: + prompt = _("Enter a password: ") + while passwd := getpass.getpass(prompt): passwd_verification = getpass.getpass(prompt=_('And one more time for verification: ')) if passwd != passwd_verification: @@ -275,8 +278,8 @@ def ask_for_swap(): 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 = str(_('Would you like to use automatic time synchronization (NTP) with the default time servers?\n')) + prompt += str(_('Hardware time and other post-configuration steps might be required in order for NTP to work.\nFor 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 @@ -286,7 +289,7 @@ def ask_hostname(): return hostname -def ask_for_superuser_account(prompt :str = '', 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(' ') @@ -301,7 +304,7 @@ def ask_for_superuser_account(prompt :str = '', forced :bool = False) -> Dict[st elif not check_for_correct_username(new_user): continue - prompt = _('Password for user "{}"').format(new_user) + prompt = str(_('Password for user "{}": ').format(new_user)) password = get_password(prompt=prompt) return {new_user: {"!password": password}} @@ -318,11 +321,14 @@ def ask_for_additional_users(prompt :str = '') -> tuple[dict[str, dict[str, str if not check_for_correct_username(new_user): continue - prompt = _('Password for user "{}"').format(new_user) - password = get_password(prompt=prompt) + password = get_password(prompt=str(_('Password for user "{}": ').format(new_user))) - prompt = _('Should this user be a superuser (sudoer)?') - choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='no').run() + choice = Menu( + str(_('Should this user be a superuser (sudoer)?')), + ['yes', 'no'], + skip=False, + default_option='no' + ).run() if choice == 'yes': superusers[new_user] = {"!password": password} @@ -399,7 +405,7 @@ def ask_additional_packages_to_install(packages :List[str] = None) -> List[str]: if len(packages): # Verify packages that were given try: - log("Verifying that additional packages exist (this might take a few seconds)") + print(_("Verifying that additional packages exist (this might take a few seconds)")) validate_package_list(packages) break except RequirementError as e: @@ -587,21 +593,28 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A # Test code: [part.__dump__() for part in block_device.partitions.values()] # TODO: Squeeze in BTRFS subvolumes here + new_partition = _('Create a new partition') + suggest_partition_layout = _('Suggest partition layout') + delete_partition = _('Delete a partition') + delete_all_partitions = _('Clear/Delete all partitions') + assign_mount_point = _('Assign mount-point for a partition') + mark_formatted = _('Mark/Unmark a partition to be formatted (wipes data)') + mark_encrypted = _('Mark/Unmark a partition as encrypted') + mark_bootable = _('Mark/Unmark a partition as bootable (automatic for /boot)') + set_filesystem_partition = _('Set desired filesystem for a partition') + while True: - modes = [ - "Create a new partition", - f"Suggest partition layout for {block_device}" - ] + modes = [new_partition, suggest_partition_layout] if len(block_device_struct['partitions']): modes += [ - "Delete a partition", - "Clear/Delete all partitions", - "Assign mount-point for a partition", - "Mark/Unmark a partition to be formatted (wipes data)", - "Mark/Unmark a partition as encrypted", - "Mark/Unmark a partition as bootable (automatic for /boot)", - "Set desired filesystem for a partition", + delete_partition, + delete_all_partitions, + assign_mount_point, + mark_formatted, + mark_encrypted, + mark_bootable, + set_filesystem_partition, ] title = _('Select what to do with\n{}').format(block_device) @@ -615,7 +628,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A if not task: break - if task == 'Create a new partition': + if task == new_partition: # if partition_type == 'gpt': # # https://www.gnu.org/software/parted/manual/html_node/mkpart.html # # https://www.gnu.org/software/parted/manual/html_node/mklabel.html @@ -632,7 +645,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A else: end_suggested = '100%' - prompt = _('Enter the end sector of the partition (percentage or block number, ex: {}): "').format(end_suggested) + prompt = _('Enter the end sector of the partition (percentage or block number, ex: {}): ').format(end_suggested) end = input(prompt).strip() if not end.strip(): @@ -656,7 +669,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A else: log(f"Invalid start ({valid_parted_position(start)}) or end ({valid_parted_position(end)}) for this partition. Ignoring this partition creation.", fg="red") continue - elif task[:len("Suggest partition layout")] == "Suggest partition layout": + elif task == suggest_partition_layout: if len(block_device_struct["partitions"]): prompt = _('{} contains queued partitions, this will remove those, are you sure?').format(block_device) choice = Menu(prompt, ['yes', 'no'], default_option='no').run() @@ -670,15 +683,15 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A else: current_layout = current_partition_layout(block_device_struct['partitions'], with_idx=True) - if task == "Delete a partition": + if task == delete_partition: 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: block_device_struct['partitions'] = [p for idx, p in enumerate(block_device_struct['partitions']) if idx not in to_delete] - elif task == "Clear/Delete all partitions": + elif task == delete_all_partitions: block_device_struct["partitions"] = [] - elif task == "Assign mount-point for a partition": + elif task == assign_mount_point: title = _('{}\n\nSelect by index which partition to mount where').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) @@ -694,7 +707,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A else: del(block_device_struct["partitions"][partition]['mountpoint']) - elif task == "Mark/Unmark a partition to be formatted (wipes data)": + elif task == mark_formatted: title = _('{}\n\nSelect which partition to mask for formatting').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) @@ -713,7 +726,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A # Negate the current wipe marking block_device_struct["partitions"][partition]['wipe'] = not block_device_struct["partitions"][partition].get('wipe', False) - elif task == "Mark/Unmark a partition as encrypted": + elif task == mark_encrypted: title = _('{}\n\nSelect which partition to mark as encrypted').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) @@ -721,14 +734,14 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A # Negate the current encryption marking 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)": + elif task == mark_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": + elif task == set_filesystem_partition: title = _('{}\n\nSelect which partition to set a filesystem on').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) @@ -762,10 +775,11 @@ def select_archinstall_language(default='English'): def select_disk_layout(block_devices :list, advanced_options=False) -> Dict[str, Any]: - 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)') + wipe_mode = str(_('Wipe all selected drives and use a best-effort default partition layout')) + custome_mode = str(_('Select what to do with each individual drive (followed by partition usage)')) modes = [wipe_mode, custome_mode] + print(modes) mode = Menu(_('Select what you wish to do with the selected block devices'), modes, skip=False).run() if mode == wipe_mode: @@ -802,7 +816,7 @@ def select_disk(dict_o_disks :Dict[str, BlockDevice]) -> BlockDevice: raise DiskError('select_disk() requires a non-empty dictionary of disks to select from.') -def select_profile() -> Optional[str]: +def select_profile() -> Optional[Profile]: """ # Asks the user to select a profile from the available profiles. # -- cgit v1.2.3-54-g00ecf