From 51714587962570c71fc2dd70de526a5172ad5b5c Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Sun, 29 May 2022 18:59:55 +1000 Subject: Fix subvol selection (#1277) * Fix subvolume selection * Update Co-authored-by: Daniel Girtler --- .../lib/user_interaction/subvolume_config.py | 33 ++++++++++++++-------- 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'archinstall/lib/user_interaction/subvolume_config.py') diff --git a/archinstall/lib/user_interaction/subvolume_config.py b/archinstall/lib/user_interaction/subvolume_config.py index af783639..94e6f5d7 100644 --- a/archinstall/lib/user_interaction/subvolume_config.py +++ b/archinstall/lib/user_interaction/subvolume_config.py @@ -80,24 +80,33 @@ class SubvolumeMenu(GeneralMenu): super().__init__(data_store=self.ds) def _setup_selection_menu_options(self): - # [str(_('Add')),str(_('Copy')),str(_('Edit')),str(_('Delete'))] - self._menu_options['name'] = Selector(str(_('Subvolume name ')), - self._select_subvolume_name if not self.action or self.action in (str(_('Add')),str(_('Copy'))) else None, + self._menu_options['name'] = Selector( + str(_('Subvolume name ')), + self._select_subvolume_name if not self.action or self.action in (str(_('Add')), str(_('Copy'))) else None, mandatory=True, enabled=True) - self._menu_options['mountpoint'] = Selector(str(_('Subvolume mountpoint')), + + self._menu_options['mountpoint'] = Selector( + str(_('Subvolume mountpoint')), self._select_subvolume_mount_point if not self.action or self.action in (str(_('Add')),str(_('Edit'))) else None, enabled=True) - self._menu_options['options'] = Selector(str(_('Subvolume options')), + + self._menu_options['options'] = Selector( + str(_('Subvolume options')), self._select_subvolume_options if not self.action or self.action in (str(_('Add')),str(_('Edit'))) else None, enabled=True) - self._menu_options['save'] = Selector(str(_('Save')), - exec_func=lambda n,v:True, - enabled=True) - self._menu_options['cancel'] = Selector(str(_('Cancel')), - # func = lambda pre:True, - exec_func=lambda n,v:self.fast_exit(n), - enabled=True) + + self._menu_options['save'] = Selector( + str(_('Save')), + exec_func=lambda n,v:True, + enabled=True) + + self._menu_options['cancel'] = Selector( + str(_('Cancel')), + # func = lambda pre:True, + exec_func=lambda n,v:self.fast_exit(n), + enabled=True) + self.cancel_action = 'cancel' self.save_action = 'save' self.bottom_list = [self.save_action,self.cancel_action] -- cgit v1.2.3-70-g09d2 From a7ca037a26de53fd242f89bc6a90fd53337b4d13 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 7 Jun 2022 01:28:46 +1000 Subject: Update the subvolume menu - fix for #1278 (#1297) * Update subvolume * Add mypy compliance Co-authored-by: Daniel Girtler Co-authored-by: Anton Hvornum --- .github/workflows/mypy.yaml | 2 +- archinstall/__init__.py | 6 +- archinstall/lib/disk/btrfs/__init__.py | 130 +------------ archinstall/lib/disk/btrfs/btrfs_helpers.py | 94 ++++------ archinstall/lib/disk/btrfs/btrfspartition.py | 6 +- archinstall/lib/disk/btrfs/btrfssubvolume.py | 191 -------------------- archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py | 192 ++++++++++++++++++++ archinstall/lib/disk/helpers.py | 9 +- archinstall/lib/disk/mapperdev.py | 10 +- archinstall/lib/disk/partition.py | 10 +- archinstall/lib/disk/user_guides.py | 19 +- archinstall/lib/installer.py | 55 ++---- archinstall/lib/menu/global_menu.py | 33 ++-- archinstall/lib/menu/list_manager.py | 39 ++-- archinstall/lib/models/subvolume.py | 68 +++++++ archinstall/lib/models/users.py | 6 +- .../lib/user_interaction/partitioning_conf.py | 12 +- .../lib/user_interaction/subvolume_config.py | 201 +++++++-------------- 18 files changed, 462 insertions(+), 621 deletions(-) delete mode 100644 archinstall/lib/disk/btrfs/btrfssubvolume.py create mode 100644 archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py create mode 100644 archinstall/lib/models/subvolume.py (limited to 'archinstall/lib/user_interaction/subvolume_config.py') diff --git a/.github/workflows/mypy.yaml b/.github/workflows/mypy.yaml index 8463afda..b0901b38 100644 --- a/.github/workflows/mypy.yaml +++ b/.github/workflows/mypy.yaml @@ -15,4 +15,4 @@ jobs: # one day this will be enabled # run: mypy --strict --module archinstall || exit 0 - name: run mypy - run: mypy --follow-imports=silent archinstall/lib/menu/selection_menu.py archinstall/lib/menu/global_menu.py archinstall/lib/models/network_configuration.py archinstall/lib/menu/list_manager.py archinstall/lib/user_interaction/network_conf.py archinstall/lib/models/users.py archinstall/lib/translation.py + run: mypy --follow-imports=silent archinstall/lib/menu/selection_menu.py archinstall/lib/menu/global_menu.py archinstall/lib/models/network_configuration.py archinstall/lib/menu/list_manager.py archinstall/lib/user_interaction/network_conf.py archinstall/lib/models/users.py archinstall/lib/user_interaction/subvolume_config.py archinstall/lib/disk/btrfs/btrfs_helpers.py archinstall/lib/translation.py diff --git a/archinstall/__init__.py b/archinstall/__init__.py index abcad3ba..1a360c67 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -226,8 +226,6 @@ def post_process_arguments(arguments): load_plugin(arguments['plugin']) if arguments.get('disk_layouts', None) is not None: - # if 'disk_layouts' not in storage: - # storage['disk_layouts'] = {} layout_storage = {} if not json_stream_to_structure('--disk_layouts',arguments['disk_layouts'],layout_storage): exit(1) @@ -236,10 +234,12 @@ def post_process_arguments(arguments): arguments['harddrives'] = [disk for disk in layout_storage] # backward compatibility. Change partition.format for partition.wipe for disk in layout_storage: - for i,partition in enumerate(layout_storage[disk].get('partitions',[])): + for i, partition in enumerate(layout_storage[disk].get('partitions',[])): if 'format' in partition: partition['wipe'] = partition['format'] del partition['format'] + elif 'btrfs' in partition: + partition['btrfs']['subvolumes'] = Subvolume.parse_arguments(partition['btrfs']['subvolumes']) arguments['disk_layouts'] = layout_storage load_config() diff --git a/archinstall/lib/disk/btrfs/__init__.py b/archinstall/lib/disk/btrfs/__init__.py index 90c58145..3c183112 100644 --- a/archinstall/lib/disk/btrfs/__init__.py +++ b/archinstall/lib/disk/btrfs/__init__.py @@ -2,8 +2,7 @@ from __future__ import annotations import pathlib import glob import logging -import re -from typing import Union, Dict, TYPE_CHECKING, Any, Iterator +from typing import Union, Dict, TYPE_CHECKING # https://stackoverflow.com/a/39757388/929999 if TYPE_CHECKING: @@ -15,30 +14,15 @@ from .btrfs_helpers import ( setup_subvolumes as setup_subvolumes, mount_subvolume as mount_subvolume ) -from .btrfssubvolume import BtrfsSubvolume as BtrfsSubvolume +from .btrfssubvolumeinfo import BtrfsSubvolumeInfo as BtrfsSubvolume from .btrfspartition import BTRFSPartition as BTRFSPartition -from ..helpers import get_mount_info from ...exceptions import DiskError, Deprecated from ...general import SysCommand from ...output import log -from ...exceptions import SysCallError -def get_subvolume_info(path :pathlib.Path) -> Dict[str, Any]: - try: - output = SysCommand(f"btrfs subvol show {path}").decode() - except SysCallError as error: - print('Error:', error) - result = {} - for line in output.replace('\r\n', '\n').split('\n'): - if ':' in line: - key, val = line.replace('\t', '').split(':', 1) - result[key.strip().lower().replace(' ', '_')] = val.strip() - - return result - -def create_subvolume(installation :Installer, subvolume_location :Union[pathlib.Path, str]) -> bool: +def create_subvolume(installation: Installer, subvolume_location :Union[pathlib.Path, str]) -> bool: """ This function uses btrfs to create a subvolume. @@ -71,112 +55,6 @@ def create_subvolume(installation :Installer, subvolume_location :Union[pathlib. if (cmd := SysCommand(f"btrfs subvolume create {target}")).exit_code != 0: raise DiskError(f"Could not create a subvolume at {target}: {cmd}") -def _has_option(option :str,options :list) -> bool: - """ auxiliary routine to check if an option is present in a list. - we check if the string appears in one of the options, 'cause it can appear in several forms (option, option=val,...) - """ - if not options: - return False - - for item in options: - if option in item: - return True - - return False - -def manage_btrfs_subvolumes(installation :Installer, - partition :Dict[str, str],) -> list: +def manage_btrfs_subvolumes(installation :Installer, partition :Dict[str, str]) -> list: raise Deprecated("Use setup_subvolumes() instead.") - - from copy import deepcopy - """ we do the magic with subvolumes in a centralized place - parameters: - * the installation object - * the partition dictionary entry which represents the physical partition - returns - * mountpoinst, the list which contains all the "new" partititon to be mounted - - We expect the partition has been mounted as / , and it to be unmounted after the processing - Then we create all the subvolumes inside btrfs as demand - We clone then, both the partition dictionary and the object inside it and adapt it to the subvolume needs - Then we return a list of "new" partitions to be processed as "normal" partitions - # TODO For encrypted devices we need some special processing prior to it - """ - # We process each of the pairs - # th mount info dict has an entry for the path of the mountpoint (named 'mountpoint') and 'options' which is a list - # of mount options (or similar used by brtfs) - mountpoints = [] - subvolumes = partition['btrfs']['subvolumes'] - for name, right_hand in subvolumes.items(): - try: - # we normalize the subvolume name (getting rid of slash at the start if exists. In our implementation has no semantic load - every subvolume is created from the top of the hierarchy- and simplifies its further use - if name.startswith('/'): - name = name[1:] - # renormalize the right hand. - location = None - subvol_options = [] - # no contents, so it is not to be mounted - if not right_hand: - location = None - # just a string. per backward compatibility the mount point - elif isinstance(right_hand,str): - location = right_hand - # a dict. two elements 'mountpoint' (obvious) and and a mount options list ¿? - elif isinstance(right_hand,dict): - location = right_hand.get('mountpoint',None) - subvol_options = right_hand.get('options',[]) - # we create the subvolume - create_subvolume(installation,name) - # Make the nodatacow processing now - # It will be the main cause of creation of subvolumes which are not to be mounted - # it is not an options which can be established by subvolume (but for whole file systems), and can be - # set up via a simple attribute change in a directory (if empty). And here the directories are brand new - if 'nodatacow' in subvol_options: - if (cmd := SysCommand(f"chattr +C {installation.target}/{name}")).exit_code != 0: - raise DiskError(f"Could not set nodatacow attribute at {installation.target}/{name}: {cmd}") - # entry is deleted so nodatacow doesn't propagate to the mount options - del subvol_options[subvol_options.index('nodatacow')] - # Make the compress processing now - # it is not an options which can be established by subvolume (but for whole file systems), and can be - # set up via a simple attribute change in a directory (if empty). And here the directories are brand new - # in this way only zstd compression is activaded - # TODO WARNING it is not clear if it should be a standard feature, so it might need to be deactivated - if 'compress' in subvol_options: - if not _has_option('compress',partition.get('filesystem',{}).get('mount_options',[])): - if (cmd := SysCommand(f"chattr +c {installation.target}/{name}")).exit_code != 0: - raise DiskError(f"Could not set compress attribute at {installation.target}/{name}: {cmd}") - # entry is deleted so compress doesn't propagate to the mount options - del subvol_options[subvol_options.index('compress')] - # END compress processing. - # we do not mount if THE basic partition will be mounted or if we exclude explicitly this subvolume - if not partition['mountpoint'] and location is not None: - # we begin to create a fake partition entry. First we copy the original -the one that corresponds to - # the primary partition. We make a deepcopy to avoid altering the original content in any case - fake_partition = deepcopy(partition) - # we start to modify entries in the "fake partition" to match the needs of the subvolumes - # to avoid any chance of entering in a loop (not expected) we delete the list of subvolumes in the copy - del fake_partition['btrfs'] - fake_partition['encrypted'] = False - fake_partition['generate-encryption-key-file'] = False - # Mount destination. As of now the right hand part - fake_partition['mountpoint'] = location - # we load the name in an attribute called subvolume, but i think it is not needed anymore, 'cause the mount logic uses a different path. - fake_partition['subvolume'] = name - # here we add the special mount options for the subvolume, if any. - # if the original partition['options'] is not a list might give trouble - if fake_partition.get('filesystem',{}).get('mount_options',[]): - fake_partition['filesystem']['mount_options'].extend(subvol_options) - else: - fake_partition['filesystem']['mount_options'] = subvol_options - # Here comes the most exotic part. The dictionary attribute 'device_instance' contains an instance of Partition. This instance will be queried along the mount process at the installer. - # As the rest will query there the path of the "partition" to be mounted, we feed it with the bind name needed to mount subvolumes - # As we made a deepcopy we have a fresh instance of this object we can manipulate problemless - fake_partition['device_instance'].path = f"{partition['device_instance'].path}[/{name}]" - - # Well, now that this "fake partition" is ready, we add it to the list of the ones which are to be mounted, - # as "normal" ones - mountpoints.append(fake_partition) - except Exception as e: - raise e - return mountpoints diff --git a/archinstall/lib/disk/btrfs/btrfs_helpers.py b/archinstall/lib/disk/btrfs/btrfs_helpers.py index 5fa94314..ab528388 100644 --- a/archinstall/lib/disk/btrfs/btrfs_helpers.py +++ b/archinstall/lib/disk/btrfs/btrfs_helpers.py @@ -1,72 +1,42 @@ -import pathlib import logging -from typing import Optional +from pathlib import Path +from typing import Optional, Dict, Any, TYPE_CHECKING +from ...models.subvolume import Subvolume from ...exceptions import SysCallError, DiskError from ...general import SysCommand from ...output import log from ..helpers import get_mount_info -from .btrfssubvolume import BtrfsSubvolume +from .btrfssubvolumeinfo import BtrfsSubvolumeInfo +if TYPE_CHECKING: + from .btrfspartition import BTRFSPartition + from ...installer import Installer -def mount_subvolume(installation, device, name, subvolume_information): - # we normalize the subvolume name (getting rid of slash at the start if exists. In our implementation has no semantic load. - # Every subvolume is created from the top of the hierarchy- and simplifies its further use - name = name.lstrip('/') - - # renormalize the right hand. - mountpoint = subvolume_information.get('mountpoint', None) - if not mountpoint: - return None - - if type(mountpoint) == str: - mountpoint = pathlib.Path(mountpoint) - installation_target = installation.target - if type(installation_target) == str: - installation_target = pathlib.Path(installation_target) +def mount_subvolume(installation: 'Installer', device: 'BTRFSPartition', subvolume: Subvolume): + # we normalize the subvolume name (getting rid of slash at the start if exists. + # In our implementation has no semantic load. + # Every subvolume is created from the top of the hierarchy- and simplifies its further use + name = subvolume.name.lstrip('/') + mountpoint = Path(subvolume.mountpoint) + installation_target = Path(installation.target) mountpoint = installation_target / mountpoint.relative_to(mountpoint.anchor) mountpoint.mkdir(parents=True, exist_ok=True) - - mount_options = subvolume_information.get('options', []) - if not any('subvol=' in x for x in mount_options): - mount_options += [f'subvol={name}'] + mount_options = subvolume.options + [f'subvol={name}'] log(f"Mounting subvolume {name} on {device} to {mountpoint}", level=logging.INFO, fg="gray") SysCommand(f"mount {device.path} {mountpoint} -o {','.join(mount_options)}") -def setup_subvolumes(installation, partition_dict): - """ - Taken from: ..user_guides.py - - partition['btrfs'] = { - "subvolumes" : { - "@": "/", - "@home": "/home", - "@log": "/var/log", - "@pkg": "/var/cache/pacman/pkg", - "@.snapshots": "/.snapshots" - } - } - """ +def setup_subvolumes(installation: 'Installer', partition_dict: Dict[str, Any]): log(f"Setting up subvolumes: {partition_dict['btrfs']['subvolumes']}", level=logging.INFO, fg="gray") - for name, right_hand in partition_dict['btrfs']['subvolumes'].items(): + + for subvolume in partition_dict['btrfs']['subvolumes']: # we normalize the subvolume name (getting rid of slash at the start if exists. In our implementation has no semantic load. # Every subvolume is created from the top of the hierarchy- and simplifies its further use - name = name.lstrip('/') - - # renormalize the right hand. - # mountpoint = None - subvol_options = [] - - match right_hand: - # case str(): # backwards-compatability - # mountpoint = right_hand - case dict(): - # mountpoint = right_hand.get('mountpoint', None) - subvol_options = right_hand.get('options', []) + name = subvolume.name.lstrip('/') # We create the subvolume using the BTRFSPartition instance. # That way we ensure not only easy access, but also accurate mount locations etc. @@ -76,27 +46,25 @@ def setup_subvolumes(installation, partition_dict): # It will be the main cause of creation of subvolumes which are not to be mounted # it is not an options which can be established by subvolume (but for whole file systems), and can be # set up via a simple attribute change in a directory (if empty). And here the directories are brand new - if 'nodatacow' in subvol_options: + if subvolume.nodatacow: if (cmd := SysCommand(f"chattr +C {installation.target}/{name}")).exit_code != 0: raise DiskError(f"Could not set nodatacow attribute at {installation.target}/{name}: {cmd}") - # entry is deleted so nodatacow doesn't propagate to the mount options - del subvol_options[subvol_options.index('nodatacow')] + # Make the compress processing now # it is not an options which can be established by subvolume (but for whole file systems), and can be # set up via a simple attribute change in a directory (if empty). And here the directories are brand new # in this way only zstd compression is activaded # TODO WARNING it is not clear if it should be a standard feature, so it might need to be deactivated - if 'compress' in subvol_options: + if subvolume.compress: if not any(['compress' in filesystem_option for filesystem_option in partition_dict.get('filesystem', {}).get('mount_options', [])]): if (cmd := SysCommand(f"chattr +c {installation.target}/{name}")).exit_code != 0: raise DiskError(f"Could not set compress attribute at {installation.target}/{name}: {cmd}") - # entry is deleted so compress doesn't propagate to the mount options - del subvol_options[subvol_options.index('compress')] -def subvolume_info_from_path(path :pathlib.Path) -> Optional[BtrfsSubvolume]: + +def subvolume_info_from_path(path: Path) -> Optional[BtrfsSubvolumeInfo]: try: - subvolume_name = None + subvolume_name = '' result = {} for index, line in enumerate(SysCommand(f"btrfs subvolume show {path}")): if index == 0: @@ -110,14 +78,14 @@ def subvolume_info_from_path(path :pathlib.Path) -> Optional[BtrfsSubvolume]: # allows for hooking in a pre-processor to do this we have to do it here: result[key.lower().replace(' ', '_').replace('(s)', 's')] = value.strip() - return BtrfsSubvolume(**{'full_path' : path, 'name' : subvolume_name, **result}) - + return BtrfsSubvolumeInfo(**{'full_path' : path, 'name' : subvolume_name, **result}) # type: ignore except SysCallError as error: log(f"Could not retrieve subvolume information from {path}: {error}", level=logging.WARNING, fg="orange") return None -def find_parent_subvolume(path :pathlib.Path, filters=[]): + +def find_parent_subvolume(path: Path, filters=[]) -> Optional[BtrfsSubvolumeInfo]: # A root path cannot have a parent if str(path) == '/': return None @@ -127,6 +95,8 @@ def find_parent_subvolume(path :pathlib.Path, filters=[]): if found_mount['target'] == '/': return None - return find_parent_subvolume(path.parent, traverse=True, filters=[*filters, found_mount['target']]) + return find_parent_subvolume(path.parent, filters=[*filters, found_mount['target']]) + + return subvolume - return subvolume \ No newline at end of file + return None diff --git a/archinstall/lib/disk/btrfs/btrfspartition.py b/archinstall/lib/disk/btrfs/btrfspartition.py index 6f7487e4..a05f1527 100644 --- a/archinstall/lib/disk/btrfs/btrfspartition.py +++ b/archinstall/lib/disk/btrfs/btrfspartition.py @@ -15,7 +15,7 @@ from .btrfs_helpers import ( if TYPE_CHECKING: from ...installer import Installer - from .btrfssubvolume import BtrfsSubvolume + from .btrfssubvolumeinfo import BtrfsSubvolumeInfo class BTRFSPartition(Partition): def __init__(self, *args, **kwargs): @@ -50,7 +50,7 @@ class BTRFSPartition(Partition): for child in iterate_children(filesystem): yield child - def create_subvolume(self, subvolume :pathlib.Path, installation :Optional['Installer'] = None) -> 'BtrfsSubvolume': + def create_subvolume(self, subvolume :pathlib.Path, installation :Optional['Installer'] = None) -> 'BtrfsSubvolumeInfo': """ Subvolumes have to be created within a mountpoint. This means we need to get the current installation target. @@ -117,4 +117,4 @@ class BTRFSPartition(Partition): # And deal with it here: SysCommand(f"btrfs subvolume create {subvolume}") - return subvolume_info_from_path(subvolume) \ No newline at end of file + return subvolume_info_from_path(subvolume) diff --git a/archinstall/lib/disk/btrfs/btrfssubvolume.py b/archinstall/lib/disk/btrfs/btrfssubvolume.py deleted file mode 100644 index bc7db612..00000000 --- a/archinstall/lib/disk/btrfs/btrfssubvolume.py +++ /dev/null @@ -1,191 +0,0 @@ -import pathlib -import datetime -import logging -import string -import random -import shutil -from dataclasses import dataclass -from typing import Optional, List# , TYPE_CHECKING -from functools import cached_property - -# if TYPE_CHECKING: -# from ..blockdevice import BlockDevice - -from ...exceptions import DiskError -from ...general import SysCommand -from ...output import log -from ...storage import storage - -@dataclass -class BtrfsSubvolume: - full_path :pathlib.Path - name :str - uuid :str - parent_uuid :str - creation_time :datetime.datetime - subvolume_id :int - generation :int - gen_at_creation :int - parent_id :int - top_level_id :int - send_transid :int - send_time :datetime.datetime - receive_transid :int - received_uuid :Optional[str] = None - flags :Optional[str] = None - receive_time :Optional[datetime.datetime] = None - snapshots :Optional[List] = None - - def __post_init__(self): - self.full_path = pathlib.Path(self.full_path) - - # Convert "-" entries to `None` - if self.parent_uuid == "-": - self.parent_uuid = None - if self.received_uuid == "-": - self.received_uuid = None - if self.flags == "-": - self.flags = None - if self.receive_time == "-": - self.receive_time = None - if self.snapshots == "": - self.snapshots = [] - - # Convert timestamps into datetime workable objects (and preserve timezone by using ISO formats) - self.creation_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.creation_time)) - self.send_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.send_time)) - if self.receive_time: - self.receive_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.receive_time)) - - @property - def parent_subvolume(self): - from .btrfs_helpers import find_parent_subvolume - - return find_parent_subvolume(self.full_path) - - @property - def root(self) -> bool: - from .btrfs_helpers import subvolume_info_from_path - - # TODO: Make this function traverse storage['MOUNT_POINT'] and find the first - # occurrence of a mountpoint that is a btrfs volume instead of lazy assume / is a subvolume. - # It would also be nice if it could use findmnt(self.full_path) and traverse backwards - # finding the last occurrence of a subvolume which 'self' belongs to. - if volume := subvolume_info_from_path(storage['MOUNT_POINT']): - return self.full_path == volume.full_path - - return False - - @cached_property - def partition(self): - from ..helpers import findmnt, get_parent_of_partition, all_blockdevices - from ..partition import Partition - from ..blockdevice import BlockDevice - from ..mapperdev import MapperDev - from .btrfspartition import BTRFSPartition - from .btrfs_helpers import subvolume_info_from_path - - try: - # If the subvolume is mounted, it's pretty trivial to lookup the partition (parent) device. - if filesystem := findmnt(self.full_path).get('filesystems', []): - if source := filesystem[0].get('source', None): - # Strip away subvolume definitions from findmnt - if '[' in source: - source = source[:source.find('[')] - - if filesystem[0].get('fstype', '') == 'btrfs': - return BTRFSPartition(source, BlockDevice(get_parent_of_partition(pathlib.Path(source)))) - elif filesystem[0].get('source', '').startswith('/dev/mapper'): - return MapperDev(source) - else: - return Partition(source, BlockDevice(get_parent_of_partition(pathlib.Path(source)))) - except DiskError: - # Subvolume has never been mounted, we have no reliable way of finding where it is. - # But we have the UUID of the partition, and can begin looking for it by mounting - # all blockdevices that we can reliably support.. This is taxing tho and won't cover all devices. - - log(f"Looking up {self}, this might take time.", fg="orange", level=logging.WARNING) - for blockdevice, instance in all_blockdevices(mappers=True, partitions=True, error=True).items(): - if type(instance) in (Partition, MapperDev): - we_mounted_it = False - detection_mountpoint = instance.mountpoint - if not detection_mountpoint: - if type(instance) == Partition and instance.encrypted: - # TODO: Perhaps support unlocking encrypted volumes? - # This will cause a lot of potential user interactions tho. - log(f"Ignoring {blockdevice} because it's encrypted.", fg="gray", level=logging.DEBUG) - continue - - detection_mountpoint = pathlib.Path(f"/tmp/{''.join([random.choice(string.ascii_letters) for x in range(20)])}") - detection_mountpoint.mkdir(parents=True, exist_ok=True) - - instance.mount(str(detection_mountpoint)) - we_mounted_it = True - - if (filesystem := findmnt(detection_mountpoint)) and (filesystem := filesystem.get('filesystems', [])): - if subvolume := subvolume_info_from_path(filesystem[0]['target']): - if subvolume.uuid == self.uuid: - # The top level subvolume matched of ourselves, - # which means the instance we're iterating has the subvol we're looking for. - log(f"Found the subvolume on device {instance}", level=logging.DEBUG, fg="gray") - return instance - - def iterate_children(struct): - for child in struct.get('children', []): - if '[' in child.get('source', ''): - yield subvolume_info_from_path(child['target']) - - for sub_child in iterate_children(child): - yield sub_child - - for child in iterate_children(filesystem[0]): - if child.uuid == self.uuid: - # We found a child within the instance that has the subvol we're looking for. - log(f"Found the subvolume on device {instance}", level=logging.DEBUG, fg="gray") - return instance - - if we_mounted_it: - instance.unmount() - shutil.rmtree(detection_mountpoint) - - @cached_property - def mount_options(self) -> Optional[List[str]]: - from ..helpers import findmnt - - if filesystem := findmnt(self.full_path).get('filesystems', []): - return filesystem[0].get('options').split(',') - - def convert_to_ISO_format(self, time_string): - time_string_almost_done = time_string.replace(' ', 'T', 1).replace(' ', '') - iso_string = f"{time_string_almost_done[:-2]}:{time_string_almost_done[-2:]}" - return iso_string - - def mount(self, mountpoint :pathlib.Path, options=None, include_previously_known_options=True): - from ..helpers import findmnt - - try: - if mnt_info := findmnt(pathlib.Path(mountpoint), traverse=False): - log(f"Unmounting {mountpoint} as it was already mounted using {mnt_info}") - SysCommand(f"umount {mountpoint}") - except DiskError: - # No previously mounted device at the mountpoint - pass - - if not options: - options = [] - - try: - if include_previously_known_options and (cached_options := self.mount_options): - options += cached_options - except DiskError: - pass - - if not any('subvol=' in x for x in options): - options += f'subvol={self.name}' - - SysCommand(f"mount {self.partition.path} {mountpoint} -o {','.join(options)}") - log(f"{self} has successfully been mounted to {mountpoint}", level=logging.INFO, fg="gray") - - def unmount(self, recurse :bool = True): - SysCommand(f"umount {'-R' if recurse else ''} {self.full_path}") - log(f"Successfully unmounted {self}", level=logging.INFO, fg="gray") \ No newline at end of file diff --git a/archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py b/archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py new file mode 100644 index 00000000..5f5bdea6 --- /dev/null +++ b/archinstall/lib/disk/btrfs/btrfssubvolumeinfo.py @@ -0,0 +1,192 @@ +import pathlib +import datetime +import logging +import string +import random +import shutil +from dataclasses import dataclass +from typing import Optional, List# , TYPE_CHECKING +from functools import cached_property + +# if TYPE_CHECKING: +# from ..blockdevice import BlockDevice + +from ...exceptions import DiskError +from ...general import SysCommand +from ...output import log +from ...storage import storage + + +@dataclass +class BtrfsSubvolumeInfo: + full_path :pathlib.Path + name :str + uuid :str + parent_uuid :str + creation_time :datetime.datetime + subvolume_id :int + generation :int + gen_at_creation :int + parent_id :int + top_level_id :int + send_transid :int + send_time :datetime.datetime + receive_transid :int + received_uuid :Optional[str] = None + flags :Optional[str] = None + receive_time :Optional[datetime.datetime] = None + snapshots :Optional[List] = None + + def __post_init__(self): + self.full_path = pathlib.Path(self.full_path) + + # Convert "-" entries to `None` + if self.parent_uuid == "-": + self.parent_uuid = None + if self.received_uuid == "-": + self.received_uuid = None + if self.flags == "-": + self.flags = None + if self.receive_time == "-": + self.receive_time = None + if self.snapshots == "": + self.snapshots = [] + + # Convert timestamps into datetime workable objects (and preserve timezone by using ISO formats) + self.creation_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.creation_time)) + self.send_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.send_time)) + if self.receive_time: + self.receive_time = datetime.datetime.fromisoformat(self.convert_to_ISO_format(self.receive_time)) + + @property + def parent_subvolume(self): + from .btrfs_helpers import find_parent_subvolume + + return find_parent_subvolume(self.full_path) + + @property + def root(self) -> bool: + from .btrfs_helpers import subvolume_info_from_path + + # TODO: Make this function traverse storage['MOUNT_POINT'] and find the first + # occurrence of a mountpoint that is a btrfs volume instead of lazy assume / is a subvolume. + # It would also be nice if it could use findmnt(self.full_path) and traverse backwards + # finding the last occurrence of a subvolume which 'self' belongs to. + if volume := subvolume_info_from_path(storage['MOUNT_POINT']): + return self.full_path == volume.full_path + + return False + + @cached_property + def partition(self): + from ..helpers import findmnt, get_parent_of_partition, all_blockdevices + from ..partition import Partition + from ..blockdevice import BlockDevice + from ..mapperdev import MapperDev + from .btrfspartition import BTRFSPartition + from .btrfs_helpers import subvolume_info_from_path + + try: + # If the subvolume is mounted, it's pretty trivial to lookup the partition (parent) device. + if filesystem := findmnt(self.full_path).get('filesystems', []): + if source := filesystem[0].get('source', None): + # Strip away subvolume definitions from findmnt + if '[' in source: + source = source[:source.find('[')] + + if filesystem[0].get('fstype', '') == 'btrfs': + return BTRFSPartition(source, BlockDevice(get_parent_of_partition(pathlib.Path(source)))) + elif filesystem[0].get('source', '').startswith('/dev/mapper'): + return MapperDev(source) + else: + return Partition(source, BlockDevice(get_parent_of_partition(pathlib.Path(source)))) + except DiskError: + # Subvolume has never been mounted, we have no reliable way of finding where it is. + # But we have the UUID of the partition, and can begin looking for it by mounting + # all blockdevices that we can reliably support.. This is taxing tho and won't cover all devices. + + log(f"Looking up {self}, this might take time.", fg="orange", level=logging.WARNING) + for blockdevice, instance in all_blockdevices(mappers=True, partitions=True, error=True).items(): + if type(instance) in (Partition, MapperDev): + we_mounted_it = False + detection_mountpoint = instance.mountpoint + if not detection_mountpoint: + if type(instance) == Partition and instance.encrypted: + # TODO: Perhaps support unlocking encrypted volumes? + # This will cause a lot of potential user interactions tho. + log(f"Ignoring {blockdevice} because it's encrypted.", fg="gray", level=logging.DEBUG) + continue + + detection_mountpoint = pathlib.Path(f"/tmp/{''.join([random.choice(string.ascii_letters) for x in range(20)])}") + detection_mountpoint.mkdir(parents=True, exist_ok=True) + + instance.mount(str(detection_mountpoint)) + we_mounted_it = True + + if (filesystem := findmnt(detection_mountpoint)) and (filesystem := filesystem.get('filesystems', [])): + if subvolume := subvolume_info_from_path(filesystem[0]['target']): + if subvolume.uuid == self.uuid: + # The top level subvolume matched of ourselves, + # which means the instance we're iterating has the subvol we're looking for. + log(f"Found the subvolume on device {instance}", level=logging.DEBUG, fg="gray") + return instance + + def iterate_children(struct): + for child in struct.get('children', []): + if '[' in child.get('source', ''): + yield subvolume_info_from_path(child['target']) + + for sub_child in iterate_children(child): + yield sub_child + + for child in iterate_children(filesystem[0]): + if child.uuid == self.uuid: + # We found a child within the instance that has the subvol we're looking for. + log(f"Found the subvolume on device {instance}", level=logging.DEBUG, fg="gray") + return instance + + if we_mounted_it: + instance.unmount() + shutil.rmtree(detection_mountpoint) + + @cached_property + def mount_options(self) -> Optional[List[str]]: + from ..helpers import findmnt + + if filesystem := findmnt(self.full_path).get('filesystems', []): + return filesystem[0].get('options').split(',') + + def convert_to_ISO_format(self, time_string): + time_string_almost_done = time_string.replace(' ', 'T', 1).replace(' ', '') + iso_string = f"{time_string_almost_done[:-2]}:{time_string_almost_done[-2:]}" + return iso_string + + def mount(self, mountpoint :pathlib.Path, options=None, include_previously_known_options=True): + from ..helpers import findmnt + + try: + if mnt_info := findmnt(pathlib.Path(mountpoint), traverse=False): + log(f"Unmounting {mountpoint} as it was already mounted using {mnt_info}") + SysCommand(f"umount {mountpoint}") + except DiskError: + # No previously mounted device at the mountpoint + pass + + if not options: + options = [] + + try: + if include_previously_known_options and (cached_options := self.mount_options): + options += cached_options + except DiskError: + pass + + if not any('subvol=' in x for x in options): + options += f'subvol={self.name}' + + SysCommand(f"mount {self.partition.path} {mountpoint} -o {','.join(options)}") + log(f"{self} has successfully been mounted to {mountpoint}", level=logging.INFO, fg="gray") + + def unmount(self, recurse :bool = True): + SysCommand(f"umount {'-R' if recurse else ''} {self.full_path}") + log(f"Successfully unmounted {self}", level=logging.INFO, fg="gray") diff --git a/archinstall/lib/disk/helpers.py b/archinstall/lib/disk/helpers.py index 85c0390f..660594ed 100644 --- a/archinstall/lib/disk/helpers.py +++ b/archinstall/lib/disk/helpers.py @@ -8,6 +8,8 @@ import time import glob from typing import Union, List, Iterator, Dict, Optional, Any, TYPE_CHECKING # https://stackoverflow.com/a/39757388/929999 +from ..models.subvolume import Subvolume + if TYPE_CHECKING: from .partition import Partition @@ -469,6 +471,7 @@ def convert_device_to_uuid(path :str) -> str: raise DiskError(f"Could not retrieve the UUID of {path} within a timely manner.") + def has_mountpoint(partition: Union[dict,Partition,MapperDev], target: str, strict: bool = True) -> bool: """ Determine if a certain partition is mounted (or has a mountpoint) as specific target (path) Coded for clarity rather than performance @@ -485,10 +488,12 @@ def has_mountpoint(partition: Union[dict,Partition,MapperDev], target: str, stri """ # we create the mountpoint list if isinstance(partition,dict): - subvols = partition.get('btrfs',{}).get('subvolumes',{}) - mountpoints = [partition.get('mountpoint'),] + [subvols[subvol] if isinstance(subvols[subvol],str) or not subvols[subvol] else subvols[subvol].get('mountpoint') for subvol in subvols] + subvolumes: List[Subvolume] = partition.get('btrfs',{}).get('subvolumes', []) + mountpoints = [partition.get('mountpoint')] + mountpoints += [volume.mountpoint for volume in subvolumes] else: mountpoints = [partition.mountpoint,] + [subvol.target for subvol in partition.subvolumes] + # we check if strict or target == '/': if target in mountpoints: diff --git a/archinstall/lib/disk/mapperdev.py b/archinstall/lib/disk/mapperdev.py index 913dbc13..49137ae9 100644 --- a/archinstall/lib/disk/mapperdev.py +++ b/archinstall/lib/disk/mapperdev.py @@ -10,7 +10,7 @@ from ..general import SysCommand from ..output import log if TYPE_CHECKING: - from .btrfs import BtrfsSubvolume + from .btrfs import BtrfsSubvolumeInfo @dataclass class MapperDev: @@ -37,12 +37,12 @@ class MapperDev: for slave in glob.glob(f"/sys/class/block/{dm_device.name}/slaves/*"): partition_belonging_to_dmcrypt_device = pathlib.Path(slave).name - + try: uevent_data = SysCommand(f"blkid -o export /dev/{partition_belonging_to_dmcrypt_device}").decode() except SysCallError as error: log(f"Could not get information on device /dev/{partition_belonging_to_dmcrypt_device}: {error}", level=logging.ERROR, fg="red") - + information = uevent(uevent_data) block_device = BlockDevice(get_parent_of_partition('/dev/' / pathlib.Path(information['DEVNAME']))) @@ -75,10 +75,10 @@ class MapperDev: return get_filesystem_type(self.path) @property - def subvolumes(self) -> Iterator['BtrfsSubvolume']: + def subvolumes(self) -> Iterator['BtrfsSubvolumeInfo']: from .btrfs import subvolume_info_from_path for mountpoint in self.mount_information: if target := mountpoint.get('target'): if subvolume := subvolume_info_from_path(pathlib.Path(target)): - yield subvolume \ No newline at end of file + yield subvolume diff --git a/archinstall/lib/disk/partition.py b/archinstall/lib/disk/partition.py index 2c9f50c2..6f25a5f7 100644 --- a/archinstall/lib/disk/partition.py +++ b/archinstall/lib/disk/partition.py @@ -14,7 +14,7 @@ from ..exceptions import DiskError, SysCallError, UnknownFilesystemFormat from ..output import log from ..general import SysCommand from .btrfs.btrfs_helpers import subvolume_info_from_path -from .btrfs.btrfssubvolume import BtrfsSubvolume +from .btrfs.btrfssubvolumeinfo import BtrfsSubvolumeInfo class Partition: def __init__(self, @@ -185,7 +185,7 @@ class Partition: for i in range(storage['DISK_RETRY_ATTEMPTS']): if not self.partprobe(): raise DiskError(f"Could not perform partprobe on {self.device_path}") - + time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * i)) partuuid = self._safe_part_uuid @@ -294,9 +294,9 @@ class Partition: return bind_name @property - def subvolumes(self) -> Iterator[BtrfsSubvolume]: + def subvolumes(self) -> Iterator[BtrfsSubvolumeInfo]: from .helpers import findmnt - + def iterate_children_recursively(information): for child in information.get('children', []): if target := child.get('target'): @@ -452,7 +452,7 @@ class Partition: if retry is True: log(f"Retrying in {storage.get('DISK_TIMEOUTS', 1)} seconds.", level=logging.WARNING, fg="orange") time.sleep(storage.get('DISK_TIMEOUTS', 1)) - + return self.format(filesystem, path, log_formatting, options, retry=False) if get_filesystem_type(path) == 'crypto_LUKS' or get_filesystem_type(self.real_device) == 'crypto_LUKS': diff --git a/archinstall/lib/disk/user_guides.py b/archinstall/lib/disk/user_guides.py index 5fa6bfdc..5809c073 100644 --- a/archinstall/lib/disk/user_guides.py +++ b/archinstall/lib/disk/user_guides.py @@ -3,6 +3,8 @@ import logging from typing import Optional, Dict, Any, List, TYPE_CHECKING # https://stackoverflow.com/a/39757388/929999 +from ..models.subvolume import Subvolume + if TYPE_CHECKING: from .blockdevice import BlockDevice _: Any @@ -107,17 +109,14 @@ def suggest_single_disk_layout(block_device :BlockDevice, # https://unix.stackexchange.com/questions/246976/btrfs-subvolume-uuid-clash # https://github.com/classy-giraffe/easy-arch/blob/main/easy-arch.sh layout[block_device.path]['partitions'][1]['btrfs'] = { - "subvolumes" : { - "@":"/", - "@home": "/home", - "@log": "/var/log", - "@pkg": "/var/cache/pacman/pkg", - "@.snapshots": "/.snapshots" - } + 'subvolumes': [ + Subvolume('@', '/'), + Subvolume('@home', '/home'), + Subvolume('@log', '/var/log'), + Subvolume('@pkg', '/var/cache/pacman/pkg'), + Subvolume('@.snapshots', '/.snapshots') + ] } - # else: - # pass # ... implement a guided setup - elif using_home_partition: # If we don't want to use subvolumes, # But we want to be able to re-use data between re-installs.. diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index bf296c2e..97c2492d 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -24,6 +24,7 @@ from .disk.partition import get_mount_fs_type from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError from .hsm import fido2_enroll from .models.users import User +from .models.subvolume import Subvolume if TYPE_CHECKING: _: Any @@ -263,47 +264,25 @@ class Installer: hsm_device_path = storage['arguments']['HSM'] fido2_enroll(hsm_device_path, partition['device_instance'], password) - # we manage the btrfs partitions - if any(btrfs_subvolumes := [entry for entry in list_part if entry.get('btrfs', {}).get('subvolumes', {})]): - for partition in btrfs_subvolumes: - if mount_options := ','.join(partition.get('filesystem',{}).get('mount_options',[])): - self.mount(partition['device_instance'], "/", options=mount_options) - else: - self.mount(partition['device_instance'], "/") - - setup_subvolumes( - installation=self, - partition_dict=partition - ) + btrfs_subvolumes = [entry for entry in list_part if entry.get('btrfs', {}).get('subvolumes', [])] - partition['device_instance'].unmount() + for partition in btrfs_subvolumes: + device_instance = partition['device_instance'] + mount_options = partition.get('filesystem', {}).get('mount_options', []) + self.mount(device_instance, "/", options=','.join(mount_options)) + setup_subvolumes(installation=self, partition_dict=partition) + device_instance.unmount() # We then handle any special cases, such as btrfs - if any(btrfs_subvolumes := [entry for entry in list_part if entry.get('btrfs', {}).get('subvolumes', {})]): - for partition_information in btrfs_subvolumes: - for name, mountpoint in sorted(partition_information['btrfs']['subvolumes'].items(), key=lambda item: item[1]): - btrfs_subvolume_information = {} - - match mountpoint: - case str(): # backwards-compatability - btrfs_subvolume_information['mountpoint'] = mountpoint - btrfs_subvolume_information['options'] = [] - case dict(): - btrfs_subvolume_information['mountpoint'] = mountpoint.get('mountpoint', None) - btrfs_subvolume_information['options'] = mountpoint.get('options', []) - case _: - continue - - if mountpoint_parsed := btrfs_subvolume_information.get('mountpoint'): - # We cache the mount call for later - mount_queue[mountpoint_parsed] = lambda device=partition_information['device_instance'], \ - name=name, \ - subvolume_information=btrfs_subvolume_information: mount_subvolume( - installation=self, - device=device, - name=name, - subvolume_information=subvolume_information - ) + for partition in btrfs_subvolumes: + subvolumes: List[Subvolume] = partition['btrfs']['subvolumes'] + for subvolume in sorted(subvolumes, key=lambda item: item.mountpoint): + # We cache the mount call for later + mount_queue[subvolume.mountpoint] = lambda sub_vol=subvolume, device=partition['device_instance']: mount_subvolume( + installation=self, + device=device, + subvolume=sub_vol + ) # We mount ordinary partitions, and we sort them by the mountpoint for partition in sorted([entry for entry in list_part if entry.get('mountpoint', False)], key=lambda part: part['mountpoint']): diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py index cb61168d..49083517 100644 --- a/archinstall/lib/menu/global_menu.py +++ b/archinstall/lib/menu/global_menu.py @@ -325,22 +325,23 @@ class GlobalMenu(GeneralMenu): def _select_harddrives(self, old_harddrives : list) -> List: harddrives = select_harddrives(old_harddrives) - if len(harddrives) == 0: - 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']) - - choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes(), skip=False).run() - - if choice.value == Menu.no(): - return self._select_harddrives(old_harddrives) - - # in case the harddrives got changed we have to reset the disk layout as well - if old_harddrives != harddrives: - self._menu_options['disk_layouts'].set_current_selection(None) - storage['arguments']['disk_layouts'] = {} + if harddrives: + if len(harddrives) == 0: + 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']) + + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes(), skip=False).run() + + if choice.value == Menu.no(): + return self._select_harddrives(old_harddrives) + + # in case the harddrives got changed we have to reset the disk layout as well + if old_harddrives != harddrives: + self._menu_options['disk_layouts'].set_current_selection(None) + storage['arguments']['disk_layouts'] = {} return harddrives diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py index 7e051528..40d01ce3 100644 --- a/archinstall/lib/menu/list_manager.py +++ b/archinstall/lib/menu/list_manager.py @@ -137,34 +137,35 @@ class ListManager: else: self._default_action = [str(default_action),] - self.header = header if header else None - self.cancel_action = str(_('Cancel')) - self.confirm_action = str(_('Confirm and exit')) - self.separator = '' - self.bottom_list = [self.confirm_action,self.cancel_action] - self.bottom_item = [self.cancel_action] - self.base_actions = base_actions if base_actions else [str(_('Add')),str(_('Copy')),str(_('Edit')),str(_('Delete'))] + self._header = header if header else None + self._cancel_action = str(_('Cancel')) + self._confirm_action = str(_('Confirm and exit')) + self._separator = '' + self._bottom_list = [self._confirm_action, self._cancel_action] + self._bottom_item = [self._cancel_action] + self._base_actions = base_actions if base_actions else [str(_('Add')), str(_('Copy')), str(_('Edit')), str(_('Delete'))] self._original_data = copy.deepcopy(base_list) self._data = copy.deepcopy(base_list) # as refs, changes are immediate + # default values for the null case self.target: Optional[Any] = None self.action = self._null_action - if len(self._data) == 0 and self._null_action: - self._data = self.exec_action(self._data) - def run(self): while True: # this will return a dictionary with the key as the menu entry to be displayed # and the value is the original value from the self._data container + data_formatted = self.reformat(self._data) options = list(data_formatted.keys()) - options.append(self.separator) + + if len(options) > 0: + options.append(self._separator) if self._default_action: options += self._default_action - options += self.bottom_list + options += self._bottom_list system('clear') @@ -174,12 +175,12 @@ class ListManager: sort=False, clear_screen=False, clear_menu_on_exit=False, - header=self.header, + header=self._header, skip_empty_entries=True, skip=False ).run() - if not target.value or target.value in self.bottom_list: + if not target.value or target.value in self._bottom_list: self.action = target break @@ -201,13 +202,13 @@ class ListManager: # Possible enhancement. If run_actions returns false a message line indicating the failure self.run_actions(target.value) - if target.value == self.cancel_action: # TODO dubious + if target.value == self._cancel_action: # TODO dubious return self._original_data # return the original list else: return self._data def run_actions(self,prompt_data=None): - options = self.action_list() + self.bottom_item + options = self.action_list() + self._bottom_item prompt = _("Select an action for < {} >").format(prompt_data if prompt_data else self.target) choice = Menu( prompt, @@ -215,13 +216,13 @@ class ListManager: sort=False, clear_screen=False, clear_menu_on_exit=False, - preset_values=self.bottom_item, + preset_values=self._bottom_item, show_search_hint=False ).run() self.action = choice.value - if self.action and self.action != self.cancel_action: + if self.action and self.action != self._cancel_action: self._data = self.exec_action(self._data) """ @@ -243,7 +244,7 @@ class ListManager: can define alternate action list or customize the list for each item. Executed after any item is selected, contained in self.target """ - return self.base_actions + return self._base_actions def exec_action(self, data: Any): """ diff --git a/archinstall/lib/models/subvolume.py b/archinstall/lib/models/subvolume.py new file mode 100644 index 00000000..34a09227 --- /dev/null +++ b/archinstall/lib/models/subvolume.py @@ -0,0 +1,68 @@ +from dataclasses import dataclass +from typing import List, Any, Dict + + +@dataclass +class Subvolume: + name: str + mountpoint: str + compress: bool = False + nodatacow: bool = False + + def display(self) -> str: + options_str = ','.join(self.options) + return f'{_("Subvolume")}: {self.name:15} {_("Mountpoint")}: {self.mountpoint:20} {_("Options")}: {options_str}' + + @property + def options(self) -> List[str]: + options = [ + 'compress' if self.compress else '', + 'nodatacow' if self.nodatacow else '' + ] + return [o for o in options if len(o)] + + def json(self) -> Dict[str, Any]: + return { + 'name': self.name, + 'mountpoint': self.mountpoint, + 'compress': self.compress, + 'nodatacow': self.nodatacow + } + + @classmethod + def _parse(cls, config_subvolumes: List[Dict[str, Any]]) -> List['Subvolume']: + subvolumes = [] + for entry in config_subvolumes: + if not entry.get('name', None) or not entry.get('mountpoint', None): + continue + + subvolumes.append( + Subvolume( + entry['name'], + entry['mountpoint'], + entry.get('compress', False), + entry.get('nodatacow', False) + ) + ) + + return subvolumes + + @classmethod + def _parse_backwards_compatible(cls, config_subvolumes) -> List['Subvolume']: + subvolumes = [] + for name, mountpoint in config_subvolumes.items(): + if not name or not mountpoint: + continue + + subvolumes.append(Subvolume(name, mountpoint)) + + return subvolumes + + @classmethod + def parse_arguments(cls, config_subvolumes: Any) -> List['Subvolume']: + if isinstance(config_subvolumes, list): + return cls._parse(config_subvolumes) + elif isinstance(config_subvolumes, dict): + return cls._parse_backwards_compatible(config_subvolumes) + + raise ValueError('Unknown disk layout btrfs subvolume format') diff --git a/archinstall/lib/models/users.py b/archinstall/lib/models/users.py index f72cabde..a8feb9ef 100644 --- a/archinstall/lib/models/users.py +++ b/archinstall/lib/models/users.py @@ -27,8 +27,10 @@ class User: } def display(self) -> str: - strength = PasswordStrength.strength(self.password) - password = '*' * len(self.password) + f' ({strength.value})' + password = '*' * (len(self.password) if self.password else 0) + if password: + strength = PasswordStrength.strength(self.password) + password += f' ({strength.value})' return f'{_("Username")}: {self.username:16} {_("Password")}: {password:20} sudo: {str(self.sudo)}' @classmethod diff --git a/archinstall/lib/user_interaction/partitioning_conf.py b/archinstall/lib/user_interaction/partitioning_conf.py index caf5f5df..63b7c7df 100644 --- a/archinstall/lib/user_interaction/partitioning_conf.py +++ b/archinstall/lib/user_interaction/partitioning_conf.py @@ -351,18 +351,16 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, if partition is not None: if not block_device_struct["partitions"][partition].get('btrfs', {}): block_device_struct["partitions"][partition]['btrfs'] = {} - if not block_device_struct["partitions"][partition]['btrfs'].get('subvolumes', {}): - block_device_struct["partitions"][partition]['btrfs']['subvolumes'] = {} + if not block_device_struct["partitions"][partition]['btrfs'].get('subvolumes', []): + block_device_struct["partitions"][partition]['btrfs']['subvolumes'] = [] prev = block_device_struct["partitions"][partition]['btrfs']['subvolumes'] - result = SubvolumeList(_("Manage btrfs subvolumes for current partition"),prev).run() - if result: - block_device_struct["partitions"][partition]['btrfs']['subvolumes'] = result - else: - del block_device_struct["partitions"][partition]['btrfs'] + result = SubvolumeList(_("Manage btrfs subvolumes for current partition"), prev).run() + block_device_struct["partitions"][partition]['btrfs']['subvolumes'] = result return block_device_struct + def select_encrypted_partitions( title :str, partitions :List[Partition], diff --git a/archinstall/lib/user_interaction/subvolume_config.py b/archinstall/lib/user_interaction/subvolume_config.py index 94e6f5d7..a54ec891 100644 --- a/archinstall/lib/user_interaction/subvolume_config.py +++ b/archinstall/lib/user_interaction/subvolume_config.py @@ -1,155 +1,94 @@ -from typing import Dict, List +from typing import Dict, List, Optional, Any, TYPE_CHECKING from ..menu.list_manager import ListManager from ..menu.menu import MenuSelectionType -from ..menu.selection_menu import Selector, GeneralMenu from ..menu.text_input import TextInput from ..menu import Menu +from ..models.subvolume import Subvolume + +if TYPE_CHECKING: + _: Any -""" -UI classes -""" class SubvolumeList(ListManager): - def __init__(self,prompt,list): - self.ObjectNullAction = None # str(_('Add')) - self.ObjectDefaultAction = str(_('Add')) - super().__init__(prompt,list,None,self.ObjectNullAction,self.ObjectDefaultAction) - - def reformat(self, data: Dict) -> Dict: - def presentation(key :str, value :Dict): - text = _(" Subvolume :{:16}").format(key) - if isinstance(value,str): - text += _(" mounted at {:16}").format(value) - else: - if value.get('mountpoint'): - text += _(" mounted at {:16}").format(value['mountpoint']) - else: - text += (' ' * 28) - - if value.get('options',[]): - text += _(" with option {}").format(', '.join(value['options'])) - return text - - formatted = {presentation(k, v): k for k, v in data.items()} - return {k: v for k, v in sorted(formatted.items(), key=lambda e: e[0])} + def __init__(self, prompt: str, current_volumes: List[Subvolume]): + self._actions = [ + str(_('Add subvolume')), + str(_('Edit subvolume')), + str(_('Delete subvolume')) + ] + super().__init__(prompt, current_volumes, self._actions, self._actions[0]) - def action_list(self): - return super().action_list() + def reformat(self, data: List[Subvolume]) -> Dict[str, Subvolume]: + return {e.display(): e for e in data} - def exec_action(self, data: Dict): - if self.target: - origkey, origval = list(self.target.items())[0] - else: - origkey = None + def action_list(self): + active_user = self.target if self.target else None - if self.action == str(_('Delete')): - del data[origkey] + if active_user is None: + return [self._actions[0]] else: - if self.action == str(_('Add')): - self.target = {} - print(_('\n Fill the desired values for a new subvolume \n')) - with SubvolumeMenu(self.target,self.action) as add_menu: - for elem in ['name','mountpoint','options']: - add_menu.exec_option(elem) - else: - SubvolumeMenu(self.target,self.action).run() + return self._actions[1:] - data.update(self.target) + def _prompt_options(self, editing: Optional[Subvolume] = None) -> List[str]: + preset_options = [] + if editing: + preset_options = editing.options - return data - - -class SubvolumeMenu(GeneralMenu): - def __init__(self,parameters,action=None): - self.data = parameters - self.action = action - self.ds = {} - self.ds['name'] = None - self.ds['mountpoint'] = None - self.ds['options'] = None - if self.data: - origkey,origval = list(self.data.items())[0] - self.ds['name'] = origkey - if isinstance(origval,str): - self.ds['mountpoint'] = origval - else: - self.ds['mountpoint'] = self.data[origkey].get('mountpoint') - self.ds['options'] = self.data[origkey].get('options') - - super().__init__(data_store=self.ds) - - def _setup_selection_menu_options(self): - self._menu_options['name'] = Selector( - str(_('Subvolume name ')), - self._select_subvolume_name if not self.action or self.action in (str(_('Add')), str(_('Copy'))) else None, - mandatory=True, - enabled=True) - - self._menu_options['mountpoint'] = Selector( - str(_('Subvolume mountpoint')), - self._select_subvolume_mount_point if not self.action or self.action in (str(_('Add')),str(_('Edit'))) else None, - enabled=True) - - self._menu_options['options'] = Selector( - str(_('Subvolume options')), - self._select_subvolume_options if not self.action or self.action in (str(_('Add')),str(_('Edit'))) else None, - enabled=True) - - self._menu_options['save'] = Selector( - str(_('Save')), - exec_func=lambda n,v:True, - enabled=True) - - self._menu_options['cancel'] = Selector( - str(_('Cancel')), - # func = lambda pre:True, - exec_func=lambda n,v:self.fast_exit(n), - enabled=True) - - self.cancel_action = 'cancel' - self.save_action = 'save' - self.bottom_list = [self.save_action,self.cancel_action] - - def fast_exit(self,accion): - if self.option(accion).get_selection(): - for item in self.list_options(): - if self.option(item).is_mandatory(): - self.option(item).set_mandatory(False) - return True - - def exit_callback(self): - # we exit without moving data - if self.option(self.cancel_action).get_selection(): - return - if not self.ds['name']: - return - else: - key = self.ds['name'] - value = {} - if self.ds['mountpoint']: - value['mountpoint'] = self.ds['mountpoint'] - if self.ds['options']: - value['options'] = self.ds['options'] - self.data.update({key : value}) - - def _select_subvolume_name(self,value): - return TextInput(str(_("Subvolume name :")),value).run() - - def _select_subvolume_mount_point(self,value): - return TextInput(str(_("Select a mount point :")),value).run() - - def _select_subvolume_options(self,value) -> List[str]: - # def __init__(self, title, p_options, skip=True, multi=False, default_option=None, sort=True): choice = Menu( str(_("Select the desired subvolume options ")), ['nodatacow','compress'], skip=True, - preset_values=value, + preset_values=preset_options, multi=True ).run() if choice.type_ == MenuSelectionType.Selection: - return choice.value + return choice.value # type: ignore return [] + + def _add_subvolume(self, editing: Optional[Subvolume] = None) -> Optional[Subvolume]: + name = TextInput(f'\n\n{_("Subvolume name")}: ', editing.name if editing else '').run() + + if not name: + return None + + mountpoint = TextInput(f'\n{_("Subvolume mountpoint")}: ', editing.mountpoint if editing else '').run() + + if not mountpoint: + return None + + options = self._prompt_options(editing) + + subvolume = Subvolume(name, mountpoint) + subvolume.compress = 'compress' in options + subvolume.nodatacow = 'nodatacow' in options + + return subvolume + + def exec_action(self, data: List[Subvolume]) -> List[Subvolume]: + if self.target: + active_subvolume = self.target + else: + active_subvolume = None + + if self.action == self._actions[0]: # add + new_subvolume = self._add_subvolume() + + if new_subvolume is not None: + # in case a user with the same username as an existing user + # was created we'll replace the existing one + data = [d for d in data if d.name != new_subvolume.name] + data += [new_subvolume] + elif self.action == self._actions[1]: # edit subvolume + new_subvolume = self._add_subvolume(active_subvolume) + + if new_subvolume is not None: + # we'll remove the original subvolume and add the modified version + data = [d for d in data if d.name != active_subvolume.name and d.name != new_subvolume.name] + data += [new_subvolume] + elif self.action == self._actions[2]: # delete + data = [d for d in data if d != active_subvolume] + + return data -- cgit v1.2.3-70-g09d2 From 0bdb46f3081aaed095f87e35b18b3714d8782ed1 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 9 Jun 2022 22:54:12 +1000 Subject: Fancy user interface (#1320) * Display submenus as tables * Update * Update * Update * Update Co-authored-by: Daniel Girtler --- archinstall/lib/menu/global_menu.py | 16 +- archinstall/lib/menu/list_manager.py | 100 +++--- archinstall/lib/menu/menu.py | 21 +- archinstall/lib/models/network_configuration.py | 67 ++-- .../lib/user_interaction/manage_users_conf.py | 42 ++- archinstall/lib/user_interaction/network_conf.py | 96 +++--- .../lib/user_interaction/subvolume_config.py | 24 +- archinstall/locales/base.pot | 2 +- archinstall/locales/cs/LC_MESSAGES/base.mo | Bin 23958 -> 23739 bytes archinstall/locales/cs/LC_MESSAGES/base.po | 52 +++- archinstall/locales/de/LC_MESSAGES/base.mo | Bin 23664 -> 23285 bytes archinstall/locales/de/LC_MESSAGES/base.po | 81 ++++- archinstall/locales/en/LC_MESSAGES/base.po | 61 +++- archinstall/locales/es/LC_MESSAGES/base.mo | Bin 24988 -> 24797 bytes archinstall/locales/es/LC_MESSAGES/base.po | 30 +- archinstall/locales/fr/LC_MESSAGES/base.mo | Bin 25735 -> 25482 bytes archinstall/locales/fr/LC_MESSAGES/base.po | 342 +++++++-------------- archinstall/locales/it/LC_MESSAGES/base.mo | Bin 23979 -> 23841 bytes archinstall/locales/it/LC_MESSAGES/base.po | 52 +++- archinstall/locales/nl/LC_MESSAGES/base.mo | Bin 18181 -> 18002 bytes archinstall/locales/nl/LC_MESSAGES/base.po | 80 ++++- archinstall/locales/pl/LC_MESSAGES/base.mo | Bin 22542 -> 22346 bytes archinstall/locales/pl/LC_MESSAGES/base.po | 67 +++- archinstall/locales/pt/LC_MESSAGES/base.mo | Bin 16887 -> 16667 bytes archinstall/locales/pt/LC_MESSAGES/base.po | 80 ++++- archinstall/locales/pt_BR/LC_MESSAGES/base.mo | Bin 23641 -> 23362 bytes archinstall/locales/pt_BR/LC_MESSAGES/base.po | 280 ++++++++--------- archinstall/locales/ru/LC_MESSAGES/base.mo | Bin 33402 -> 33398 bytes archinstall/locales/ru/LC_MESSAGES/base.po | 4 +- archinstall/locales/sv/LC_MESSAGES/base.mo | Bin 23439 -> 23045 bytes archinstall/locales/sv/LC_MESSAGES/base.po | 81 ++++- archinstall/locales/tr/LC_MESSAGES/base.mo | Bin 24972 -> 24757 bytes archinstall/locales/tr/LC_MESSAGES/base.po | 330 ++++++++------------ archinstall/locales/ur/LC_MESSAGES/base.mo | Bin 21216 -> 20880 bytes archinstall/locales/ur/LC_MESSAGES/base.po | 80 ++++- 35 files changed, 1184 insertions(+), 804 deletions(-) (limited to 'archinstall/lib/user_interaction/subvolume_config.py') diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py index b73fb48f..1a292476 100644 --- a/archinstall/lib/menu/global_menu.py +++ b/archinstall/lib/menu/global_menu.py @@ -163,7 +163,8 @@ class GlobalMenu(GeneralMenu): Selector( _('Network configuration'), ask_to_configure_network, - display_func=lambda x: self._prev_network_configuration(x), + display_func=lambda x: self._display_network_conf(x), + preview_func=self._prev_network_config, default={}) self._menu_options['timezone'] = \ Selector( @@ -226,16 +227,23 @@ class GlobalMenu(GeneralMenu): return _('Install ({} config(s) missing)').format(missing) return _('Install') - def _prev_network_configuration(self, cur_value: Union[NetworkConfiguration, List[NetworkConfiguration]]) -> str: + def _display_network_conf(self, cur_value: Union[NetworkConfiguration, List[NetworkConfiguration]]) -> str: if not cur_value: return _('Not configured, unavailable unless setup manually') else: if isinstance(cur_value, list): - ifaces = [x.iface for x in cur_value] - return f'Configured ifaces: {ifaces}' + return str(_('Configured {} interfaces')).format(len(cur_value)) else: return str(cur_value) + def _prev_network_config(self) -> Optional[str]: + selector = self._menu_options['nic'] + if selector.has_selection(): + ifaces = selector.current_selection + if isinstance(ifaces, list): + return FormattedOutput.as_table(ifaces) + return None + def _prev_harddrives(self) -> Optional[str]: selector = self._menu_options['harddrives'] if selector.has_selection(): diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py index 40d01ce3..fe491caa 100644 --- a/archinstall/lib/menu/list_manager.py +++ b/archinstall/lib/menu/list_manager.py @@ -86,7 +86,7 @@ The contents in the base class of this methods serve for a very basic usage, and """ import copy from os import system -from typing import Union, Any, TYPE_CHECKING, Dict, Optional +from typing import Union, Any, TYPE_CHECKING, Dict, Optional, Tuple, List from .text_input import TextInput from .menu import Menu @@ -135,9 +135,9 @@ class ListManager: elif isinstance(default_action,(list,tuple)): self._default_action = default_action else: - self._default_action = [str(default_action),] + self._default_action = [str(default_action)] - self._header = header if header else None + self._header = header if header else '' self._cancel_action = str(_('Cancel')) self._confirm_action = str(_('Confirm and exit')) self._separator = '' @@ -155,61 +155,81 @@ class ListManager: while True: # this will return a dictionary with the key as the menu entry to be displayed # and the value is the original value from the self._data container - data_formatted = self.reformat(self._data) - options = list(data_formatted.keys()) - - if len(options) > 0: - options.append(self._separator) + options, header = self._prepare_selection(data_formatted) - if self._default_action: - options += self._default_action + menu_header = self._header - options += self._bottom_list + if header: + menu_header += header system('clear') - target = Menu( + choice = Menu( self._prompt, options, sort=False, clear_screen=False, clear_menu_on_exit=False, - header=self._header, + header=header, skip_empty_entries=True, - skip=False + skip=False, + show_search_hint=False ).run() - if not target.value or target.value in self._bottom_list: - self.action = target + if not choice.value or choice.value in self._bottom_list: + self.action = choice break - if target.value and target.value in self._default_action: - self.action = target.value + if choice.value and choice.value in self._default_action: + self.action = choice.value self.target = None self._data = self.exec_action(self._data) continue - if isinstance(self._data,dict): - data_key = data_formatted[target.value] + if isinstance(self._data, dict): + data_key = data_formatted[choice.value] key = self._data[data_key] self.target = {data_key: key} elif isinstance(self._data, list): - self.target = [d for d in self._data if d == data_formatted[target.value]][0] + self.target = [d for d in self._data if d == data_formatted[choice.value]][0] else: - self.target = self._data[data_formatted[target.value]] + self.target = self._data[data_formatted[choice.value]] # Possible enhancement. If run_actions returns false a message line indicating the failure - self.run_actions(target.value) + self.run_actions(choice.value) - if target.value == self._cancel_action: # TODO dubious + if choice.value == self._cancel_action: return self._original_data # return the original list else: return self._data - def run_actions(self,prompt_data=None): + def _prepare_selection(self, data_formatted: Dict[str, Any]) -> Tuple[List[str], str]: + # header rows are mapped to None so make sure + # to exclude those from the selectable data + options: List[str] = [key for key, val in data_formatted.items() if val is not None] + header = '' + + if len(options) > 0: + table_header = [key for key, val in data_formatted.items() if val is None] + header = '\n'.join(table_header) + + if len(options) > 0: + options.append(self._separator) + + if self._default_action: + # done only for mypy -> todo fix the self._default_action declaration + options += [action for action in self._default_action if action] + + options += self._bottom_list + return options, header + + def run_actions(self,prompt_data=''): options = self.action_list() + self._bottom_item - prompt = _("Select an action for < {} >").format(prompt_data if prompt_data else self.target) + display_value = self.selected_action_display(self.target) if self.target else prompt_data + + prompt = _("Select an action for '{}'").format(display_value) + choice = Menu( prompt, options, @@ -225,26 +245,28 @@ class ListManager: if self.action and self.action != self._cancel_action: self._data = self.exec_action(self._data) - """ - The following methods are expected to be overwritten by the user if the needs of the list are beyond the simple case - """ + def selected_action_display(self, selection: Any) -> str: + # this will return the value to be displayed in the + # "Select an action for '{}'" string + raise NotImplementedError('Please implement me in the child class') - def reformat(self, data: Any) -> Dict[str, Any]: - """ - method to get the data in a format suitable to be shown - It is executed once for run loop and processes the whole self._data structure - """ - if isinstance(data,dict): - return {f'{k}: {v}': k for k, v in data.items()} - else: - return {str(k): k for k in data} + def reformat(self, data: List[Any]) -> Dict[str, Any]: + # this should return a dictionary of display string to actual data entry + # mapping; if the value for a given display string is None it will be used + # in the header value (useful when displaying tables) + raise NotImplementedError('Please implement me in the child class') def action_list(self): """ can define alternate action list or customize the list for each item. Executed after any item is selected, contained in self.target """ - return self._base_actions + active_entry = self.target if self.target else None + + if active_entry is None: + return [self._base_actions[0]] + else: + return self._base_actions[1:] def exec_action(self, data: Any): """ diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py index 3a26f6e7..80982db0 100644 --- a/archinstall/lib/menu/menu.py +++ b/archinstall/lib/menu/menu.py @@ -1,5 +1,6 @@ from dataclasses import dataclass from enum import Enum, auto +from os import system from typing import Dict, List, Union, Any, TYPE_CHECKING, Optional from archinstall.lib.menu.simple_menu import TerminalMenu @@ -57,7 +58,11 @@ class Menu(TerminalMenu): header :Union[List[str],str] = None, explode_on_interrupt :bool = False, explode_warning :str = '', - **kwargs + clear_screen: bool = True, + show_search_hint: bool = True, + cycle_cursor: bool = True, + clear_menu_on_exit: bool = True, + skip_empty_entries: bool = False ): """ Creates a new menu @@ -153,8 +158,7 @@ class Menu(TerminalMenu): if header: if not isinstance(header,(list,tuple)): header = [header] - header = '\n'.join(header) - menu_title += f'\n{header}\n' + menu_title += '\n'.join(header) action_info = '' if skip: @@ -178,10 +182,6 @@ class Menu(TerminalMenu): cursor = "> " main_menu_cursor_style = ("fg_cyan", "bold") main_menu_style = ("bg_blue", "fg_gray") - # defaults that can be changed up the stack - kwargs['clear_screen'] = kwargs.get('clear_screen',True) - kwargs['show_search_hint'] = kwargs.get('show_search_hint',True) - kwargs['cycle_cursor'] = kwargs.get('cycle_cursor',True) super().__init__( menu_entries=self._menu_options, @@ -200,7 +200,11 @@ class Menu(TerminalMenu): preview_title=preview_title, explode_on_interrupt=self._explode_on_interrupt, multi_select_select_on_accept=False, - **kwargs, + clear_screen=clear_screen, + show_search_hint=show_search_hint, + cycle_cursor=cycle_cursor, + clear_menu_on_exit=clear_menu_on_exit, + skip_empty_entries=skip_empty_entries ) def _show(self) -> MenuSelection: @@ -238,6 +242,7 @@ class Menu(TerminalMenu): return self.run() if ret.type_ is not MenuSelectionType.Selection and not self._skip: + system('clear') return self.run() return ret diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py index 4f135da5..e026e97b 100644 --- a/archinstall/lib/models/network_configuration.py +++ b/archinstall/lib/models/network_configuration.py @@ -39,8 +39,22 @@ class NetworkConfiguration: else: return 'Unknown type' - # for json serialization when calling json.dumps(...) on this class - def json(self): + def as_json(self) -> Dict: + exclude_fields = ['type'] + data = {} + for k, v in self.__dict__.items(): + if k not in exclude_fields: + if isinstance(v, list) and len(v) == 0: + v = '' + elif v is None: + v = '' + + data[k] = v + + return data + + def json(self) -> Dict: + # for json serialization when calling json.dumps(...) on this class return self.__dict__ def is_iso(self) -> bool: @@ -111,19 +125,10 @@ class NetworkConfigurationHandler: else: # not recognized return None - def _parse_manual_config(self, config: Dict[str, Any]) -> Union[None, List[NetworkConfiguration]]: - manual_configs: List = config.get('config', []) - - if not manual_configs: - return None - - if not isinstance(manual_configs, list): - log(_('Manual configuration setting must be a list')) - exit(1) - + def _parse_manual_config(self, configs: List[Dict[str, Any]]) -> Optional[List[NetworkConfiguration]]: configurations = [] - for manual_config in manual_configs: + for manual_config in configs: iface = manual_config.get('iface', None) if iface is None: @@ -135,7 +140,7 @@ class NetworkConfigurationHandler: NetworkConfiguration(NicType.MANUAL, iface=iface) ) else: - ip = config.get('ip', '') + ip = manual_config.get('ip', '') if not ip: log(_('Manual nic configuration with no auto DHCP requires an IP address'), fg='red') exit(1) @@ -145,32 +150,34 @@ class NetworkConfigurationHandler: NicType.MANUAL, iface=iface, ip=ip, - gateway=config.get('gateway', ''), - dns=config.get('dns', []), + gateway=manual_config.get('gateway', ''), + dns=manual_config.get('dns', []), dhcp=False ) ) return configurations - def parse_arguments(self, config: Any): - nic_type = config.get('type', None) - - if not nic_type: - # old style definitions - network_config = self._backwards_compability_config(config) - if network_config: - return network_config - return None - + def _parse_nic_type(self, nic_type: str) -> NicType: try: - type_ = NicType(nic_type) + return NicType(nic_type) except ValueError: options = [e.value for e in NicType] log(_('Unknown nic type: {}. Possible values are {}').format(nic_type, options), fg='red') exit(1) - if type_ != NicType.MANUAL: - self._configuration = NetworkConfiguration(type_) - else: # manual configuration settings + def parse_arguments(self, config: Any): + if isinstance(config, list): # new data format self._configuration = self._parse_manual_config(config) + elif nic_type := config.get('type', None): # new data format + type_ = self._parse_nic_type(nic_type) + + if type_ != NicType.MANUAL: + self._configuration = NetworkConfiguration(type_) + else: # manual configuration settings + self._configuration = self._parse_manual_config([config]) + else: # old style definitions + network_config = self._backwards_compability_config(config) + if network_config: + return network_config + return None diff --git a/archinstall/lib/user_interaction/manage_users_conf.py b/archinstall/lib/user_interaction/manage_users_conf.py index 567a2964..33c31342 100644 --- a/archinstall/lib/user_interaction/manage_users_conf.py +++ b/archinstall/lib/user_interaction/manage_users_conf.py @@ -7,6 +7,7 @@ from .utils import get_password from ..menu import Menu from ..menu.list_manager import ListManager from ..models.users import User +from ..output import FormattedOutput if TYPE_CHECKING: _: Any @@ -18,13 +19,6 @@ class UserList(ListManager): """ def __init__(self, prompt: str, lusers: List[User]): - """ - param: prompt - type: str - param: lusers dict with the users already defined for the system - type: Dict - param: sudo. boolean to determine if we handle superusers or users. If None handles both types - """ self._actions = [ str(_('Add a user')), str(_('Change password')), @@ -34,21 +28,25 @@ class UserList(ListManager): super().__init__(prompt, lusers, self._actions, self._actions[0]) def reformat(self, data: List[User]) -> Dict[str, User]: - return {e.display(): e for e in data} + table = FormattedOutput.as_table(data) + rows = table.split('\n') - def action_list(self): - active_user = self.target if self.target else None + # these are the header rows of the table and do not map to any User obviously + # we're adding 2 spaces as prefix because the menu selector '> ' will be put before + # the selectable rows so the header has to be aligned + display_data = {f' {rows[0]}': None, f' {rows[1]}': None} + + for row, user in zip(rows[2:], data): + row = row.replace('|', '\\|') + display_data[row] = user - if active_user is None: - return [self._actions[0]] - else: - return self._actions[1:] + return display_data + + def selected_action_display(self, user: User) -> str: + return user.username def exec_action(self, data: List[User]) -> List[User]: - if self.target: - active_user = self.target - else: - active_user = None + active_user = self.target if self.target else None if self.action == self._actions[0]: # add new_user = self._add_user() @@ -77,8 +75,7 @@ class UserList(ListManager): return False def _add_user(self) -> Optional[User]: - print(_('\nDefine a new user\n')) - prompt = str(_('Enter username (leave blank to skip): ')) + prompt = '\n\n' + str(_('Enter username (leave blank to skip): ')) while True: username = input(prompt).strip(' ') @@ -94,7 +91,9 @@ class UserList(ListManager): choice = Menu( str(_('Should "{}" be a superuser (sudo)?')).format(username), Menu.yes_no(), skip=False, - default_option=Menu.no() + default_option=Menu.no(), + clear_screen=False, + show_search_hint=False ).run() sudo = True if choice.value == Menu.yes() else False @@ -102,6 +101,5 @@ class UserList(ListManager): def ask_for_additional_users(prompt: str = '', defined_users: List[User] = []) -> List[User]: - prompt = prompt if prompt else _('Enter username (leave blank to skip): ') users = UserList(prompt, defined_users).run() return users diff --git a/archinstall/lib/user_interaction/network_conf.py b/archinstall/lib/user_interaction/network_conf.py index 5154d8b1..4f22b790 100644 --- a/archinstall/lib/user_interaction/network_conf.py +++ b/archinstall/lib/user_interaction/network_conf.py @@ -2,7 +2,7 @@ from __future__ import annotations import ipaddress import logging -from typing import Any, Optional, TYPE_CHECKING, List, Union +from typing import Any, Optional, TYPE_CHECKING, List, Union, Dict from ..menu.menu import MenuSelectionType from ..menu.text_input import TextInput @@ -10,7 +10,7 @@ from ..models.network_configuration import NetworkConfiguration, NicType from ..networking import list_interfaces from ..menu import Menu -from ..output import log +from ..output import log, FormattedOutput from ..menu.list_manager import ListManager if TYPE_CHECKING: @@ -19,55 +19,57 @@ if TYPE_CHECKING: class ManualNetworkConfig(ListManager): """ - subclass of ListManager for the managing of network configuration accounts + subclass of ListManager for the managing of network configurations """ - def __init__(self, prompt: str, ifaces: Union[None, NetworkConfiguration, List[NetworkConfiguration]]): - """ - param: prompt - type: str - param: ifaces already defined previously - type: Dict - """ + def __init__(self, prompt: str, ifaces: List[NetworkConfiguration]): + self._actions = [ + str(_('Add interface')), + str(_('Edit interface')), + str(_('Delete interface')) + ] - if ifaces is not None and isinstance(ifaces, list): - display_values = {iface.iface: iface for iface in ifaces} - else: - display_values = {} + super().__init__(prompt, ifaces, self._actions, self._actions[0]) + + def reformat(self, data: List[NetworkConfiguration]) -> Dict[str, Optional[NetworkConfiguration]]: + table = FormattedOutput.as_table(data) + rows = table.split('\n') - self._action_add = str(_('Add interface')) - self._action_edit = str(_('Edit interface')) - self._action_delete = str(_('Delete interface')) + # these are the header rows of the table and do not map to any User obviously + # we're adding 2 spaces as prefix because the menu selector '> ' will be put before + # the selectable rows so the header has to be aligned + display_data: Dict[str, Optional[NetworkConfiguration]] = {f' {rows[0]}': None, f' {rows[1]}': None} - self._iface_actions = [self._action_edit, self._action_delete] + for row, iface in zip(rows[2:], data): + row = row.replace('|', '\\|') + display_data[row] = iface - super().__init__(prompt, display_values, self._iface_actions, self._action_add) + return display_data - def run_manual(self) -> List[NetworkConfiguration]: - ifaces = super().run() - if ifaces is not None: - return list(ifaces.values()) - return [] + def selected_action_display(self, iface: NetworkConfiguration) -> str: + return iface.iface if iface.iface else '' - def exec_action(self, data: Any): - if self.action == self._action_add: - iface_name = self._select_iface(data.keys()) + def exec_action(self, data: List[NetworkConfiguration]): + active_iface: Optional[NetworkConfiguration] = self.target if self.target else None + + if self.action == self._actions[0]: # add + iface_name = self._select_iface(data) if iface_name: iface = NetworkConfiguration(NicType.MANUAL, iface=iface_name) - data[iface_name] = self._edit_iface(iface) - elif self.target: - iface_name = list(self.target.keys())[0] - iface = data[iface_name] - - if self.action == self._action_edit: - data[iface_name] = self._edit_iface(iface) - elif self.action == self._action_delete: - del data[iface_name] + iface = self._edit_iface(iface) + data += [iface] + elif active_iface: + if self.action == self._actions[1]: # edit interface + data = [d for d in data if d.iface != active_iface.iface] + data.append(self._edit_iface(active_iface)) + elif self.action == self._actions[2]: # delete + data = [d for d in data if d != active_iface] return data - def _select_iface(self, existing_ifaces: List[str]) -> Optional[Any]: + def _select_iface(self, data: List[NetworkConfiguration]) -> Optional[Any]: all_ifaces = list_interfaces().values() + existing_ifaces = [d.iface for d in data] available = set(all_ifaces) - set(existing_ifaces) choice = Menu(str(_('Select interface to add')), list(available), skip=True).run() @@ -76,7 +78,7 @@ class ManualNetworkConfig(ListManager): return choice.value - def _edit_iface(self, edit_iface :NetworkConfiguration): + def _edit_iface(self, edit_iface: NetworkConfiguration): iface_name = edit_iface.iface modes = ['DHCP (auto detect)', 'IP (static)'] default_mode = 'DHCP (auto detect)' @@ -99,11 +101,13 @@ class ManualNetworkConfig(ListManager): gateway = None while 1: - gateway_input = TextInput(_('Enter your gateway (router) IP address or leave blank for none: '), - edit_iface.gateway).run().strip() + gateway = TextInput( + _('Enter your gateway (router) IP address or leave blank for none: '), + edit_iface.gateway + ).run().strip() try: - if len(gateway_input) > 0: - ipaddress.ip_address(gateway_input) + if len(gateway) > 0: + ipaddress.ip_address(gateway) break except ValueError: log("You need to enter a valid gateway (router) IP address.", level=logging.WARNING, fg='red') @@ -124,7 +128,9 @@ class ManualNetworkConfig(ListManager): return NetworkConfiguration(NicType.MANUAL, iface=iface_name) -def ask_to_configure_network(preset: Union[None, NetworkConfiguration, List[NetworkConfiguration]]) -> Optional[Union[List[NetworkConfiguration], NetworkConfiguration]]: +def ask_to_configure_network( + preset: Union[NetworkConfiguration, List[NetworkConfiguration]] +) -> Optional[NetworkConfiguration | List[NetworkConfiguration]]: """ Configure the network on the newly installed system """ @@ -165,7 +171,7 @@ def ask_to_configure_network(preset: Union[None, NetworkConfiguration, List[Netw elif choice.value == network_options['network_manager']: return NetworkConfiguration(NicType.NM) elif choice.value == network_options['manual']: - manual = ManualNetworkConfig('Configure interfaces', preset) - return manual.run_manual() + preset_ifaces = preset if isinstance(preset, list) else [] + return ManualNetworkConfig('Configure interfaces', preset_ifaces).run() return preset diff --git a/archinstall/lib/user_interaction/subvolume_config.py b/archinstall/lib/user_interaction/subvolume_config.py index a54ec891..e2797bba 100644 --- a/archinstall/lib/user_interaction/subvolume_config.py +++ b/archinstall/lib/user_interaction/subvolume_config.py @@ -5,6 +5,7 @@ from ..menu.menu import MenuSelectionType from ..menu.text_input import TextInput from ..menu import Menu from ..models.subvolume import Subvolume +from ... import FormattedOutput if TYPE_CHECKING: _: Any @@ -19,16 +20,23 @@ class SubvolumeList(ListManager): ] super().__init__(prompt, current_volumes, self._actions, self._actions[0]) - def reformat(self, data: List[Subvolume]) -> Dict[str, Subvolume]: - return {e.display(): e for e in data} + def reformat(self, data: List[Subvolume]) -> Dict[str, Optional[Subvolume]]: + table = FormattedOutput.as_table(data) + rows = table.split('\n') - def action_list(self): - active_user = self.target if self.target else None + # these are the header rows of the table and do not map to any User obviously + # we're adding 2 spaces as prefix because the menu selector '> ' will be put before + # the selectable rows so the header has to be aligned + display_data: Dict[str, Optional[Subvolume]] = {f' {rows[0]}': None, f' {rows[1]}': None} - if active_user is None: - return [self._actions[0]] - else: - return self._actions[1:] + for row, subvol in zip(rows[2:], data): + row = row.replace('|', '\\|') + display_data[row] = subvol + + return display_data + + def selected_action_display(self, subvolume: Subvolume) -> str: + return subvolume.name def _prompt_options(self, editing: Optional[Subvolume] = None) -> List[str]: preset_options = [] diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index 54ddbba3..baaf622c 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -414,7 +414,7 @@ msgstr "" msgid "Delete" msgstr "" -msgid "Select an action for < {} >" +msgid "Select an action for '{}'" msgstr "" msgid "Copy to new key:" diff --git a/archinstall/locales/cs/LC_MESSAGES/base.mo b/archinstall/locales/cs/LC_MESSAGES/base.mo index 0f9ad704..6a54b145 100644 Binary files a/archinstall/locales/cs/LC_MESSAGES/base.mo and b/archinstall/locales/cs/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/cs/LC_MESSAGES/base.po b/archinstall/locales/cs/LC_MESSAGES/base.po index 51cce8ac..143e815c 100644 --- a/archinstall/locales/cs/LC_MESSAGES/base.po +++ b/archinstall/locales/cs/LC_MESSAGES/base.po @@ -423,8 +423,8 @@ msgstr "Upravit" msgid "Delete" msgstr "Smazat" -msgid "Select an action for < {} >" -msgstr "Zvolte akci pro < {} >" +msgid "Select an action for '{}'" +msgstr "Zvolte akci pro '{}'" msgid "Copy to new key:" msgstr "Zkopírovat k novému klíči:" @@ -654,7 +654,8 @@ msgstr "Základní instalace, která vám umožní si nastavit Arch Linux jakkol msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" msgstr "Nabízí výběr různých serverových balíčků k instalaci a aktivaci, např. httpd, nginx, mariadb" -msgid "Choose which servers to install, if none then a minimal installation wil be done" +#, fuzzy +msgid "Choose which servers to install, if none then a minimal installation will be done" msgstr "Vyberte servery, které mají být nainstalovány, pokud nezvolíte žádné, bude provedena jen základní instalace" msgid "Installs a minimal system as well as xorg and graphics drivers." @@ -743,3 +744,48 @@ msgstr "Volné místo" msgid "Bus-type" msgstr "Typ sběrnice" + +#, fuzzy +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Musí být zadáno heslo správce (root) nebo musí existovat alespoň jeden superuživatel" + +#, fuzzy +msgid "Enter username (leave blank to skip): " +msgstr "Zadejte uživatelské jméno k přidání dalšího uživatele (ponechte prázdné k přeskočení): " + +msgid "The username you entered is invalid. Try again" +msgstr "" + +#, fuzzy +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "Má být {} superuživatelem (sudoer)?" + +#, fuzzy +msgid "Select which partitions to encrypt:" +msgstr "" +"{}\n" +"\n" +"Zvolte oddíl, který bude označen jako šifrovaný" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +#, fuzzy +msgid "Add subvolume" +msgstr " Podsvazek :{:16}" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "Smazat uživatele" diff --git a/archinstall/locales/de/LC_MESSAGES/base.mo b/archinstall/locales/de/LC_MESSAGES/base.mo index 3d58d749..ca3f2971 100644 Binary files a/archinstall/locales/de/LC_MESSAGES/base.mo and b/archinstall/locales/de/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/de/LC_MESSAGES/base.po b/archinstall/locales/de/LC_MESSAGES/base.po index 51ecd8e9..ee40cea5 100644 --- a/archinstall/locales/de/LC_MESSAGES/base.po +++ b/archinstall/locales/de/LC_MESSAGES/base.po @@ -255,10 +255,12 @@ msgstr "Lokale Kodierung" msgid "Drive(s)" msgstr "Laufwerke" -msgid "Select disk layout" -msgstr "Laufwerke-layout auswählen" +#, fuzzy +msgid "Disk layout" +msgstr "Laufwerke-layout speichern" -msgid "Set encryption password" +#, fuzzy +msgid "Encryption password" msgstr "Verschlüsselungspasswort angeben" msgid "Swap" @@ -267,7 +269,8 @@ msgstr "Swap" msgid "Bootloader" msgstr "Bootloader" -msgid "root password" +#, fuzzy +msgid "Root password" msgstr "Root Passwort" msgid "Superuser account" @@ -425,8 +428,8 @@ msgstr "Bearbeiten" msgid "Delete" msgstr "Löschen" -msgid "Select an action for < {} >" -msgstr "Wählen sie eine Aktion aus für < {} >" +msgid "Select an action for '{}'" +msgstr "Wählen sie eine Aktion aus für '{}'" msgid "Copy to new key:" msgstr "Kopieren nach neuem Schlüssel:" @@ -659,7 +662,8 @@ msgstr "Eine sehr minimale Installation welche es erlaubt Arch Linux weitgehend msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" msgstr "Auswahl von Serverpaketen welche installiert werden sollen, z.B. httpd, nginx, mariadb" -msgid "Choose which servers to install, if none then a minimal installation wil be done" +#, fuzzy +msgid "Choose which servers to install, if none then a minimal installation will be done" msgstr "Wählen sie die gewünschten Server aus welche installiert werden sollen" msgid "Installs a minimal system as well as xorg and graphics drivers." @@ -741,6 +745,69 @@ msgstr "" "\n" "Bitte wählen sie welche Partition formatiert werden soll" +msgid "Use HSM to unlock encrypted drive" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "Size" +msgstr "" + +msgid "Free space" +msgstr "" + +msgid "Bus-type" +msgstr "" + +#, fuzzy +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Entweder root Passwort oder wenigstens 1 super-user muss konfiguriert sein" + +#, fuzzy +msgid "Enter username (leave blank to skip): " +msgstr "Geben sie einen weiteren Benutzernamen an der angelegt werden soll (leer lassen um zu Überspringen): " + +msgid "The username you entered is invalid. Try again" +msgstr "" + +#, fuzzy +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "Soll {} ein superuser sein (sudoer)?" + +#, fuzzy +msgid "Select which partitions to encrypt:" +msgstr "" +"{}\n" +"\n" +"Bitte wählen sie welche Partition verschlüsselt werden soll" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +#, fuzzy +msgid "Add subvolume" +msgstr " Subvolume :{:16}" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "Benutzerkonto löschen" + +#~ msgid "Select disk layout" +#~ msgstr "Laufwerke-layout auswählen" + #~ msgid "Add :" #~ msgstr "Hinzufügen :" diff --git a/archinstall/locales/en/LC_MESSAGES/base.po b/archinstall/locales/en/LC_MESSAGES/base.po index 531e20a9..63e9f804 100644 --- a/archinstall/locales/en/LC_MESSAGES/base.po +++ b/archinstall/locales/en/LC_MESSAGES/base.po @@ -232,10 +232,10 @@ msgstr "" msgid "Drive(s)" msgstr "" -msgid "Select disk layout" +msgid "Disk layout" msgstr "" -msgid "Set encryption password" +msgid "Encryption password" msgstr "" msgid "Swap" @@ -244,7 +244,7 @@ msgstr "" msgid "Bootloader" msgstr "" -msgid "root password" +msgid "Root password" msgstr "" msgid "Superuser account" @@ -392,7 +392,7 @@ msgstr "" msgid "Delete" msgstr "" -msgid "Select an action for < {} >" +msgid "Select an action for '{}'" msgstr "" msgid "Copy to new key:" @@ -617,7 +617,7 @@ msgstr "" msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" msgstr "" -msgid "Choose which servers to install, if none then a minimal installation wil be done" +msgid "Choose which servers to install, if none then a minimal installation will be done" msgstr "" msgid "Installs a minimal system as well as xorg and graphics drivers." @@ -689,3 +689,54 @@ msgstr "" msgid "Select which partitions to mark for formatting:" msgstr "" + +msgid "Use HSM to unlock encrypted drive" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "Size" +msgstr "" + +msgid "Free space" +msgstr "" + +msgid "Bus-type" +msgstr "" + +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "" + +msgid "Enter username (leave blank to skip): " +msgstr "" + +msgid "The username you entered is invalid. Try again" +msgstr "" + +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "" + +msgid "Select which partitions to encrypt:" +msgstr "" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +msgid "Add subvolume" +msgstr "" + +msgid "Edit subvolume" +msgstr "" + +msgid "Delete subvolume" +msgstr "" diff --git a/archinstall/locales/es/LC_MESSAGES/base.mo b/archinstall/locales/es/LC_MESSAGES/base.mo index d5efa958..6006c274 100644 Binary files a/archinstall/locales/es/LC_MESSAGES/base.mo and b/archinstall/locales/es/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/es/LC_MESSAGES/base.po b/archinstall/locales/es/LC_MESSAGES/base.po index 0373a40d..6f18c5f8 100644 --- a/archinstall/locales/es/LC_MESSAGES/base.po +++ b/archinstall/locales/es/LC_MESSAGES/base.po @@ -423,8 +423,8 @@ msgstr "Editar" msgid "Delete" msgstr "Eliminar" -msgid "Select an action for < {} >" -msgstr "Seleccione una acción para < {} >" +msgid "Select an action for '{}'" +msgstr "Seleccione una acción para '{}'" msgid "Copy to new key:" msgstr "Copiar a nueva clave:" @@ -657,7 +657,8 @@ msgstr "Una instalación muy básica que te permite personalizar Arch Linux como msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" msgstr "Proporciona una selección de varios paquetes de servidor para instalar y habilitar, p.e. httpd, nginx, mariadb" -msgid "Choose which servers to install, if none then a minimal installation wil be done" +#, fuzzy +msgid "Choose which servers to install, if none then a minimal installation will be done" msgstr "Elija qué servidores instalar, si no hay ninguno, se realizará una instalación mínima" msgid "Installs a minimal system as well as xorg and graphics drivers." @@ -764,6 +765,29 @@ msgstr "¿Debe \"{}\" ser un superusuario (sudo)?" msgid "Select which partitions to encrypt:" msgstr "Seleccione qué particiones cifrar:" +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +#, fuzzy +msgid "Add subvolume" +msgstr " Subvolumen :{:16}" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "Eliminar usuario" + #~ msgid "Select disk layout" #~ msgstr "Seleccione el diseño del disco" diff --git a/archinstall/locales/fr/LC_MESSAGES/base.mo b/archinstall/locales/fr/LC_MESSAGES/base.mo index 364b8964..b8f19aa7 100644 Binary files a/archinstall/locales/fr/LC_MESSAGES/base.mo and b/archinstall/locales/fr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/fr/LC_MESSAGES/base.po b/archinstall/locales/fr/LC_MESSAGES/base.po index 0e783a58..359dbdd7 100644 --- a/archinstall/locales/fr/LC_MESSAGES/base.po +++ b/archinstall/locales/fr/LC_MESSAGES/base.po @@ -14,12 +14,8 @@ msgstr "" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Un fichier journal a été créé ici : {} {}" -msgid "" -" Please submit this issue (and file) to https://github.com/archlinux/" -"archinstall/issues" -msgstr "" -" Veuillez soumettre ce problème (et le fichier) à https://github.com/" -"archlinux/archinstall/issues" +msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr " Veuillez soumettre ce problème (et le fichier) à https://github.com/archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Voulez-vous vraiment abandonner ?" @@ -34,13 +30,10 @@ msgid "Desired hostname for the installation: " msgstr "Nom d'hôte souhaité pour l'installation : " msgid "Username for required superuser with sudo privileges: " -msgstr "" -"Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : " +msgstr "Nom d'utilisateur pour le superutilisateur requis avec les privilèges sudo : " msgid "Any additional users to install (leave blank for no users): " -msgstr "" -"Utilisateur supplémentaire à installer (laisser vide pour aucun " -"utilisateur) : " +msgstr "Utilisateur supplémentaire à installer (laisser vide pour aucun utilisateur) : " msgid "Should this user be a superuser (sudoer)?" msgstr "Cet utilisateur doit-il être un superutilisateur (sudoer) ?" @@ -49,9 +42,7 @@ msgid "Select a timezone" msgstr "Sélectionner un fuseau horaire" msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" -msgstr "" -"Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-" -"boot ?" +msgstr "Souhaitez-vous utiliser GRUB comme chargeur de démarrage au lieu de systemd-boot ?" msgid "Choose a bootloader" msgstr "Choisir un chargeur de démarrage" @@ -59,60 +50,38 @@ msgstr "Choisir un chargeur de démarrage" msgid "Choose an audio server" msgstr "Choisir un serveur audio" -msgid "" -"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr " -"and optional profile packages are installed." -msgstr "" -"Seuls les packages tels que base, base-devel, linux, linux-firmware, " -"efibootmgr et les packages de profil optionnels sont installés." +msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." +msgstr "Seuls les packages tels que base, base-devel, linux, linux-firmware, efibootmgr et les packages de profil optionnels sont installés." -msgid "" -"If you desire a web browser, such as firefox or chromium, you may specify it " -"in the following prompt." -msgstr "" -"Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le " -"spécifier dans l'invite suivante." +msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." +msgstr "Si vous désirez un navigateur Web, tel que firefox ou chrome, vous pouvez le spécifier dans l'invite suivante." -msgid "" -"Write additional packages to install (space separated, leave blank to skip): " -msgstr "" -"Écrire des packages supplémentaires à installer (espaces séparés, laisser " -"vide pour ignorer) : " +msgid "Write additional packages to install (space separated, leave blank to skip): " +msgstr "Écrire des packages supplémentaires à installer (espaces séparés, laisser vide pour ignorer) : " msgid "Copy ISO network configuration to installation" msgstr "Copier la configuration réseau ISO dans l'installation" -msgid "" -"Use NetworkManager (necessary to configure internet graphically in GNOME and " -"KDE)" -msgstr "" -"Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet " -"dans GNOME et KDE)" +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Utiliser NetworkManager (nécessaire pour configurer graphiquement Internet dans GNOME et KDE)" msgid "Select one network interface to configure" msgstr "Sélectionner une interface réseau à configurer" -msgid "" -"Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "" -"Sélectionner le mode à configurer pour \"{}\" ou ignorer pour utiliser le " -"mode par défaut \"{}\"" +msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "Sélectionner le mode à configurer pour \"{}\" ou ignorer pour utiliser le mode par défaut \"{}\"" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "Entrer l'IP et le sous-réseau pour {} (exemple : 192.168.0.5/24) : " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "" -"Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour " -"aucune : " +msgstr "Entrer l'adresse IP de votre passerelle (routeur) ou laisser vide pour aucune : " msgid "Enter your DNS servers (space separated, blank for none): " msgstr "Entrer vos serveurs DNS (séparés par des espaces, vide pour aucun) : " msgid "Select which filesystem your main partition should use" -msgstr "" -"Sélectionner le système de fichiers que votre partition principale doit " -"utiliser" +msgstr "Sélectionner le système de fichiers que votre partition principale doit utiliser" msgid "Current partition layout" msgstr "Disposition actuelle des partitions" @@ -128,20 +97,13 @@ msgid "Enter a desired filesystem type for the partition" msgstr "Entrer un type de système de fichiers souhaité pour la partition" msgid "Enter the start sector (percentage or block number, default: {}): " -msgstr "" -"Entrer le secteur de début (pourcentage ou numéro de bloc, par défaut : " -"{}) : " +msgstr "Entrer le secteur de début (pourcentage ou numéro de bloc, par défaut : {}) : " -msgid "" -"Enter the end sector of the partition (percentage or block number, ex: {}): " -msgstr "" -"Entrer le secteur de fin de la partition (pourcentage ou numéro de bloc, " -"ex : {}) : " +msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " +msgstr "Entrer le secteur de fin de la partition (pourcentage ou numéro de bloc, ex : {}) : " msgid "{} contains queued partitions, this will remove those, are you sure?" -msgstr "" -"{} contient des partitions en file d'attente, cela les supprimera, êtes-vous " -"sûr ?" +msgstr "{} contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" msgid "" "{}\n" @@ -161,17 +123,11 @@ msgstr "" "\n" "Sélectionner par index où et quelle partition montée" -msgid "" -" * Partition mount-points are relative to inside the installation, the boot " -"would be /boot as an example." -msgstr "" -" * Les points de montage de la partition sont relatifs à l'intérieur de " -"l'installation, le démarrage serait /boot par exemple." +msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Les points de montage de la partition sont relatifs à l'intérieur de l'installation, le démarrage serait /boot par exemple." msgid "Select where to mount partition (leave blank to remove mountpoint): " -msgstr "" -"Sélectionner où monter la partition (laisser vide pour supprimer le point de " -"montage) : " +msgstr "Sélectionner où monter la partition (laisser vide pour supprimer le point de montage) : " msgid "" "{}\n" @@ -216,58 +172,34 @@ msgid "Archinstall language" msgstr "Langue d'Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "" -"Effacer tous les lecteurs sélectionnés et utiliser une disposition de " -"partition par défaut optimale" +msgstr "Effacer tous les lecteurs sélectionnés et utiliser une disposition de partition par défaut optimale" -msgid "" -"Select what to do with each individual drive (followed by partition usage)" -msgstr "" -"Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de " -"l'utilisation de la partition)" +msgid "Select what to do with each individual drive (followed by partition usage)" +msgstr "Sélectionner ce qu'il faut faire avec chaque lecteur individuel (suivi de l'utilisation de la partition)" msgid "Select what you wish to do with the selected block devices" -msgstr "" -"Sélectionner ce que vous souhaitez faire avec les périphériques de bloc " -"sélectionnés" +msgstr "Sélectionner ce que vous souhaitez faire avec les périphériques de bloc sélectionnés" -msgid "" -"This is a list of pre-programmed profiles, they might make it easier to " -"install things like desktop environments" -msgstr "" -"Ceci est une liste de profils préprogrammés, ils pourraient faciliter " -"l'installation d'outils comme les environnements de bureau" +msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" +msgstr "Ceci est une liste de profils préprogrammés, ils pourraient faciliter l'installation d'outils comme les environnements de bureau" msgid "Select keyboard layout" msgstr "Sélectionner la disposition du clavier" msgid "Select one of the regions to download packages from" -msgstr "" -"Sélectionner l'une des régions depuis lesquelles télécharger les packages" +msgstr "Sélectionner l'une des régions depuis lesquelles télécharger les packages" msgid "Select one or more hard drives to use and configure" msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer" -msgid "" -"For the best compatibility with your AMD hardware, you may want to use " -"either the all open-source or AMD / ATI options." -msgstr "" -"Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez " -"utiliser les options entièrement open source ou AMD / ATI." +msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." +msgstr "Pour une meilleure compatibilité avec votre matériel AMD, vous pouvez utiliser les options entièrement open source ou AMD / ATI." -msgid "" -"For the best compatibility with your Intel hardware, you may want to use " -"either the all open-source or Intel options.\n" -msgstr "" -"Pour une compatibilité optimale avec votre matériel Intel, vous pouvez " -"utiliser les options entièrement open source ou Intel.\n" +msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" +msgstr "Pour une compatibilité optimale avec votre matériel Intel, vous pouvez utiliser les options entièrement open source ou Intel.\n" -msgid "" -"For the best compatibility with your Nvidia hardware, you may want to use " -"the Nvidia proprietary driver.\n" -msgstr "" -"Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez " -"utiliser le pilote propriétaire Nvidia.\n" +msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" +msgstr "Pour une meilleure compatibilité avec votre matériel Nvidia, vous pouvez utiliser le pilote propriétaire Nvidia.\n" msgid "" "\n" @@ -276,8 +208,7 @@ msgid "" msgstr "" "\n" "\n" -"Sélectionner un pilote graphique ou laisser vide pour installer tous les " -"pilotes open-source" +"Sélectionner un pilote graphique ou laisser vide pour installer tous les pilotes open-source" msgid "All open-source (default)" msgstr "Tout open-source (par défaut)" @@ -300,12 +231,8 @@ msgstr "Sélectionner une ou plusieurs des options ci-dessous : " msgid "Adding partition...." msgstr "Ajout de la partition...." -msgid "" -"You need to enter a valid fs-type in order to continue. See `man parted` for " -"valid fs-type's." -msgstr "" -"Vous devez entrer un type de fs valide pour continuer. Voir `man parted` " -"pour les types de fs valides." +msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." +msgstr "Vous devez entrer un type de fs valide pour continuer. Voir `man parted` pour les types de fs valides." msgid "Error: Listing profiles on URL \"{}\" resulted in:" msgstr "Erreur : la liste des profils sur l'URL \"{}\" a entraîné :" @@ -378,8 +305,7 @@ msgid "" msgstr "" "Vous avez décidé d'ignorer la sélection du disque dur\n" "et vous utiliserez la configuration de disque montée sur {} (expérimental)\n" -"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette " -"configuration\n" +"ATTENTION : Archinstall ne vérifiera pas l'adéquation de cette configuration\n" "Souhaitez-vous continuer ?" msgid "Re-using partition instance: {}" @@ -404,8 +330,7 @@ msgid "Mark/Unmark a partition as encrypted" msgstr "Marquer/Démarquer une partition comme chiffrée" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "" -"Marquer/Démarquer une partition comme amorçable (automatique pour /boot)" +msgstr "Marquer/Démarquer une partition comme amorçable (automatique pour /boot)" msgid "Set desired filesystem for a partition" msgstr "Définir le système de fichiers souhaité pour une partition" @@ -445,9 +370,7 @@ msgid "Enter a encryption password for {}" msgstr "Entrer un mot de passe de cryptage pour {}" msgid "Enter disk encryption password (leave blank for no encryption): " -msgstr "" -"Entrer le mot de passe de chiffrement du disque (laisser vide pour aucun " -"chiffrement) : " +msgstr "Entrer le mot de passe de chiffrement du disque (laisser vide pour aucun chiffrement) : " msgid "Create a required super-user with sudo privileges: " msgstr "Créer un super-utilisateur requis avec les privilèges sudo : " @@ -458,44 +381,31 @@ msgstr "Entrer le mot de passe root (laisser vide pour désactiver root) : " msgid "Password for user \"{}\": " msgstr "Mot de passe pour l'utilisateur \"{}\" : " -msgid "" -"Verifying that additional packages exist (this might take a few seconds)" -msgstr "" -"Vérifier que des packages supplémentaires existent (cela peut prendre " -"quelques secondes)" +msgid "Verifying that additional packages exist (this might take a few seconds)" +msgstr "Vérifier que des packages supplémentaires existent (cela peut prendre quelques secondes)" -msgid "" -"Would you like to use automatic time synchronization (NTP) with the default " -"time servers?\n" -msgstr "" -"Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec " -"les serveurs de temps par défaut ?\n" +msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" +msgstr "Souhaitez-vous utiliser la synchronisation automatique de l'heure (NTP) avec les serveurs de temps par défaut ?\n" msgid "" -"Hardware time and other post-configuration steps might be required in order " -"for NTP to work.\n" +"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"Le temps matériel et d'autres étapes de post-configuration peuvent être " -"nécessaires pour que NTP fonctionne.\n" +"Le temps matériel et d'autres étapes de post-configuration peuvent être nécessaires pour que NTP fonctionne.\n" "Pour plus d'informations, veuillez consulter le wiki Arch" msgid "Enter a username to create an additional user (leave blank to skip): " -msgstr "" -"Entrer un nom d'utilisateur pour créer un utilisateur supplémentaire " -"(laisser vide pour ignorer) : " +msgstr "Entrer un nom d'utilisateur pour créer un utilisateur supplémentaire (laisser vide pour ignorer) : " msgid "Use ESC to skip\n" msgstr "Utiliser ESC pour ignorer\n" msgid "" "\n" -" Choose an object from the list, and select one of the available actions for " -"it to execute" +" Choose an object from the list, and select one of the available actions for it to execute" msgstr "" "\n" -"Choisir un objet dans la liste et sélectionner l'une des actions disponibles " -"pour qu'il s'exécute" +"Choisir un objet dans la liste et sélectionner l'une des actions disponibles pour qu'il s'exécute" msgid "Cancel" msgstr "Annuler" @@ -515,8 +425,8 @@ msgstr "Modifier" msgid "Delete" msgstr "Supprimer" -msgid "Select an action for < {} >" -msgstr "Sélectionner une action pour < {} >" +msgid "Select an action for '{}'" +msgstr "Sélectionner une action pour '{}'" msgid "Copy to new key:" msgstr "Copier vers une nouvelle clé :" @@ -531,18 +441,11 @@ msgstr "" "\n" "Voici la configuration choisie :" -msgid "" -"Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "" -"Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour " -"qu'il se termine." +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "Pacman est déjà en cours d'exécution, attendez au maximum 10 minutes pour qu'il se termine." -msgid "" -"Pre-existing pacman lock never exited. Please clean up any existing pacman " -"sessions before using archinstall." -msgstr "" -"Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes " -"les sessions pacman existantes avant d'utiliser archinstall." +msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." +msgstr "Le verrou pacman préexistant n'a jamais été fermé. Veuillez nettoyer toutes les sessions pacman existantes avant d'utiliser archinstall." msgid "Choose which optional additional repositories to enable" msgstr "Choisir les référentiels supplémentaires en option à activer" @@ -679,16 +582,13 @@ msgid "Select the desired subvolume options " msgstr "Sélectionner les options de sous-volume souhaitées " msgid "Define users with sudo privilege, by username: " -msgstr "" -"Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : " +msgstr "Définir les utilisateurs avec le privilège sudo, par nom d'utilisateur : " msgid "[!] A log file has been created here: {}" msgstr "[!] Un fichier journal a été créé ici : {}" msgid "Would you like to use BTRFS subvolumes with a default structure?" -msgstr "" -"Souhaitez-vous utiliser des sous-volumes BTRFS avec une structure par " -"défaut ?" +msgstr "Souhaitez-vous utiliser des sous-volumes BTRFS avec une structure par défaut ?" msgid "Would you like to use BTRFS compression?" msgstr "Souhaitez-vous utiliser la compression BTRFS ?" @@ -696,12 +596,8 @@ msgstr "Souhaitez-vous utiliser la compression BTRFS ?" msgid "Would you like to create a separate partition for /home?" msgstr "Souhaitez-vous créer une partition séparée pour /home ?" -msgid "" -"The selected drives do not have the minimum capacity required for an " -"automatic suggestion\n" -msgstr "" -"Les disques sélectionnés n'ont pas la capacité minimale requise pour une " -"suggestion automatique\n" +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "Les disques sélectionnés n'ont pas la capacité minimale requise pour une suggestion automatique\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "Capacité minimale pour la partition /home : {} Go\n" @@ -728,9 +624,7 @@ msgid "No iface specified for manual configuration" msgstr "Aucun iface spécifié pour la configuration manuelle" msgid "Manual nic configuration with no auto DHCP requires an IP address" -msgstr "" -"La configuration manuelle de la carte réseau sans DHCP automatique nécessite " -"une adresse IP" +msgstr "La configuration manuelle de la carte réseau sans DHCP automatique nécessite une adresse IP" msgid "Add interface" msgstr "Ajouter une interface" @@ -750,42 +644,24 @@ msgstr "Configuration manuelle" msgid "Mark/Unmark a partition as compressed (btrfs only)" msgstr "Marquer/Démarquer une partition comme compressée (btrfs uniquement)" -msgid "" -"The password you are using seems to be weak, are you sure you want to use it?" -msgstr "" -"Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir " -"l'utiliser ?" +msgid "The password you are using seems to be weak, are you sure you want to use it?" +msgstr "Le mot de passe que vous utilisez semble faible, êtes-vous sûr de vouloir l'utiliser ?" -msgid "" -"Provides a selection of desktop environments and tiling window managers, e." -"g. gnome, kde, sway" -msgstr "" -"Fournit une sélection d'environnements de bureau et de gestionnaires de " -"fenêtres en mosaïque, par ex. gnome, kde, sway" +msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "Fournit une sélection d'environnements de bureau et de gestionnaires de fenêtres en mosaïque, par ex. gnome, kde, sway" msgid "Select your desired desktop environment" msgstr "Sélectionner l'environnement de bureau souhaité" -msgid "" -"A very basic installation that allows you to customize Arch Linux as you see " -"fit." -msgstr "" -"Une installation très basique qui vous permet de personnaliser Arch Linux " -"comme bon vous semble." +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "Une installation très basique qui vous permet de personnaliser Arch Linux comme bon vous semble." -msgid "" -"Provides a selection of various server packages to install and enable, e.g. " -"httpd, nginx, mariadb" -msgstr "" -"Fournit une sélection de divers paquets de serveur à installer et à activer, " -"par ex. httpd, nginx, mariadb" +msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "Fournit une sélection de divers paquets de serveur à installer et à activer, par ex. httpd, nginx, mariadb" -msgid "" -"Choose which servers to install, if none then a minimal installation wil be " -"done" -msgstr "" -"Choisir les serveurs à installer, s'il n'y en a pas, une installation " -"minimale sera effectuée" +#, fuzzy +msgid "Choose which servers to install, if none then a minimal installation will be done" +msgstr "Choisir les serveurs à installer, s'il n'y en a pas, une installation minimale sera effectuée" msgid "Installs a minimal system as well as xorg and graphics drivers." msgstr "Installe un système minimal ainsi que les pilotes graphiques et xorg." @@ -793,12 +669,8 @@ msgstr "Installe un système minimal ainsi que les pilotes graphiques et xorg." msgid "Press Enter to continue." msgstr "Appuyer sur Entrée pour continuer." -msgid "" -"Would you like to chroot into the newly created installation and perform " -"post-installation configuration?" -msgstr "" -"Souhaitez-vous chrooter dans l'installation nouvellement créée et effectuer " -"la configuration post-installation ?" +msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" +msgstr "Souhaitez-vous chrooter dans l'installation nouvellement créée et effectuer la configuration post-installation ?" msgid "Are you sure you want to reset this setting?" msgstr "Voulez-vous vraiment réinitialiser ce paramètre ?" @@ -807,16 +679,10 @@ msgid "Select one or more hard drives to use and configure\n" msgstr "Sélectionner un ou plusieurs disques durs à utiliser et à configurer\n" msgid "Any modifications to the existing setting will reset the disk layout!" -msgstr "" -"Toute modification du paramètre existant réinitialisera la disposition du " -"disque !" +msgstr "Toute modification du paramètre existant réinitialisera la disposition du disque !" -msgid "" -"If you reset the harddrive selection this will also reset the current disk " -"layout. Are you sure?" -msgstr "" -"Si vous réinitialisez la sélection du disque dur, cela réinitialisera " -"également la disposition actuelle du disque. Êtes-vous sûr ?" +msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" +msgstr "Si vous réinitialisez la sélection du disque dur, cela réinitialisera également la disposition actuelle du disque. Êtes-vous sûr ?" msgid "Save and exit" msgstr "Sauvegarder et quitter" @@ -826,8 +692,7 @@ msgid "" "contains queued partitions, this will remove those, are you sure?" msgstr "" "{}\n" -"contient des partitions en file d'attente, cela les supprimera, êtes-vous " -"sûr ?" +"contient des partitions en file d'attente, cela les supprimera, êtes-vous sûr ?" msgid "No audio server" msgstr "Pas de serveur audio" @@ -863,13 +728,8 @@ msgstr "Ajouter: " msgid "Value: " msgstr "Valeur: " -msgid "" -"You can skip selecting a drive and partitioning and use whatever drive-setup " -"is mounted at /mnt (experimental)" -msgstr "" -"Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et " -"utiliser n'importe quelle configuration de lecteur montée sur /mnt " -"(expérimental)" +msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" +msgstr "Vous pouvez ignorer la sélection d'un lecteur et le partitionnement et utiliser n'importe quelle configuration de lecteur montée sur /mnt (expérimental)" msgid "Select one of the disks or skip and use /mnt as default" msgstr "Sélectionner l'un des disques ou ignorer et utiliser /mnt par défaut" @@ -892,12 +752,8 @@ msgstr "Espace libre" msgid "Bus-type" msgstr "Type de bus" -msgid "" -"Either root-password or at least 1 user with sudo privileges must be " -"specified" -msgstr "" -"Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit " -"être spécifié" +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Le mot de passe root ou au moins 1 utilisateur avec des privilèges sudo doit être spécifié" msgid "Enter username (leave blank to skip): " msgstr "Entrer le nom d'utilisateur (laisser vide pour passer) :" @@ -908,6 +764,36 @@ msgstr "Le nom d'utilisateur que vous avez saisi n'est pas valide. Réessayer" msgid "Should \"{}\" be a superuser (sudo)?" msgstr "\"{}\" devrait-il être un superutilisateur (sudo) ?" +#, fuzzy +msgid "Select which partitions to encrypt:" +msgstr "" +"{}\n" +"\n" +"Sélectionner la partition à marquer comme chiffrée" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +#, fuzzy +msgid "Add subvolume" +msgstr " Sous-volume : {:16}" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "Supprimer l'utilisateur" + #~ msgid "Select disk layout" #~ msgstr "Sélectionner la disposition du disque" diff --git a/archinstall/locales/it/LC_MESSAGES/base.mo b/archinstall/locales/it/LC_MESSAGES/base.mo index 34a6c898..c6d984c3 100644 Binary files a/archinstall/locales/it/LC_MESSAGES/base.mo and b/archinstall/locales/it/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/it/LC_MESSAGES/base.po b/archinstall/locales/it/LC_MESSAGES/base.po index 36e8d5b4..529fb47b 100644 --- a/archinstall/locales/it/LC_MESSAGES/base.po +++ b/archinstall/locales/it/LC_MESSAGES/base.po @@ -425,8 +425,8 @@ msgstr "Modifica" msgid "Delete" msgstr "Elimina" -msgid "Select an action for < {} >" -msgstr "Seleziona un'azione per < {} >" +msgid "Select an action for '{}'" +msgstr "Seleziona un'azione per '{}'" msgid "Copy to new key:" msgstr "Copia su nuova chiave:" @@ -659,7 +659,8 @@ msgstr "Un'installazione molto semplice che ti consente di personalizzare Arch L msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" msgstr "Fornisce una selezione di vari pacchetti server da installare e abilitare, per esempio httpd, nginx, mariadb" -msgid "Choose which servers to install, if none then a minimal installation wil be done" +#, fuzzy +msgid "Choose which servers to install, if none then a minimal installation will be done" msgstr "Scegli quali server installare, se nessuno verrà eseguita un'installazione minima" msgid "Installs a minimal system as well as xorg and graphics drivers." @@ -750,3 +751,48 @@ msgstr "Spazio libero" msgid "Bus-type" msgstr "Tipo di bus" + +#, fuzzy +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "È necessario specificare la password di root o almeno 1 superuser" + +#, fuzzy +msgid "Enter username (leave blank to skip): " +msgstr "Inserisci un nome utente per creare un utente aggiuntivo (lascia vuoto per saltare): " + +msgid "The username you entered is invalid. Try again" +msgstr "" + +#, fuzzy +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "{} dovrebbe essere un superutente? (sudoer)" + +#, fuzzy +msgid "Select which partitions to encrypt:" +msgstr "" +"{}\n" +"\n" +"Seleziona quale partizione contrassegnare come crittografata" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +#, fuzzy +msgid "Add subvolume" +msgstr " Sottovolume :{:16}" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "Elimina utente" diff --git a/archinstall/locales/nl/LC_MESSAGES/base.mo b/archinstall/locales/nl/LC_MESSAGES/base.mo index d7dcabc6..62b7af82 100644 Binary files a/archinstall/locales/nl/LC_MESSAGES/base.mo and b/archinstall/locales/nl/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/nl/LC_MESSAGES/base.po b/archinstall/locales/nl/LC_MESSAGES/base.po index b712d8ca..72547f7a 100644 --- a/archinstall/locales/nl/LC_MESSAGES/base.po +++ b/archinstall/locales/nl/LC_MESSAGES/base.po @@ -256,10 +256,12 @@ msgstr "Taalvariant" msgid "Drive(s)" msgstr "Harde schijven" -msgid "Select disk layout" -msgstr "Kies een schijfindeling" +#, fuzzy +msgid "Disk layout" +msgstr "Schijfindeling vastleggen" -msgid "Set encryption password" +#, fuzzy +msgid "Encryption password" msgstr "Versleutelwachtwoord instellen" msgid "Swap" @@ -268,7 +270,8 @@ msgstr "Wisselgeheugen" msgid "Bootloader" msgstr "Opstartlader" -msgid "root password" +#, fuzzy +msgid "Root password" msgstr "Rootwachtwoord" msgid "Superuser account" @@ -426,8 +429,8 @@ msgstr "Bewerken" msgid "Delete" msgstr "Verwijderen" -msgid "Select an action for < {} >" -msgstr "Kies een actie voor < {} >" +msgid "Select an action for '{}'" +msgstr "Kies een actie voor '{}'" msgid "Copy to new key:" msgstr "Kopiëren naar nieuwe sleutel:" @@ -670,7 +673,7 @@ msgstr "" msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" msgstr "" -msgid "Choose which servers to install, if none then a minimal installation wil be done" +msgid "Choose which servers to install, if none then a minimal installation will be done" msgstr "" msgid "Installs a minimal system as well as xorg and graphics drivers." @@ -757,6 +760,69 @@ msgstr "" "\n" "Kies welke partitie moet worden gemaskeerd alvorens te formatteren" +msgid "Use HSM to unlock encrypted drive" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "Size" +msgstr "" + +msgid "Free space" +msgstr "" + +msgid "Bus-type" +msgstr "" + +#, fuzzy +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Stel een rootwachtwoord of minimaal één beheerder in" + +#, fuzzy +msgid "Enter username (leave blank to skip): " +msgstr "Voer een gebruikersnaam in om een tweede account toe te voegen (laat leeg om over te slaan): " + +msgid "The username you entered is invalid. Try again" +msgstr "" + +#, fuzzy +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "Moet {} gebruiker een beheerder (sudoer) worden?" + +#, fuzzy +msgid "Select which partitions to encrypt:" +msgstr "" +"{}\n" +"\n" +"Kies welke partitie moet worden versleuteld" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +#, fuzzy +msgid "Add subvolume" +msgstr " Subvolume :{:16}" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "Gebruiker verwijderen" + +#~ msgid "Select disk layout" +#~ msgstr "Kies een schijfindeling" + #~ msgid "Add :" #~ msgstr "Toevoegen:" diff --git a/archinstall/locales/pl/LC_MESSAGES/base.mo b/archinstall/locales/pl/LC_MESSAGES/base.mo index dc26f1d7..d60d939a 100644 Binary files a/archinstall/locales/pl/LC_MESSAGES/base.mo and b/archinstall/locales/pl/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pl/LC_MESSAGES/base.po b/archinstall/locales/pl/LC_MESSAGES/base.po index d92637ad..f6730462 100644 --- a/archinstall/locales/pl/LC_MESSAGES/base.po +++ b/archinstall/locales/pl/LC_MESSAGES/base.po @@ -428,8 +428,8 @@ msgstr "Edytuj" msgid "Delete" msgstr "Usuń" -msgid "Select an action for < {} >" -msgstr "Wybierz akcję dla < {} >" +msgid "Select an action for '{}'" +msgstr "Wybierz akcję dla '{}'" msgid "Copy to new key:" msgstr "Skopiuj do nowego klucza:" @@ -663,7 +663,8 @@ msgstr "Bardzo podstawowa instalacja pozwalająca ci dostosowanie Arch Linuxa do msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" msgstr "Dostarcza wybór różnych pakietów serwerowych do zainstalowania i uruchomienia, np. httpd, nginx, mariadb" -msgid "Choose which servers to install, if none then a minimal installation wil be done" +#, fuzzy +msgid "Choose which servers to install, if none then a minimal installation will be done" msgstr "Wybierz jakie serwery zainstalować, jeżeli żadne, wtedy minimalna instalacja będzie użyta" msgid "Installs a minimal system as well as xorg and graphics drivers." @@ -745,6 +746,66 @@ msgstr "" msgid "Select which partitions to mark for formatting:" msgstr "Wybierz partycja która ma zostać sformatowana:" +msgid "Use HSM to unlock encrypted drive" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "Size" +msgstr "" + +msgid "Free space" +msgstr "" + +msgid "Bus-type" +msgstr "" + +#, fuzzy +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Musi być podane albo hasło root, albo co najmniej jednego superużytkownika" + +#, fuzzy +msgid "Enter username (leave blank to skip): " +msgstr "Wprowadź nazwę użytkownika, aby utworzyć dodatkowego użytkownika (pozostaw puste, aby pominąć): " + +msgid "The username you entered is invalid. Try again" +msgstr "" + +#, fuzzy +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "Czy {} powinien być superuserem (sudoer)?" + +#, fuzzy +msgid "Select which partitions to encrypt:" +msgstr "" +"{}\n" +"\n" +"Wybierz partycja która ma zostać zaszyfrowana" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +#, fuzzy +msgid "Add subvolume" +msgstr " Subwolumin :{:16}" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "Usuń użytkownika" + #~ msgid "Select disk layout" #~ msgstr "Wybierz układ dysku" diff --git a/archinstall/locales/pt/LC_MESSAGES/base.mo b/archinstall/locales/pt/LC_MESSAGES/base.mo index 3b9f7c26..113b26a6 100644 Binary files a/archinstall/locales/pt/LC_MESSAGES/base.mo and b/archinstall/locales/pt/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt/LC_MESSAGES/base.po b/archinstall/locales/pt/LC_MESSAGES/base.po index 4352cf34..e44a0133 100644 --- a/archinstall/locales/pt/LC_MESSAGES/base.po +++ b/archinstall/locales/pt/LC_MESSAGES/base.po @@ -254,10 +254,12 @@ msgstr "Codificação de localização" msgid "Drive(s)" msgstr "Discos rígidos" -msgid "Select disk layout" -msgstr "Seleciona o esquema de disco" +#, fuzzy +msgid "Disk layout" +msgstr "Selecionar esquema de disco" -msgid "Set encryption password" +#, fuzzy +msgid "Encryption password" msgstr "Define a palavra-passe de encriptação" msgid "Swap" @@ -266,7 +268,8 @@ msgstr "Swap" msgid "Bootloader" msgstr "Carregador de arranque" -msgid "root password" +#, fuzzy +msgid "Root password" msgstr "Palavra-passe de root" msgid "Superuser account" @@ -429,8 +432,8 @@ msgid "Delete" msgstr "Eliminar" #, fuzzy -msgid "Select an action for < {} >" -msgstr "Selecione uma ação para < {} >" +msgid "Select an action for '{}'" +msgstr "Selecione uma ação para '{}'" msgid "Copy to new key:" msgstr "Copiar para nova chave:" @@ -688,7 +691,7 @@ msgstr "" msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" msgstr "" -msgid "Choose which servers to install, if none then a minimal installation wil be done" +msgid "Choose which servers to install, if none then a minimal installation will be done" msgstr "" msgid "Installs a minimal system as well as xorg and graphics drivers." @@ -777,6 +780,69 @@ msgstr "" "\n" "Seleciona a partição a mascar para formatar" +msgid "Use HSM to unlock encrypted drive" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "Size" +msgstr "" + +msgid "Free space" +msgstr "" + +msgid "Bus-type" +msgstr "" + +#, fuzzy +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "É necessário especificar uma senha de root ou pelo menos 1 super-utilizador" + +#, fuzzy +msgid "Enter username (leave blank to skip): " +msgstr "Introduzir um nome de utilizador para criar um utilizador adicional (deixar em branco para saltar): " + +msgid "The username you entered is invalid. Try again" +msgstr "" + +#, fuzzy +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "Deve {} ser um superutilizador (sudoer)?" + +#, fuzzy +msgid "Select which partitions to encrypt:" +msgstr "" +"{}\n" +"\n" +"Seleciona a partição a marcar como encriptada" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +#, fuzzy +msgid "Add subvolume" +msgstr " Subvolume :{:16}" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "Eliminar Utilizador" + +#~ msgid "Select disk layout" +#~ msgstr "Seleciona o esquema de disco" + #~ msgid "Add :" #~ msgstr "Adicionar :" diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo index 9c71d906..bfc9645c 100644 Binary files a/archinstall/locales/pt_BR/LC_MESSAGES/base.mo and b/archinstall/locales/pt_BR/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/pt_BR/LC_MESSAGES/base.po b/archinstall/locales/pt_BR/LC_MESSAGES/base.po index 35db5f95..004ad022 100644 --- a/archinstall/locales/pt_BR/LC_MESSAGES/base.po +++ b/archinstall/locales/pt_BR/LC_MESSAGES/base.po @@ -9,12 +9,8 @@ msgstr "" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Um arquivo de log foi criado aqui: {} {}" -msgid "" -" Please submit this issue (and file) to https://github.com/archlinux/" -"archinstall/issues" -msgstr "" -" Por favor, envie este problema (e o arquivo) para: " -"https://github.com/archlinux/archinstall/issues" +msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr " Por favor, envie este problema (e o arquivo) para: https://github.com/archlinux/archinstall/issues" msgid "Do you really want to abort?" msgstr "Deseja realmente abortar?" @@ -49,42 +45,26 @@ msgstr "Escolha um bootloader" msgid "Choose an audio server" msgstr "Escolha um servidor de áudio" -msgid "" -"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr " -"and optional profile packages are installed." -msgstr "" -"Apenas pacotes como base, base-devel, linux, linux-firmware, efibootmgr " -"e pacotes opcionais de perfil são instalados." +msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." +msgstr "Apenas pacotes como base, base-devel, linux, linux-firmware, efibootmgr e pacotes opcionais de perfil são instalados." -msgid "" -"If you desire a web browser, such as firefox or chromium, you may specify it " -"in the following prompt." -msgstr "" -"Se deseja um navegador web, como firefox ou chromium, pode especificá-lo " -"no próximo prompt." +msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." +msgstr "Se deseja um navegador web, como firefox ou chromium, pode especificá-lo no próximo prompt." -msgid "" -"Write additional packages to install (space separated, leave blank to skip): " -msgstr "" -"Digite pacotes adicionais para instalar (separados por espaço, deixe em branco para pular): " +msgid "Write additional packages to install (space separated, leave blank to skip): " +msgstr "Digite pacotes adicionais para instalar (separados por espaço, deixe em branco para pular): " msgid "Copy ISO network configuration to installation" msgstr "Copiar a configuração de rede da ISO para a instalação" -msgid "" -"Use NetworkManager (necessary to configure internet graphically in GNOME and " -"KDE)" -msgstr "" -"Usar NetworkManager (necessário para configurar internet graficamente no GNOME e " -"KDE)" +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Usar NetworkManager (necessário para configurar internet graficamente no GNOME e KDE)" msgid "Select one network interface to configure" msgstr "Selecione uma interface de rede para configurar" -msgid "" -"Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "" -"Selecione qual modo configurar para \"{}\" ou ignore para usar o modo padrão \"{}\"" +msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "Selecione qual modo configurar para \"{}\" ou ignore para usar o modo padrão \"{}\"" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "Digite o IP e a subrede para {} (exemplo: 192.168.0.5/24): " @@ -114,10 +94,8 @@ msgstr "Digite o tipo de sistema de arquivos desejado para a partição" msgid "Enter the start sector (percentage or block number, default: {}): " msgstr "Digite o setor de início (porcentagem ou número do bloco, padrão: {}): " -msgid "" -"Enter the end sector of the partition (percentage or block number, ex: {}): " -msgstr "" -"Digite o setor final da partição (porcentagem ou número de bloco, ex: {}): " +msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " +msgstr "Digite o setor final da partição (porcentagem ou número de bloco, ex: {}): " msgid "{} contains queued partitions, this will remove those, are you sure?" msgstr "{} contém partições em fila, isto irá removê-las, tem certeza?" @@ -140,13 +118,8 @@ msgstr "" "\n" "Selecione por índice quais partições montar em" -msgid "" -" * Partition mount-points are relative to inside the installation, the boot " -"would be /boot as an example." -msgstr "" -" * Os pontos de montagem das partições são relativos aos de dentro da instalação, boot " -"por exemplo seria /boot." - +msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Os pontos de montagem das partições são relativos aos de dentro da instalação, boot por exemplo seria /boot." msgid "Select where to mount partition (leave blank to remove mountpoint): " msgstr "Selecione onde montar a partição (deixe em branco para remover o ponto de montagem): " @@ -196,19 +169,14 @@ msgstr "Idioma do Archinstall" msgid "Wipe all selected drives and use a best-effort default partition layout" msgstr "Apagar todos os discos selecionados e usar um esquema de partições padrão de melhor desempenho" -msgid "" -"Select what to do with each individual drive (followed by partition usage)" -msgstr "" -"Selecione o que fazer com cada disco individual (seguido de uso da partição)" +msgid "Select what to do with each individual drive (followed by partition usage)" +msgstr "Selecione o que fazer com cada disco individual (seguido de uso da partição)" msgid "Select what you wish to do with the selected block devices" msgstr "Selecione o que deseja fazer com os dispositivos de bloco selecionados" -msgid "" -"This is a list of pre-programmed profiles, they might make it easier to " -"install things like desktop environments" -msgstr "Esta é uma lista de perfis pré-programados, que podem por exemplo " -"facilitar a instalação de ambientes gráficos" +msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" +msgstr "Esta é uma lista de perfis pré-programados, que podem por exemplo facilitar a instalação de ambientes gráficos" msgid "Select keyboard layout" msgstr "Selecione o layout de teclado" @@ -219,26 +187,14 @@ msgstr "Selecione uma das regiões de onde baixar os pacotes" msgid "Select one or more hard drives to use and configure" msgstr "Selecione um ou mais discos rígidos para usar e configurar" -msgid "" -"For the best compatibility with your AMD hardware, you may want to use " -"either the all open-source or AMD / ATI options." -msgstr "" -"Para melhor compatibilidade com o seu hardware AMD, talvez queira usar " -"a opção de drivers completamente open-source ou as opções da AMD / ATI." +msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." +msgstr "Para melhor compatibilidade com o seu hardware AMD, talvez queira usar a opção de drivers completamente open-source ou as opções da AMD / ATI." -msgid "" -"For the best compatibility with your Intel hardware, you may want to use " -"either the all open-source or Intel options.\n" -msgstr "" -"Para melhor compatibilidade com o seu hardware Intel, talvez queira usar " -"a opção de drivers completamente open-source ou as opções da Intel.\n" +msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" +msgstr "Para melhor compatibilidade com o seu hardware Intel, talvez queira usar a opção de drivers completamente open-source ou as opções da Intel.\n" -msgid "" -"For the best compatibility with your Nvidia hardware, you may want to use " -"the Nvidia proprietary driver.\n" -msgstr "" -"Para melhor compatibilidade com o seu hardware Nvidia, talvez queira usar " -"o driver proprietário da Nvidia.\n" +msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" +msgstr "Para melhor compatibilidade com o seu hardware Nvidia, talvez queira usar o driver proprietário da Nvidia.\n" msgid "" "\n" @@ -270,13 +226,8 @@ msgstr "Selecione uma ou mais das opções abaixo: " msgid "Adding partition...." msgstr "Adicionando partição...." -msgid "" -"You need to enter a valid fs-type in order to continue. See `man parted` for " -"valid fs-type's." -msgstr "" -"Você precisa definir um tipo de sistema de arquivo válido. Consulte o `man parted` para " -"verificar os tipos de sistemas de arquivo válido." - +msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." +msgstr "Você precisa definir um tipo de sistema de arquivo válido. Consulte o `man parted` para verificar os tipos de sistemas de arquivo válido." msgid "Error: Listing profiles on URL \"{}\" resulted in:" msgstr "Erro: Listando os perfis em URL \"{}\" resulta em:" @@ -425,25 +376,17 @@ msgstr "Digite uma senha de root (deixe em branco para desativar root): " msgid "Password for user \"{}\": " msgstr "Senha para o usuário \"{}\": " -msgid "" -"Verifying that additional packages exist (this might take a few seconds)" -msgstr "" -"Verificando se existem pacotes adicionais (isto pode demorar alguns segundos)" +msgid "Verifying that additional packages exist (this might take a few seconds)" +msgstr "Verificando se existem pacotes adicionais (isto pode demorar alguns segundos)" -msgid "" -"Would you like to use automatic time synchronization (NTP) with the default " -"time servers?\n" -msgstr "" -"Deseja usar sincronização de tempo automática (NTP) " -"com os servidores de tempo padrão?\n" +msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" +msgstr "Deseja usar sincronização de tempo automática (NTP) com os servidores de tempo padrão?\n" msgid "" -"Hardware time and other post-configuration steps might be required in order " -"for NTP to work.\n" +"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"A hora de hardware e outros passos de pós-configuração podem ser necessários " -"para que o NTP funcione.\n" +"A hora de hardware e outros passos de pós-configuração podem ser necessários para que o NTP funcione.\n" "Para mais informações, por favor visite a Arch wiki" msgid "Enter a username to create an additional user (leave blank to skip): " @@ -454,12 +397,10 @@ msgstr "Use ESC para pular\n" msgid "" "\n" -" Choose an object from the list, and select one of the available actions for " -"it to execute" +" Choose an object from the list, and select one of the available actions for it to execute" msgstr "" "\n" -" Escolha um objeto da lista, e selecione uma das ações disponíveis " -"para executar" +" Escolha um objeto da lista, e selecione uma das ações disponíveis para executar" msgid "Cancel" msgstr "Cancelar" @@ -479,8 +420,8 @@ msgstr "Editar" msgid "Delete" msgstr "Deletar" -msgid "Select an action for < {} >" -msgstr "Selecione uma ação para < {} >" +msgid "Select an action for '{}'" +msgstr "Selecione uma ação para '{}'" msgid "Copy to new key:" msgstr "Copiar para nova chave:" @@ -495,17 +436,11 @@ msgstr "" "\n" "Esta é a configuração escolhida escolhida por você:" -msgid "" -"Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "" -"O Pacman já está a rodando, aguarde no máximo até 10 minutos para terminar." +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "O Pacman já está a rodando, aguarde no máximo até 10 minutos para terminar." -msgid "" -"Pre-existing pacman lock never exited. Please clean up any existing pacman " -"sessions before using archinstall." -msgstr "" -"Pacman lock não terminou. Por favor, limpe as sessões " -"de pacman existentes antes de usar o archinstall." +msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." +msgstr "Pacman lock não terminou. Por favor, limpe as sessões de pacman existentes antes de usar o archinstall." msgid "Choose which optional additional repositories to enable" msgstr "Escolha quais repositórios adicionais opcionais ativar" @@ -656,12 +591,8 @@ msgstr "Deseja usar a compressão BTRFS?" msgid "Would you like to create a separate partition for /home?" msgstr "Deseja criar uma partição separada para /home?" -msgid "" -"The selected drives do not have the minimum capacity required for an " -"automatic suggestion\n" -msgstr "" -"As unidades selecionadas não tem a capacidade mínima para " -"sugestão automática\n" +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "As unidades selecionadas não tem a capacidade mínima para sugestão automática\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "Capacidade mínima para partição /home : {}GB\n" @@ -708,41 +639,24 @@ msgstr "Configuração manual" msgid "Mark/Unmark a partition as compressed (btrfs only)" msgstr "Marcar/desmarcar a partição como comprimida (apenas btrfs)" -msgid "" -"The password you are using seems to be weak, are you sure you want to use it?" -msgstr "" -"A senha que você está usando parece ser fraca, tem certeza que deseja utilizá-la?" +msgid "The password you are using seems to be weak, are you sure you want to use it?" +msgstr "A senha que você está usando parece ser fraca, tem certeza que deseja utilizá-la?" -msgid "" -"Provides a selection of desktop environments and tiling window managers, e." -"g. gnome, kde, sway" -msgstr "" -"Proporciona uma seleção de ambientes gráficos e gerenciadores de janela como por exemplo " -"gnome, kde, sway" +msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "Proporciona uma seleção de ambientes gráficos e gerenciadores de janela como por exemplo gnome, kde, sway" msgid "Select your desired desktop environment" msgstr "Selecione o ambiente gráfico desejado" -msgid "" -"A very basic installation that allows you to customize Arch Linux as you see " -"fit." -msgstr "" -"Uma instalação bem básica que permite a você customizar o Arch Linux " -"como desejar." +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "Uma instalação bem básica que permite a você customizar o Arch Linux como desejar." -msgid "" -"Provides a selection of various server packages to install and enable, e.g. " -"httpd, nginx, mariadb" -msgstr "" -"Proporciona uma seleção de diversos pacotes de servidor para instalar e habilitar " -"como por exemplo httpd, nginx, mariadb" +msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "Proporciona uma seleção de diversos pacotes de servidor para instalar e habilitar como por exemplo httpd, nginx, mariadb" -msgid "" -"Choose which servers to install, if none then a minimal installation wil be " -"done" -msgstr "" -"Selecione quais servidores instalar, se há nenhum uma instalação mínima será " -"feita" +#, fuzzy +msgid "Choose which servers to install, if none then a minimal installation will be done" +msgstr "Selecione quais servidores instalar, se há nenhum uma instalação mínima será feita" msgid "Installs a minimal system as well as xorg and graphics drivers." msgstr "Instala um sistema mínimo assim como xorg e drivers de vídeo." @@ -750,12 +664,8 @@ msgstr "Instala um sistema mínimo assim como xorg e drivers de vídeo." msgid "Press Enter to continue." msgstr "Tecle Enter para continuar." -msgid "" -"Would you like to chroot into the newly created installation and perform " -"post-installation configuration?" -msgstr "" -"Deseja fazer chroot para a nova instalação e realizar " -"configurações pós-instalação?" +msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" +msgstr "Deseja fazer chroot para a nova instalação e realizar configurações pós-instalação?" msgid "Are you sure you want to reset this setting?" msgstr "Tem certeza que desejar resetar essa configuração?" @@ -766,12 +676,8 @@ msgstr "Selecione uma ou mais unidades para usar e configurar\n" msgid "Any modifications to the existing setting will reset the disk layout!" msgstr "Quaisquer modificações para configurações existentes vão resetar o layout de disco!" -msgid "" -"If you reset the harddrive selection this will also reset the current disk " -"layout. Are you sure?" -msgstr "" -"Se você resetar a seleção de unidades isso também resetará o layout " -"da unidade atual. Tem certeza?" +msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" +msgstr "Se você resetar a seleção de unidades isso também resetará o layout da unidade atual. Tem certeza?" msgid "Save and exit" msgstr "Salvar e sair" @@ -817,15 +723,71 @@ msgstr "Adicionar: " msgid "Value: " msgstr "Valor: " -msgid "" -"You can skip selecting a drive and partitioning and use whatever drive-setup " -"is mounted at /mnt (experimental)" -msgstr "" -"Você pode ignorar a seleção de unidade e particionar seja lá o que estiver " -"montado em /mnt (experimental)" +msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" +msgstr "Você pode ignorar a seleção de unidade e particionar seja lá o que estiver montado em /mnt (experimental)" msgid "Select one of the disks or skip and use /mnt as default" msgstr "Selecione um dos discos ou ignore e use /mnt como padrão" msgid "Select which partitions to mark for formatting:" msgstr "Selecione quais partições marcar para formatar:" + +msgid "Use HSM to unlock encrypted drive" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "Size" +msgstr "" + +msgid "Free space" +msgstr "" + +msgid "Bus-type" +msgstr "" + +#, fuzzy +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Deve se especificar uma senha de root ou pelo menos 1 superusuário" + +#, fuzzy +msgid "Enter username (leave blank to skip): " +msgstr "Digite um nome de usuário para criar um usuário adicional (deixe em branco para pular): " + +msgid "The username you entered is invalid. Try again" +msgstr "" + +#, fuzzy +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "{} deve ser um superusuário (sudoer)?" + +#, fuzzy +msgid "Select which partitions to encrypt:" +msgstr "" +"{}\n" +"\n" +"Selecione qual partição marcar como encriptada" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +#, fuzzy +msgid "Add subvolume" +msgstr " Subvolume :{:16}" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "Deletar usuário" diff --git a/archinstall/locales/ru/LC_MESSAGES/base.mo b/archinstall/locales/ru/LC_MESSAGES/base.mo index 8c0dc224..0930b800 100644 Binary files a/archinstall/locales/ru/LC_MESSAGES/base.mo and b/archinstall/locales/ru/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ru/LC_MESSAGES/base.po b/archinstall/locales/ru/LC_MESSAGES/base.po index 5a6f440a..2a88a85f 100644 --- a/archinstall/locales/ru/LC_MESSAGES/base.po +++ b/archinstall/locales/ru/LC_MESSAGES/base.po @@ -508,8 +508,8 @@ msgstr "Редактировать" msgid "Delete" msgstr "Удалить" -msgid "Select an action for < {} >" -msgstr "Выберите действие для < {} >" +msgid "Select an action for '{}'" +msgstr "Выберите действие для '{}'" msgid "Copy to new key:" msgstr "Копировать в новый ключ:" diff --git a/archinstall/locales/sv/LC_MESSAGES/base.mo b/archinstall/locales/sv/LC_MESSAGES/base.mo index b7999fbb..f2798bd4 100644 Binary files a/archinstall/locales/sv/LC_MESSAGES/base.mo and b/archinstall/locales/sv/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/sv/LC_MESSAGES/base.po b/archinstall/locales/sv/LC_MESSAGES/base.po index 2c587af5..747e28a1 100644 --- a/archinstall/locales/sv/LC_MESSAGES/base.po +++ b/archinstall/locales/sv/LC_MESSAGES/base.po @@ -256,10 +256,12 @@ msgstr "Teckenuppsättning du vill använda" msgid "Drive(s)" msgstr "Hårddiskar" -msgid "Select disk layout" -msgstr "Välj hårddisk-layout" +#, fuzzy +msgid "Disk layout" +msgstr "Spara disk konfigurering" -msgid "Set encryption password" +#, fuzzy +msgid "Encryption password" msgstr "Välj ett krypterings-lösenord" msgid "Swap" @@ -268,7 +270,8 @@ msgstr "Swap" msgid "Bootloader" msgstr "Boot-loader" -msgid "root password" +#, fuzzy +msgid "Root password" msgstr "root-lösenord" msgid "Superuser account" @@ -426,8 +429,8 @@ msgstr "Editera" msgid "Delete" msgstr "Ta bort" -msgid "Select an action for < {} >" -msgstr "Välj vad du vill göra med < {} >" +msgid "Select an action for '{}'" +msgstr "Välj vad du vill göra med '{}'" msgid "Copy to new key:" msgstr "Kopiera till en ny nyckel:" @@ -660,7 +663,8 @@ msgstr "En väldigt minimal installation som möjliggör konfigurering av Arch L msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" msgstr "Erbjuder ett urval av olika server-packeteringar, t.ex. httpd, nginx och mariadb" -msgid "Choose which servers to install, if none then a minimal installation wil be done" +#, fuzzy +msgid "Choose which servers to install, if none then a minimal installation will be done" msgstr "Välj vilka servrar du vill installera, en minimal installation sker om du hoppar över detta" msgid "Installs a minimal system as well as xorg and graphics drivers." @@ -736,3 +740,66 @@ msgstr "Välj en disk eller hoppa över och använd /mnt/archinstall utan format msgid "Select which partitions to mark for formatting:" msgstr "Välj vilken partition som skall markeras för formatering:" + +msgid "Use HSM to unlock encrypted drive" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "Size" +msgstr "" + +msgid "Free space" +msgstr "" + +msgid "Bus-type" +msgstr "" + +#, fuzzy +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Antingen måste ett root-lösenord sättas eller 1 superanvändarkonto skapas" + +#, fuzzy +msgid "Enter username (leave blank to skip): " +msgstr "Mata in ett användarnamn för att skapa ytterligare användare (lämna tom för att hoppa över): " + +msgid "The username you entered is invalid. Try again" +msgstr "" + +#, fuzzy +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "Är detta en superanvändare (sudo-rättigheter)?" + +#, fuzzy +msgid "Select which partitions to encrypt:" +msgstr "" +"{}\n" +"\n" +"Välj vilken partition som skall markeras för kryptering" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +#, fuzzy +msgid "Add subvolume" +msgstr " Sub-volym :{:16}" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "Ta bort användare" + +#~ msgid "Select disk layout" +#~ msgstr "Välj hårddisk-layout" diff --git a/archinstall/locales/tr/LC_MESSAGES/base.mo b/archinstall/locales/tr/LC_MESSAGES/base.mo index 6eb05010..6a205da9 100644 Binary files a/archinstall/locales/tr/LC_MESSAGES/base.mo and b/archinstall/locales/tr/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/tr/LC_MESSAGES/base.po b/archinstall/locales/tr/LC_MESSAGES/base.po index 03125510..60475156 100644 --- a/archinstall/locales/tr/LC_MESSAGES/base.po +++ b/archinstall/locales/tr/LC_MESSAGES/base.po @@ -15,12 +15,8 @@ msgstr "" msgid "[!] A log file has been created here: {} {}" msgstr "[!] Burada bir günlük (log) dosyası oluşturuldu: {} {}" -msgid "" -" Please submit this issue (and file) to https://github.com/archlinux/" -"archinstall/issues" -msgstr "" -" Lütfen bu sorunu (ve dosyayı) https://github.com/archlinux/archinstall/" -"issues 'a bildirin" +msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr " Lütfen bu sorunu (ve dosyayı) https://github.com/archlinux/archinstall/issues 'a bildirin" msgid "Do you really want to abort?" msgstr "Gerçekten iptal etmek istiyor musunuz?" @@ -35,12 +31,10 @@ msgid "Desired hostname for the installation: " msgstr "Kurulum için tercih edilen bilgisayar ismi: " msgid "Username for required superuser with sudo privileges: " -msgstr "" -"Sudo yetkilerine sahip, gerekli bir süper kullanıcı için kullanıcı adı: " +msgstr "Sudo yetkilerine sahip, gerekli bir süper kullanıcı için kullanıcı adı: " msgid "Any additional users to install (leave blank for no users): " -msgstr "" -"Kurulum için ek kullanıcılar (ek kullanıcı istemiyorsanız boş bırakın): " +msgstr "Kurulum için ek kullanıcılar (ek kullanıcı istemiyorsanız boş bırakın): " msgid "Should this user be a superuser (sudoer)?" msgstr "Bu kullanıcı bir süper kullanıcı (sudoer) olmalı mı?" @@ -57,55 +51,35 @@ msgstr "Bir önyükleyici seçin" msgid "Choose an audio server" msgstr "Bir ses sunucusu seçin" -msgid "" -"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr " -"and optional profile packages are installed." -msgstr "" -"Sadece base, base-devel, linux, linux-firmware, efibootmgr ve tercihi profil " -"paketleri kuruldu." +msgid "Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed." +msgstr "Sadece base, base-devel, linux, linux-firmware, efibootmgr ve tercihi profil paketleri kuruldu." -msgid "" -"If you desire a web browser, such as firefox or chromium, you may specify it " -"in the following prompt." -msgstr "" -"Eğer bir internet tarayıcısı arzu ediyorsanız, Firefox ya da Chromium gibi, " -"sıra gelen ekranda belirtebilirsiniz." +msgid "If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt." +msgstr "Eğer bir internet tarayıcısı arzu ediyorsanız, Firefox ya da Chromium gibi, sıra gelen ekranda belirtebilirsiniz." -msgid "" -"Write additional packages to install (space separated, leave blank to skip): " -msgstr "" -"Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için boş bırakın): " +msgid "Write additional packages to install (space separated, leave blank to skip): " +msgstr "Kurulacak ek paketleri yazınız (boşlukla ayrılmış, geçmek için boş bırakın): " msgid "Copy ISO network configuration to installation" msgstr "Kuruluma ISO'dan ağ yapılandırmasını kopyala" -msgid "" -"Use NetworkManager (necessary to configure internet graphically in GNOME and " -"KDE)" -msgstr "" -"NetworkManager'ı kullan (GNOME ya da KDE'de interneti grafik olarak " -"yapılandırmak için gerekli)" +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "NetworkManager'ı kullan (GNOME ya da KDE'de interneti grafik olarak yapılandırmak için gerekli)" msgid "Select one network interface to configure" msgstr "Yapılandırmak için bir ağ arayüzü seçin" -msgid "" -"Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" -msgstr "" -"\"{}\"i yapılandırmak için bir yöntem seçin ya da varsayılan yöntemi \"{}\" " -"kullanmak için geçin" +msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "\"{}\"i yapılandırmak için bir yöntem seçin ya da varsayılan yöntemi \"{}\" kullanmak için geçin" msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " msgstr "{} için IP ve altağ girin (örnek: 192.168.0.5/24): " msgid "Enter your gateway (router) IP address or leave blank for none: " -msgstr "" -"Ağ geçidi (yönlendirici) IP adresini girin ya da kullanılmaması için boş " -"bırakın: " +msgstr "Ağ geçidi (yönlendirici) IP adresini girin ya da kullanılmaması için boş bırakın: " msgid "Enter your DNS servers (space separated, blank for none): " -msgstr "" -"DNS sunucularını girin (boşlukla ayrılmış, kullanılmaması için boş bırakın): " +msgstr "DNS sunucularını girin (boşlukla ayrılmış, kullanılmaması için boş bırakın): " msgid "Select which filesystem your main partition should use" msgstr "Ana disk bölümünde kullanması gereken dosya sistemini seçin" @@ -126,15 +100,11 @@ msgstr "Disk bölümü için arzu edilen bir dosya systemi tipi girin" msgid "Enter the start sector (percentage or block number, default: {}): " msgstr "Başlangıç kesimini girin (yüzde ya da blok numarası, varsayılan: {}): " -msgid "" -"Enter the end sector of the partition (percentage or block number, ex: {}): " -msgstr "" -"Disk bölümünün bitiş kesimini girin (yüzde ya da blok numarası, ör: {}): " +msgid "Enter the end sector of the partition (percentage or block number, ex: {}): " +msgstr "Disk bölümünün bitiş kesimini girin (yüzde ya da blok numarası, ör: {}): " msgid "{} contains queued partitions, this will remove those, are you sure?" -msgstr "" -"{} işlem sırasında bekleyen disk bölümleri bulunduruyor, bu onları " -"kaldıracak, emin misiniz?" +msgstr "{} işlem sırasında bekleyen disk bölümleri bulunduruyor, bu onları kaldıracak, emin misiniz?" msgid "" "{}\n" @@ -154,17 +124,11 @@ msgstr "" "\n" "Dizinden hangi disk bölümünün nereye mount (monte) edileceğini seçin" -msgid "" -" * Partition mount-points are relative to inside the installation, the boot " -"would be /boot as an example." -msgstr "" -" * Disk bölümü mount (monte) noktaları iç kurulumla ilişkilidir, örnek " -"olarak boot, /boot olacaktır." +msgid " * Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr " * Disk bölümü mount (monte) noktaları iç kurulumla ilişkilidir, örnek olarak boot, /boot olacaktır." msgid "Select where to mount partition (leave blank to remove mountpoint): " -msgstr "" -"Disk bölümünün nereye mount (monte) edileceğini seçin (mount noktasını " -"kaldırmak için boş bırakın): " +msgstr "Disk bölümünün nereye mount (monte) edileceğini seçin (mount noktasını kaldırmak için boş bırakın): " msgid "" "{}\n" @@ -209,25 +173,16 @@ msgid "Archinstall language" msgstr "Archinstall dili" msgid "Wipe all selected drives and use a best-effort default partition layout" -msgstr "" -"Bütün seçilmiş diskleri temizle ve elden gelen en iyi varsayılan disk bölümü " -"düzenini kullan" +msgstr "Bütün seçilmiş diskleri temizle ve elden gelen en iyi varsayılan disk bölümü düzenini kullan" -msgid "" -"Select what to do with each individual drive (followed by partition usage)" -msgstr "" -"Her bir disk ile ne yapılacağını seçin (disk bölümü kullanımı ile takip " -"edilir)" +msgid "Select what to do with each individual drive (followed by partition usage)" +msgstr "Her bir disk ile ne yapılacağını seçin (disk bölümü kullanımı ile takip edilir)" msgid "Select what you wish to do with the selected block devices" msgstr "Seçili blok cihazları ile ne yapmak istediğinizi seçin" -msgid "" -"This is a list of pre-programmed profiles, they might make it easier to " -"install things like desktop environments" -msgstr "" -"Bu ön-programlanmış profillerin bir listesidir, bunlar masaüstü ortamları " -"gibi şeyler kurmayı kolaylaştırabilir" +msgid "This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments" +msgstr "Bu ön-programlanmış profillerin bir listesidir, bunlar masaüstü ortamları gibi şeyler kurmayı kolaylaştırabilir" msgid "Select keyboard layout" msgstr "Klavye düzeni seçin" @@ -238,26 +193,14 @@ msgstr "Paketleri indirmek için bölgelerden birini seçin" msgid "Select one or more hard drives to use and configure" msgstr "Kullanmak ve yapılandırmak için bir veya daha fazla sabit disk seçin" -msgid "" -"For the best compatibility with your AMD hardware, you may want to use " -"either the all open-source or AMD / ATI options." -msgstr "" -"AMD donanımınızla en iyi uyumluluk için, tam açık-kaynak ya da AMD / ATI " -"ayarlarından birini kullanmak isteyebilirsiniz." +msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." +msgstr "AMD donanımınızla en iyi uyumluluk için, tam açık-kaynak ya da AMD / ATI ayarlarından birini kullanmak isteyebilirsiniz." -msgid "" -"For the best compatibility with your Intel hardware, you may want to use " -"either the all open-source or Intel options.\n" -msgstr "" -"Intel donanımınızla en iyi uyumluluk için, tam açık-kaynak ya da Intel " -"ayarlarından birini kullanmak isteyebilirsiniz.\n" +msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" +msgstr "Intel donanımınızla en iyi uyumluluk için, tam açık-kaynak ya da Intel ayarlarından birini kullanmak isteyebilirsiniz.\n" -msgid "" -"For the best compatibility with your Nvidia hardware, you may want to use " -"the Nvidia proprietary driver.\n" -msgstr "" -"Nvidia donanımınızla en iyi uyumluluk için, Nvidia patentli sürücüyü " -"kullanmak isteyebilirsiniz.\n" +msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" +msgstr "Nvidia donanımınızla en iyi uyumluluk için, Nvidia patentli sürücüyü kullanmak isteyebilirsiniz.\n" msgid "" "\n" @@ -266,16 +209,13 @@ msgid "" msgstr "" "\n" "\n" -"Bir grafik sürücüsü seçin ya da bütün açık-kaynak sürücüleri kurmak için boş " -"bırakın" +"Bir grafik sürücüsü seçin ya da bütün açık-kaynak sürücüleri kurmak için boş bırakın" msgid "All open-source (default)" msgstr "Tam açık-kaynak (varsayılan)" msgid "Choose which kernels to use or leave blank for default \"{}\"" -msgstr "" -"Hangi çekirdekleri kullanmak istediğinizi seçin ya da varsayılan \"{}\" için " -"boş bırakın" +msgstr "Hangi çekirdekleri kullanmak istediğinizi seçin ya da varsayılan \"{}\" için boş bırakın" # Burada "locale" hesaba özgü yani profil içinde yerel anlamına geliyor olabilir. Kurulumda ilgili metin ile karşılaşmadığımdan karar veremiyorum. msgid "Choose which locale language to use" @@ -295,12 +235,8 @@ msgstr "Aşağıdaki seçeneklerden bir ya da daha fazlasını seçin: " msgid "Adding partition...." msgstr "Disk bölümü ekleniyor…." -msgid "" -"You need to enter a valid fs-type in order to continue. See `man parted` for " -"valid fs-type's." -msgstr "" -"Devam etmek için geçerli bir ds-tipi (fs-type) girmeniz gerekiyor. Geçerli " -"ds-tiplerini görmek için `man parted`a bakın." +msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." +msgstr "Devam etmek için geçerli bir ds-tipi (fs-type) girmeniz gerekiyor. Geçerli ds-tiplerini görmek için `man parted`a bakın." msgid "Error: Listing profiles on URL \"{}\" resulted in:" msgstr "Hata: Bağlantı \"{}\"daki profilleri listeleme şöyle sonuçlandı:" @@ -394,17 +330,13 @@ msgid "Assign mount-point for a partition" msgstr "Bir disk bölümü için mount (monte) noktası ata" msgid "Mark/Unmark a partition to be formatted (wipes data)" -msgstr "" -"Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (veriyi " -"temizler)" +msgstr "Bir disk bölümünü biçimlendirilmek üzere işaretle/işareti kaldır (veriyi temizler)" msgid "Mark/Unmark a partition as encrypted" msgstr "Bir disk bölümünü şifrelenmiş olarak işaretle/işareti kaldır" msgid "Mark/Unmark a partition as bootable (automatic for /boot)" -msgstr "" -"Bir disk bölümünü boot edilebilir olarak işaretle/işareti kaldır (/boot için " -"otomatik)" +msgstr "Bir disk bölümünü boot edilebilir olarak işaretle/işareti kaldır (/boot için otomatik)" msgid "Set desired filesystem for a partition" msgstr "Bir disk bölümü için arzu edilen dosya sistemini ayarla" @@ -448,48 +380,36 @@ msgid "Create a required super-user with sudo privileges: " msgstr "Gerekli bir sudo yetkilerine sahip süper-kullanıcı oluşturun: " msgid "Enter root password (leave blank to disable root): " -msgstr "" -"Root (kök) şifresi girin (root'u hizmet dışı bırakmak için boş bırakın): " +msgstr "Root (kök) şifresi girin (root'u hizmet dışı bırakmak için boş bırakın): " msgid "Password for user \"{}\": " msgstr "Kullanıcı \"{}\" için şifre: " -msgid "" -"Verifying that additional packages exist (this might take a few seconds)" +msgid "Verifying that additional packages exist (this might take a few seconds)" msgstr "Ek paketlerin varlığı doğrulanıyor (bu bir kaç saniye alabilir)" -msgid "" -"Would you like to use automatic time synchronization (NTP) with the default " -"time servers?\n" -msgstr "" -"Varsayılan zaman sunucularıyla otomatik zaman eşzamanlamasını (NTP) " -"kullanmak ister misiniz?\n" +msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" +msgstr "Varsayılan zaman sunucularıyla otomatik zaman eşzamanlamasını (NTP) kullanmak ister misiniz?\n" msgid "" -"Hardware time and other post-configuration steps might be required in order " -"for NTP to work.\n" +"Hardware time and other post-configuration steps might be required in order for NTP to work.\n" "For more information, please check the Arch wiki" msgstr "" -"NTP'nin çalışması için donanım zamanı ve diğer yapılandırma sonrası adımlar " -"gerekebilir.\n" +"NTP'nin çalışması için donanım zamanı ve diğer yapılandırma sonrası adımlar gerekebilir.\n" "Daha fazla bilgi için, lütfen Arch wikiye göz atın" msgid "Enter a username to create an additional user (leave blank to skip): " -msgstr "" -"Ek kullanıcı oluşturmak için bir kullanıcı adı girin (geçmek için boş " -"bırakın): " +msgstr "Ek kullanıcı oluşturmak için bir kullanıcı adı girin (geçmek için boş bırakın): " msgid "Use ESC to skip\n" msgstr "Geçmek için ESC'yi kullanın\n" msgid "" "\n" -" Choose an object from the list, and select one of the available actions for " -"it to execute" +" Choose an object from the list, and select one of the available actions for it to execute" msgstr "" "\n" -"Listeden bir obje seçin ve çalıştırılmak üzere mevcut eylemlerden birini " -"seçin" +"Listeden bir obje seçin ve çalıştırılmak üzere mevcut eylemlerden birini seçin" msgid "Cancel" msgstr "İptal et" @@ -509,8 +429,8 @@ msgstr "Düzenle" msgid "Delete" msgstr "Sil" -msgid "Select an action for < {} >" -msgstr "< {} > için bir eylem seçin" +msgid "Select an action for '{}'" +msgstr "'{}' için bir eylem seçin" msgid "Copy to new key:" msgstr "Yeni anahtara kopyala:" @@ -525,22 +445,14 @@ msgstr "" "\n" "Bu sizin seçilmiş yapılandırmanız:" -msgid "" -"Pacman is already running, waiting maximum 10 minutes for it to terminate." -msgstr "" -"Pacman hâlihazırda çalışıyor, işlemin sonlandırılması için en fazla 10 " -"dakika beklenecek." +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "Pacman hâlihazırda çalışıyor, işlemin sonlandırılması için en fazla 10 dakika beklenecek." -msgid "" -"Pre-existing pacman lock never exited. Please clean up any existing pacman " -"sessions before using archinstall." -msgstr "" -"Önceden var olan pacman kilidi çıkış yapmadı. Lütfen archinstall'ı " -"kullanmadan once mevcut olan pacman oturumlarını temizleyin." +msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." +msgstr "Önceden var olan pacman kilidi çıkış yapmadı. Lütfen archinstall'ı kullanmadan once mevcut olan pacman oturumlarını temizleyin." msgid "Choose which optional additional repositories to enable" -msgstr "" -"Hangi tercihi ek repositorylerin (depoların) aktifleştirileceğini seçin" +msgstr "Hangi tercihi ek repositorylerin (depoların) aktifleştirileceğini seçin" msgid "Add a user" msgstr "Kullanıcı ekle" @@ -628,9 +540,7 @@ msgid "Missing configurations:\n" msgstr "Eksik yapılandırmalar:\n" msgid "Either root-password or at least 1 superuser must be specified" -msgstr "" -"Root (kök) şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek " -"zorunda" +msgstr "Root (kök) şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda" msgid "Manage superuser accounts: " msgstr "Süper-kullanıcı hesaplarını yönet: " @@ -690,12 +600,8 @@ msgstr "BTRFS sıkıştırmasını kullanmak ister misiniz?" msgid "Would you like to create a separate partition for /home?" msgstr "/home dizini için ayrı bir disk bölümü oluşturmak ister misiniz?" -msgid "" -"The selected drives do not have the minimum capacity required for an " -"automatic suggestion\n" -msgstr "" -"Seçilmiş diskler otomatik öneriler için gerekli minimum kapasiteye sahip " -"değil\n" +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "Seçilmiş diskler otomatik öneriler için gerekli minimum kapasiteye sahip değil\n" msgid "Minimum capacity for /home partition: {}GB\n" msgstr "/home disk bölümü için minimum kapasite: {}GB\n" @@ -740,43 +646,26 @@ msgid "Manual configuration" msgstr "Manuel yapılandırma" msgid "Mark/Unmark a partition as compressed (btrfs only)" -msgstr "" -"Bir disk bölümünü sıkıştırılmış olarak işaretle/işareti kaldır (sadece btrfs)" +msgstr "Bir disk bölümünü sıkıştırılmış olarak işaretle/işareti kaldır (sadece btrfs)" -msgid "" -"The password you are using seems to be weak, are you sure you want to use it?" -msgstr "" -"Kullandığınız şifre zayıf gözüküyor, kullanmak istediğinize emin misiniz?" +msgid "The password you are using seems to be weak, are you sure you want to use it?" +msgstr "Kullandığınız şifre zayıf gözüküyor, kullanmak istediğinize emin misiniz?" -msgid "" -"Provides a selection of desktop environments and tiling window managers, e." -"g. gnome, kde, sway" -msgstr "" -"Masaüstü ortamları ve otomatik hizalamalı pencere yöneticilerine bir seçenek " -"sunar, örnek olarak gnome, kde, sway" +msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "Masaüstü ortamları ve otomatik hizalamalı pencere yöneticilerine bir seçenek sunar, örnek olarak gnome, kde, sway" msgid "Select your desired desktop environment" msgstr "Arzu ettiğiniz masaüstü ortamını seçin" -msgid "" -"A very basic installation that allows you to customize Arch Linux as you see " -"fit." -msgstr "" -"Arch Linux'u istediğin gibi kişiselleştirmeni sağlayan çok basit bir kurulum." +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "Arch Linux'u istediğin gibi kişiselleştirmeni sağlayan çok basit bir kurulum." -msgid "" -"Provides a selection of various server packages to install and enable, e.g. " -"httpd, nginx, mariadb" -msgstr "" -"Kurmak ve aktif etmek için bazı seçilmiş çeşitli sunucu paketleri sunar, " -"örnek olarak httpd, nginx, mariadb" +msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "Kurmak ve aktif etmek için bazı seçilmiş çeşitli sunucu paketleri sunar, örnek olarak httpd, nginx, mariadb" -msgid "" -"Choose which servers to install, if none then a minimal installation wil be " -"done" -msgstr "" -"Hangi sunucuların kurulacağını seçin, eğer hiçbiri seçilmezse minimal bir " -"kurulum gerçekleştirilecektir" +#, fuzzy +msgid "Choose which servers to install, if none then a minimal installation will be done" +msgstr "Hangi sunucuların kurulacağını seçin, eğer hiçbiri seçilmezse minimal bir kurulum gerçekleştirilecektir" msgid "Installs a minimal system as well as xorg and graphics drivers." msgstr "Xorg ve grafik sürücüleri ile minimal bir sistem kurar." @@ -784,29 +673,20 @@ msgstr "Xorg ve grafik sürücüleri ile minimal bir sistem kurar." msgid "Press Enter to continue." msgstr "Devam etmek için Enter'a bas." -msgid "" -"Would you like to chroot into the newly created installation and perform " -"post-installation configuration?" -msgstr "" -"Yeni oluşturulmuş kuruluma chroot etmek ve kurulum sonrası konfigürasyon " -"gerçekleştirmek ister misiniz?" +msgid "Would you like to chroot into the newly created installation and perform post-installation configuration?" +msgstr "Yeni oluşturulmuş kuruluma chroot etmek ve kurulum sonrası konfigürasyon gerçekleştirmek ister misiniz?" msgid "Are you sure you want to reset this setting?" msgstr "Bu ayarı sıfırlamak istediğinize emin misiniz?" msgid "Select one or more hard drives to use and configure\n" -msgstr "" -"Kullanmak ve yapılandırmak için bir ya da daha fazla sabit disk seçin\n" +msgstr "Kullanmak ve yapılandırmak için bir ya da daha fazla sabit disk seçin\n" msgid "Any modifications to the existing setting will reset the disk layout!" msgstr "Mevcut ayara herhangi bir modifikasyon disk şemasını sıfırlayacaktır!" -msgid "" -"If you reset the harddrive selection this will also reset the current disk " -"layout. Are you sure?" -msgstr "" -"Eğer sabit disk seçimini sıfırlarsanız bu ayrıca mevcut disk şemasını da " -"sıfırlayacaktır. Emin misiniz?" +msgid "If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" +msgstr "Eğer sabit disk seçimini sıfırlarsanız bu ayrıca mevcut disk şemasını da sıfırlayacaktır. Emin misiniz?" msgid "Save and exit" msgstr "Kaydet ve çık" @@ -816,8 +696,7 @@ msgid "" "contains queued partitions, this will remove those, are you sure?" msgstr "" "{}\n" -"işlem sırasında bekleyen disk bölümleri bulunduruyor, bu onları kaldıracak, " -"emin misiniz?" +"işlem sırasında bekleyen disk bölümleri bulunduruyor, bu onları kaldıracak, emin misiniz?" msgid "No audio server" msgstr "Ses sunucusu yok" @@ -853,17 +732,11 @@ msgstr "Ekle: " msgid "Value: " msgstr "Değer: " -msgid "" -"You can skip selecting a drive and partitioning and use whatever drive-setup " -"is mounted at /mnt (experimental)" -msgstr "" -"Disk seçmeyi ve disk bölümlendirmeyi geçebilir ve disk kurulumu /mnt " -"dizininde neye mount (monte) ediliyse onu kullanabilirsiniz (deneysel)" +msgid "You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)" +msgstr "Disk seçmeyi ve disk bölümlendirmeyi geçebilir ve disk kurulumu /mnt dizininde neye mount (monte) ediliyse onu kullanabilirsiniz (deneysel)" msgid "Select one of the disks or skip and use /mnt as default" -msgstr "" -"Disklerden birini seçin ya da geçin ve /mnt dizinini varsayılan olarak " -"kullanın" +msgstr "Disklerden birini seçin ya da geçin ve /mnt dizinini varsayılan olarak kullanın" msgid "Select which partitions to mark for formatting:" msgstr "Biçimlendirme için işaretlenecek disk bölümünü seçin:" @@ -882,3 +755,48 @@ msgstr "Boş alan" msgid "Bus-type" msgstr "Bus(veri yolu)-tipi" + +#, fuzzy +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Root (kök) şifresi ya da en azından 1 adet süper-kullanıcı belirtilmek zorunda" + +#, fuzzy +msgid "Enter username (leave blank to skip): " +msgstr "Ek kullanıcı oluşturmak için bir kullanıcı adı girin (geçmek için boş bırakın): " + +msgid "The username you entered is invalid. Try again" +msgstr "" + +#, fuzzy +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "{} bir süper kullanıcı (sudoer) olmalı mı?" + +#, fuzzy +msgid "Select which partitions to encrypt:" +msgstr "" +"{}\n" +"\n" +"Hangi disk bölümünün şifrelenmiş olarak işaretleneceğini seçin" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +#, fuzzy +msgid "Add subvolume" +msgstr " alt disk bölümü :{:16}" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "Kullanıcı Sil" diff --git a/archinstall/locales/ur/LC_MESSAGES/base.mo b/archinstall/locales/ur/LC_MESSAGES/base.mo index dad80f9d..44e38ad4 100644 Binary files a/archinstall/locales/ur/LC_MESSAGES/base.mo and b/archinstall/locales/ur/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ur/LC_MESSAGES/base.po b/archinstall/locales/ur/LC_MESSAGES/base.po index 9baa850c..710c08d7 100644 --- a/archinstall/locales/ur/LC_MESSAGES/base.po +++ b/archinstall/locales/ur/LC_MESSAGES/base.po @@ -256,10 +256,12 @@ msgstr "مقامی انکوڈنگ" msgid "Drive(s)" msgstr "ہارڈ ڈرائیوز" -msgid "Select disk layout" -msgstr "ڈسک لے آؤٹ کو منتخب کریں" +#, fuzzy +msgid "Disk layout" +msgstr "ڈسک لے آؤٹ کو محفوظ کریں" -msgid "Set encryption password" +#, fuzzy +msgid "Encryption password" msgstr "انکرپشن پاس ورڈ سیٹ کریں" msgid "Swap" @@ -268,7 +270,8 @@ msgstr "سواپ" msgid "Bootloader" msgstr "بوٹ لوڈر" -msgid "root password" +#, fuzzy +msgid "Root password" msgstr "روٹ پاس ورڈ" msgid "Superuser account" @@ -424,8 +427,9 @@ msgstr "ترمیم" msgid "Delete" msgstr "حذف" -msgid "Select an action for < {} >" -msgstr "< {} > کے لیے ایک عمل منتخب کریں" +#, fuzzy +msgid "Select an action for '{}'" +msgstr "'{}' کے لیے ایک عمل منتخب کریں" msgid "Copy to new key:" msgstr "نئی کلید میں کاپی کریں:" @@ -672,7 +676,7 @@ msgstr "" msgid "Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" msgstr "" -msgid "Choose which servers to install, if none then a minimal installation wil be done" +msgid "Choose which servers to install, if none then a minimal installation will be done" msgstr "" msgid "Installs a minimal system as well as xorg and graphics drivers." @@ -759,6 +763,68 @@ msgstr "" "\n" "فارمیٹنگ کے لیے کس پارٹیشن کو ماسک کرنا ہے" +msgid "Use HSM to unlock encrypted drive" +msgstr "" + +msgid "Device" +msgstr "" + +msgid "Size" +msgstr "" + +msgid "Free space" +msgstr "" + +msgid "Bus-type" +msgstr "" + +#, fuzzy +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "روٹ پاس ورڈ یا کم از کم ۱ سپر یوزر کی وضاحت ہونی چاہیے" + +#, fuzzy +msgid "Enter username (leave blank to skip): " +msgstr "ایک اضافی صارف بنانے کے لیے صارف نام درج کریں (چھوڑنے کے لیے خالی چھوڑیں):" + +msgid "The username you entered is invalid. Try again" +msgstr "" + +#, fuzzy +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "کیا {} کو سپر یوزر (sudoer) ہونا چاہیے؟" + +#, fuzzy +msgid "Select which partitions to encrypt:" +msgstr "" +"{}\n" +"\n" +"منتخب کریں کہ کس پارٹیشن کو انکرپٹڈ یا خفیہ رکھنا ہے" + +msgid "very weak" +msgstr "" + +msgid "weak" +msgstr "" + +msgid "moderate" +msgstr "" + +msgid "strong" +msgstr "" + +msgid "Add subvolume" +msgstr "" + +msgid "Edit subvolume" +msgstr "" + +#, fuzzy +msgid "Delete subvolume" +msgstr "صارف کو حذف کریں" + +#~ msgid "Select disk layout" +#~ msgstr "ڈسک لے آؤٹ کو منتخب کریں" + #~ msgid "Add :" #~ msgstr "شامل:" -- cgit v1.2.3-70-g09d2 From 5c3c1312a49e1c110d4c5825fbb8242868544900 Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Tue, 14 Jun 2022 22:38:39 +1000 Subject: Update list manager (#1331) * Rework partition management * Update * Update list manager * Update * Update Co-authored-by: Daniel Girtler --- archinstall/lib/disk/partition.py | 35 ++- archinstall/lib/menu/list_manager.py | 262 +++------------------ .../lib/user_interaction/manage_users_conf.py | 22 +- archinstall/lib/user_interaction/network_conf.py | 20 +- .../lib/user_interaction/partitioning_conf.py | 14 +- .../lib/user_interaction/subvolume_config.py | 32 ++- 6 files changed, 109 insertions(+), 276 deletions(-) (limited to 'archinstall/lib/user_interaction/subvolume_config.py') diff --git a/archinstall/lib/disk/partition.py b/archinstall/lib/disk/partition.py index 062c79ab..17c24d57 100644 --- a/archinstall/lib/disk/partition.py +++ b/archinstall/lib/disk/partition.py @@ -17,14 +17,16 @@ from .btrfs.btrfs_helpers import subvolume_info_from_path from .btrfs.btrfssubvolumeinfo import BtrfsSubvolumeInfo class Partition: - def __init__(self, + def __init__( + self, path: str, block_device: BlockDevice, part_id :Optional[str] = None, filesystem :Optional[str] = None, mountpoint :Optional[str] = None, encrypted :bool = False, - autodetect_filesystem :bool = True): + autodetect_filesystem :bool = True + ): if not part_id: part_id = os.path.basename(path) @@ -76,7 +78,30 @@ class Partition: else: return f'Partition(path={self.path}, size={self.size}, PARTUUID={self._safe_uuid}, fs={self.filesystem}{mount_repr})' + def as_json(self) -> Dict[str, Any]: + """ + this is used for the table representation of the partition (see FormattedOutput) + """ + partition_info = { + 'type': 'primary', + 'PARTUUID': self._safe_uuid, + 'wipe': self.allow_formatting, + 'boot': self.boot, + 'ESP': self.boot, + 'mountpoint': self.target_mountpoint, + 'encrypted': self._encrypted, + 'start': self.start, + 'size': self.end, + 'filesystem': self.filesystem_type + } + + return partition_info + def __dump__(self) -> Dict[str, Any]: + # TODO remove this in favour of as_json + + log(get_filesystem_type(self.path)) + return { 'type': 'primary', 'PARTUUID': self._safe_uuid, @@ -88,10 +113,14 @@ class Partition: 'start': self.start, 'size': self.end, 'filesystem': { - 'format': get_filesystem_type(self.path) + 'format': self.filesystem_type } } + @property + def filesystem_type(self) -> Optional[str]: + return get_filesystem_type(self.path) + @property def mountpoint(self) -> Optional[str]: try: diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py index fe491caa..e7a9c2ac 100644 --- a/archinstall/lib/menu/list_manager.py +++ b/archinstall/lib/menu/list_manager.py @@ -1,94 +1,7 @@ -#!/usr/bin/python -""" -# Purpose -ListManager is a widget based on `menu` which allows the handling of repetitive operations in a list. -Imagine you have a list and want to add/copy/edit/delete their elements. With this widget you will be shown the list -``` -Vamos alla - -Use ESC to skip - - -> uno : 1 -dos : 2 -tres : 3 -cuatro : 4 -==> -Confirm and exit -Cancel -(Press "/" to search) -``` -Once you select one of the elements of the list, you will be promted with the action to be done to the selected element -``` - -uno : 1 -dos : 2 -> tres : 3 -cuatro : 4 -==> -Confirm and exit -Cancel -(Press "/" to search) - -Select an action for < {'tres': 3} > - - -Add -Copy -Edit -Delete -> Cancel -``` -You execute the action for this element (which might or not involve user interaction) and return to the list main page -till you call one of the options `confirm and exit` which returns the modified list or `cancel` which returns the original list unchanged. -If the list is empty one action can be defined as default (usually Add). We call it **null_action** -YOu can also define a **default_action** which will appear below the separator, not tied to any element of the list. Barring explicit definition, default_action will be the null_action -``` -==> -Add -Confirm and exit -Cancel -(Press "/" to search) -``` -The default implementation can handle simple lists and a key:value dictionary. The default actions are the shown above. -A sample of basic usage is included at the end of the source. - -More sophisticaded uses can be achieved by -* changing the action list and the null_action during initialization -``` - opciones = ListManager('Vamos alla',opciones,[str(_('Add')),str(_('Delete'))],_('Add')).run() -``` -* And using following methods to overwrite/define user actions and other details: -* * `reformat`. To change the appearance of the list elements -* * `action_list`. To modify the content of the action list once an element is defined. F.i. to avoid Delete to appear for certain elements, or to add/modify action based in the value of the element. -* * `exec_action` which contains the actual code to be executed when an action is selected - -The contents in the base class of this methods serve for a very basic usage, and are to be taken as samples. Thus the best use of this class would be to subclass in your code - -``` - class ObjectList(archinstall.ListManager): - def __init__(prompt,list): - self.ObjectAction = [... list of actions ...] - self.ObjectNullAction = one ObjectAction - super().__init__(prompt,list,ObjectActions,ObjectNullAction) - def reformat(self): - ... beautfy the output of the list - def action_list(self): - ... if you need some changes to the action list based on self.target - def exec_action(self): - if self.action == self.ObjectAction[0]: - performFirstAction(self.target, ...) - - ... - resultList = ObjectList(prompt,originallist).run() -``` - -""" import copy from os import system -from typing import Union, Any, TYPE_CHECKING, Dict, Optional, Tuple, List +from typing import Any, TYPE_CHECKING, Dict, Optional, Tuple, List -from .text_input import TextInput from .menu import Menu if TYPE_CHECKING: @@ -98,58 +11,38 @@ if TYPE_CHECKING: class ListManager: def __init__( self, - prompt :str, - base_list :Union[list,dict] , - base_actions :list = None, - null_action :str = None, - default_action :Union[str,list] = None, - header :Union[str,list] = None): + prompt: str, + entries: List[Any], + base_actions: List[str], + sub_menu_actions: List[str] + ): """ - param :prompt Text which will appear at the header + :param prompt: Text which will appear at the header type param: string | DeferredTranslation - param :base:_list list/dict of option to be shown / mainpulated - type param: list | dict - - param base_actions an alternate list of actions to the items of the object + :param entries: list/dict of option to be shown / manipulated type param: list - param: null_action action which will be taken (if any) when base_list is empty - type param: string - - param: default_action action which will be presented at the bottom of the list. Shouldn't need a target. If not present, null_action is set there. - Both Null and Default actions can be defined outside the base_actions list, as long as they are launched in exec_action - type param: string or list + :param base_actions: list of actions that is displayed in the main list manager, + usually global actions such as 'Add...' + type param: list - param: header one or more header lines for the list - type param: string or list + :param sub_menu_actions: list of actions available for a chosen entry + type param: list """ + self._original_data = copy.deepcopy(entries) + self._data = copy.deepcopy(entries) explainer = str(_('\n Choose an object from the list, and select one of the available actions for it to execute')) self._prompt = prompt + explainer if prompt else explainer - self._null_action = str(null_action) if null_action else None - - if not default_action: - self._default_action = [self._null_action] - elif isinstance(default_action,(list,tuple)): - self._default_action = default_action - else: - self._default_action = [str(default_action)] - - self._header = header if header else '' - self._cancel_action = str(_('Cancel')) - self._confirm_action = str(_('Confirm and exit')) self._separator = '' - self._bottom_list = [self._confirm_action, self._cancel_action] - self._bottom_item = [self._cancel_action] - self._base_actions = base_actions if base_actions else [str(_('Add')), str(_('Copy')), str(_('Edit')), str(_('Delete'))] - self._original_data = copy.deepcopy(base_list) - self._data = copy.deepcopy(base_list) # as refs, changes are immediate + self._confirm_action = str(_('Confirm and exit')) + self._cancel_action = str(_('Cancel')) - # default values for the null case - self.target: Optional[Any] = None - self.action = self._null_action + self._terminate_actions = [self._confirm_action, self._cancel_action] + self._base_actions = base_actions + self._sub_menu_actions = sub_menu_actions def run(self): while True: @@ -158,11 +51,6 @@ class ListManager: data_formatted = self.reformat(self._data) options, header = self._prepare_selection(data_formatted) - menu_header = self._header - - if header: - menu_header += header - system('clear') choice = Menu( @@ -177,27 +65,13 @@ class ListManager: show_search_hint=False ).run() - if not choice.value or choice.value in self._bottom_list: - self.action = choice + if choice.value in self._base_actions: + self._data = self.handle_action(choice.value, None, self._data) + elif choice.value in self._terminate_actions: break - - if choice.value and choice.value in self._default_action: - self.action = choice.value - self.target = None - self._data = self.exec_action(self._data) - continue - - if isinstance(self._data, dict): - data_key = data_formatted[choice.value] - key = self._data[data_key] - self.target = {data_key: key} - elif isinstance(self._data, list): - self.target = [d for d in self._data if d == data_formatted[choice.value]][0] - else: - self.target = self._data[data_formatted[choice.value]] - - # Possible enhancement. If run_actions returns false a message line indicating the failure - self.run_actions(choice.value) + else: # an entry of the existing selection was choosen + selected_entry = data_formatted[choice.value] + self._run_actions_on_entry(selected_entry) if choice.value == self._cancel_action: return self._original_data # return the original list @@ -217,16 +91,14 @@ class ListManager: if len(options) > 0: options.append(self._separator) - if self._default_action: - # done only for mypy -> todo fix the self._default_action declaration - options += [action for action in self._default_action if action] + options += self._base_actions + options += self._terminate_actions - options += self._bottom_list return options, header - def run_actions(self,prompt_data=''): - options = self.action_list() + self._bottom_item - display_value = self.selected_action_display(self.target) if self.target else prompt_data + def _run_actions_on_entry(self, entry: Any): + options = self._sub_menu_actions + [self._cancel_action] + display_value = self.selected_action_display(entry) prompt = _("Select an action for '{}'").format(display_value) @@ -236,14 +108,11 @@ class ListManager: sort=False, clear_screen=False, clear_menu_on_exit=False, - preset_values=self._bottom_item, show_search_hint=False ).run() - self.action = choice.value - - if self.action and self.action != self._cancel_action: - self._data = self.exec_action(self._data) + if choice.value and choice.value != self._cancel_action: + self._data = self.handle_action(choice.value, entry, self._data) def selected_action_display(self, selection: Any) -> str: # this will return the value to be displayed in the @@ -256,64 +125,7 @@ class ListManager: # in the header value (useful when displaying tables) raise NotImplementedError('Please implement me in the child class') - def action_list(self): - """ - can define alternate action list or customize the list for each item. - Executed after any item is selected, contained in self.target - """ - active_entry = self.target if self.target else None - - if active_entry is None: - return [self._base_actions[0]] - else: - return self._base_actions[1:] - - def exec_action(self, data: Any): - """ - what's executed one an item (self.target) and one action (self.action) is selected. - Should be overwritten by the user - The result is expected to update self._data in this routine, else it is ignored - The basic code is useful for simple lists and dictionaries (key:value pairs, both strings) - """ - # TODO guarantee unicity - if isinstance(self._data,list): - if self.action == str(_('Add')): - self.target = TextInput(_('Add: '),None).run() - self._data.append(self.target) - if self.action == str(_('Copy')): - while True: - target = TextInput(_('Copy to: '),self.target).run() - if target != self.target: - self._data.append(self.target) - break - elif self.action == str(_('Edit')): - tgt = self.target - idx = self._data.index(self.target) - result = TextInput(_('Edit: '),tgt).run() - self._data[idx] = result - elif self.action == str(_('Delete')): - del self._data[self._data.index(self.target)] - elif isinstance(self._data,dict): - # allows overwrites - if self.target: - origkey,origval = list(self.target.items())[0] - else: - origkey = None - origval = None - if self.action == str(_('Add')): - key = TextInput(_('Key: '),None).run() - value = TextInput(_('Value: '),None).run() - self._data[key] = value - if self.action == str(_('Copy')): - while True: - key = TextInput(_('Copy to new key:'),origkey).run() - if key != origkey: - self._data[key] = origval - break - elif self.action == str(_('Edit')): - value = TextInput(_('Edit {}: ').format(origkey), origval).run() - self._data[origkey] = value - elif self.action == str(_('Delete')): - del self._data[origkey] - - return self._data + def handle_action(self, action: Any, entry: Optional[Any], data: List[Any]) -> List[Any]: + # this function is called when a base action or + # a specific action for an entry is triggered + raise NotImplementedError('Please implement me in the child class') diff --git a/archinstall/lib/user_interaction/manage_users_conf.py b/archinstall/lib/user_interaction/manage_users_conf.py index 33c31342..a97328c2 100644 --- a/archinstall/lib/user_interaction/manage_users_conf.py +++ b/archinstall/lib/user_interaction/manage_users_conf.py @@ -25,7 +25,7 @@ class UserList(ListManager): str(_('Promote/Demote user')), str(_('Delete User')) ] - super().__init__(prompt, lusers, self._actions, self._actions[0]) + super().__init__(prompt, lusers, [self._actions[0]], self._actions[1:]) def reformat(self, data: List[User]) -> Dict[str, User]: table = FormattedOutput.as_table(data) @@ -45,27 +45,25 @@ class UserList(ListManager): def selected_action_display(self, user: User) -> str: return user.username - def exec_action(self, data: List[User]) -> List[User]: - active_user = self.target if self.target else None - - if self.action == self._actions[0]: # add + def handle_action(self, action: str, entry: Optional[User], data: List[User]) -> List[User]: + if action == self._actions[0]: # add new_user = self._add_user() if new_user is not None: # in case a user with the same username as an existing user # was created we'll replace the existing one data = [d for d in data if d.username != new_user.username] data += [new_user] - elif self.action == self._actions[1]: # change password - prompt = str(_('Password for user "{}": ').format(active_user.username)) + elif action == self._actions[1]: # change password + prompt = str(_('Password for user "{}": ').format(entry.username)) new_password = get_password(prompt=prompt) if new_password: - user = next(filter(lambda x: x == active_user, data), 1) + user = next(filter(lambda x: x == entry, data), 1) user.password = new_password - elif self.action == self._actions[2]: # promote/demote - user = next(filter(lambda x: x == active_user, data), 1) + elif action == self._actions[2]: # promote/demote + user = next(filter(lambda x: x == entry, data), 1) user.sudo = False if user.sudo else True - elif self.action == self._actions[3]: # delete - data = [d for d in data if d != active_user] + elif action == self._actions[3]: # delete + data = [d for d in data if d != entry] return data diff --git a/archinstall/lib/user_interaction/network_conf.py b/archinstall/lib/user_interaction/network_conf.py index 4f22b790..1908603e 100644 --- a/archinstall/lib/user_interaction/network_conf.py +++ b/archinstall/lib/user_interaction/network_conf.py @@ -29,7 +29,7 @@ class ManualNetworkConfig(ListManager): str(_('Delete interface')) ] - super().__init__(prompt, ifaces, self._actions, self._actions[0]) + super().__init__(prompt, ifaces, [self._actions[0]], self._actions[1:]) def reformat(self, data: List[NetworkConfiguration]) -> Dict[str, Optional[NetworkConfiguration]]: table = FormattedOutput.as_table(data) @@ -49,21 +49,19 @@ class ManualNetworkConfig(ListManager): def selected_action_display(self, iface: NetworkConfiguration) -> str: return iface.iface if iface.iface else '' - def exec_action(self, data: List[NetworkConfiguration]): - active_iface: Optional[NetworkConfiguration] = self.target if self.target else None - - if self.action == self._actions[0]: # add + def handle_action(self, action: str, entry: Optional[NetworkConfiguration], data: List[NetworkConfiguration]): + if action == self._actions[0]: # add iface_name = self._select_iface(data) if iface_name: iface = NetworkConfiguration(NicType.MANUAL, iface=iface_name) iface = self._edit_iface(iface) data += [iface] - elif active_iface: - if self.action == self._actions[1]: # edit interface - data = [d for d in data if d.iface != active_iface.iface] - data.append(self._edit_iface(active_iface)) - elif self.action == self._actions[2]: # delete - data = [d for d in data if d != active_iface] + elif entry: + if action == self._actions[1]: # edit interface + data = [d for d in data if d.iface != entry.iface] + data.append(self._edit_iface(entry)) + elif action == self._actions[2]: # delete + data = [d for d in data if d != entry] return data diff --git a/archinstall/lib/user_interaction/partitioning_conf.py b/archinstall/lib/user_interaction/partitioning_conf.py index 0f4784b6..8bf76121 100644 --- a/archinstall/lib/user_interaction/partitioning_conf.py +++ b/archinstall/lib/user_interaction/partitioning_conf.py @@ -154,9 +154,6 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, block_device_struct = {"partitions": [partition.__dump__() for partition in block_device.partitions.values()]} original_layout = copy.deepcopy(block_device_struct) - # Test code: [part.__dump__() for part in block_device.partitions.values()] - # TODO: Squeeze in BTRFS subvolumes here - new_partition = str(_('Create a new partition')) suggest_partition_layout = str(_('Suggest partition layout')) delete_partition = str(_('Delete a partition')) @@ -209,6 +206,7 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, return original_layout elif task == save_and_exit: break + if task == new_partition: from ..disk import valid_parted_position @@ -222,8 +220,9 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, if fs_choice.type_ == MenuSelectionType.Esc: continue - prompt = _('Enter the start sector (percentage or block number, default: {}): ').format( - block_device.first_free_sector) + prompt = str(_('Enter the start sector (percentage or block number, default: {}): ')).format( + block_device.first_free_sector + ) start = input(prompt).strip() if not start.strip(): @@ -232,8 +231,9 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, else: end_suggested = '100%' - prompt = _('Enter the end sector of the partition (percentage or block number, ex: {}): ').format( - end_suggested) + prompt = str(_('Enter the end sector of the partition (percentage or block number, ex: {}): ')).format( + end_suggested + ) end = input(prompt).strip() if not end.strip(): diff --git a/archinstall/lib/user_interaction/subvolume_config.py b/archinstall/lib/user_interaction/subvolume_config.py index e2797bba..94150dee 100644 --- a/archinstall/lib/user_interaction/subvolume_config.py +++ b/archinstall/lib/user_interaction/subvolume_config.py @@ -12,13 +12,13 @@ if TYPE_CHECKING: class SubvolumeList(ListManager): - def __init__(self, prompt: str, current_volumes: List[Subvolume]): + def __init__(self, prompt: str, subvolumes: List[Subvolume]): self._actions = [ str(_('Add subvolume')), str(_('Edit subvolume')), str(_('Delete subvolume')) ] - super().__init__(prompt, current_volumes, self._actions, self._actions[0]) + super().__init__(prompt, subvolumes, [self._actions[0]], self._actions[1:]) def reformat(self, data: List[Subvolume]) -> Dict[str, Optional[Subvolume]]: table = FormattedOutput.as_table(data) @@ -75,13 +75,8 @@ class SubvolumeList(ListManager): return subvolume - def exec_action(self, data: List[Subvolume]) -> List[Subvolume]: - if self.target: - active_subvolume = self.target - else: - active_subvolume = None - - if self.action == self._actions[0]: # add + def handle_action(self, action: str, entry: Optional[Subvolume], data: List[Subvolume]) -> List[Subvolume]: + if action == self._actions[0]: # add new_subvolume = self._add_subvolume() if new_subvolume is not None: @@ -89,14 +84,15 @@ class SubvolumeList(ListManager): # was created we'll replace the existing one data = [d for d in data if d.name != new_subvolume.name] data += [new_subvolume] - elif self.action == self._actions[1]: # edit subvolume - new_subvolume = self._add_subvolume(active_subvolume) - - if new_subvolume is not None: - # we'll remove the original subvolume and add the modified version - data = [d for d in data if d.name != active_subvolume.name and d.name != new_subvolume.name] - data += [new_subvolume] - elif self.action == self._actions[2]: # delete - data = [d for d in data if d != active_subvolume] + elif entry is not None: + if action == self._actions[1]: # edit subvolume + new_subvolume = self._add_subvolume(entry) + + if new_subvolume is not None: + # we'll remove the original subvolume and add the modified version + data = [d for d in data if d.name != entry.name and d.name != new_subvolume.name] + data += [new_subvolume] + elif action == self._actions[2]: # delete + data = [d for d in data if d != entry] return data -- cgit v1.2.3-70-g09d2