diff --git a/.github/workflows/bandit.yaml b/.github/workflows/bandit.yaml index 551e1fad..68072742 100644 --- a/.github/workflows/bandit.yaml +++ b/.github/workflows/bandit.yaml @@ -6,7 +6,7 @@ jobs: container: image: archlinux/archlinux:latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - run: pacman --noconfirm -Syu bandit - name: Security checkup with Bandit run: bandit -r archinstall || exit 0 diff --git a/.github/workflows/flake8.yaml b/.github/workflows/flake8.yaml index 3d4a4d42..278a9840 100644 --- a/.github/workflows/flake8.yaml +++ b/.github/workflows/flake8.yaml @@ -6,7 +6,7 @@ jobs: container: image: archlinux/archlinux:latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Prepare arch run: | pacman-key --init diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 4465aa41..5f129d82 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -21,8 +21,8 @@ jobs: image: archlinux/archlinux:latest options: --privileged steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 - name: Install pre-dependencies run: | pacman -Sy --noconfirm tree git python-pyparted python-setuptools python-sphinx python-sphinx_rtd_theme python-build python-installer python-wheel diff --git a/.github/workflows/iso-build.yaml b/.github/workflows/iso-build.yaml index aa7f49b6..0e6eaabc 100644 --- a/.github/workflows/iso-build.yaml +++ b/.github/workflows/iso-build.yaml @@ -26,7 +26,7 @@ jobs: image: archlinux/archlinux:latest options: --privileged steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - run: pwd - run: find . - run: cat /etc/os-release diff --git a/.github/workflows/mypy.yaml b/.github/workflows/mypy.yaml index 0fac32ba..35909767 100644 --- a/.github/workflows/mypy.yaml +++ b/.github/workflows/mypy.yaml @@ -6,7 +6,7 @@ jobs: container: image: archlinux/archlinux:latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Prepare arch run: | pacman-key --init diff --git a/.github/workflows/pylint.yaml b/.github/workflows/pylint.yaml index 30d251a8..0c6975c9 100644 --- a/.github/workflows/pylint.yaml +++ b/.github/workflows/pylint.yaml @@ -6,7 +6,7 @@ jobs: container: image: archlinux/archlinux:latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Prepare arch run: | pacman-key --init diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index b427d3e8..0378544b 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -7,7 +7,7 @@ jobs: image: archlinux/archlinux:latest options: --privileged steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Prepare arch run: | pacman-key --init diff --git a/.github/workflows/python-build.yml b/.github/workflows/python-build.yml index 67ca0165..313e63fb 100644 --- a/.github/workflows/python-build.yml +++ b/.github/workflows/python-build.yml @@ -11,7 +11,7 @@ jobs: image: archlinux/archlinux:latest options: --privileged steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Prepare arch run: | pacman-key --init diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 539a606e..8868cd38 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -18,7 +18,7 @@ jobs: image: archlinux/archlinux:latest options: --privileged steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Prepare arch run: | pacman-key --init diff --git a/.github/workflows/ruff-format.yaml b/.github/workflows/ruff-format.yaml index 9f520be0..6c0e99bf 100644 --- a/.github/workflows/ruff-format.yaml +++ b/.github/workflows/ruff-format.yaml @@ -4,6 +4,6 @@ jobs: ruff_format_check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - - uses: astral-sh/ruff-action@0ce1b0bf8b818ef400413f810f8a11cdbda0034b # v4.0.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + - uses: astral-sh/ruff-action@278981a28ce3188b1e39527901f38254bf3aac89 # v4.1.0 - run: ruff format --diff diff --git a/.github/workflows/ruff-lint.yaml b/.github/workflows/ruff-lint.yaml index ff37a6a8..56ae13a6 100644 --- a/.github/workflows/ruff-lint.yaml +++ b/.github/workflows/ruff-lint.yaml @@ -4,5 +4,5 @@ jobs: ruff: runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - - uses: astral-sh/ruff-action@0ce1b0bf8b818ef400413f810f8a11cdbda0034b # v4.0.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + - uses: astral-sh/ruff-action@278981a28ce3188b1e39527901f38254bf3aac89 # v4.1.0 diff --git a/.github/workflows/translation-check.yaml b/.github/workflows/translation-check.yaml index ebc521cb..927e8858 100644 --- a/.github/workflows/translation-check.yaml +++ b/.github/workflows/translation-check.yaml @@ -15,7 +15,7 @@ jobs: name: Validate translations runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Install gettext run: sudo apt-get update && sudo apt-get install -y gettext - name: Run translation checks diff --git a/.github/workflows/uki-build.yaml b/.github/workflows/uki-build.yaml index ff75250d..87e2feb0 100644 --- a/.github/workflows/uki-build.yaml +++ b/.github/workflows/uki-build.yaml @@ -26,7 +26,7 @@ jobs: image: archlinux/archlinux:latest options: --privileged steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - run: pwd - run: find . - run: cat /etc/os-release diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 33f112cf..188bbcf3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ default_stages: ['pre-commit'] repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.16 + rev: v0.15.21 hooks: # fix unused imports and sort them - id: ruff @@ -31,7 +31,7 @@ repos: args: [--config=.flake8] fail_fast: true - repo: https://github.com/pre-commit/mirrors-mypy - rev: v2.1.0 + rev: v2.3.0 hooks: - id: mypy args: [ diff --git a/PKGBUILD b/PKGBUILD index e9ee1173..af7cd5a0 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -5,7 +5,7 @@ # Contributor: demostanis worlds pkgname=archinstall -pkgver=4.3 +pkgver=4.4 pkgrel=1 pkgdesc="Just another guided/automated Arch Linux installer with a twist" arch=(any) diff --git a/archinstall/default_profiles/desktop.py b/archinstall/default_profiles/desktop.py index b70f9f11..f9195357 100644 --- a/archinstall/default_profiles/desktop.py +++ b/archinstall/default_profiles/desktop.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING, Self, override -from archinstall.default_profiles.profile import DisplayServerType, GreeterType, Profile, ProfileType, SelectResult +from archinstall.default_profiles.desktops.utils import provision_seat_access +from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType, SelectResult from archinstall.lib.log import info from archinstall.lib.menu.helpers import Selection from archinstall.lib.profile.profiles_handler import profile_handler @@ -94,6 +95,9 @@ class DesktopProfile(Profile): for profile in self.current_selection: profile.provision(install_session, users) + if seat_access := profile.custom_settings.get(CustomSetting.SeatAccess): + provision_seat_access(install_session, users, seat_access) + @override def install(self, install_session: Installer) -> None: # Install common packages for all desktop environments diff --git a/archinstall/default_profiles/desktops/utils.py b/archinstall/default_profiles/desktops/utils.py index 03e85aa5..b179ad50 100644 --- a/archinstall/default_profiles/desktops/utils.py +++ b/archinstall/default_profiles/desktops/utils.py @@ -1,6 +1,8 @@ from enum import Enum +from archinstall.lib.installer import Installer from archinstall.lib.menu.helpers import Selection +from archinstall.lib.models.users import User from archinstall.lib.translationhandler import tr from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType @@ -11,6 +13,16 @@ class SeatAccess(Enum): polkit = 'polkit' +def provision_seat_access( + install_session: Installer, + users: list[User], + seat_access: str, +) -> None: + if seat_access == SeatAccess.seatd.value: + for user in users: + install_session.arch_chroot(f'usermod -a -G seat {user.username}') + + async def select_seat_access(profile_name: str, default: str | None) -> SeatAccess: header = tr('{} needs access to your seat').format(profile_name) header += f' ({tr("collection of hardware devices i.e. keyboard, mouse")})' + '\n' diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index e3729504..95869c2f 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -1,7 +1,9 @@ import argparse import json import os +import stat import sys +import tempfile import urllib.error import urllib.parse from argparse import ArgumentParser, Namespace @@ -11,9 +13,10 @@ from pathlib import Path from typing import Any, Self from urllib.request import Request, urlopen +from pydantic import TypeAdapter from pydantic.dataclasses import dataclass as p_dataclass -from archinstall.lib.crypt import decrypt +from archinstall.lib.crypt import decrypt, encrypt from archinstall.lib.log import debug, error, logger, warn from archinstall.lib.menu.util import get_password from archinstall.lib.models.application import ApplicationConfiguration, ZramConfiguration @@ -31,6 +34,8 @@ from archinstall.lib.models.profile import ProfileConfiguration from archinstall.lib.models.users import Password, User, UserSerialization from archinstall.lib.plugins import load_plugin from archinstall.lib.translationhandler import Language, tr, translation_handler +from archinstall.lib.utils.format import as_key_value_pair +from archinstall.lib.utils.util import is_valid_path from archinstall.lib.version import get_version from archinstall.tui.components import tui @@ -56,7 +61,8 @@ class Arguments: debug: bool = False offline: bool = False no_pkg_lookups: bool = False - plugin: str | None = None + plugin: Path | None = None + plugin_url: str | None = None skip_version_check: bool = False skip_wifi_check: bool = False advanced: bool = False @@ -140,6 +146,11 @@ class ArchConfigType(StrEnum): return tr('Disk encryption password') +USER_CONFIG_FILE: Path = Path('user_configuration.json') +USER_CREDS_FILE: Path = Path('user_credentials.json') +DEFAULT_SAVE_PATH = logger.directory + + @dataclass class ArchConfig: version: str | None = None @@ -367,6 +378,94 @@ class ArchConfig: return arch_config + def user_config_to_json(self) -> str: + config = self.safe_config() + + adapter = TypeAdapter(dict[ArchConfigType, Any]) + python_dict = adapter.dump_python(config) + return json.dumps(python_dict, indent=4, sort_keys=True) + + def user_credentials_to_json(self) -> str: + cfg = self.unsafe_config() + + adapter = TypeAdapter(dict[ArchConfigType, Any]) + python_dict = adapter.dump_python(cfg) + return json.dumps(python_dict, indent=4, sort_keys=True) + + def write_debug(self) -> None: + debug(' -- Chosen configuration --') + debug(self.user_config_to_json()) + + def save( + self, + dest_path: Path | None = None, + creds: bool = False, + password: str | None = None, + ) -> None: + save_path = dest_path or DEFAULT_SAVE_PATH + + if not is_valid_path(save_path): + warn( + f'Destination directory {save_path} does not exist or is not a directory\n.', + 'Configuration files can not be saved', + ) + return + + self.save_user_config(save_path) + if creds: + self.save_user_creds(save_path, password=password) + + def save_user_config(self, dest_path: Path) -> None: + if not is_valid_path(dest_path): + error(f'Invalid path {dest_path}. User configuration could not be saved.') + return + + target = dest_path / USER_CONFIG_FILE + data = self.user_config_to_json() + target.write_text(data) + target.chmod(stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) + + def save_user_creds( + self, + dest_path: Path, + password: str | None = None, + ) -> None: + if not is_valid_path(dest_path): + error(f'Invalid path {dest_path}. User credentials could not be saved.') + return + + data = self.user_credentials_to_json() + + if password: + data = encrypt(password, data) + + target = dest_path / USER_CREDS_FILE + target.write_text(data) + target.chmod(stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) + + def as_summary(self) -> str: + """ + Render a concise two-column summary of the current configuration. + + Returns an empty string if nothing meaningful to show. + """ + cfg: dict[str, str | list[str] | bool] = {} + + for key, value in self.plain_cfg().items(): + cfg[key.text()] = value + + for config_type, obj in self.sub_cfg().items(): + if not hasattr(obj, 'summary'): + continue + + summary = obj.summary() + if summary: + cfg[config_type.text()] = summary + + simple_summary = as_key_value_pair(cfg, ignore_empty=True) + + return simple_summary + class ArchConfigHandler: def __init__(self) -> None: @@ -517,10 +616,17 @@ class ArchConfigHandler: parser.add_argument( '--plugin', nargs='?', - type=str, + type=Path, default=None, help='File path to a plugin to load', ) + parser.add_argument( + '--plugin-url', + type=str, + nargs='?', + default=None, + help='Url to a plugin file to load', + ) parser.add_argument( '--skip-version-check', action='store_true', @@ -560,7 +666,11 @@ class ArchConfigHandler: warn(f'Warning: --debug mode will write certain credentials to {logger.path}!') if args.plugin: - plugin_path = Path(args.plugin) + load_plugin(args.plugin) + + if args.plugin_url: + plugin_data = self._fetch_from_url(args.plugin_url) + plugin_path = self._write_plugin_to_temp_file(plugin_data) load_plugin(plugin_path) if args.creds_decryption_key is None: @@ -652,6 +762,27 @@ class ArchConfigHandler: sys.exit(1) + def _write_plugin_to_temp_file(self, plugin_data: str) -> Path: + if not plugin_data.strip(): + error('The downloaded plugin is empty') + sys.exit(1) + + tmp_file = tempfile.NamedTemporaryFile( + mode='w', + suffix='.py', + prefix='archinstall_plugin_', + delete=False, + ) + + try: + with tmp_file as f: + f.write(plugin_data) + except OSError as err: + error(f'Could not write the downloaded plugin to a temporary file: {err}') + sys.exit(1) + + return Path(tmp_file.name) + def _read_file(self, path: Path) -> str: if not path.exists(): error(f'Could not find file {path}') diff --git a/archinstall/lib/bootloader/bootloader_menu.py b/archinstall/lib/bootloader/bootloader_menu.py index d32a5a21..88fe8f73 100644 --- a/archinstall/lib/bootloader/bootloader_menu.py +++ b/archinstall/lib/bootloader/bootloader_menu.py @@ -3,7 +3,7 @@ from typing import override from archinstall.lib.menu.abstract_menu import AbstractSubMenu from archinstall.lib.menu.helpers import Confirmation, Selection -from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration +from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration, PlymouthTheme from archinstall.lib.translationhandler import tr from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType @@ -66,6 +66,13 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]): key='removable', enabled=removable_enabled, ), + MenuItem( + text=tr('Plymouth'), + action=self._select_plymouth, + value=self._bootloader_conf.plymouth, + preview_action=self._prev_plymouth, + key='plymouth', + ), ] def _prev_bootloader(self, item: MenuItem) -> str | None: @@ -85,6 +92,11 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]): return tr('Will install to /EFI/BOOT/ (removable location, safe default)') return tr('Will install to custom location with NVRAM entry') + def _prev_plymouth(self, item: MenuItem) -> str | None: + if item.value: + return f'{tr("Plymouth")}: {item.value.value}' + return None + @override async def show(self) -> BootloaderConfiguration: _ = await super().show() @@ -117,6 +129,37 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]): return bootloader + async def _select_plymouth(self, preset: PlymouthTheme | None) -> PlymouthTheme | None: + # Plymouth is purely cosmetic and a frequent source of boot breakage + # (notably with the NVIDIA driver and disk encryption), so confirm before + # enabling it. When it is already enabled the user is only changing the + # theme, so the warning is skipped. + if preset is None: + prompt = ( + '[ansi_bright_yellow]' + + tr('Plymouth adds a cosmetic boot splash but can cause boot problems on some setups:') + + '\n\n • ' + + tr('black screen with the NVIDIA driver') + + '\n • ' + + tr('hidden password prompt with disk encryption (LUKS)') + + '\n\n' + + tr('Would you like to enable it?') + + '[/]\n' + ) + + result = await Confirmation(header=prompt, allow_skip=True, preset=False).show() + + match result.type_: + case ResultType.Skip: + return preset + case ResultType.Selection: + if not result.get_value(): + return preset + case ResultType.Reset: + raise ValueError('Unhandled result type') + + return await select_plymouth_theme(preset) + async def _select_uki(self, preset: bool) -> bool: prompt = tr('Would you like to use unified kernel images?') + '\n' @@ -215,3 +258,24 @@ async def select_bootloader( return result.get_value() case ResultType.Reset: raise ValueError('Unhandled result type') + + +async def select_plymouth_theme(preset: PlymouthTheme | None = None) -> PlymouthTheme | None: + items = [MenuItem(t.value, value=t) for t in PlymouthTheme] + group = MenuItemGroup(items, sort_items=False) + group.set_focus_by_value(preset) + + result = await Selection[PlymouthTheme]( + group, + header=tr('Select Plymouth theme'), + allow_reset=True, + allow_skip=True, + ).show() + + match result.type_: + case ResultType.Skip: + return preset + case ResultType.Reset: + return None + case ResultType.Selection: + return PlymouthTheme(result.get_value()) diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index 5754dfcf..cb6844c3 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -1,171 +1,53 @@ -import json import readline -import stat -from pathlib import Path -from typing import Any -from pydantic import TypeAdapter - -from archinstall.lib.args import ArchConfig, ArchConfigType -from archinstall.lib.crypt import encrypt -from archinstall.lib.log import debug, logger, warn +from archinstall.lib.args import USER_CONFIG_FILE, USER_CREDS_FILE, ArchConfig +from archinstall.lib.log import debug from archinstall.lib.menu.helpers import Confirmation, Selection from archinstall.lib.menu.util import get_password, prompt_dir from archinstall.lib.translationhandler import tr -from archinstall.lib.utils.format import as_key_value_pair from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType -class ConfigurationOutput: - def __init__(self, config: ArchConfig): - """ - Configuration output handler to parse the existing - configuration data structure and prepare for output on the - console and for saving it to configuration files +async def confirm_config(config: ArchConfig) -> bool: + header = f'{tr("The specified configuration will be applied")}. ' + header += tr('Would you like to continue?') + '\n' - :param config: Archinstall configuration object - :type config: ArchConfig - """ + group = MenuItemGroup.yes_no() + group.set_preview_for_all(lambda x: config.user_config_to_json()) - self._config = config - self._default_save_path = logger.directory - self._user_config_file = Path('user_configuration.json') - self._user_creds_file = Path('user_credentials.json') + result = await Confirmation( + group=group, + header=header, + allow_skip=False, + preset=True, + preview_location='bottom', + preview_header=tr('Configuration preview'), + ).show() - @property - def user_configuration_file(self) -> Path: - return self._user_config_file + if not result.get_value(): + return False - @property - def user_credentials_file(self) -> Path: - return self._user_creds_file - - def user_config_to_json(self) -> str: - config = self._config.safe_config() - - adapter = TypeAdapter(dict[ArchConfigType, Any]) - python_dict = adapter.dump_python(config) - return json.dumps(python_dict, indent=4, sort_keys=True) - - def user_credentials_to_json(self) -> str: - cfg = self._config.unsafe_config() - - adapter = TypeAdapter(dict[ArchConfigType, Any]) - python_dict = adapter.dump_python(cfg) - return json.dumps(python_dict, indent=4, sort_keys=True) - - def write_debug(self) -> None: - debug(' -- Chosen configuration --') - debug(self.user_config_to_json()) - - def as_summary(self) -> str: - """ - Render a concise two-column summary of the current configuration. - - Returns an empty string if nothing meaningful to show. - """ - cfg: dict[str, str | list[str] | bool] = {} - - for key, value in self._config.plain_cfg().items(): - cfg[key.text()] = value - - for config_type, obj in self._config.sub_cfg().items(): - if not hasattr(obj, 'summary'): - continue - - summary = obj.summary() - if summary: - cfg[config_type.text()] = summary - - simple_summary = as_key_value_pair(cfg, ignore_empty=True) - - return simple_summary - - async def confirm_config(self) -> bool: - header = f'{tr("The specified configuration will be applied")}. ' - header += tr('Would you like to continue?') + '\n' - - group = MenuItemGroup.yes_no() - group.set_preview_for_all(lambda x: self.user_config_to_json()) - - result = await Confirmation( - group=group, - header=header, - allow_skip=False, - preset=True, - preview_location='bottom', - preview_header=tr('Configuration preview'), - ).show() - - if not result.get_value(): - return False - - return True - - def _is_valid_path(self, dest_path: Path) -> bool: - dest_path_ok = dest_path.exists() and dest_path.is_dir() - if not dest_path_ok: - warn( - f'Destination directory {dest_path.resolve()} does not exist or is not a directory\n.', - 'Configuration files can not be saved', - ) - return dest_path_ok - - def save_user_config(self, dest_path: Path) -> None: - if self._is_valid_path(dest_path): - target = dest_path / self._user_config_file - target.write_text(self.user_config_to_json()) - target.chmod(stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) - - def save_user_creds( - self, - dest_path: Path, - password: str | None = None, - ) -> None: - data = self.user_credentials_to_json() - - if password: - data = encrypt(password, data) - - if self._is_valid_path(dest_path): - target = dest_path / self._user_creds_file - target.write_text(data) - target.chmod(stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) - - def save( - self, - dest_path: Path | None = None, - creds: bool = False, - password: str | None = None, - ) -> None: - save_path = dest_path or self._default_save_path - - if self._is_valid_path(save_path): - self.save_user_config(save_path) - if creds: - self.save_user_creds(save_path, password=password) + return True async def save_config(config: ArchConfig) -> None: def preview(item: MenuItem) -> str | None: match item.value: case 'user_config': - serialized = config_output.user_config_to_json() - return f'{config_output.user_configuration_file}\n{serialized}' + serialized = config.user_config_to_json() + return f'{USER_CONFIG_FILE}\n{serialized}' case 'user_creds': - if maybe_serial := config_output.user_credentials_to_json(): - return f'{config_output.user_credentials_file}\n{maybe_serial}' + if maybe_serial := config.user_credentials_to_json(): + return f'{USER_CREDS_FILE}\n{maybe_serial}' return tr('No configuration') case 'all': - output = [str(config_output.user_configuration_file)] - config_output.user_credentials_to_json() - output.append(str(config_output.user_credentials_file)) + output = [str(USER_CONFIG_FILE)] + config.user_credentials_to_json() + output.append(str(USER_CREDS_FILE)) return '\n'.join(output) return None - config_output = ConfigurationOutput(config) - items = [ MenuItem( tr('Save user configuration (including disk layout)'), @@ -248,8 +130,8 @@ async def save_config(config: ArchConfig) -> None: match save_option: case 'user_config': - config_output.save_user_config(dest_path) + config.save_user_config(dest_path) case 'user_creds': - config_output.save_user_creds(dest_path, password=enc_password) + config.save_user_creds(dest_path, password=enc_password) case 'all': - config_output.save(dest_path, creds=True, password=enc_password) + config.save(dest_path, creds=True, password=enc_password) diff --git a/archinstall/lib/disk/disk_menu.py b/archinstall/lib/disk/disk_menu.py index f8e8e2e3..f89635af 100644 --- a/archinstall/lib/disk/disk_menu.py +++ b/archinstall/lib/disk/disk_menu.py @@ -311,6 +311,10 @@ async def select_devices(preset: list[BDevice] | None = []) -> list[BDevice] | N devices = device_handler.devices + if len(devices) < 1: + await Notify(tr('No disks were detected. A disk is required to be able to install Arch Linux')).show() + return None + items = [ MenuItem( str(d.device_info.path), diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 67a98885..3a7aaac5 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -6,7 +6,7 @@ from archinstall.lib.args import ArchConfig from archinstall.lib.authentication.authentication_menu import AuthenticationMenu from archinstall.lib.bootloader.bootloader_menu import BootloaderMenu from archinstall.lib.bootloader.utils import validate_bootloader_layout -from archinstall.lib.configuration import ConfigurationOutput, save_config +from archinstall.lib.configuration import save_config from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu from archinstall.lib.exceptions import SysCallError from archinstall.lib.general.general_menu import select_hostname, select_ntp, select_timezone @@ -581,7 +581,6 @@ class GlobalMenu(AbstractMenu[None]): def _prev_install_invalid_config(self, item: MenuItem) -> PreviewResult | None: self.sync_all_to_config() - config_output = ConfigurationOutput(self._arch_config) warnings = self._get_install_warnings() messages: list[tuple[str, MsgLevelType]] = [] @@ -608,7 +607,7 @@ class GlobalMenu(AbstractMenu[None]): messages.append((text, MsgLevelType.MsgWarning)) if not errors: - summary = config_output.as_summary() + summary = self._arch_config.as_summary() if summary: messages.append((summary, MsgLevelType.MsgNone)) diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index bf7d91ba..985e16cd 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -32,7 +32,7 @@ from archinstall.lib.locale.utils import verify_keyboard_layout, verify_x11_keyb from archinstall.lib.log import debug, error, info, log, logger, warn from archinstall.lib.mirror.mirror_handler import MirrorListHandler from archinstall.lib.models.application import ZramAlgorithm -from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration +from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration, PlymouthTheme from archinstall.lib.models.device import ( DiskEncryption, DiskLayoutConfiguration, @@ -228,6 +228,9 @@ class Installer: while self._service_state('archlinux-keyring-wkd-sync.service') not in ('dead', 'failed', 'exited'): time.sleep(1) + if self._service_state('archlinux-keyring-wkd-sync.service') == 'failed': + warn('archlinux-keyring-wkd-sync failed, keyring may need reinit during pacman sync') + def _verify_boot_part(self) -> None: """ Check that mounted /boot device has at minimum size for installation @@ -1755,6 +1758,28 @@ class Installer: self._helper_flags['bootloader'] = 'refind' + def _install_plymouth(self, plymouth: PlymouthTheme) -> None: + debug(f'Installing plymouth with theme: {plymouth.value}') + self.add_additional_packages(['plymouth']) + + for param in ('quiet', 'splash'): + if param not in self._kernel_params: + self._kernel_params.append(param) + + if 'plymouth' not in self._hooks: + for hook, insert_after in [('encrypt', False), ('sd-encrypt', False), ('systemd', True), ('filesystems', False), ('keyboard', True)]: + try: + idx = self._hooks.index(hook) + self._hooks.insert(idx + (1 if insert_after else 0), 'plymouth') + break + except ValueError: + continue + else: + self._hooks.append('plymouth') + + self.arch_chroot(f'plymouth-set-default-theme {plymouth.value}') + self.mkinitcpio(['-P']) + def _config_uki( self, root: PartitionModification | LvmVolume, @@ -1807,10 +1832,7 @@ class Installer: error('Error generating initramfs (continuing anyway)') def add_bootloader( - self, - bootloader: Bootloader, - uki_enabled: bool = False, - bootloader_removable: bool = False, + self, bootloader: Bootloader, uki_enabled: bool = False, bootloader_removable: bool = False, plymouth: PlymouthTheme | None = None ) -> None: """ Adds a bootloader to the installation instance. @@ -1824,6 +1846,7 @@ class Installer: :param bootloader: Type of bootloader to be added :param uki_enabled: Whether to use unified kernel images :param bootloader_removable: Whether to install to removable media location (UEFI only, for GRUB and Limine) + :param plymouth: Optional Plymouth theme to install and configure """ for plugin in plugins.values(): @@ -1859,6 +1882,9 @@ class Installer: warn(f'Bootloader {bootloader.value} lacks removable support; disabling.') bootloader_removable = False + if plymouth is not None: + self._install_plymouth(plymouth) + if uki_enabled: keep_initramfs = ( bootloader == Bootloader.Grub diff --git a/archinstall/lib/menu/util.py b/archinstall/lib/menu/util.py index 5efef09d..6e227a7f 100644 --- a/archinstall/lib/menu/util.py +++ b/archinstall/lib/menu/util.py @@ -19,14 +19,15 @@ async def get_password( def password_hint(value: str) -> InputInfo | None: if not value: return None + strength = PasswordStrength.strength(value) - if strength in (PasswordStrength.VERY_WEAK, PasswordStrength.WEAK): - return InputInfo(message=tr('Password strength: Weak'), msg_level=MsgLevelType.MsgError) - elif strength == PasswordStrength.MODERATE: - return InputInfo(message=tr('Password strength: Moderate'), msg_level=MsgLevelType.MsgWarning) - elif strength == PasswordStrength.STRONG: - return InputInfo(message=tr('Password strength: Strong'), msg_level=MsgLevelType.MsgInfo) - return None + match strength: + case PasswordStrength.VERY_WEAK | PasswordStrength.WEAK: + return InputInfo(message=tr('Password strength: Weak'), msg_level=MsgLevelType.MsgError) + case PasswordStrength.MODERATE: + return InputInfo(message=tr('Password strength: Moderate'), msg_level=MsgLevelType.MsgWarning) + case PasswordStrength.STRONG: + return InputInfo(message=tr('Password strength: Strong'), msg_level=MsgLevelType.MsgInfo) while True: result = await Input( diff --git a/archinstall/lib/models/bootloader.py b/archinstall/lib/models/bootloader.py index bd6bce7e..040b6746 100644 --- a/archinstall/lib/models/bootloader.py +++ b/archinstall/lib/models/bootloader.py @@ -60,15 +60,48 @@ class Bootloader(Enum): return cls(bootloader) +class PlymouthTheme(Enum): + BGRT = 'bgrt' + FADE = 'fade-in' + GLOW = 'glow' + SCRIPT = 'script' + SOLAR = 'solar' + SPINNER = 'spinner' + SPINFINITY = 'spinfinity' + TRIBAR = 'tribar' + TEXT = 'text' + DETAILS = 'details' + + @classmethod + def from_arg(cls, plymouth: str | None) -> Self | None: + if plymouth is None: + return None + + plymouth = plymouth.lower() + + values = [e.value for e in cls] + + if plymouth not in values: + warn(f'Invalid plymouth value "{plymouth}". Allowed values: {", ".join(values)}') + sys.exit(1) + + return cls(plymouth) + + @dataclass class BootloaderConfiguration(SubConfig): bootloader: Bootloader uki: bool = False removable: bool = True + plymouth: PlymouthTheme | None = None @override def json(self) -> dict[str, Any]: - return {'bootloader': self.bootloader.json(), 'uki': self.uki, 'removable': self.removable} + data = {'bootloader': self.bootloader.json(), 'uki': self.uki, 'removable': self.removable} + + if self.plymouth is not None: + data['plymouth'] = self.plymouth.value + return data @override def summary(self) -> list[str]: @@ -78,6 +111,8 @@ class BootloaderConfiguration(SubConfig): out.append(tr('UKI enabled')) if self.removable: out.append(tr('Removable')) + if self.plymouth is not None: + out.append(tr('Plymouth "{}"').format(self.plymouth.value)) return out @@ -86,14 +121,16 @@ class BootloaderConfiguration(SubConfig): bootloader = Bootloader.from_arg(config.get('bootloader', ''), skip_boot) uki = config.get('uki', False) removable = config.get('removable', True) - return cls(bootloader=bootloader, uki=uki, removable=removable) + plymouth = PlymouthTheme.from_arg(config.get('plymouth', None)) + return cls(bootloader=bootloader, uki=uki, removable=removable, plymouth=plymouth) @classmethod def get_default(cls, uefi: bool, skip_boot: bool = False) -> Self: bootloader = Bootloader.get_default(uefi, skip_boot) removable = uefi and bootloader.has_removable_support() uki = uefi and bootloader.has_uki_support() - return cls(bootloader=bootloader, uki=uki, removable=removable) + plymouth = None + return cls(bootloader=bootloader, uki=uki, removable=removable, plymouth=plymouth) def preview(self, uefi: bool) -> str: text = f'{tr("Bootloader")}: {self.bootloader.value}' @@ -112,4 +149,7 @@ class BootloaderConfiguration(SubConfig): removable_string = tr('Disabled') text += f'{tr("Removable")}: {removable_string}' text += '\n' + if self.plymouth is not None: + text += f'{tr("Plymouth")}: {self.plymouth.value}' + text += '\n' return text diff --git a/archinstall/lib/pacman/pacman.py b/archinstall/lib/pacman/pacman.py index c78f6145..83b965e2 100644 --- a/archinstall/lib/pacman/pacman.py +++ b/archinstall/lib/pacman/pacman.py @@ -4,8 +4,8 @@ from collections.abc import Callable from pathlib import Path from archinstall.lib.command import SysCommand -from archinstall.lib.exceptions import RequirementError -from archinstall.lib.log import error, info, warn +from archinstall.lib.exceptions import RequirementError, SysCallError +from archinstall.lib.log import debug, error, info, warn from archinstall.lib.pathnames import PACMAN_CONF from archinstall.lib.plugins import plugins from archinstall.lib.translationhandler import tr @@ -53,15 +53,45 @@ class Pacman: def sync(self) -> None: if self.synced: return - self.ask( - 'Could not sync a new package database', - 'Could not sync mirrors', - self.run, - '-Syy', - default_cmd='pacman', - ) + + try: + self.run('-Syy') + except SysCallError as err: + if b'GPGME' in err.worker_log or b'keyring' in err.worker_log.lower(): + warn('Pacman sync failed with keyring error, attempting keyring reinit') + self._reinit_keyring() + msg = 'Could not sync a new package database after keyring reinit' + else: + msg = 'Could not sync a new package database' + + self.ask(msg, 'Could not sync mirrors', self.run, '-Syy') + self.synced = True + @staticmethod + def _is_running(process: str) -> bool: + try: + SysCommand(f'pgrep -x {process}') + return True + except SysCallError: + debug(f'{process} is not running') + return False + + @staticmethod + def _reinit_keyring() -> None: + if Pacman._is_running('gpg-agent'): + try: + SysCommand('killall gpg-agent') + except SysCallError as err: + debug(f'Failed to kill gpg-agent: {err}') + + try: + SysCommand('pacman-key --init') + SysCommand('pacman-key --populate archlinux') + debug('Keyring reinitialized successfully') + except SysCallError as err: + debug(f'Keyring reinit failed: {err}') + def strap(self, packages: str | list[str]) -> None: self.sync() if isinstance(packages, str): diff --git a/archinstall/lib/plugins.py b/archinstall/lib/plugins.py index ac8383b1..6019f114 100644 --- a/archinstall/lib/plugins.py +++ b/archinstall/lib/plugins.py @@ -1,9 +1,6 @@ -import hashlib import importlib.util import os import sys -import urllib.parse -import urllib.request from importlib import metadata from pathlib import Path @@ -34,23 +31,6 @@ def plugin(f, *args, **kwargs) -> None: # type: ignore[no-untyped-def] plugins[f.__name__] = f -def _localize_path(path: Path) -> Path: - """ - Support structures for load_plugin() - """ - url = urllib.parse.urlparse(str(path)) - - if url.scheme and url.scheme in ('https', 'http'): - converted_path = Path(f'/tmp/{path.stem}_{hashlib.md5(os.urandom(12)).hexdigest()}.py') - - with open(converted_path, 'w') as temp_file: - temp_file.write(urllib.request.urlopen(url.geturl()).read().decode('utf-8')) - - return converted_path - else: - return path - - def _import_via_path(path: Path, namespace: str | None = None) -> str: if not namespace: namespace = os.path.basename(path) @@ -82,17 +62,10 @@ def _import_via_path(path: Path, namespace: str | None = None) -> str: def load_plugin(path: Path) -> None: namespace: str | None = None - parsed_url = urllib.parse.urlparse(str(path)) - info(f'Loading plugin from url {parsed_url}') + info(f'Loading plugin from {path}') - # The Profile was not a direct match on a remote URL - if not parsed_url.scheme: - # Path was not found in any known examples, check if it's an absolute path - if os.path.isfile(path): - namespace = _import_via_path(path) - elif parsed_url.scheme in ('https', 'http'): - localized = _localize_path(path) - namespace = _import_via_path(localized) + if os.path.isfile(path): + namespace = _import_via_path(path) if namespace and namespace in sys.modules: # Version dependency via __archinstall__version__ variable (if present) in the plugin diff --git a/archinstall/lib/utils/util.py b/archinstall/lib/utils/util.py index f94ca18c..a0c34f5f 100644 --- a/archinstall/lib/utils/util.py +++ b/archinstall/lib/utils/util.py @@ -1,6 +1,7 @@ import secrets import string from datetime import UTC, datetime +from pathlib import Path from archinstall.lib.pathnames import ARCHISO_MOUNTPOINT from archinstall.lib.utils.format import as_columns @@ -46,3 +47,7 @@ def format_cols(items: list[str], header: str | None = None) -> str: # remove whitespaces on each row text = '\n'.join(t.strip() for t in text.split('\n')) return text + + +def is_valid_path(path: Path) -> bool: + return path.exists() and path.is_dir() diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index 8aa83084..05f3ebae 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -209,12 +209,29 @@ msgstr "" msgid "Install to removable location" msgstr "" +msgid "Plymouth" +msgstr "" + msgid "Will install to /EFI/BOOT/ (removable location, safe default)" msgstr "" msgid "Will install to custom location with NVRAM entry" msgstr "" +msgid "" +"Plymouth adds a cosmetic boot splash but can cause boot problems on some " +"setups:" +msgstr "" + +msgid "black screen with the NVIDIA driver" +msgstr "" + +msgid "hidden password prompt with disk encryption (LUKS)" +msgstr "" + +msgid "Would you like to enable it?" +msgstr "" + msgid "Would you like to use unified kernel images?" msgstr "" @@ -248,6 +265,9 @@ msgstr "" msgid "UEFI is not detected and some options are disabled" msgstr "" +msgid "Select Plymouth theme" +msgstr "" + msgid "The specified configuration will be applied" msgstr "" @@ -888,6 +908,10 @@ msgstr "" msgid "Removable" msgstr "" +#, python-brace-format +msgid "Plymouth \"{}\"" +msgstr "" + msgid "Use a best-effort default partition layout" msgstr "" diff --git a/archinstall/locales/el/LC_MESSAGES/base.mo b/archinstall/locales/el/LC_MESSAGES/base.mo index cee51a48..6e052eaf 100644 Binary files a/archinstall/locales/el/LC_MESSAGES/base.mo and b/archinstall/locales/el/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/el/LC_MESSAGES/base.po b/archinstall/locales/el/LC_MESSAGES/base.po index 5a56491a..41db88f9 100644 --- a/archinstall/locales/el/LC_MESSAGES/base.po +++ b/archinstall/locales/el/LC_MESSAGES/base.po @@ -1423,7 +1423,7 @@ msgid "Yes" msgstr "ναι" msgid "No" -msgstr "" +msgstr "όχι" #, fuzzy msgid "Archinstall help" @@ -1434,7 +1434,7 @@ msgid " (default)" msgstr "(προκαθορισμένο)" msgid "Press Ctrl+h for help" -msgstr "" +msgstr "Πατήστε Ctrl+h για βοήθεια" #, fuzzy msgid "Choose an option to give Sway access to your hardware" @@ -1621,7 +1621,7 @@ msgid "Select execution mode" msgstr "Επιλέξτε μία ενέργεια για '{}'" msgid "Press ? for help" -msgstr "" +msgstr "Πατήστε ? για βοήθεια" #, fuzzy msgid "Choose an option to give Hyprland access to your hardware" @@ -1754,31 +1754,31 @@ msgid "Only ASCII characters are supported" msgstr "" msgid "Show help" -msgstr "" +msgstr "Εμφάνιση βοήθειας" msgid "Exit help" -msgstr "" +msgstr "Έξοδος από τη βοήθεια" msgid "Preview scroll up" -msgstr "" +msgstr "Κύλιση προεπισκόπησης πάνω" msgid "Preview scroll down" -msgstr "" +msgstr "Κύλιση προεπισκόπησης κάτω" msgid "Move up" -msgstr "" +msgstr "Μετακίνηση πάνω" msgid "Move down" -msgstr "" +msgstr "Μετακίνηση κάτω" msgid "Move right" -msgstr "" +msgstr "Μετακίνηση δεξιά" msgid "Move left" -msgstr "" +msgstr "Μετακίνηση αριστερά" msgid "Jump to entry" -msgstr "" +msgstr "Μετάβαση σε καταχώρηση" msgid "Skip selection (if available)" msgstr "" @@ -1795,20 +1795,20 @@ msgid "Select on multi select" msgstr "Επιλέξτε μία ζώνη ώρας" msgid "Reset" -msgstr "" +msgstr "Επαναφορά" #, fuzzy msgid "Skip selection menu" msgstr "Επιλέξτε μία ενέργεια για '{}'" msgid "Start search mode" -msgstr "" +msgstr "Έναρξη λειτουργίας αναζήτησης" msgid "Exit search mode" -msgstr "" +msgstr "Έξοδος από τη λειτουργία αναζήτησης" msgid "General" -msgstr "" +msgstr "Γενικά" #, fuzzy msgid "Navigation" @@ -1819,115 +1819,115 @@ msgid "Selection" msgstr "Επιλέξτε διεπαφή προς προσθήκη" msgid "Search" -msgstr "" +msgstr "Αναζήτηση" msgid "Down" -msgstr "" +msgstr "Κάτω" msgid "Up" -msgstr "" +msgstr "Πάνω" #, fuzzy msgid "Confirm" msgstr "Επιβεβαίωση και έξοδος" msgid "Focus right" -msgstr "" +msgstr "Εστίαση δεξιά" msgid "Focus left" -msgstr "" +msgstr "Εστίαση αριστερά" msgid "Toggle" -msgstr "" +msgstr "Εναλλαγή" msgid "Show/Hide help" -msgstr "" +msgstr "Εμφάνιση/Απόκρυψη βοήθειας" msgid "Quit" -msgstr "" +msgstr "Έξοδος" msgid "First" -msgstr "" +msgstr "Πρώτο" msgid "Last" -msgstr "" +msgstr "Τελευταίο" #, fuzzy msgid "Select" msgstr "Επιλέξτε διεπαφή προς προσθήκη" msgid "Page Up" -msgstr "" +msgstr "Σελίδα πάνω" msgid "Page Down" -msgstr "" +msgstr "Σελίδα κάτω" msgid "Page up" -msgstr "" +msgstr "Σελίδα πάνω" msgid "Page down" -msgstr "" +msgstr "Σελίδα κάτω" msgid "Page Left" -msgstr "" +msgstr "Σελίδα αριστερά" msgid "Page Right" -msgstr "" +msgstr "Σελίδα δεξιά" msgid "Cursor up" -msgstr "" +msgstr "Δρομέας πάνω" msgid "Cursor down" -msgstr "" +msgstr "Δρομέας κάτω" msgid "Cursor right" -msgstr "" +msgstr "Δρομέας δεξιά" msgid "Cursor left" -msgstr "" +msgstr "Δρομέας αριστερά" msgid "Top" -msgstr "" +msgstr "Κορυφή" msgid "Bottom" -msgstr "" +msgstr "Βάση" msgid "Home" -msgstr "" +msgstr "Αρχή" msgid "End" -msgstr "" +msgstr "Τέλος" #, fuzzy msgid "Toggle option" msgstr "Επιλογές υποόγκου" msgid "Scroll Up" -msgstr "" +msgstr "Κύλιση πάνω" msgid "Scroll Down" -msgstr "" +msgstr "Κύλιση κάτω" msgid "Scroll Left" -msgstr "" +msgstr "Κύλιση αριστερά" msgid "Scroll Right" -msgstr "" +msgstr "Κύλιση δεξιά" msgid "Scroll Home" -msgstr "" +msgstr "Κύλιση στην αρχή" msgid "Scroll End" -msgstr "" +msgstr "Κύλιση στο τέλος" msgid "Focus Next" -msgstr "" +msgstr "Εστίαση στο επόμενο" msgid "Focus Previous" -msgstr "" +msgstr "Εστίαση στο προηγούμενο" msgid "Copy selected text" -msgstr "" +msgstr "Αντιγραφή επιλεγμένου κειμένου" msgid "labwc needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" msgstr "" @@ -2115,7 +2115,7 @@ msgid "Enter wifi password" msgstr "Εισάγετε κωδικό: " msgid "Ok" -msgstr "" +msgstr "Εντάξει" msgid "Removable" msgstr "" diff --git a/archinstall/locales/ja/LC_MESSAGES/base.mo b/archinstall/locales/ja/LC_MESSAGES/base.mo index 0331225b..cf6e5372 100644 Binary files a/archinstall/locales/ja/LC_MESSAGES/base.mo and b/archinstall/locales/ja/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/ja/LC_MESSAGES/base.po b/archinstall/locales/ja/LC_MESSAGES/base.po index 162f5dba..945405e6 100644 --- a/archinstall/locales/ja/LC_MESSAGES/base.po +++ b/archinstall/locales/ja/LC_MESSAGES/base.po @@ -9,7 +9,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.8\n" +"X-Generator: Poedit 3.9\n" msgid "Recommended" msgstr "推奨" @@ -103,7 +103,7 @@ msgid "Select font packages to install" msgstr "インストールするフォントパッケージを選択" msgid "ArchInstall Language" -msgstr "ArchInstall の言語" +msgstr "archInstall の言語" msgid "Version" msgstr "バージョン" @@ -166,7 +166,7 @@ msgid "Users" msgstr "ユーザー" msgid "Root encrypted password" -msgstr "Root 暗号化パスワード" +msgstr "root 暗号化パスワード" msgid "Disk encryption password" msgstr "ディスク暗号化パスワード" @@ -221,6 +221,9 @@ msgstr "Unified カーネルイメージ" msgid "Install to removable location" msgstr "リムーバブルな場所にインストールする" +msgid "Plymouth" +msgstr "Plymouth" + msgid "Will install to /EFI/BOOT/ (removable location, safe default)" msgstr "/EFI/BOOT/(リムーバブルな場所、安全なデフォルト)にインストール" @@ -254,6 +257,9 @@ msgstr "インストールするブートローダーを選択" msgid "UEFI is not detected and some options are disabled" msgstr "UEFIが検出されず、一部のオプションが無効になります" +msgid "Select Plymouth theme" +msgstr "Plymouth のテーマを選択" + msgid "The specified configuration will be applied" msgstr "指定された設定が適用されます" @@ -263,9 +269,6 @@ msgstr "続行しますか?" msgid "Configuration preview" msgstr "設定プレビュー" -msgid "Warning: no network configuration selected. Network will need to be set up manually on the installed system." -msgstr "警告: ネットワーク設定が選択されていません。インストール後のシステムで、ネットワークを手動で設定する必要があります。" - msgid "No configuration" msgstr "設定なし" @@ -279,7 +282,7 @@ msgid "Save all" msgstr "すべて保存" msgid "Enter a directory for the configuration(s) to be saved" -msgstr "設定を保存するディレクトリを入力" +msgstr "設定を保存するディレクトリーを入力してください" #, python-brace-format msgid "Do you want to save the configuration file(s) to {}?" @@ -348,7 +351,7 @@ msgid "You will use whatever drive-setup is mounted at the specified directory" msgstr "指定されたディレクトリにマウントされているドライブの設定を使用します" msgid "WARNING: Archinstall won't check the suitability of this setup" -msgstr "警告: Archinstall はこのセットアップの適合性をチェックしません" +msgstr "警告: archinstall はこのセットアップの適合性をチェックしません" msgid "Select main filesystem" msgstr "メインファイルシステムを選択" @@ -369,7 +372,7 @@ msgid "Would you like to create a separate partition for /home?" msgstr "/home 用に別のパーティションを作成しますか?" msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" -msgstr "選択したドライブには、自動提案に必要な最小容量がありません\n" +msgstr "選択したドライブは、自動提案に必要な最小容量を満たしていません\n" #, python-brace-format msgid "Minimum capacity for /home partition: {}GiB\n" @@ -570,13 +573,13 @@ msgid "For the best compatibility with your Nvidia hardware, you may want to use msgstr "Nvidia ハードウェアとの互換性を最大限に高めるには、Nvidia 独自のドライバーを使用することをお勧めします。\n" msgid "Would you like to use swap on zram?" -msgstr "zram でスワップを使用しますか?" +msgstr "zram 上でスワップを使用しますか?" msgid "Select zram compression algorithm:" -msgstr "zram 圧縮アルゴリズムを選択:" +msgstr "zram の圧縮アルゴリズムを選択:" msgid "Archinstall language" -msgstr "Archinstall の言語" +msgstr "archinstall の言語" msgid "Applications" msgstr "アプリケーション" @@ -613,7 +616,7 @@ msgid "Btrfs snapshot type: {}" msgstr "Btrfs スナップショットのタイプ: {}" msgid "Swap on zram" -msgstr "Zram 上でスワップ" +msgstr "zram 上でスワップ" msgid "Compression algorithm" msgstr "圧縮アルゴリズム" @@ -627,16 +630,21 @@ msgstr "カラー出力" msgid "Kernel" msgstr "カーネル" -msgid "Missing configurations:\n" -msgstr "設定が存在しません:\n" +msgid "No network configuration selected. Network will need to be set up manually on the installed system." +msgstr "ネットワーク設定が選択されていません。インストール後のシステムで、ネットワークを手動で設定する必要があります。" -#, python-brace-format -msgid "Invalid configuration: {}" -msgstr "無効な設定: {}" +msgid "Missing configurations:" +msgstr "不足している設定:" + +msgid "Invalid configuration:" +msgstr "無効な設定:" msgid "Ready to install" msgstr "インストールの準備が完了" +msgid "Warnings:" +msgstr "警告:" + msgid "Profiles" msgstr "プロファイル" @@ -804,11 +812,11 @@ msgid "wide Unicode coverage, good fallback font" msgstr "Unicode を広くカバーする良い代替フォント" msgid "Zram enabled" -msgstr "Zram を有効にする" +msgstr "zram を有効にする" #, python-brace-format msgid "Zram algorithm {}" -msgstr "Zram アルゴリズム {}" +msgstr "zram のアルゴリズム {}" msgid "Bluetooth enabled" msgstr "Bluetooth を有効にする" @@ -839,7 +847,7 @@ msgid "Second factor login" msgstr "2要素ログイン" msgid "Root password set" -msgstr "Root パスワードを設定" +msgstr "root パスワードを設定" #, python-brace-format msgid "Configured {} user(s)" @@ -858,6 +866,10 @@ msgstr "UKI を有効にする" msgid "Removable" msgstr "リムーバブル" +#, python-brace-format +msgid "Plymouth \"{}\"" +msgstr "Plymouth \"{}\"" + msgid "Use a best-effort default partition layout" msgstr "ベストエフォートのデフォルトパーティションレイアウトを使用" @@ -965,7 +977,7 @@ msgid "Color enabled" msgstr "カラー出力を有効にする" #, python-brace-format -msgid "{} grphics driver" +msgid "{} graphics driver" msgstr "{} グラフィックドライバー" #, python-brace-format @@ -1031,15 +1043,15 @@ msgstr "Wi-Fi に接続しますか?" msgid "No wifi interface found" msgstr "Wi-Fi インターフェースが見つかりません" +msgid "No wifi networks found" +msgstr "Wi-Fi ネットワークが見つかりません" + msgid "Select wifi network to connect to" msgstr "接続する Wi-Fi ネットワークを選択" msgid "Scanning wifi networks..." msgstr "Wi-Fi ネットワークをスキャンしています..." -msgid "No wifi networks found" -msgstr "Wi-Fi ネットワークが見つかりません" - msgid "Failed setting up wifi" msgstr "Wi-Fi の設定に失敗しました" @@ -1137,7 +1149,7 @@ msgstr "パスワードを入力" #, python-brace-format msgid "Should \"{}\" be a superuser (sudo)?\n" -msgstr "\"{}\" はスーパーユーザーに昇格しますか(sudo)?\n" +msgstr "\"{}\" に sudo 権限を与えますか?\n" #, python-brace-format msgid "About to upload \"{}\" to the publicly accessible {}" @@ -1154,7 +1166,7 @@ msgid "Failed to upload log." msgstr "ログのアップロードに失敗しました。" msgid "Archinstall requires root privileges to run. See --help for more." -msgstr "Archinstall を実行するには root 権限が必要です。詳細については --help を参照してください。" +msgstr "archinstall を実行するには root 権限が必要です。詳細については --help を参照してください。" msgid "New version available" msgstr "新しいバージョンが利用可能" @@ -1608,9 +1620,6 @@ msgstr " (デフォルト)" #~ msgid "Subvolume name :" #~ msgstr "サブボリューム名:" -#~ msgid "Select a mount point :" -#~ msgstr "マウントポイントを選択:" - #~ msgid "Select the desired subvolume options " #~ msgstr "サブボリュームのオプションを選択" diff --git a/archinstall/locales/languages.json b/archinstall/locales/languages.json index 8641461b..04127a8a 100644 --- a/archinstall/locales/languages.json +++ b/archinstall/locales/languages.json @@ -116,7 +116,7 @@ {"abbr": "ne", "lang": "Nepali", "translated_lang": "नेपाली", "sys_lang": "ne_NP.UTF-8", "default_timezone": "Asia/Kathmandu"}, {"abbr": "nl", "lang": "Dutch", "translated_lang": "Nederlands", "sys_lang": "nl_NL.UTF-8", "default_timezone": "Europe/Amsterdam"}, {"abbr": "nn", "lang": "Norwegian Nynorsk"}, - {"abbr": "nb", "lang": "Norwegian Bokmål"}, + {"abbr": "nb", "lang": "Norwegian Bokmål", "translated_lang": "Norsk bokmål"}, {"abbr": "no", "lang": "Norwegian"}, {"abbr": "ny", "lang": "Nyanja"}, {"abbr": "oc", "lang": "Occitan (post 1500)"}, @@ -173,7 +173,7 @@ {"abbr": "ur", "lang": "Urdu", "translated_lang": "اردو", "sys_lang": "ur_PK.UTF-8", "default_timezone": "Asia/Karachi"}, {"abbr": "uz", "lang": "Uzbek", "translated_lang": "O'zbek", "sys_lang": "uz_UZ.UTF-8", "default_timezone": "Asia/Tashkent"}, {"abbr": "ve", "lang": "Venda"}, - {"abbr": "vi", "lang": "Vietnamese"}, + {"abbr": "vi", "lang": "Vietnamese", "translated_lang": "Tiếng Việt"}, {"abbr": "vo", "lang": "Volapük"}, {"abbr": "wa", "lang": "Walloon"}, {"abbr": "wo", "lang": "Wolof"}, diff --git a/archinstall/locales/nb/LC_MESSAGES/base.mo b/archinstall/locales/nb/LC_MESSAGES/base.mo new file mode 100644 index 00000000..5acd8170 Binary files /dev/null and b/archinstall/locales/nb/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/nb/LC_MESSAGES/base.po b/archinstall/locales/nb/LC_MESSAGES/base.po new file mode 100644 index 00000000..3821d1b0 --- /dev/null +++ b/archinstall/locales/nb/LC_MESSAGES/base.po @@ -0,0 +1,1192 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: Norwegian Bokmål\n" +"Language: nb\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Recommended" +msgstr "Anbefalt" + +msgid "Package" +msgstr "Pakke" + +msgid "Curated selection of KDE Plasma packages" +msgstr "Kuratert utvalg av KDE Plasma-pakker" + +msgid "Dependencies" +msgstr "Avhengigheter" + +msgid "Package group" +msgstr "Pakkegruppe" + +msgid "Extensive KDE Plasma installation" +msgstr "Omfattende KDE Plasma-installasjon" + +msgid "Packages in group" +msgstr "Pakker i gruppen" + +msgid "Minimal KDE Plasma installation" +msgstr "Minimal KDE Plasma-installasjon" + +msgid "Type" +msgstr "Type" + +msgid "Description" +msgstr "Beskrivelse" + +msgid "Select a flavor of KDE Plasma to install" +msgstr "Velg en variant av KDE Plasma å installere" + +#, python-brace-format +msgid "{} needs access to your seat" +msgstr "{} trenger tilgang til seat-en din" + +msgid "collection of hardware devices i.e. keyboard, mouse" +msgstr "samling av maskinvareenheter, f.eks. tastatur, mus" + +#, python-brace-format +msgid "Choose an option how to give {} access to your hardware" +msgstr "Velg hvordan {} skal få tilgang til maskinvaren din" + +#, python-brace-format +msgid "Environment type: {} {}" +msgstr "Miljøtype: {} {}" + +#, python-brace-format +msgid "Environment type: {}" +msgstr "Miljøtype: {}" + +msgid "Installed packages" +msgstr "Installerte pakker" + +msgid "Bluetooth" +msgstr "Bluetooth" + +msgid "Audio" +msgstr "Lyd" + +msgid "Print service" +msgstr "Utskriftstjeneste" + +msgid "Power management" +msgstr "Strømstyring" + +msgid "Firewall" +msgstr "Brannmur" + +msgid "Additional fonts" +msgstr "Tilleggsskrifter" + +msgid "Enabled" +msgstr "Aktivert" + +msgid "Disabled" +msgstr "Deaktivert" + +msgid "Would you like to configure Bluetooth?" +msgstr "Vil du konfigurere Bluetooth?" + +msgid "Would you like to configure the print service?" +msgstr "Vil du konfigurere utskriftstjenesten?" + +msgid "Select audio configuration" +msgstr "Velg lydkonfigurasjon" + +msgid "Select font packages to install" +msgstr "Velg skriftpakker å installere" + +msgid "ArchInstall Language" +msgstr "ArchInstall-språk" + +msgid "Version" +msgstr "Versjon" + +msgid "Installation Script" +msgstr "Installasjonsskript" + +msgid "Locales" +msgstr "Lokaliteter" + +msgid "Disk configuration" +msgstr "Diskkonfigurasjon" + +msgid "Profile" +msgstr "Profil" + +msgid "Mirrors and repositories" +msgstr "Speil og pakkebrønner" + +msgid "Network" +msgstr "Nettverk" + +msgid "Bootloader" +msgstr "Oppstartslaster" + +msgid "Application" +msgstr "Program" + +msgid "Authentication" +msgstr "Autentisering" + +msgid "Swap" +msgstr "Veksleminne" + +msgid "Hostname" +msgstr "Vertsnavn" + +msgid "Kernels" +msgstr "Kjerner" + +msgid "Automatic time sync (NTP)" +msgstr "Automatisk tidssynkronisering (NTP)" + +msgid "Timezone" +msgstr "Tidssone" + +msgid "Services" +msgstr "Tjenester" + +msgid "Additional packages" +msgstr "Tilleggspakker" + +msgid "Pacman" +msgstr "Pacman" + +msgid "Custom commands" +msgstr "Egendefinerte kommandoer" + +msgid "Users" +msgstr "Brukere" + +msgid "Root encrypted password" +msgstr "Kryptert root-passord" + +msgid "Disk encryption password" +msgstr "Passord for diskkryptering" + +msgid "Incorrect credentials file decryption password" +msgstr "Feil dekrypteringspassord for legitimasjonsfil" + +msgid "Enter credentials file decryption password" +msgstr "Skriv inn dekrypteringspassord for legitimasjonsfil" + +msgid "Incorrect password" +msgstr "Feil passord" + +#, python-brace-format +msgid "Setting up U2F login: {}" +msgstr "Setter opp U2F-innlogging: {}" + +#, python-brace-format +msgid "Setting up U2F device for user: {}" +msgstr "Setter opp U2F-enhet for bruker: {}" + +msgid "You may need to enter the PIN and then touch your U2F device to register it" +msgstr "Du må kanskje skrive inn PIN-koden og deretter berøre U2F-enheten for å registrere den" + +msgid "Root password" +msgstr "Root-passord" + +msgid "User account" +msgstr "Brukerkonto" + +msgid "U2F login setup" +msgstr "Oppsett av U2F-innlogging" + +msgid "U2F login method: " +msgstr "U2F-innloggingsmetode: " + +msgid "Passwordless sudo: " +msgstr "Passordløs sudo: " + +msgid "No U2F devices found" +msgstr "Ingen U2F-enheter funnet" + +msgid "Enter root password" +msgstr "Skriv inn root-passord" + +msgid "Enable passwordless sudo?" +msgstr "Aktivere passordløs sudo?" + +msgid "Unified kernel images" +msgstr "Samlede kjernebilder (UKI)" + +msgid "Install to removable location" +msgstr "Installer til flyttbar plassering" + +msgid "Plymouth" +msgstr "Plymouth" + +msgid "Will install to /EFI/BOOT/ (removable location, safe default)" +msgstr "Installeres til /EFI/BOOT/ (flyttbar plassering, trygt standardvalg)" + +msgid "Will install to custom location with NVRAM entry" +msgstr "Installeres til egendefinert plassering med NVRAM-oppføring" + +msgid "Would you like to use unified kernel images?" +msgstr "Vil du bruke samlede kjernebilder (UKI)?" + +msgid "Would you like to install the bootloader to the default removable media search location?" +msgstr "Vil du installere oppstartslasteren til standard søkeplassering for flyttbare medier?" + +msgid "This installs the bootloader to /EFI/BOOT/BOOTX64.EFI (or similar) which is useful for:" +msgstr "Dette installerer oppstartslasteren til /EFI/BOOT/BOOTX64.EFI (eller lignende), noe som er nyttig for:" + +msgid "Firmware that does not properly support NVRAM boot entries like most MSI motherboards," +msgstr "Fastvare som ikke støtter NVRAM-oppstartsoppføringer ordentlig, som de fleste MSI-hovedkort," + +msgid "most Apple Macs, many laptops..." +msgstr "de fleste Apple Mac-er, mange bærbare ..." + +msgid "USB drives or other portable external media." +msgstr "USB-stasjoner eller andre bærbare eksterne medier." + +msgid "Systems where you want the disk to be bootable on any computer." +msgstr "Systemer der du vil at disken skal kunne starte opp på enhver datamaskin." + +msgid "Select bootloader to install" +msgstr "Velg oppstartslaster å installere" + +msgid "UEFI is not detected and some options are disabled" +msgstr "UEFI er ikke oppdaget, og enkelte valg er deaktivert" + +msgid "Select Plymouth theme" +msgstr "Velg Plymouth-tema" + +msgid "The specified configuration will be applied" +msgstr "Den angitte konfigurasjonen vil bli tatt i bruk" + +msgid "Would you like to continue?" +msgstr "Vil du fortsette?" + +msgid "Configuration preview" +msgstr "Forhåndsvisning av konfigurasjon" + +msgid "No configuration" +msgstr "Ingen konfigurasjon" + +msgid "Save user configuration (including disk layout)" +msgstr "Lagre brukerkonfigurasjon (inkludert diskoppsett)" + +msgid "Save user credentials" +msgstr "Lagre brukerlegitimasjon" + +msgid "Save all" +msgstr "Lagre alt" + +msgid "Enter a directory for the configuration(s) to be saved" +msgstr "Angi en mappe der konfigurasjonen(e) skal lagres" + +#, python-brace-format +msgid "Do you want to save the configuration file(s) to {}?" +msgstr "Vil du lagre konfigurasjonsfilen(e) til {}?" + +msgid "Do you want to encrypt the user_credentials.json file?" +msgstr "Vil du kryptere filen user_credentials.json?" + +msgid "Credentials file encryption password" +msgstr "Krypteringspassord for legitimasjonsfil" + +msgid "Partitioning" +msgstr "Partisjonering" + +msgid "Disk encryption" +msgstr "Diskkryptering" + +#, python-brace-format +msgid "Configuration type: {}" +msgstr "Konfigurasjonstype: {}" + +msgid "Mountpoint" +msgstr "Monteringspunkt" + +msgid "Configuration" +msgstr "Konfigurasjon" + +msgid "Wipe" +msgstr "Slett" + +msgid "Physical volumes" +msgstr "Fysiske volumer" + +msgid "Volumes" +msgstr "Volumer" + +#, python-brace-format +msgid "Snapshot type: {}" +msgstr "Øyeblikksbildetype: {}" + +msgid "LVM disk encryption with more than 2 partitions is currently not supported" +msgstr "LVM-diskkryptering med mer enn 2 partisjoner støttes foreløpig ikke" + +msgid "Encryption type" +msgstr "Krypteringstype" + +msgid "Password" +msgstr "Passord" + +msgid "Iteration time" +msgstr "Iterasjonstid" + +msgid "Select disks for the installation" +msgstr "Velg disker for installasjonen" + +msgid "Partitions" +msgstr "Partisjoner" + +msgid "Select a disk configuration" +msgstr "Velg en diskkonfigurasjon" + +msgid "Enter root mount directory" +msgstr "Angi rotmonteringsmappe" + +msgid "You will use whatever drive-setup is mounted at the specified directory" +msgstr "Du vil bruke det diskoppsettet som er montert på den angitte mappen" + +msgid "WARNING: Archinstall won't check the suitability of this setup" +msgstr "ADVARSEL: Archinstall vil ikke kontrollere om dette oppsettet egner seg" + +msgid "Select main filesystem" +msgstr "Velg hovedfilsystem" + +msgid "Would you like to use compression or disable CoW?" +msgstr "Vil du bruke komprimering eller deaktivere CoW?" + +msgid "Use compression" +msgstr "Bruk komprimering" + +msgid "Disable Copy-on-Write" +msgstr "Deaktiver Copy-on-Write" + +msgid "Would you like to use BTRFS subvolumes with a default structure?" +msgstr "Vil du bruke BTRFS-undervolumer med en standardstruktur?" + +msgid "Would you like to create a separate partition for /home?" +msgstr "Vil du opprette en egen partisjon for /home?" + +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "De valgte stasjonene har ikke den minimumskapasiteten som kreves for et automatisk forslag\n" + +#, python-brace-format +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Minimumskapasitet for /home-partisjonen: {} GiB\n" + +#, python-brace-format +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Minimumskapasitet for Arch Linux-partisjonen: {} GiB" + +msgid "Encryption password" +msgstr "Krypteringspassord" + +msgid "LVM volumes" +msgstr "LVM-volumer" + +msgid "HSM" +msgstr "HSM" + +msgid "Partitions to be encrypted" +msgstr "Partisjoner som skal krypteres" + +msgid "LVM volumes to be encrypted" +msgstr "LVM-volumer som skal krypteres" + +msgid "HSM device" +msgstr "HSM-enhet" + +msgid "Select encryption type" +msgstr "Velg krypteringstype" + +msgid "Enter disk encryption password (leave blank for no encryption)" +msgstr "Skriv inn passord for diskkryptering (la stå tomt for ingen kryptering)" + +msgid "Select a FIDO2 device to use for HSM" +msgstr "Velg en FIDO2-enhet å bruke for HSM" + +msgid "Enter iteration time for LUKS encryption (in milliseconds)" +msgstr "Angi iterasjonstid for LUKS-kryptering (i millisekunder)" + +msgid "Higher values increase security but slow down boot time" +msgstr "Høyere verdier øker sikkerheten, men gir lengre oppstartstid" + +#, python-brace-format +msgid "Default: {}ms, Recommended range: 1000-60000" +msgstr "Standard: {} ms, anbefalt område: 1000–60000" + +msgid "Iteration time must be at least 100ms" +msgstr "Iterasjonstiden må være minst 100 ms" + +msgid "Iteration time must be at most 120000ms" +msgstr "Iterasjonstiden kan være maksimalt 120000 ms" + +msgid "Please enter a valid number" +msgstr "Skriv inn et gyldig tall" + +msgid "Suggest partition layout" +msgstr "Foreslå partisjonsoppsett" + +msgid "Remove all newly added partitions" +msgstr "Fjern alle nylig lagt til partisjoner" + +msgid "Assign mountpoint" +msgstr "Tildel monteringspunkt" + +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Merk/fjern merking for formatering (sletter data)" + +msgid "Mark/Unmark as bootable" +msgstr "Merk/fjern merking som oppstartbar" + +msgid "Mark/Unmark as ESP" +msgstr "Merk/fjern merking som ESP" + +msgid "Mark/Unmark as XBOOTLDR" +msgstr "Merk/fjern merking som XBOOTLDR" + +msgid "Change filesystem" +msgstr "Endre filsystem" + +msgid "Mark/Unmark as compressed" +msgstr "Merk/fjern merking som komprimert" + +msgid "Mark/Unmark as nodatacow" +msgstr "Merk/fjern merking som nodatacow" + +msgid "Set subvolumes" +msgstr "Angi undervolumer" + +msgid "Delete partition" +msgstr "Slett partisjon" + +#, python-brace-format +msgid "Partition management: {}" +msgstr "Partisjonsbehandling: {}" + +#, python-brace-format +msgid "Total length: {}" +msgstr "Total lengde: {}" + +msgid "Partition - New" +msgstr "Partisjon – Ny" + +msgid "Partition" +msgstr "Partisjon" + +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "Denne partisjonen er for øyeblikket kryptert. For å formatere den må et filsystem angis" + +msgid "Partition mount-points are relative to inside the installation, the boot would be /boot as an example." +msgstr "Monteringspunkter for partisjoner er relative til innsiden av installasjonen; oppstart vil for eksempel være /boot." + +msgid "Enter a mountpoint" +msgstr "Angi et monteringspunkt" + +msgid "Invalid size" +msgstr "Ugyldig størrelse" + +#, python-brace-format +msgid "Selected free space segment on device {}:" +msgstr "Valgt ledig diskområde på enhet {}:" + +#, python-brace-format +msgid "Size: {} / {}" +msgstr "Størrelse: {} / {}" + +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "Alle angitte verdier kan ha en enhet som suffiks: %, B, KB, KiB, MB, MiB ..." + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "Hvis ingen enhet angis, tolkes verdien som sektorer" + +#, python-brace-format +msgid "Enter a size (default: {}): " +msgstr "Angi en størrelse (standard: {}): " + +msgid "This will remove all newly added partitions, continue?" +msgstr "Dette vil fjerne alle nylig lagt til partisjoner. Fortsette?" + +msgid "Add subvolume" +msgstr "Legg til undervolum" + +msgid "Edit subvolume" +msgstr "Rediger undervolum" + +msgid "Delete subvolume" +msgstr "Slett undervolum" + +msgid "Value cannot be empty" +msgstr "Verdien kan ikke være tom" + +msgid "Enter subvolume name" +msgstr "Angi navn på undervolum" + +msgid "Subvolume name" +msgstr "Navn på undervolum" + +msgid "Enter subvolume mountpoint" +msgstr "Angi monteringspunkt for undervolum" + +msgid "Exit archinstall" +msgstr "Avslutt archinstall" + +msgid "Reboot system" +msgstr "Start systemet på nytt" + +msgid "chroot into installation for post-installation configurations" +msgstr "chroot inn i installasjonen for konfigurasjon etter installasjon" + +msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" +msgstr "Vil du bruke automatisk tidssynkronisering (NTP) med standard tidsservere?\n" + +msgid "" +"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 "" +"Maskinvaretid og andre konfigurasjonssteg i etterkant kan være nødvendige for at NTP skal fungere.\n" +"For mer informasjon, se Arch-wikien" + +msgid "Enter a hostname" +msgstr "Angi et vertsnavn" + +msgid "Select timezone" +msgstr "Velg tidssone" + +msgid "What would you like to do next?" +msgstr "Hva vil du gjøre nå?" + +msgid "Select which kernel(s) to install" +msgstr "Velg hvilke(n) kjerne(r) som skal installeres" + +msgid "For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options." +msgstr "For best kompatibilitet med AMD-maskinvaren din kan du bruke enten det helt åpne kildekode-alternativet eller AMD/ATI-alternativet." + +msgid "For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n" +msgstr "For best kompatibilitet med Intel-maskinvaren din kan du bruke enten det helt åpne kildekode-alternativet eller Intel-alternativet.\n" + +msgid "For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n" +msgstr "For best kompatibilitet med Nvidia-maskinvaren din kan du bruke den proprietære Nvidia-driveren.\n" + +msgid "Would you like to use swap on zram?" +msgstr "Vil du bruke veksleminne på zram?" + +msgid "Select zram compression algorithm:" +msgstr "Velg komprimeringsalgoritme for zram:" + +msgid "Archinstall language" +msgstr "Archinstall-språk" + +msgid "Applications" +msgstr "Programmer" + +msgid "Network configuration" +msgstr "Nettverkskonfigurasjon" + +msgid "Save configuration" +msgstr "Lagre konfigurasjon" + +msgid "Install" +msgstr "Installer" + +msgid "Abort" +msgstr "Avbryt" + +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Enten root-passord eller minst én bruker med sudo-rettigheter må angis" + +msgid "The selected desktop profile requires a regular user to log in via the greeter" +msgstr "Den valgte skrivebordsprofilen krever en vanlig bruker for å logge inn via innloggingsskjermen" + +msgid "Language" +msgstr "Språk" + +msgid "NTP" +msgstr "NTP" + +msgid "LVM configuration type" +msgstr "LVM-konfigurasjonstype" + +#, python-brace-format +msgid "Btrfs snapshot type: {}" +msgstr "Btrfs-øyeblikksbildetype: {}" + +msgid "Swap on zram" +msgstr "Veksleminne på zram" + +msgid "Compression algorithm" +msgstr "Komprimeringsalgoritme" + +msgid "Parallel Downloads" +msgstr "Parallelle nedlastinger" + +msgid "Color" +msgstr "Farge" + +msgid "Kernel" +msgstr "Kjerne" + +msgid "No network configuration selected. Network will need to be set up manually on the installed system." +msgstr "Ingen nettverkskonfigurasjon valgt. Nettverket må settes opp manuelt på det installerte systemet." + +msgid "Missing configurations:" +msgstr "Manglende konfigurasjoner:" + +msgid "Invalid configuration:" +msgstr "Ugyldig konfigurasjon:" + +msgid "Ready to install" +msgstr "Klar til å installere" + +msgid "Warnings:" +msgstr "Advarsler:" + +msgid "Profiles" +msgstr "Profiler" + +msgid "Graphics driver" +msgstr "Grafikkdriver" + +msgid "Greeter" +msgstr "Innloggingsskjerm (greeter)" + +msgid "Selected mirror regions" +msgstr "Valgte speilregioner" + +msgid "Custom servers" +msgstr "Egendefinerte servere" + +msgid "Optional repositories" +msgstr "Valgfrie pakkebrønner" + +msgid "Custom repositories" +msgstr "Egendefinerte pakkebrønner" + +#, python-brace-format +msgid "[!] A log file has been created here: {}" +msgstr "[!] En loggfil er opprettet her: {}" + +msgid "Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr "Send inn dette problemet (og filen) til https://github.com/archlinux/archinstall/issues" + +msgid "Syncing the system..." +msgstr "Synkroniserer systemet ..." + +msgid "Waiting for time sync (timedatectl show) to complete." +msgstr "Venter på at tidssynkronisering (timedatectl show) skal fullføres." + +msgid "Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/" +msgstr "Tidssynkroniseringen fullføres ikke. Mens du venter – sjekk dokumentasjonen for løsninger: https://archinstall.readthedocs.io/" + +msgid "Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)" +msgstr "Hopper over venting på automatisk tidssynkronisering (dette kan føre til problemer hvis tiden ikke er synkronisert under installasjonen)" + +msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete." +msgstr "Venter på at synkronisering av Arch Linux-nøkkelring (archlinux-keyring-wkd-sync) skal fullføres." + +msgid "Keyboard layout" +msgstr "Tastaturoppsett" + +msgid "Locale language" +msgstr "Lokalitetsspråk" + +msgid "Locale encoding" +msgstr "Lokalitetskoding" + +msgid "Console font" +msgstr "Konsollskrift" + +msgid "Back" +msgstr "Tilbake" + +msgid "Are you sure you want to reset this setting?" +msgstr "Er du sikker på at du vil tilbakestille denne innstillingen?" + +msgid "Confirm and exit" +msgstr "Bekreft og avslutt" + +msgid "Cancel" +msgstr "Avbryt" + +msgid "Password strength: Weak" +msgstr "Passordstyrke: Svakt" + +msgid "Password strength: Moderate" +msgstr "Passordstyrke: Middels" + +msgid "Password strength: Strong" +msgstr "Passordstyrke: Sterkt" + +msgid "Confirm password" +msgstr "Bekreft passord" + +msgid "The password did not match, please try again" +msgstr "Passordene var ikke like, prøv igjen" + +msgid "Not a valid directory" +msgstr "Ikke en gyldig mappe" + +msgid "Do you really want to abort?" +msgstr "Vil du virkelig avbryte?" + +msgid "Add a custom repository" +msgstr "Legg til en egendefinert pakkebrønn" + +msgid "Change custom repository" +msgstr "Endre egendefinert pakkebrønn" + +msgid "Delete custom repository" +msgstr "Slett egendefinert pakkebrønn" + +msgid "Enter a repository name" +msgstr "Angi et navn på pakkebrønnen" + +msgid "Name" +msgstr "Navn" + +msgid "Enter the repository url" +msgstr "Angi URL-en til pakkebrønnen" + +msgid "Url" +msgstr "URL" + +msgid "Select signature check" +msgstr "Velg signatursjekk" + +msgid "Signature check" +msgstr "Signatursjekk" + +msgid "Select signature option" +msgstr "Velg signaturvalg" + +msgid "Add a custom server" +msgstr "Legg til en egendefinert server" + +msgid "Change custom server" +msgstr "Endre egendefinert server" + +msgid "Delete custom server" +msgstr "Slett egendefinert server" + +msgid "Enter server url" +msgstr "Angi server-URL" + +msgid "Select regions" +msgstr "Velg regioner" + +msgid "Add custom servers" +msgstr "Legg til egendefinerte servere" + +msgid "Add custom repository" +msgstr "Legg til egendefinert pakkebrønn" + +msgid "Additional repositories" +msgstr "Flere pakkebrønner" + +msgid "Loading mirror regions..." +msgstr "Laster speilregioner ..." + +msgid "Select mirror regions to be enabled" +msgstr "Velg speilregioner som skal aktiveres" + +msgid "Select optional repositories to be enabled" +msgstr "Velg valgfrie pakkebrønner som skal aktiveres" + +msgid "Unicode font coverage for most languages" +msgstr "Unicode-skriftdekning for de fleste språk" + +msgid "color emoji for browsers and apps" +msgstr "fargeemoji for nettlesere og apper" + +msgid "Chinese, Japanese, Korean characters" +msgstr "kinesiske, japanske og koreanske tegn" + +msgid "Arial/Times/Courier replacement, Cyrillic support for Steam/games" +msgstr "erstatning for Arial/Times/Courier, kyrillisk støtte for Steam/spill" + +msgid "wide Unicode coverage, good fallback font" +msgstr "bred Unicode-dekning, god reserveskrift" + +msgid "Zram enabled" +msgstr "Zram aktivert" + +#, python-brace-format +msgid "Zram algorithm {}" +msgstr "Zram-algoritme {}" + +msgid "Bluetooth enabled" +msgstr "Bluetooth aktivert" + +#, python-brace-format +msgid "Audio server \"{}\"" +msgstr "Lydserver «{}»" + +#, python-brace-format +msgid "Power management \"{}\"" +msgstr "Strømstyring «{}»" + +msgid "Print service enabled" +msgstr "Utskriftstjeneste aktivert" + +#, python-brace-format +msgid "Firewall \"{}\"" +msgstr "Brannmur «{}»" + +#, python-brace-format +msgid "Extra fonts \"{}\"" +msgstr "Ekstra skrifter «{}»" + +msgid "Passwordless login" +msgstr "Passordløs innlogging" + +msgid "Second factor login" +msgstr "Tofaktorinnlogging" + +msgid "Root password set" +msgstr "Root-passord angitt" + +#, python-brace-format +msgid "Configured {} user(s)" +msgstr "Konfigurerte {} bruker(e)" + +msgid "U2F set up" +msgstr "U2F satt opp" + +#, python-brace-format +msgid "Bootloader \"{}\"" +msgstr "Oppstartslaster «{}»" + +msgid "UKI enabled" +msgstr "UKI aktivert" + +msgid "Removable" +msgstr "Flyttbar" + +#, python-brace-format +msgid "Plymouth \"{}\"" +msgstr "Plymouth «{}»" + +msgid "Use a best-effort default partition layout" +msgstr "Bruk et anbefalt standard partisjonsoppsett" + +msgid "Manual Partitioning" +msgstr "Manuell partisjonering" + +msgid "Pre-mounted configuration" +msgstr "Forhåndsmontert konfigurasjon" + +msgid "Default" +msgstr "Standard" + +msgid "Manual" +msgstr "Manuell" + +msgid "Pre-mount" +msgstr "Forhåndsmontering" + +#, python-brace-format +msgid "{} layout" +msgstr "{}-oppsett" + +#, python-brace-format +msgid "Devices {}" +msgstr "Enheter {}" + +msgid "LVM set up" +msgstr "LVM satt opp" + +#, python-brace-format +msgid "{} encryption" +msgstr "{}-kryptering" + +#, python-brace-format +msgid "Btrfs snapshot \"{}\"" +msgstr "Btrfs-øyeblikksbilde «{}»" + +msgid "Unknown" +msgstr "Ukjent" + +msgid "Default layout" +msgstr "Standardoppsett" + +msgid "No Encryption" +msgstr "Ingen kryptering" + +msgid "LUKS" +msgstr "LUKS" + +msgid "LVM on LUKS" +msgstr "LVM på LUKS" + +msgid "LUKS on LVM" +msgstr "LUKS på LVM" + +#, python-brace-format +msgid "Keyboard layout \"{}\"" +msgstr "Tastaturoppsett «{}»" + +#, python-brace-format +msgid "Locale language \"{}\"" +msgstr "Lokalitetsspråk «{}»" + +#, python-brace-format +msgid "Locale encoding \"{}\"" +msgstr "Lokalitetskoding «{}»" + +#, python-brace-format +msgid "Console font \"{}\"" +msgstr "Konsollskrift «{}»" + +#, python-brace-format +msgid "Mirror regions \"{}\"" +msgstr "Speilregioner «{}»" + +#, python-brace-format +msgid "Optional repositories \"{}\"" +msgstr "Valgfrie pakkebrønner «{}»" + +msgid "Custom servers set up" +msgstr "Egendefinerte servere satt opp" + +msgid "Custom repositories set up" +msgstr "Egendefinerte pakkebrønner satt opp" + +msgid "Copy ISO network configuration to installation" +msgstr "Kopier ISO-nettverkskonfigurasjon til installasjonen" + +msgid "Use Network Manager (default backend)" +msgstr "Bruk Network Manager (standard bakgrunnssystem)" + +msgid "Use Network Manager (iwd backend)" +msgstr "Bruk Network Manager (iwd-bakgrunnssystem)" + +msgid "Use standalone iwd" +msgstr "Bruk frittstående iwd" + +msgid "Manual configuration" +msgstr "Manuell konfigurasjon" + +msgid "Package group:" +msgstr "Pakkegruppe:" + +msgid "Color enabled" +msgstr "Farge aktivert" + +#, python-brace-format +msgid "{} graphics driver" +msgstr "{}-grafikkdriver" + +#, python-brace-format +msgid "{} greeter" +msgstr "{}-innloggingsskjerm" + +msgid "very weak" +msgstr "svært svakt" + +msgid "weak" +msgstr "svakt" + +msgid "moderate" +msgstr "middels" + +msgid "strong" +msgstr "sterkt" + +msgid "Add interface" +msgstr "Legg til grensesnitt" + +msgid "Edit interface" +msgstr "Rediger grensesnitt" + +msgid "Delete interface" +msgstr "Slett grensesnitt" + +msgid "Select an interface" +msgstr "Velg et grensesnitt" + +msgid "You need to enter a valid IP in IP-config mode" +msgstr "Du må angi en gyldig IP i IP-konfigurasjonsmodus" + +#, python-brace-format +msgid "Select which mode to configure for \"{}\"" +msgstr "Velg hvilken modus som skal konfigureres for «{}»" + +#, python-brace-format +msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " +msgstr "Angi IP og subnett for {} (eksempel: 192.168.0.5/24): " + +msgid "Enter your gateway (router) IP address (leave blank for none)" +msgstr "Angi IP-adressen til gatewayen (ruteren) (la stå tomt for ingen)" + +msgid "Enter your DNS servers with space separated (leave blank for none)" +msgstr "Angi DNS-serverne dine, atskilt med mellomrom (la stå tomt for ingen)" + +msgid "Choose network configuration" +msgstr "Velg nettverkskonfigurasjon" + +msgid "Recommended: Network Manager for desktop, Manual for server" +msgstr "Anbefalt: Network Manager for skrivebord, Manuell for server" + +msgid "Configure interfaces" +msgstr "Konfigurer grensesnitt" + +msgid "No network connection found" +msgstr "Ingen nettverkstilkobling funnet" + +msgid "Would you like to connect to a Wifi?" +msgstr "Vil du koble til et wifi?" + +msgid "No wifi interface found" +msgstr "Ingen wifi-grensesnitt funnet" + +msgid "No wifi networks found" +msgstr "Ingen wifi-nettverk funnet" + +msgid "Select wifi network to connect to" +msgstr "Velg wifi-nettverk å koble til" + +msgid "Scanning wifi networks..." +msgstr "Søker etter wifi-nettverk ..." + +msgid "Failed setting up wifi" +msgstr "Kunne ikke sette opp wifi" + +msgid "Enter wifi password" +msgstr "Skriv inn wifi-passord" + +#, python-brace-format +msgid "Repositories: {}" +msgstr "Pakkebrønner: {}" + +msgid "Loading packages..." +msgstr "Laster pakker ..." + +msgid "No packages found" +msgstr "Ingen pakker funnet" + +msgid "Only packages such as base, sudo, linux, linux-firmware, efibootmgr and optional profile packages are installed." +msgstr "Bare pakker som base, sudo, linux, linux-firmware, efibootmgr og valgfrie profilpakker installeres." + +msgid "Note: base-devel is no longer installed by default. Add it here if you need build tools." +msgstr "Merk: base-devel installeres ikke lenger som standard. Legg den til her hvis du trenger byggeverktøy." + +msgid "Select any packages from the below list that should be installed additionally" +msgstr "Velg eventuelle pakker fra listen nedenfor som skal installeres i tillegg" + +#, python-brace-format +msgid "Enter the number of parallel downloads (1-{})" +msgstr "Angi antall parallelle nedlastinger (1–{})" + +#, python-brace-format +msgid "Value must be between 1 and {}" +msgstr "Verdien må være mellom 1 og {}" + +msgid "Enable colored output for pacman" +msgstr "Aktiver farget utdata for pacman" + +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "Pacman kjører allerede. Venter maksimalt 10 minutter på at den skal avsluttes." + +msgid "Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall." +msgstr "En eksisterende pacman-lås ble aldri frigjort. Rydd opp i eventuelle eksisterende pacman-økter før du bruker archinstall." + +msgid "The proprietary Nvidia driver is not supported by Sway." +msgstr "Den proprietære Nvidia-driveren støttes ikke av Sway." + +msgid "It is likely that you will run into issues, are you okay with that?" +msgstr "Det er sannsynlig at du vil støte på problemer. Er det greit for deg?" + +msgid "Selected profiles: " +msgstr "Valgte profiler: " + +msgid "Select which greeter to install" +msgstr "Velg hvilken innloggingsskjerm som skal installeres" + +msgid "Select a profile type" +msgstr "Velg en profiltype" + +#, python-brace-format +msgid "Unable to fetch profile from specified url: {}" +msgstr "Kunne ikke hente profil fra angitt URL: {}" + +#, python-brace-format +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "Profiler må ha unike navn, men profildefinisjoner med duplikatnavn ble funnet: {}" + +msgid "Add a user" +msgstr "Legg til en bruker" + +msgid "Change password" +msgstr "Endre passord" + +msgid "Promote/Demote user" +msgstr "Forfrem/degrader bruker" + +msgid "Delete User" +msgstr "Slett bruker" + +msgid "User" +msgstr "Bruker" + +msgid "Enter new password" +msgstr "Skriv inn nytt passord" + +msgid "The username you entered is invalid" +msgstr "Brukernavnet du oppga er ugyldig" + +msgid "Enter a username" +msgstr "Angi et brukernavn" + +msgid "Username" +msgstr "Brukernavn" + +msgid "Enter a password" +msgstr "Skriv inn et passord" + +#, python-brace-format +msgid "Should \"{}\" be a superuser (sudo)?\n" +msgstr "Skal «{}» være superbruker (sudo)?\n" + +#, python-brace-format +msgid "About to upload \"{}\" to the publicly accessible {}" +msgstr "Er i ferd med å laste opp «{}» til det offentlig tilgjengelige {}" + +msgid "Do you want to continue?" +msgstr "Vil du fortsette?" + +#, python-brace-format +msgid "Log uploaded successfully. URL: {}" +msgstr "Loggen ble lastet opp. URL: {}" + +msgid "Failed to upload log." +msgstr "Kunne ikke laste opp loggen." + +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "Archinstall krever root-rettigheter for å kjøre. Se --help for mer." + +msgid "New version available" +msgstr "Ny versjon tilgjengelig" + +msgid "Starting device modifications in " +msgstr "Starter endringer på enheten om " + +msgid "Ok" +msgstr "OK" + +msgid "Input cannot be empty" +msgstr "Inndata kan ikke være tom" + +msgid "Yes" +msgstr "Ja" + +msgid "No" +msgstr "Nei" + +msgid " (default)" +msgstr " (standard)" + diff --git a/archinstall/locales/uk/LC_MESSAGES/base.mo b/archinstall/locales/uk/LC_MESSAGES/base.mo index bf82c210..a101de6d 100644 Binary files a/archinstall/locales/uk/LC_MESSAGES/base.mo and b/archinstall/locales/uk/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po index 65ffacc7..9df7edf4 100644 --- a/archinstall/locales/uk/LC_MESSAGES/base.po +++ b/archinstall/locales/uk/LC_MESSAGES/base.po @@ -2395,3 +2395,33 @@ msgstr "набір апаратних пристроїв, напр. клавіа msgid "No network configuration selected. Network will need to be set up manually on the installed system." msgstr "Конфігурацію мережі не обрано. Мережу доведеться налаштувати вручну на встановленій системі." + +msgid "Plymouth" +msgstr "Plymouth" + +msgid "" +"Plymouth adds a cosmetic boot splash but can cause boot problems on some " +"setups:" +msgstr "" +"Plymouth додає косметичну заставку завантаження, але на деяких конфігураціях " +"може спричинити проблеми із завантаженням:" + +msgid "black screen with the NVIDIA driver" +msgstr "чорний екран із драйвером NVIDIA" + +msgid "hidden password prompt with disk encryption (LUKS)" +msgstr "прихований запит пароля при шифруванні диска (LUKS)" + +msgid "Would you like to enable it?" +msgstr "Увімкнути Plymouth?" + +msgid "Select Plymouth theme" +msgstr "Оберіть тему Plymouth" + +#, python-brace-format +msgid "Plymouth \"{}\"" +msgstr "Plymouth \"{}\"" + +#, python-brace-format +msgid "{} graphics driver" +msgstr "Графічний драйвер {}" diff --git a/archinstall/locales/vi/LC_MESSAGES/base.mo b/archinstall/locales/vi/LC_MESSAGES/base.mo new file mode 100644 index 00000000..164367e2 Binary files /dev/null and b/archinstall/locales/vi/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/vi/LC_MESSAGES/base.po b/archinstall/locales/vi/LC_MESSAGES/base.po new file mode 100644 index 00000000..6b62bb62 --- /dev/null +++ b/archinstall/locales/vi/LC_MESSAGES/base.po @@ -0,0 +1,2753 @@ +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: BlackCat \n" +"Language-Team: \n" +"Language: vi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.9\n" + +msgid "[!] A log file has been created here: {} {}" +msgstr "[!] Một tệp nhật ký (log) đã được tạo ở đây: {} {}" + +msgid " Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr "" +" Vui lòng gửi vấn đề này (và tệp tin) đến địa chỉ https://github.com/archlinux/archinstall/issues" + +msgid "Do you really want to abort?" +msgstr "Bạn thật sự có muốn hủy bỏ không?" + +msgid "And one more time for verification: " +msgstr "Xin hãy xác nhận một lần nữa: " + +msgid "Would you like to use swap on zram?" +msgstr "Bạn có muốn sử dụng swap trên zram không?" + +msgid "Desired hostname for the installation: " +msgstr "Tên của máy mà bạn mong muốn trong quá trình cài đặt: " + +msgid "Username for required superuser with sudo privileges: " +msgstr "Tên người dùng cho tài khoản quản trị có quyền sudo: " + +msgid "Any additional users to install (leave blank for no users): " +msgstr "Thêm người dùng khác (để trống nếu không cần): " + +msgid "Should this user be a superuser (sudoer)?" +msgstr "Bạn có muốn người dùng này là quản trị viên (superuser/sudoer) không ?" + +msgid "Select a timezone" +msgstr "Vui lòng chọn múi giờ" + +msgid "Would you like to use GRUB as a bootloader instead of systemd-boot?" +msgstr "Bạn có muốn GRUB là trình khởi động (bootloader) mặc định thay vì systemd-boot không?" + +msgid "Choose a bootloader" +msgstr "Chọn một trình khởi động (bootloader)" + +msgid "Choose an audio server" +msgstr "Chọn hệ thống âm thanh" + +msgid "" +"Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile " +"packages are installed." +msgstr "" +"Chỉ có base, base-devel, linux, linux-firmware, efibootmgr và các gói cấu hình (profile) tùy chọn " +"sẽ được cài đặt." + +msgid "Note: base-devel is no longer installed by default. Add it here if you need build tools." +msgstr "" +"Lưu ý: base-devel không còn được cài đặt mặc định nữa. Thêm nó ở đây nếu bạn cần công cụ biên dịch." + +msgid "" +"If you desire a web browser, such as firefox or chromium, you may specify it in the following " +"prompt." +msgstr "" +"Nếu bạn cần trình duyệt như Firefox hay Chromium, bạn có thể chỉ định chúng ở dòng nhắc dưới đây." + +msgid "Write additional packages to install (space separated, leave blank to skip): " +msgstr "Ghi tất cả gói mà bạn muốn cài đặt (cách nhau bằng dấu cách, bỏ trống để bỏ qua): " + +msgid "Copy ISO network configuration to installation" +msgstr "Sao chép cấu hình mạng của ISO sang thư mục cài đặt" + +msgid "Use NetworkManager (necessary for configuring internet graphically in GNOME and KDE)" +msgstr "Dùng NetworkManager (cần thiết cho cấu hình mạng bằng giao diện đồ họa trên GNOME và KDE)" + +msgid "Select one network interface to configure" +msgstr "Chọn một giao diện mạng để cấu hình" + +msgid "Select which mode to configure for \"{}\" or skip to use default mode \"{}\"" +msgstr "Chọn chế độ nào để cấu hình cho \"{}\" hoặc bỏ qua để dùng cái mặc định \"{}\"" + +#, python-brace-format +msgid "Enter the IP and subnet for {} (example: 192.168.0.5/24): " +msgstr "Nhập địa chỉ IP và subnet cho {} (ví dụ: 192.168.0.5/24): " + +msgid "Enter your gateway (router) IP address or leave blank for none: " +msgstr "Nhập địa chỉ IP gateway (router) của bạn hoặc bỏ trống nếu không có: " + +msgid "Enter your DNS servers (space separated, blank for none): " +msgstr "Nhập máy chủ DNS mà bạn muốn (cách nhau bởi dấu cách, bỏ trống nếu muốn dùng mặc định): " + +msgid "Select which filesystem your main partition should use" +msgstr "Chọn hệ thống tệp nào mà phân vùng chính của bạn nên dùng" + +msgid "Current partition layout" +msgstr "Bố cục phân vùng hiện tại" + +msgid "" +"Select what to do with\n" +"{}" +msgstr "" +"Chọn hành động cho\n" +"{}" + +msgid "Enter a desired filesystem type for the partition" +msgstr "Vui lòng nhập loại hệ thống tập tin mà bạn muốn cho phân vùng" + +msgid "Enter the start location (in parted units: s, GB, %, etc. ; default: {}): " +msgstr "Nhập vị trí bắt đầu của phân vùng (theo đơn vị phân vùng: s, GB, %, v.v.; mặc định: {}): " + +msgid "Enter the end location (in parted units: s, GB, %, etc. ; ex: {}): " +msgstr "Nhập vị trí kết thúc của phân vùng (theo đơn vị phân vùng: s, GB, %, v.v.; mặc định: {}): " + +msgid "{} contains queued partitions, this will remove those, are you sure?" +msgstr "{} chứa các phân vùng trong hàng đợi, hành động này sẽ loại bỏ chúng, bạn có chắc chắn không?" + +msgid "" +"{}\n" +"\n" +"Select by index which partitions to delete" +msgstr "" +"{}\n" +"\n" +"Chọn số thứ tự của phân vùng cần xóa" + +msgid "" +"{}\n" +"\n" +"Select by index which partition to mount where" +msgstr "" +"{}\n" +"\n" +"Chọn số thứ tự phân vùng nàu cần gắn (mount)" + +msgid "" +" * Partition mount-points are relative to inside the installation, the boot would be /boot as an " +"example." +msgstr "" +" * Các điểm gắn phân vùng được tính tương đối với bên trong hệ thống mới, ví dụ phân vùng khởi động " +"sẽ là /boot." + +msgid "Select where to mount partition (leave blank to remove mountpoint): " +msgstr "Chọn điểm gắn cho phân vùng (để trống để xóa điểm gắn): " + +msgid "" +"{}\n" +"\n" +"Select which partition to mask for formatting" +msgstr "" +"{}\n" +"\n" +"Chọn phân vùng muốn đánh dấu để định dạng" + +msgid "" +"{}\n" +"\n" +"Select which partition to mark as encrypted" +msgstr "" +"{}\n" +"\n" +"Chọn phân vùng để mã hóa" + +msgid "" +"{}\n" +"\n" +"Select which partition to mark as bootable" +msgstr "" +"{}\n" +"\n" +"Chọn phân vùng làm phân vùng khởi động" + +msgid "" +"{}\n" +"\n" +"Select which partition to set a filesystem on" +msgstr "" +"{}\n" +"\n" +"Chọn phân vùng để đặt tệp hệ thống" + +msgid "Enter a desired filesystem type for the partition: " +msgstr "Chọn loại hệ thống tập tin mà bạn muốn cho phân vùng: " + +msgid "Archinstall language" +msgstr "Ngôn ngữ (Archinstall)" + +msgid "Wipe all selected drives and use a best-effort default partition layout" +msgstr "Xóa sạch các ổ đĩa đã chọn và dùng bố cục phân vùng mặc định tốt nhất" + +msgid "Select what to do with each individual drive (followed by partition usage)" +msgstr "Chọn việc với mỗi ổ đĩa (theo sau là cách sử dụng phân vùng)" + +msgid "Select what you wish to do with the selected block devices" +msgstr "Chọn những gì bạn muốn làm với các thiết bị đã chọn" + +msgid "" +"This is a list of pre-programmed profiles, they might make it easier to install things like desktop " +"environments" +msgstr "" +"Đây là danh sách các cấu hình được lập trình sẵn, chúng có thể giúp cài đặt những thứ như môi " +"trường máy tính dễ dàng hơn" + +msgid "Select keyboard layout" +msgstr "Bố cục bàn phím hiện tại" + +msgid "Select one of the regions to download packages from" +msgstr "Chọn một trong các khu vực để tải xuống các gói" + +msgid "Select one or more hard drives to use and configure" +msgstr "Chọn một hoặc nhiều ổ cứng để sử dụng và cấu hình" + +msgid "" +"For the best compatibility with your AMD hardware, you may want to use either the all open-source " +"or AMD / ATI options." +msgstr "" +"Để có khả năng tương thích tốt nhất với phần cứng AMD của bạn, bạn có thể muốn sử dụng tùy chọn tất " +"cả nguồn mở hoặc 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 "" +"Để có khả năng tương thích tốt nhất với phần cứng Intel của bạn, bạn có thể muốn sử dụng tùy chọn " +"tất cả nguồn mở hoặc Intel.\n" + +msgid "" +"For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary " +"driver.\n" +msgstr "" +"Để có khả năng tương thích tốt nhất với phần cứng Nvidia của bạn, bạn có thể muốn sử dụng trình " +"điều khiển độc quyền của Nvidia.\n" + +msgid "" +"\n" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "" +"\n" +"\n" +"Chọn trình điều khiển đồ họa hoặc để trống để cài đặt tất cả trình điều khiển nguồn mở" + +msgid "All open-source (default)" +msgstr "Tất cả nguồn mở (mặc định)" + +msgid "Choose which kernels to use or leave blank for default \"{}\"" +msgstr "Chọn nhân nào sẽ sử dụng hoặc để trống mặc định \"{}\"" + +msgid "Choose which locale language to use" +msgstr "Chọn ngôn ngữ hệ thống muốn sử dụng" + +msgid "Choose which locale encoding to use" +msgstr "Chọn mã hóa ngôn ngữ nào sẽ sử dụng" + +msgid "Select one of the values shown below: " +msgstr "Chọn một trong các giá trị hiển thị bên dưới: " + +msgid "Select one or more of the options below: " +msgstr "Chọn một hoặc nhiều tùy chọn bên dưới: " + +msgid "Adding partition...." +msgstr "Đang thêm phân vùng...." + +msgid "You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's." +msgstr "" +"Vui lòng nhập loại hệ thống tập tin (fs) hợp lệ để tiếp tục. Xem `man parted` để biết loại fs hợp " +"lệ." + +msgid "Error: Listing profiles on URL \"{}\" resulted in:" +msgstr "Lỗi: Liệt kê cấu hình trên URL \"{}\" dẫn đến:" + +msgid "Error: Could not decode \"{}\" result as JSON:" +msgstr "Lỗi: Không thể giải mã kết quả \"{}\" dưới dạng JSON:" + +msgid "Keyboard layout" +msgstr "Bố cục bàn phím" + +msgid "Mirror region" +msgstr "Máy chủ bản sao" + +msgid "Locale language" +msgstr "Ngôn ngữ hệ thống (Locale)" + +msgid "Locale encoding" +msgstr "Mã hóa ngôn ngữ hệ thống" + +msgid "Console font" +msgstr "Phông chữ console" + +msgid "Drive(s)" +msgstr "(Các) ổ đĩa" + +msgid "Disk layout" +msgstr "Bố trí đĩa" + +msgid "Encryption password" +msgstr "Mật khẩu mã hóa" + +msgid "Swap" +msgstr "Bộ nhớ đệm (Swap)" + +msgid "Bootloader" +msgstr "Trình khởi động" + +msgid "Root password" +msgstr "Mật khẩu root" + +msgid "Superuser account" +msgstr "Tài khoản siêu người dùng" + +msgid "User account" +msgstr "Tài khoản người dùng" + +msgid "Profile" +msgstr "Cấu hình (Profile)" + +msgid "Audio" +msgstr "Âm thanh" + +msgid "Kernels" +msgstr "Nhân" + +msgid "Additional packages" +msgstr "Gói bổ sung" + +msgid "Network configuration" +msgstr "Cấu hình mạng" + +msgid "Automatic time sync (NTP)" +msgstr "Đồng bộ hóa thời gian tự động (NTP)" + +#, fuzzy +msgid "Install ({} config(s) missing)" +msgstr "Cài đặt (thiếu (các) cấu hình {})" + +msgid "" +"You decided to skip harddrive selection\n" +"and 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?" +msgstr "" +"Bạn quyết định bỏ qua việc lựa chọn ổ cứng\n" +"và sẽ sử dụng bất kỳ thiết lập ổ đĩa nào được gắn ở {} (thử nghiệm)\n" +"CẢNH BÁO: Archinstall sẽ không kiểm tra tính phù hợp của thiết lập này\n" +"Bạn có muốn tiếp tục không?" + +msgid "Re-using partition instance: {}" +msgstr "Sử dụng lại phiên bản phân vùng: {}" + +msgid "Create a new partition" +msgstr "Tạo một phân vùng mới" + +msgid "Delete a partition" +msgstr "Xóa một phân vùng" + +msgid "Clear/Delete all partitions" +msgstr "Xóa tất cả các phân vùng" + +msgid "Assign mount-point for a partition" +msgstr "Gán điểm gắn cho một phân vùng" + +msgid "Mark/Unmark a partition to be formatted (wipes data)" +msgstr "Đánh dấu/Bỏ đánh dấu phân vùng cần định dạng (xóa dữ liệu)" + +msgid "Mark/Unmark a partition as encrypted" +msgstr "Đánh dấu/Bỏ đánh dấu phân vùng là đã mã hóa" + +msgid "Mark/Unmark a partition as bootable (automatic for /boot)" +msgstr "Đánh dấu/Bỏ đánh dấu phân vùng là có khả năng khởi động (tự động cho/boot)" + +msgid "Set desired filesystem for a partition" +msgstr "Đặt hệ thống tập tin mong muốn cho một phân vùng" + +msgid "Abort" +msgstr "Hủy bỏ" + +msgid "Hostname" +msgstr "Tên máy" + +msgid "Not configured, unavailable unless setup manually" +msgstr "Chưa được định cấu hình, không khả dụng trừ khi được thiết lập thủ công" + +msgid "Timezone" +msgstr "Múi giờ" + +msgid "Set/Modify the below options" +msgstr "Đặt/Sửa đổi các tùy chọn bên dưới" + +msgid "Install" +msgstr "Cài đặt" + +msgid "" +"Use ESC to skip\n" +"\n" +msgstr "" +"Sử dụng ESC để bỏ qua\n" +"\n" + +msgid "Suggest partition layout" +msgstr "Đề xuất bố trí phân vùng" + +msgid "Enter a password: " +msgstr "Nhập mật khẩu: " + +msgid "Enter a encryption password for {}" +msgstr "Nhập mật khẩu mã hóa cho {}" + +msgid "Enter disk encryption password (leave blank for no encryption): " +msgstr "Nhập mật khẩu mã hóa ổ đĩa (để trống nếu không mã hóa): " + +msgid "Create a required super-user with sudo privileges: " +msgstr "Tạo một siêu người dùng bắt buộc với các đặc quyền sudo: " + +msgid "Enter root password (leave blank to disable root): " +msgstr "Nhập mật khẩu root (để trống để tắt root): " + +msgid "Password for user \"{}\": " +msgstr "Mật khẩu cho người dùng \"{}\": " + +msgid "Verifying that additional packages exist (this might take a few seconds)" +msgstr "Xác minh rằng các gói bổ sung tồn tại (việc này có thể mất vài giây)" + +msgid "Would you like to use automatic time synchronization (NTP) with the default time servers?\n" +msgstr "" +"Bạn có muốn sử dụng đồng bộ hóa thời gian tự động (NTP) với máy chủ thời gian mặc định không?\n" + +#, fuzzy +msgid "" +"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 "" +"Thời gian phần cứng và các bước sau cấu hình khác có thể được yêu cầu để NTP hoạt động.\n" +"Để biết thêm thông tin, vui lòng kiểm tra Arch wiki" + +msgid "Enter a username to create an additional user (leave blank to skip): " +msgstr "Nhập tên người dùng để tạo thêm người dùng (để trống để bỏ qua): " + +msgid "Use ESC to skip\n" +msgstr "Sử dụng ESC để bỏ qua\n" + +msgid "" +"\n" +" Choose an object from the list, and select one of the available actions for it to execute" +msgstr "" +"\n" +" Chọn một đối tượng từ danh sách và chọn một trong các hành động có sẵn để nó thực thi" + +msgid "Cancel" +msgstr "Hủy bỏ" + +msgid "Confirm and exit" +msgstr "Xác nhận và thoát" + +msgid "Add" +msgstr "Thêm" + +msgid "Copy" +msgstr "Sao chép" + +msgid "Edit" +msgstr "Chỉnh sửa" + +msgid "Delete" +msgstr "Xóa" + +msgid "Select an action for '{}'" +msgstr "Chọn hành động cho '{}'" + +msgid "Copy to new key:" +msgstr "Sao chép sang khóa mới:" + +#, fuzzy +msgid "Unknown nic type: {}. Possible values are {}" +msgstr "Loại nic không xác định: {} . Giá trị có thể là {}" + +msgid "" +"\n" +"This is your chosen configuration:" +msgstr "" +"\n" +"Đây là cấu hình bạn đã chọn:" + +msgid "Pacman is already running, waiting maximum 10 minutes for it to terminate." +msgstr "Pacman đang chạy, đợi tối đa 10 phút để nó kết thúc." + +msgid "" +"Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using " +"archinstall." +msgstr "" +"Khóa pacman có sẵn không bao giờ thoát. Vui lòng dọn sạch mọi phiên pacman hiện có trước khi sử " +"dụng Archinstall." + +#, fuzzy +msgid "Choose which optional additional repositories to enable" +msgstr "Chọn kho bổ sung tùy chọn nào để kích hoạt" + +msgid "Add a user" +msgstr "Thêm người dùng" + +msgid "Change password" +msgstr "Thay đổi mật khẩu" + +msgid "Promote/Demote user" +msgstr "Cấp/Tước quyền quản trị cho người dùng" + +msgid "Delete User" +msgstr "Xóa người dùng" + +msgid "" +"\n" +"Define a new user\n" +msgstr "" +"\n" +"Xác định người dùng mới\n" + +msgid "User Name : " +msgstr "Tên người dùng: " + +#, fuzzy +msgid "Should {} be a superuser (sudoer)?" +msgstr "{} có nên là siêu người dùng (sudoer) không?" + +msgid "Define users with sudo privilege: " +msgstr "Xác định người dùng có đặc quyền sudo: " + +msgid "No network configuration" +msgstr "Không có cấu hình mạng" + +msgid "Set desired subvolumes on a btrfs partition" +msgstr "Đặt các phân vùng con (subvolume) mong muốn trên phân vùng btrfs" + +#, fuzzy +msgid "" +"{}\n" +"\n" +"Select which partition to set subvolumes on" +msgstr "" +"{} \n" +"\n" +"Chọn phân vùng để đặt subvolume trên" + +msgid "Manage btrfs subvolumes for current partition" +msgstr "Quản lý subvolume btrfs cho phân vùng hiện tại" + +msgid "No configuration" +msgstr "Không có cấu hình" + +msgid "Save user configuration" +msgstr "Lưu cấu hình người dùng" + +msgid "Save user credentials" +msgstr "Lưu thông tin đăng nhập của người dùng" + +msgid "Save disk layout" +msgstr "Lưu bố cục đĩa" + +msgid "Save all" +msgstr "Lưu tất cả" + +msgid "Choose which configuration to save" +msgstr "Chọn cấu hình nào để lưu" + +msgid "Enter a directory for the configuration(s) to be saved: " +msgstr "Nhập thư mục để lưu (các) cấu hình: " + +msgid "Not a valid directory: {}" +msgstr "Thư mục không hợp lệ: {}" + +msgid "The password you are using seems to be weak," +msgstr "Mật khẩu bạn đang sử dụng có vẻ yếu," + +msgid "are you sure you want to use it?" +msgstr "bạn có chắc chắn muốn sử dụng nó không?" + +msgid "Optional repositories" +msgstr "Kho phần mềm tùy chọn" + +msgid "Save configuration" +msgstr "Lưu cấu hình" + +msgid "Missing configurations:\n" +msgstr "Thiếu cấu hình:\n" + +msgid "Either root-password or at least 1 superuser must be specified" +msgstr "Phải chỉ định mật khẩu gốc hoặc ít nhất 1 siêu người dùng" + +msgid "Manage superuser accounts: " +msgstr "Quản lý tài khoản siêu người dùng: " + +#, fuzzy +msgid "Manage ordinary user accounts: " +msgstr "Quản lý tài khoản người dùng thông thường:" + +msgid " Subvolume :{:16}" +msgstr " Phân vùng con (subvolume): {:16}" + +msgid " mounted at {:16}" +msgstr " gắn ở {:16}" + +msgid " with option {}" +msgstr " với tùy chọn {}" + +msgid "" +"\n" +" Fill the desired values for a new subvolume \n" +msgstr "" +"\n" +"Điền các giá trị mong muốn cho một subvolume mới\n" + +msgid "Subvolume name " +msgstr "Tên phân vùng con (subvolume) " + +msgid "Subvolume mountpoint" +msgstr "Điểm gắn phân vùng con (subvolume)" + +msgid "Subvolume options" +msgstr "Tùy chọn phân vùng con (subvolume) phụ" + +msgid "Save" +msgstr "Lưu" + +msgid "Subvolume name :" +msgstr "Tên tập con:" + +msgid "Select a mount point :" +msgstr "Chọn điểm gắn:" + +msgid "Select the desired subvolume options " +msgstr "Chọn các tùy chọn tập con mong muốn " + +msgid "Define users with sudo privilege, by username: " +msgstr "Xác định người dùng có đặc quyền sudo, theo tên người dùng: " + +#, python-brace-format +msgid "[!] A log file has been created here: {}" +msgstr "[!] Một tệp nhật ký đã được tạo ở đây: {}" + +#, fuzzy +msgid "Would you like to use BTRFS subvolumes with a default structure?" +msgstr "Bạn có muốn sử dụng các tập con BTRFS có cấu trúc mặc định không?" + +msgid "Would you like to use BTRFS compression?" +msgstr "Bạn có muốn sử dụng tính năng nén BTRFS không?" + +msgid "Would you like to create a separate partition for /home?" +msgstr "Bạn có muốn tạo một phân vùng riêng cho /home không?" + +msgid "The selected drives do not have the minimum capacity required for an automatic suggestion\n" +msgstr "Các ổ đĩa đã chọn không có dung lượng tối thiểu cần thiết để tự động đề xuất\n" + +msgid "Minimum capacity for /home partition: {}GB\n" +msgstr "Dung lượng tối thiểu cho phân vùng /home: {} GB\n" + +msgid "Minimum capacity for Arch Linux partition: {}GB" +msgstr "Dung lượng tối thiểu cho phân vùng Arch Linux: {}GB" + +msgid "Continue" +msgstr "Tiếp tục" + +msgid "yes" +msgstr "có" + +msgid "no" +msgstr "không" + +msgid "set: {}" +msgstr "thiết lập: {}" + +msgid "Manual configuration setting must be a list" +msgstr "Cài đặt cấu hình thủ công phải là một danh sách" + +msgid "No iface specified for manual configuration" +msgstr "Không có iface được chỉ định cho cấu hình thủ công" + +msgid "Manual nic configuration with no auto DHCP requires an IP address" +msgstr "Cấu hình nic thủ công không có DHCP tự động yêu cầu địa chỉ IP" + +msgid "Add interface" +msgstr "Thêm giao diện" + +msgid "Edit interface" +msgstr "Chỉnh sửa giao diện" + +msgid "Delete interface" +msgstr "Xóa giao diện" + +msgid "Select interface to add" +msgstr "Chọn giao diện để thêm" + +msgid "Manual configuration" +msgstr "Cấu hình thủ công" + +msgid "Mark/Unmark a partition as compressed (btrfs only)" +msgstr "Đánh dấu/Bỏ đánh dấu phân vùng là đã nén (chỉ btrfs)" + +msgid "The password you are using seems to be weak, are you sure you want to use it?" +msgstr "Mật khẩu bạn đang sử dụng có vẻ yếu, bạn có chắc chắn muốn sử dụng nó không?" + +msgid "Provides a selection of desktop environments and tiling window managers, e.g. gnome, kde, sway" +msgstr "" +"Cung cấp lựa chọn môi trường máy tính để bàn và trình quản lý cửa sổ xếp lớp, ví dụ: gnome, kde, " +"sway" + +msgid "Select your desired desktop environment" +msgstr "Chọn môi trường máy tính mong muốn của bạn" + +msgid "A very basic installation that allows you to customize Arch Linux as you see fit." +msgstr "Một bản cài đặt rất cơ bản cho phép bạn tùy chỉnh Arch Linux khi bạn thấy phù hợp." + +msgid "" +"Provides a selection of various server packages to install and enable, e.g. httpd, nginx, mariadb" +msgstr "" +"Cung cấp nhiều lựa chọn gói máy chủ khác nhau để cài đặt và kích hoạt, ví dụ: httpd, nginx, mariadb" + +msgid "Choose which servers to install, if none then a minimal installation will be done" +msgstr "Chọn máy chủ nào sẽ cài đặt, nếu không có thì cài đặt tối thiểu sẽ được thực hiện" + +#, fuzzy +msgid "Installs a minimal system as well as xorg and graphics drivers." +msgstr "Cài đặt một hệ thống tối thiểu cũng như trình điều khiển xorg và đồ họa." + +msgid "Press Enter to continue." +msgstr "Nhấn Enter để tiếp tục." + +msgid "" +"Would you like to chroot into the newly created installation and perform post-installation " +"configuration?" +msgstr "Bạn có muốn chroot vào bản cài đặt mới tạo và thực hiện cấu hình sau khi cài đặt không?" + +msgid "Are you sure you want to reset this setting?" +msgstr "Bạn có chắc chắn muốn đặt lại cài đặt này không?" + +msgid "Select one or more hard drives to use and configure\n" +msgstr "Chọn một hoặc nhiều ổ cứng để sử dụng và cấu hình\n" + +msgid "Any modifications to the existing setting will reset the disk layout!" +msgstr "Mọi sửa đổi đối với cài đặt hiện có sẽ đặt lại bố cục đĩa!" + +msgid "" +"If you reset the harddrive selection this will also reset the current disk layout. Are you sure?" +msgstr "" +"Nếu bạn đặt lại lựa chọn ổ cứng, thao tác này cũng sẽ đặt lại bố cục đĩa hiện tại. Bạn có chắc " +"không?" + +msgid "Save and exit" +msgstr "Lưu và thoát" + +msgid "" +"{}\n" +"contains queued partitions, this will remove those, are you sure?" +msgstr "" +"{}\n" +"chứa các phân vùng được xếp hàng đợi, thao tác này sẽ xóa những phân vùng đó, bạn có chắc không?" + +msgid "No audio server" +msgstr "Không có hệ thống âm thanh" + +msgid "(default)" +msgstr "(mặc định)" + +msgid "Use ESC to skip" +msgstr "Sử dụng ESC để bỏ qua" + +msgid "" +"Use CTRL+C to reset current selection\n" +"\n" +msgstr "Sử dụng CTRL+C để đặt lại lựa chọn hiện tại\n" + +msgid "Copy to: " +msgstr "Sao chép vào: " + +msgid "Edit: " +msgstr "Chỉnh sửa: " + +msgid "Key: " +msgstr "Khóa: " + +msgid "Edit {}: " +msgstr "Chỉnh sửa {}: " + +msgid "Add: " +msgstr "Thêm: " + +msgid "Value: " +msgstr "Giá trị: " + +msgid "" +"You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt " +"(experimental)" +msgstr "" +"Bạn có thể bỏ qua việc chọn ổ đĩa và phân vùng và sử dụng bất kỳ thiết lập ổ đĩa nào được gắn tại /" +"mnt (thử nghiệm)" + +#, fuzzy +msgid "Select one of the disks or skip and use /mnt as default" +msgstr "Chọn một trong các đĩa hoặc bỏ qua và sử dụng /mnt làm mặc định" + +#, fuzzy +msgid "Select which partitions to mark for formatting:" +msgstr "Chọn phân vùng cần đánh dấu để định dạng:" + +msgid "Use HSM to unlock encrypted drive" +msgstr "Sử dụng HSM để mở khóa ổ đĩa được mã hóa" + +msgid "Device" +msgstr "Thiết bị" + +msgid "Size" +msgstr "Kích cỡ" + +msgid "Free space" +msgstr "Không gian trống" + +msgid "Bus-type" +msgstr "Loại đường truyền" + +msgid "Either root-password or at least 1 user with sudo privileges must be specified" +msgstr "Phải chỉ định mật khẩu gốc hoặc ít nhất 1 người dùng có đặc quyền sudo" + +msgid "Enter username (leave blank to skip): " +msgstr "Nhập tên người dùng (để trống để bỏ qua): " + +msgid "The username you entered is invalid. Try again" +msgstr "Tên người dùng bạn đã nhập không hợp lệ. Thử lại" + +msgid "Should \"{}\" be a superuser (sudo)?" +msgstr "\"{}\" có nên là siêu người dùng (sudo) không?" + +msgid "Select which partitions to encrypt" +msgstr "Chọn phân vùng để mã hóa" + +msgid "very weak" +msgstr "rất yếu" + +msgid "weak" +msgstr "yếu" + +msgid "moderate" +msgstr "vừa phải" + +msgid "strong" +msgstr "mạnh" + +msgid "Add subvolume" +msgstr "Thêm tập con" + +msgid "Edit subvolume" +msgstr "Chỉnh sửa tập con" + +msgid "Delete subvolume" +msgstr "Xóa tập con" + +msgid "Configured {} interfaces" +msgstr "Giao diện {} được định cấu hình" + +msgid "This option enables the number of parallel downloads that can occur during installation" +msgstr "Tùy chọn này cho phép số lượng tải xuống song song có thể xảy ra trong quá trình cài đặt" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +" (Enter a value between 1 to {})\n" +"Note:" +msgstr "" +"Nhập số lượng tải xuống song song sẽ được kích hoạt.\n" +" (Nhập giá trị từ 1 đến {} )\n" +"Lưu ý:" + +#, fuzzy +msgid " - Maximum value : {} ( Allows {} parallel downloads, allows {} downloads at a time )" +msgstr "- Giá trị tối đa: {} (Cho phép tải song song {}, cho phép tải {} cùng một lúc)" + +#, fuzzy +msgid " - Minimum value : 1 ( Allows 1 parallel download, allows 2 downloads at a time )" +msgstr "- Giá trị tối thiểu: 1 (Cho phép tải 1 song song, cho phép tải 2 lần cùng lúc)" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )" +msgstr "- Tắt/Mặc định: 0 ( Vô hiệu hóa tải xuống song song, chỉ cho phép tải xuống 1 lần mỗi lần )" + +#, python-brace-format +msgid "Invalid input! Try again with a valid input [1 to {max_downloads}, or 0 to disable]" +msgstr "" +"Đầu vào không hợp lệ! Hãy thử lại với giá trị đầu vào hợp lệ [1 đến {max_downloads} hoặc 0 để tắt]" + +msgid "Parallel Downloads" +msgstr "Tải xuống song song" + +msgid "Pacman" +msgstr "Pacman" + +msgid "Color" +msgstr "Màu sắc" + +#, fuzzy, python-brace-format +msgid "Enter the number of parallel downloads (1-{})" +msgstr "Nhập số lượt tải song song (1- {} )" + +msgid "Enable colored output for pacman" +msgstr "Kích hoạt đầu ra màu cho pacman" + +msgid "ESC to skip" +msgstr "ESC để bỏ qua" + +msgid "CTRL+C to reset" +msgstr "CTRL+C để đặt lại" + +msgid "TAB to select" +msgstr "TAB để chọn" + +msgid "[Default value: 0] > " +msgstr "[Giá trị mặc định: 0] > " + +msgid "To be able to use this translation, please install a font manually that supports the language." +msgstr "" +"Để có thể sử dụng bản dịch này, vui lòng cài đặt phông chữ hỗ trợ ngôn ngữ theo cách thủ công." + +msgid "The font should be stored as {}" +msgstr "Phông chữ phải được lưu dưới dạng {}" + +msgid "Archinstall requires root privileges to run. See --help for more." +msgstr "Archinstall yêu cầu quyền root để chạy. Xem --help để biết thêm." + +#, fuzzy +msgid "Select an execution mode" +msgstr "Chọn chế độ thực hiện" + +#, python-brace-format +msgid "Unable to fetch profile from specified url: {}" +msgstr "Không thể tìm nạp cấu hình từ url được chỉ định: {}" + +#, fuzzy, python-brace-format +msgid "Profiles must have unique name, but profile definitions with duplicate name found: {}" +msgstr "" +"Cấu hình (Profile) phải có tên duy nhất, nhưng tìm thấy định nghĩa cấu hình có tên trùng lặp: {}" + +msgid "Select one or more devices to use and configure" +msgstr "Chọn một hoặc nhiều thiết bị để sử dụng và định cấu hình" + +#, fuzzy +msgid "If you reset the device selection this will also reset the current disk layout. Are you sure?" +msgstr "" +"Nếu bạn đặt lại lựa chọn thiết bị, thao tác này cũng sẽ đặt lại bố cục đĩa hiện tại. Bạn có chắc " +"không?" + +msgid "Existing Partitions" +msgstr "Phân vùng hiện có" + +msgid "Select a partitioning option" +msgstr "Chọn một tùy chọn phân vùng" + +#, fuzzy +msgid "Enter the root directory of the mounted devices: " +msgstr "Nhập thư mục gốc của thiết bị được gắn:" + +#, fuzzy, python-brace-format +msgid "Minimum capacity for /home partition: {}GiB\n" +msgstr "Dung lượng tối thiểu cho phân vùng /home: {} GiB" + +#, fuzzy, python-brace-format +msgid "Minimum capacity for Arch Linux partition: {}GiB" +msgstr "Dung lượng tối thiểu cho phân vùng Arch Linux: {} GiB" + +#, fuzzy +msgid "" +"This is a list of pre-programmed profiles_bck, they might make it easier to install things like " +"desktop environments" +msgstr "" +"Đây là danh sách các profile_bck được lập trình sẵn, chúng có thể giúp cài đặt những thứ như môi " +"trường máy tính để bàn dễ dàng hơn" + +msgid "Current profile selection" +msgstr "Lựa chọn cấu hình hiện tại" + +msgid "Remove all newly added partitions" +msgstr "Xóa tất cả các phân vùng mới được thêm vào" + +#, fuzzy +msgid "Assign mountpoint" +msgstr "Chỉ định điểm gắn kết" + +msgid "Mark/Unmark to be formatted (wipes data)" +msgstr "Đánh dấu/Bỏ đánh dấu để định dạng (xóa dữ liệu)" + +msgid "Mark/Unmark as bootable" +msgstr "Đánh dấu/Bỏ đánh dấu là có khả năng khởi động" + +msgid "Change filesystem" +msgstr "Thay đổi hệ thống tập tin" + +msgid "Mark/Unmark as compressed" +msgstr "Đánh dấu/Bỏ đánh dấu là đã nén" + +#, fuzzy +msgid "Set subvolumes" +msgstr "Đặt tập con" + +msgid "Delete partition" +msgstr "Xóa phân vùng" + +msgid "Partition" +msgstr "Phân vùng" + +#, fuzzy +msgid "This partition is currently encrypted, to format it a filesystem has to be specified" +msgstr "Phân vùng này hiện đã được mã hóa, để định dạng nó, phải chỉ định hệ thống tập tin" + +#, fuzzy +msgid "" +"Partition mount-points are relative to inside the installation, the boot would be /boot as an " +"example." +msgstr "" +"Các điểm gắn kết phân vùng có liên quan đến bên trong quá trình cài đặt, khởi động sẽ là /boot làm " +"ví dụ." + +#, fuzzy +msgid "If mountpoint /boot is set, then the partition will also be marked as bootable." +msgstr "" +"Nếu điểm gắn kết/khởi động được đặt thì phân vùng cũng sẽ được đánh dấu là có khả năng khởi động." + +msgid "Mountpoint: " +msgstr "Điểm gắn: " + +msgid "Current free sectors on device {}:" +msgstr "Các phân khu trống hiện có trên thiết bị {}:" + +msgid "Total sectors: {}" +msgstr "Tổng số phân khu: {}" + +msgid "Enter the start sector (default: {}): " +msgstr "Nhập phân khu bắt đầu (mặc định: {} ): " + +#, fuzzy +msgid "Enter the end sector of the partition (percentage or block number, default: {}): " +msgstr "Nhập khu vực cuối của phân vùng (phần trăm hoặc số khối, mặc định: {} ):" + +#, fuzzy +msgid "This will remove all newly added partitions, continue?" +msgstr "Điều này sẽ loại bỏ tất cả các phân vùng mới được thêm vào, tiếp tục?" + +#, python-brace-format +msgid "Partition management: {}" +msgstr "Quản lý phân vùng: {}" + +#, python-brace-format +msgid "Total length: {}" +msgstr "Tổng chiều dài: {}" + +#, fuzzy +msgid "Encryption type" +msgstr "Kiểu mã hóa" + +#, fuzzy +msgid "Iteration time" +msgstr "Thời gian lặp lại" + +#, fuzzy +msgid "Enter iteration time for LUKS encryption (in milliseconds)" +msgstr "Nhập thời gian lặp lại để mã hóa LUKS (tính bằng mili giây)" + +#, fuzzy +msgid "Higher values increase security but slow down boot time" +msgstr "Giá trị cao hơn tăng cường bảo mật nhưng làm chậm thời gian khởi động" + +#, fuzzy +msgid "Default: 10000ms, Recommended range: 1000-60000" +msgstr "Mặc định: 10000ms, Phạm vi khuyến nghị: 1000-60000" + +#, fuzzy +msgid "Iteration time cannot be empty" +msgstr "Thời gian lặp lại không được để trống" + +#, fuzzy +msgid "Iteration time must be at least 100ms" +msgstr "Thời gian lặp phải ít nhất là 100ms" + +#, fuzzy +msgid "Iteration time must be at most 120000ms" +msgstr "Thời gian lặp lại tối đa là 120000ms" + +msgid "Please enter a valid number" +msgstr "Vui lòng nhập một số hợp lệ" + +msgid "Partitions" +msgstr "Phân vùng" + +#, fuzzy +msgid "No HSM devices available" +msgstr "Không có thiết bị HSM nào" + +#, fuzzy +msgid "Partitions to be encrypted" +msgstr "Các phân vùng được mã hóa" + +msgid "Select disk encryption option" +msgstr "Chọn tùy chọn mã hóa ổ đĩa" + +msgid "Select a FIDO2 device to use for HSM" +msgstr "Chọn thiết bị FIDO2 để sử dụng cho HSM" + +#, fuzzy +msgid "Use a best-effort default partition layout" +msgstr "Sử dụng bố cục phân vùng mặc định với nỗ lực tốt nhất" + +#, fuzzy +msgid "Manual Partitioning" +msgstr "Phân vùng thủ công" + +#, fuzzy +msgid "Pre-mounted configuration" +msgstr "Cấu hình được gắn sẵn" + +#, fuzzy +msgid "Unknown" +msgstr "Không xác định" + +#, fuzzy +msgid "Partition encryption" +msgstr "Mã hóa phân vùng" + +#, fuzzy, python-brace-format +msgid " ! Formatting {} in " +msgstr "! Định dạng {} trong" + +msgid "← Back" +msgstr "← Quay lại" + +msgid "Disk encryption" +msgstr "Mã hóa ổ đĩa" + +#, fuzzy +msgid "Configuration" +msgstr "Cấu hình" + +#, fuzzy +msgid "Password" +msgstr "Mật khẩu" + +#, fuzzy +msgid "All settings will be reset, are you sure?" +msgstr "Tất cả cài đặt sẽ được đặt lại, bạn có chắc chắn không?" + +msgid "Back" +msgstr "Quay lại" + +msgid "Please chose which greeter to install for the chosen profiles: {}" +msgstr "Vui lòng chọn màn hình đăng nhập cho các cấu hình đã chọn: {}" + +#, fuzzy, python-brace-format +msgid "Environment type: {}" +msgstr "Loại môi trường: {}" + +#, fuzzy +msgid "" +"The proprietary Nvidia driver is not supported by Sway. It is likely that you will run into issues, " +"are you okay with that?" +msgstr "" +"Trình điều khiển Nvidia độc quyền không được Sway hỗ trợ. Có khả năng là bạn sẽ gặp phải vấn đề, " +"bạn có ổn với điều đó không?" + +msgid "Installed packages" +msgstr "Gói đã cài đặt" + +msgid "Add profile" +msgstr "Thêm cấu hình" + +msgid "Edit profile" +msgstr "Chỉnh sửa cấu hình" + +msgid "Delete profile" +msgstr "Xóa cấu hình" + +msgid "Profile name: " +msgstr "Tên cấu hình: " + +msgid "The profile name you entered is already in use. Try again" +msgstr "Tên cấu hình bạn nhập đã được sử dụng. Thử lại" + +msgid "Packages to be install with this profile (space separated, leave blank to skip): " +msgstr "Các gói sẽ được cài đặt với cấu hình này (cách nhau bằng dấu cách, để trống để bỏ qua): " + +msgid "Services to be enabled with this profile (space separated, leave blank to skip): " +msgstr "Các dịch vụ được kích hoạt với cấu hình này (cách nhau bằng dấu cách, để trống để bỏ qua): " + +#, fuzzy +msgid "Should this profile be enabled for installation?" +msgstr "Cấu hình này có nên được kích hoạt để cài đặt không?" + +#, fuzzy +msgid "Create your own" +msgstr "Tạo của riêng bạn" + +#, fuzzy +msgid "" +"\n" +"Select a graphics driver or leave blank to install all open-source drivers" +msgstr "Chọn trình điều khiển đồ họa hoặc để trống để cài đặt tất cả trình điều khiển nguồn mở" + +msgid "Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" +"Sway cần quyền truy cập vào seat của bạn (bộ sưu tập các thiết bị phần cứng, ví dụ như bàn phím, " +"chuột, v.v.)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Sway access to your hardware" +msgstr "Chọn một tùy chọn để cấp cho Sway quyền truy cập vào phần cứng của bạn" + +#, fuzzy +msgid "Graphics driver" +msgstr "Trình điều khiển đồ họa" + +msgid "Greeter" +msgstr "Màn hình đăng nhập" + +msgid "Please chose which greeter to install" +msgstr "Vui lòng chọn màn hình đăng nhập nào để cài đặt" + +msgid "This is a list of pre-programmed default_profiles" +msgstr "Đây là danh sách cấu hình mặc định được lập trình sẵn" + +msgid "Disk configuration" +msgstr "Cấu hình đĩa" + +msgid "Profiles" +msgstr "Cấu hình (Profile)" + +#, fuzzy +msgid "Finding possible directories to save configuration files ..." +msgstr "Tìm các thư mục có thể lưu file cấu hình..." + +#, fuzzy +msgid "Select directory (or directories) for saving configuration files" +msgstr "Chọn thư mục (hoặc các thư mục) để lưu file cấu hình" + +msgid "Add a custom mirror" +msgstr "Thêm máy chủ bản sao (mirror) tùy chỉnh" + +msgid "Change custom mirror" +msgstr "Thay đổi máy chủ bản sao tùy chỉnh" + +msgid "Delete custom mirror" +msgstr "Xóa máy chủ bản sao tùy chỉnh" + +#, fuzzy +msgid "Enter name (leave blank to skip): " +msgstr "Nhập tên (để trống để bỏ qua):" + +#, fuzzy +msgid "Enter url (leave blank to skip): " +msgstr "Nhập url (để trống để bỏ qua):" + +#, fuzzy +msgid "Select signature check option" +msgstr "Chọn tùy chọn kiểm tra chữ ký" + +#, fuzzy +msgid "Select signature option" +msgstr "Chọn tùy chọn chữ ký" + +#, fuzzy +msgid "Custom mirrors" +msgstr "Máy chủ bản sao (Mirror) tùy chỉnh" + +#, fuzzy +msgid "Defined" +msgstr "Đã xác định" + +msgid "Save user configuration (including disk layout)" +msgstr "Lưu cấu hình người dùng (bao gồm cả bố cục đĩa)" + +#, fuzzy +msgid "" +"Enter a directory for the configuration(s) to be saved (tab completion enabled)\n" +"Save directory: " +msgstr "" +"Nhập thư mục để lưu (các) cấu hình (bật tính năng hoàn thành tab)\n" +"Lưu thư mục:" + +#, fuzzy +msgid "" +"Do you want to save {} configuration file(s) in the following location?\n" +"\n" +"{}" +msgstr "" +"Bạn có muốn lưu (các) tệp cấu hình {} ở vị trí sau không?\n" +"\n" +" {}" + +#, fuzzy +msgid "Saving {} configuration files to {}" +msgstr "Lưu tệp cấu hình {} vào {}" + +#, fuzzy +msgid "Mirrors" +msgstr "Máy chủ bản sao (Mirror)" + +msgid "Mirror regions" +msgstr "Máy chủ bản sao" + +#, fuzzy +msgid "" +" - Maximum value : {} ( Allows {} parallel downloads, allows {max_downloads+1} downloads at a " +"time )" +msgstr "- Giá trị tối đa: {} (Cho phép tải song song {}, cho phép tải {max_downloads+1} cùng lúc)" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [1 to {}, or 0 to disable]" +msgstr "Đầu vào không hợp lệ! Hãy thử lại với giá trị đầu vào hợp lệ [1 đến {} hoặc 0 để tắt]" + +#, fuzzy +msgid "Locales" +msgstr "Ngôn ngữ" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE)" +msgstr "Sử dụng Trình quản lý mạng (cần thiết để định cấu hình đồ họa internet trong Gnome và KDE)" + +#, fuzzy +msgid "Total: {} / {}" +msgstr "Tổng cộng: {}/{}" + +#, fuzzy +msgid "All entered values can be suffixed with a unit: B, KB, KiB, MB, MiB..." +msgstr "Tất cả các giá trị đã nhập có thể được thêm vào một đơn vị: B, KB, KiB, MB, MiB..." + +msgid "If no unit is provided, the value is interpreted as sectors" +msgstr "Nếu không có đơn vị nào được cung cấp, giá trị sẽ được hiểu theo đơn vị sector" + +#, fuzzy +msgid "Enter start (default: sector {}): " +msgstr "Nhập bắt đầu (mặc định: khu vực {} ):" + +#, fuzzy +msgid "Enter end (default: {}): " +msgstr "Nhập cuối (mặc định: {} ):" + +#, fuzzy +msgid "Unable to determine fido2 devices. Is libfido2 installed?" +msgstr "Không thể xác định thiết bị fido2. libfido2 đã được cài đặt chưa?" + +#, fuzzy +msgid "Path" +msgstr "Con đường" + +msgid "Manufacturer" +msgstr "Nhà sản xuất" + +#, fuzzy +msgid "Product" +msgstr "Sản phẩm" + +#, fuzzy, python-brace-format +msgid "Invalid configuration: {}" +msgstr "Cấu hình không hợp lệ: {}" + +msgid "Ready to install" +msgstr "Sẵn sàng để cài đặt" + +msgid "Disks" +msgstr "Đĩa" + +msgid "Packages" +msgstr "Gói" + +msgid "Network" +msgstr "Mạng" + +msgid "Locale" +msgstr "Ngôn ngữ" + +msgid "Type" +msgstr "Kiểu" + +#, fuzzy +msgid "This option enables the number of parallel downloads that can occur during package downloads" +msgstr "" +"Tùy chọn này cho phép số lượng tải xuống song song có thể xảy ra trong quá trình tải xuống gói" + +#, fuzzy +msgid "" +"Enter the number of parallel downloads to be enabled.\n" +"\n" +"Note:\n" +msgstr "" +"Nhập số lượng tải xuống song song sẽ được kích hoạt.\n" +"\n" +"Ghi chú:" + +#, fuzzy, python-brace-format +msgid " - Maximum recommended value : {} ( Allows {} parallel downloads at a time )" +msgstr "- Giá trị khuyến nghị tối đa: {} (Cho phép tải xuống song song {} cùng một lúc)" + +#, fuzzy +msgid " - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n" +msgstr "- Tắt/Mặc định: 0 ( Vô hiệu hóa tải xuống song song, chỉ cho phép tải xuống 1 lần mỗi lần )" + +#, fuzzy +msgid "Invalid input! Try again with a valid input [or 0 to disable]" +msgstr "Đầu vào không hợp lệ! Hãy thử lại với thông tin đầu vào hợp lệ [hoặc 0 để tắt]" + +msgid "Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" +"Hyprland cần quyền truy cập vào seat của bạn (bộ sưu tập các thiết bị phần cứng như bàn phím, " +"chuột, v.v.)" + +#, fuzzy +msgid "" +"\n" +"\n" +"Choose an option to give Hyprland access to your hardware" +msgstr "Chọn một tùy chọn để cấp cho Hyprland quyền truy cập vào phần cứng của bạn" + +#, fuzzy +msgid "All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB..." +msgstr "Tất cả các giá trị đã nhập có thể được gắn với một đơn vị: %, B, KB, KiB, MB, MiB..." + +msgid "Would you like to use unified kernel images?" +msgstr "Bạn có muốn sử dụng hình ảnh nhân hợp nhất không?" + +msgid "Unified kernel images" +msgstr "Hình ảnh nhân hợp nhất" + +msgid "Waiting for time sync (timedatectl show) to complete." +msgstr "" + +#, fuzzy +msgid "" +"Time syncronization not completing, while you wait - check the docs for workarounds: https://" +"archinstall.readthedocs.io/" +msgstr "" +"Quá trình đồng bộ hóa thời gian không hoàn tất trong khi bạn chờ đợi - hãy kiểm tra tài liệu để " +"biết cách giải quyết: https://archinstall.readthedocs.io/" + +#, fuzzy +msgid "" +"Skipping waiting for automatic time sync (this can cause issues if time is out of sync during " +"installation)" +msgstr "" +"Bỏ qua việc chờ đồng bộ hóa thời gian tự động (điều này có thể gây ra sự cố nếu thời gian không " +"đồng bộ trong khi cài đặt)" + +#, fuzzy +msgid "Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete." +msgstr "Đang chờ đồng bộ hóa khóa Arch Linux (archlinux-keyring-wkd-sync) hoàn tất." + +#, fuzzy +msgid "Selected profiles: " +msgstr "Cấu hình (Profile) đã chọn:" + +#, fuzzy +msgid "" +"Time synchronization not completing, while you wait - check the docs for workarounds: https://" +"archinstall.readthedocs.io/" +msgstr "" +"Đồng bộ hóa thời gian không hoàn tất, trong khi bạn chờ đợi - hãy kiểm tra tài liệu để biết cách " +"giải quyết: https://archinstall.readthedocs.io/" + +msgid "Mark/Unmark as nodatacow" +msgstr "Đánh dấu/Bỏ đánh dấu là nodatacow" + +msgid "Would you like to use compression or disable CoW?" +msgstr "Bạn muốn sử dụng tính năng nén hay tắt CoW?" + +msgid "Use compression" +msgstr "Sử dụng tính năng nén" + +msgid "Disable Copy-on-Write" +msgstr "Tắt tính năng Sao chép khi ghi" + +#, fuzzy +msgid "" +"Provides a selection of desktop environments and tiling window managers, e.g. GNOME, KDE Plasma, " +"Sway" +msgstr "" +"Cung cấp lựa chọn môi trường máy tính để bàn và trình quản lý cửa sổ xếp lớp, ví dụ: Gnome, KDE " +"Plasma, Sway" + +#, fuzzy, python-brace-format +msgid "Configuration type: {}" +msgstr "Loại cấu hình: {}" + +#, fuzzy +msgid "LVM configuration type" +msgstr "Loại cấu hình LVM" + +#, fuzzy +msgid "LVM disk encryption with more than 2 partitions is currently not supported" +msgstr "Mã hóa đĩa LVM với hơn 2 phân vùng hiện không được hỗ trợ" + +#, fuzzy +msgid "Use NetworkManager (necessary to configure internet graphically in GNOME and KDE Plasma)" +msgstr "" +"Sử dụng Trình quản lý mạng (cần thiết để định cấu hình đồ họa internet trong Gnome và KDE Plasma)" + +#, fuzzy +msgid "Select a LVM option" +msgstr "Chọn tùy chọn LVM" + +#, fuzzy +msgid "Partitioning" +msgstr "Phân vùng" + +#, fuzzy +msgid "Logical Volume Management (LVM)" +msgstr "Quản lý khối hợp lý (LVM)" + +msgid "Physical volumes" +msgstr "Các phân vùng vật lý (PV)" + +#, fuzzy +msgid "Volumes" +msgstr "Tập" + +#, fuzzy +msgid "LVM volumes" +msgstr "Khối lượng LVM" + +#, fuzzy +msgid "LVM volumes to be encrypted" +msgstr "Khối lượng LVM được mã hóa" + +#, fuzzy +msgid "Select which LVM volumes to encrypt" +msgstr "Chọn khối LVM nào sẽ mã hóa" + +#, fuzzy +msgid "Default layout" +msgstr "Bố cục mặc định" + +#, fuzzy +msgid "No Encryption" +msgstr "Không mã hóa" + +#, fuzzy +msgid "LUKS" +msgstr "LUKS" + +#, fuzzy +msgid "LVM on LUKS" +msgstr "LVM trên LUKS" + +#, fuzzy +msgid "LUKS on LVM" +msgstr "LUKS trên LVM" + +msgid "Yes" +msgstr "Có" + +msgid "No" +msgstr "Không" + +#, fuzzy +msgid "Archinstall help" +msgstr "Trợ giúp cài đặt Arch" + +msgid " (default)" +msgstr " (mặc định)" + +#, fuzzy +msgid "Press Ctrl+h for help" +msgstr "Nhấn Ctrl+h để được trợ giúp" + +#, fuzzy +msgid "Choose an option to give Sway access to your hardware" +msgstr "Chọn một tùy chọn để cấp cho Sway quyền truy cập vào phần cứng của bạn" + +msgid "Seat access" +msgstr "Quyền truy cập seat" + +msgid "Mountpoint" +msgstr "Điểm gắn" + +#, fuzzy +msgid "HSM" +msgstr "HSM" + +msgid "Enter disk encryption password (leave blank for no encryption)" +msgstr "Nhập mật khẩu mã hóa ổ đĩa (để trống nếu không mã hóa)" + +msgid "Disk encryption password" +msgstr "Mật khẩu mã hóa ổ đĩa" + +#, fuzzy +msgid "Partition - New" +msgstr "Phân vùng - Mới" + +#, fuzzy +msgid "Filesystem" +msgstr "Hệ thống tập tin" + +#, fuzzy +msgid "Invalid size" +msgstr "Kích thước không hợp lệ" + +#, fuzzy +msgid "Start (default: sector {}): " +msgstr "Bắt đầu (mặc định: khu vực {} ):" + +#, fuzzy +msgid "End (default: {}): " +msgstr "Kết thúc (mặc định: {} ):" + +msgid "Subvolume name" +msgstr "Tên tập con" + +#, fuzzy +msgid "Disk configuration type" +msgstr "Kiểu cấu hình đĩa" + +#, fuzzy +msgid "Root mount directory" +msgstr "Thư mục gắn kết gốc" + +msgid "Select language" +msgstr "Chọn ngôn ngữ" + +#, fuzzy +msgid "Write additional packages to install (space separated, leave blank to skip)" +msgstr "Ghi thêm các gói cần cài đặt (cách nhau dấu cách, để trống để bỏ qua)" + +#, fuzzy +msgid "Invalid download number" +msgstr "Số tải xuống không hợp lệ" + +#, fuzzy +msgid "Number downloads" +msgstr "Số lượt tải xuống" + +#, fuzzy +msgid "The username you entered is invalid" +msgstr "Tên người dùng bạn đã nhập không hợp lệ" + +#, fuzzy +msgid "Username" +msgstr "Tên người dùng" + +#, python-brace-format +msgid "Should \"{}\" be a superuser (sudo)?\n" +msgstr "\"{}\" có nên là siêu người dùng (sudo) không?\n" + +#, fuzzy +msgid "Interfaces" +msgstr "Giao diện" + +#, fuzzy +msgid "You need to enter a valid IP in IP-config mode" +msgstr "Bạn cần nhập IP hợp lệ ở chế độ IP-config" + +#, fuzzy +msgid "Modes" +msgstr "Chế độ" + +#, fuzzy +msgid "IP address" +msgstr "địa chỉ IP" + +msgid "Enter your gateway (router) IP address (leave blank for none)" +msgstr "Nhập địa chỉ IP gateway (bộ định tuyến) của bạn (để trống nếu không có)" + +#, fuzzy +msgid "Gateway address" +msgstr "Địa chỉ cổng" + +msgid "Enter your DNS servers with space separated (leave blank for none)" +msgstr "Nhập các máy chủ DNS của bạn, cách nhau bằng dấu cách (để trống nếu không có)" + +#, fuzzy +msgid "DNS servers" +msgstr "máy chủ DNS" + +#, fuzzy +msgid "Configure interfaces" +msgstr "Cấu hình giao diện" + +#, fuzzy +msgid "Kernel" +msgstr "hạt nhân" + +msgid "UEFI is not detected and some options are disabled" +msgstr "UEFI không được phát hiện và một số tùy chọn bị tắt" + +msgid "Info" +msgstr "Thông tin" + +msgid "The proprietary Nvidia driver is not supported by Sway." +msgstr "Trình điều khiển Nvidia độc quyền không được Sway hỗ trợ." + +msgid "It is likely that you will run into issues, are you okay with that?" +msgstr "Có khả năng là bạn sẽ gặp phải vấn đề, bạn có ổn với điều đó không?" + +msgid "Main profile" +msgstr "Cấu hình (Profile) chính" + +msgid "Confirm password" +msgstr "Xác nhận mật khẩu" + +msgid "The confirmation password did not match, please try again" +msgstr "Mật khẩu xác nhận không khớp, vui lòng thử lại" + +#, fuzzy +msgid "Not a valid directory" +msgstr "Không phải là một thư mục hợp lệ" + +msgid "Would you like to continue?" +msgstr "Bạn có muốn tiếp tục không?" + +msgid "Directory" +msgstr "Thư mục" + +#, fuzzy +msgid "Enter a directory for the configuration(s) to be saved (tab completion enabled)" +msgstr "Nhập thư mục để lưu (các) cấu hình (bật tính năng hoàn thành tab)" + +#, fuzzy, python-brace-format +msgid "Do you want to save the configuration file(s) to {}?" +msgstr "Bạn có muốn lưu (các) tệp cấu hình vào {} không?" + +msgid "Enabled" +msgstr "Đã bật" + +msgid "Disabled" +msgstr "Vô hiệu hóa" + +msgid "Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues" +msgstr "Vui lòng gửi vấn đề này (và tệp) tới https://github.com/archlinux/archinstall/issues" + +msgid "Mirror name" +msgstr "Tên máy chủ bản sao" + +#, fuzzy +msgid "Url" +msgstr "Url" + +#, fuzzy +msgid "Select signature check" +msgstr "Chọn kiểm tra chữ ký" + +#, fuzzy +msgid "Select execution mode" +msgstr "Chọn chế độ thực hiện" + +#, fuzzy +msgid "Press ? for help" +msgstr "Nhấn ? để được giúp đỡ" + +#, fuzzy +msgid "Choose an option to give Hyprland access to your hardware" +msgstr "Chọn một tùy chọn để cấp cho Hyprland quyền truy cập vào phần cứng của bạn" + +#, fuzzy +msgid "Additional repositories" +msgstr "Kho bổ sung" + +msgid "NTP" +msgstr "NTP" + +#, fuzzy +msgid "Swap on zram" +msgstr "Trao đổi trên zram" + +msgid "Name" +msgstr "Tên" + +#, fuzzy +msgid "Signature check" +msgstr "Kiểm tra chữ ký" + +#, python-brace-format +msgid "Selected free space segment on device {}:" +msgstr "Phân đoạn không gian trống đã chọn trên thiết bị {} :" + +#, fuzzy, python-brace-format +msgid "Size: {} / {}" +msgstr "Kích thước: {}/{}" + +#, fuzzy, python-brace-format +msgid "Size (default: {}): " +msgstr "Kích thước (mặc định: {} ):" + +#, fuzzy +msgid "HSM device" +msgstr "thiết bị HSM" + +msgid "Some packages could not be found in the repository" +msgstr "Không thể tìm thấy một số gói trong kho phần mềm" + +#, fuzzy +msgid "User" +msgstr "người dùng" + +#, fuzzy +msgid "The specified configuration will be applied" +msgstr "Cấu hình được chỉ định sẽ được áp dụng" + +msgid "Wipe" +msgstr "Ghi đè dữ liệu" + +#, fuzzy +msgid "Mark/Unmark as XBOOTLDR" +msgstr "Đánh dấu/Bỏ đánh dấu là XBOOTLDR" + +msgid "Loading packages..." +msgstr "Đang tải gói..." + +#, fuzzy +msgid "Select any packages from the below list that should be installed additionally" +msgstr "Chọn bất kỳ gói nào từ danh sách bên dưới cần được cài đặt bổ sung" + +msgid "Add a custom repository" +msgstr "Thêm kho phần mềm tùy chỉnh" + +msgid "Change custom repository" +msgstr "Thay đổi kho phần mềm tùy chỉnh" + +msgid "Delete custom repository" +msgstr "Xóa kho phần mềm tùy chỉnh" + +msgid "Repository name" +msgstr "Tên kho phần mềm" + +#, fuzzy +msgid "Add a custom server" +msgstr "Thêm một máy chủ tùy chỉnh" + +#, fuzzy +msgid "Change custom server" +msgstr "Thay đổi máy chủ tùy chỉnh" + +#, fuzzy +msgid "Delete custom server" +msgstr "Xóa máy chủ tùy chỉnh" + +#, fuzzy +msgid "Server url" +msgstr "Url máy chủ" + +#, fuzzy +msgid "Select regions" +msgstr "Chọn vùng" + +#, fuzzy +msgid "Add custom servers" +msgstr "Thêm máy chủ tùy chỉnh" + +msgid "Add custom repository" +msgstr "Thêm kho phần mềm tùy chỉnh" + +msgid "Loading mirror regions..." +msgstr "Đang tải các máy chủ bản sao..." + +msgid "Mirrors and repositories" +msgstr "Máy chủ bản sao (Mirror) và kho phần mềm" + +msgid "Selected mirror regions" +msgstr "Máy chủ bản sao được chọn" + +#, fuzzy +msgid "Custom servers" +msgstr "Máy chủ tùy chỉnh" + +#, fuzzy +msgid "Custom repositories" +msgstr "Kho tùy chỉnh" + +#, fuzzy +msgid "Only ASCII characters are supported" +msgstr "Chỉ hỗ trợ các ký tự ASCII" + +msgid "Show help" +msgstr "Hiển thị trợ giúp" + +msgid "Exit help" +msgstr "Thoát trợ giúp" + +#, fuzzy +msgid "Preview scroll up" +msgstr "Xem trước cuộn lên" + +#, fuzzy +msgid "Preview scroll down" +msgstr "Xem trước cuộn xuống" + +msgid "Move up" +msgstr "Di chuyển lên" + +msgid "Move down" +msgstr "Di chuyển xuống" + +msgid "Move right" +msgstr "Di chuyển sang phải" + +msgid "Move left" +msgstr "Di chuyển sang trái" + +#, fuzzy +msgid "Jump to entry" +msgstr "Chuyển đến mục nhập" + +#, fuzzy +msgid "Skip selection (if available)" +msgstr "Bỏ qua lựa chọn (nếu có)" + +#, fuzzy +msgid "Reset selection (if available)" +msgstr "Đặt lại lựa chọn (nếu có)" + +#, fuzzy +msgid "Select on single select" +msgstr "Chọn trên một lần chọn" + +#, fuzzy +msgid "Select on multi select" +msgstr "Chọn trên nhiều lựa chọn" + +#, fuzzy +msgid "Reset" +msgstr "Cài lại" + +#, fuzzy +msgid "Skip selection menu" +msgstr "Bỏ qua menu lựa chọn" + +#, fuzzy +msgid "Start search mode" +msgstr "Bắt đầu chế độ tìm kiếm" + +#, fuzzy +msgid "Exit search mode" +msgstr "Thoát chế độ tìm kiếm" + +msgid "General" +msgstr "Tổng quan" + +msgid "Navigation" +msgstr "Điều hướng" + +msgid "Selection" +msgstr "Lựa chọn" + +msgid "Search" +msgstr "Tìm kiếm" + +msgid "Down" +msgstr "Xuống" + +#, fuzzy +msgid "Up" +msgstr "Hướng lên" + +msgid "Confirm" +msgstr "Xác nhận" + +#, fuzzy +msgid "Focus right" +msgstr "Lấy nét phải" + +#, fuzzy +msgid "Focus left" +msgstr "Tập trung vào bên trái" + +#, fuzzy +msgid "Toggle" +msgstr "Chuyển đổi" + +#, fuzzy +msgid "Show/Hide help" +msgstr "Hiển thị/Ẩn trợ giúp" + +#, fuzzy +msgid "Quit" +msgstr "Từ bỏ" + +#, fuzzy +msgid "First" +msgstr "Đầu tiên" + +#, fuzzy +msgid "Last" +msgstr "Cuối cùng" + +msgid "Select" +msgstr "Lựa chọn" + +msgid "Page Up" +msgstr "Trang Lên" + +msgid "Page Down" +msgstr "Trang Xuống" + +msgid "Page up" +msgstr "Trang lên" + +msgid "Page down" +msgstr "Trang xuống" + +msgid "Page Left" +msgstr "Trang sang trái" + +msgid "Page Right" +msgstr "Trang sang phải" + +msgid "Cursor up" +msgstr "Con trỏ lên" + +msgid "Cursor down" +msgstr "Con trỏ xuống" + +msgid "Cursor right" +msgstr "Con trỏ sang phải" + +msgid "Cursor left" +msgstr "Con trỏ sang trái" + +msgid "Top" +msgstr "Đầu trang" + +msgid "Bottom" +msgstr "Cuối trang" + +msgid "Home" +msgstr "Home" + +msgid "End" +msgstr "Kết thúc" + +#, fuzzy +msgid "Toggle option" +msgstr "Tùy chọn chuyển đổi" + +msgid "Scroll Up" +msgstr "Cuộn lên" + +msgid "Scroll Down" +msgstr "Cuộn xuống" + +#, fuzzy +msgid "Scroll Left" +msgstr "Cuộn sang trái" + +#, fuzzy +msgid "Scroll Right" +msgstr "Cuộn sang phải" + +#, fuzzy +msgid "Scroll Home" +msgstr "Cuộn về nhà" + +#, fuzzy +msgid "Scroll End" +msgstr "Cuối cuộn" + +#, fuzzy +msgid "Focus Next" +msgstr "Tập trung tiếp theo" + +#, fuzzy +msgid "Focus Previous" +msgstr "Tập trung trước đó" + +#, fuzzy +msgid "Copy selected text" +msgstr "Sao chép văn bản đã chọn" + +msgid "labwc needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" +"labwc cần quyền truy cập vào seat của bạn (bộ sưu tập các thiết bị phần cứng như bàn phím, chuột, " +"v.v.)" + +#, fuzzy +msgid "Choose an option to give labwc access to your hardware" +msgstr "Chọn một tùy chọn để cấp quyền truy cập labwc vào phần cứng của bạn" + +msgid "niri needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)" +msgstr "" +"niri cần quyền truy cập vào seat của bạn (bộ sưu tập các thiết bị phần cứng như bàn phím, chuột, " +"v.v.)" + +#, fuzzy +msgid "Choose an option to give niri access to your hardware" +msgstr "Chọn một tùy chọn để cấp cho niri quyền truy cập vào phần cứng của bạn" + +#, fuzzy +msgid "Mark/Unmark as ESP" +msgstr "Đánh dấu/Bỏ đánh dấu là ESP" + +#, fuzzy +msgid "Package group:" +msgstr "Nhóm gói:" + +#, fuzzy +msgid "Exit archinstall" +msgstr "Thoát khỏi cài đặt Arch" + +#, fuzzy +msgid "Reboot system" +msgstr "Khởi động lại hệ thống" + +#, fuzzy +msgid "chroot into installation for post-installation configurations" +msgstr "chroot vào cài đặt để cấu hình sau khi cài đặt" + +msgid "Installation completed" +msgstr "Cài đặt hoàn tất" + +msgid "What would you like to do next?" +msgstr "Bạn muốn làm gì tiếp theo?" + +#, fuzzy, python-brace-format +msgid "Select which mode to configure for \"{}\"" +msgstr "Chọn chế độ cấu hình cho \"{}\"" + +#, fuzzy +msgid "Incorrect credentials file decryption password" +msgstr "Mật khẩu giải mã tập tin thông tin xác thực không chính xác" + +msgid "Incorrect password" +msgstr "Mật khẩu không chính xác" + +#, fuzzy +msgid "Credentials file decryption password" +msgstr "Mật khẩu giải mã tập tin thông tin xác thực" + +#, fuzzy +msgid "Do you want to encrypt the user_credentials.json file?" +msgstr "Bạn có muốn mã hóa tệp user_credentials.json không?" + +msgid "Credentials file encryption password" +msgstr "Mật khẩu mã hóa tập tin thông tin xác thực" + +#, python-brace-format +msgid "Repositories: {}" +msgstr "Kho phần mềm: {}" + +#, fuzzy +msgid "New version available" +msgstr "Đã có phiên bản mới" + +#, fuzzy +msgid "Passwordless login" +msgstr "Đăng nhập không cần mật khẩu" + +#, fuzzy +msgid "Second factor login" +msgstr "Đăng nhập yếu tố thứ hai" + +msgid "Bluetooth" +msgstr "Bluetooth" + +msgid "Would you like to configure Bluetooth?" +msgstr "Bạn có muốn định cấu hình Bluetooth không?" + +msgid "Print service" +msgstr "Dịch vụ in ấn" + +msgid "Would you like to configure the print service?" +msgstr "Bạn có muốn định cấu hình dịch vụ in không?" + +msgid "Power management" +msgstr "Quản lý nguồn điện" + +msgid "Authentication" +msgstr "Xác thực" + +msgid "Applications" +msgstr "Ứng dụng" + +#, fuzzy +msgid "U2F login method: " +msgstr "Phương thức đăng nhập U2F:" + +#, fuzzy +msgid "Passwordless sudo: " +msgstr "Sudo không cần mật khẩu:" + +#, python-brace-format +msgid "Btrfs snapshot type: {}" +msgstr "Loại bản sao lưu tức thời Btrfs \"{}\"" + +#, fuzzy +msgid "Syncing the system..." +msgstr "Đang đồng bộ hóa hệ thống..." + +#, fuzzy +msgid "Value cannot be empty" +msgstr "Giá trị không thể trống" + +msgid "Snapshot type" +msgstr "Kiểu của bản sao lưu" + +#, fuzzy, python-brace-format +msgid "Snapshot type: {}" +msgstr "Kiểu chụp nhanh: {}" + +#, fuzzy +msgid "U2F login setup" +msgstr "Thiết lập đăng nhập U2F" + +#, fuzzy +msgid "No U2F devices found" +msgstr "Không tìm thấy thiết bị U2F nào" + +#, fuzzy +msgid "U2F Login Method" +msgstr "Phương thức đăng nhập U2F" + +#, fuzzy +msgid "Enable passwordless sudo?" +msgstr "Kích hoạt sudo không mật khẩu?" + +#, fuzzy, python-brace-format +msgid "Setting up U2F device for user: {}" +msgstr "Thiết lập thiết bị U2F cho người dùng: {}" + +#, fuzzy +msgid "You may need to enter the PIN and then touch your U2F device to register it" +msgstr "Bạn có thể cần nhập mã PIN rồi chạm vào thiết bị U2F của mình để đăng ký" + +#, fuzzy +msgid "Starting device modifications in " +msgstr "Bắt đầu sửa đổi thiết bị trong" + +msgid "No network connection found" +msgstr "Không tìm thấy kết nối mạng" + +msgid "Would you like to connect to a Wifi?" +msgstr "Bạn có muốn kết nối với Wifi không?" + +msgid "No wifi interface found" +msgstr "Không tìm thấy giao diện wifi" + +msgid "Select wifi network to connect to" +msgstr "Chọn mạng wifi để kết nối" + +msgid "Scanning wifi networks..." +msgstr "Đang quét mạng wifi..." + +msgid "No wifi networks found" +msgstr "Không tìm thấy mạng wifi" + +msgid "Failed setting up wifi" +msgstr "Cài đặt wifi không thành công" + +msgid "Enter wifi password" +msgstr "Nhập mật khẩu wifi" + +#, fuzzy +msgid "Ok" +msgstr "Được rồi" + +#, fuzzy +msgid "Removable" +msgstr "Có thể tháo rời" + +#, fuzzy +msgid "Install to removable location" +msgstr "Cài đặt vào vị trí có thể tháo rời" + +#, fuzzy +msgid "Will install to /EFI/BOOT/ (removable location)" +msgstr "Sẽ cài đặt vào /EFI/BOOT/ (vị trí có thể tháo rời)" + +#, fuzzy +msgid "Will install to standard location with NVRAM entry" +msgstr "Sẽ cài đặt vào vị trí tiêu chuẩn với mục nhập NVRAM" + +#, fuzzy +msgid "Would you like to install the bootloader to the default removable media search location?" +msgstr "Bạn có muốn cài đặt bộ nạp khởi động vào vị trí tìm kiếm phương tiện di động mặc định không?" + +#, fuzzy +msgid "This installs the bootloader to /EFI/BOOT/BOOTX64.EFI (or similar) which is useful for:" +msgstr "" +"Việc này sẽ cài đặt bộ tải khởi động vào /EFI/BOOT/BOOTX64.EFI (hoặc tương tự), rất hữu ích cho:" + +#, fuzzy +msgid "USB drives or other portable external media." +msgstr "Ổ USB hoặc phương tiện di động bên ngoài khác." + +#, fuzzy +msgid "Systems where you want the disk to be bootable on any computer." +msgstr "Các hệ thống mà bạn muốn đĩa có khả năng khởi động trên bất kỳ máy tính nào." + +#, fuzzy +msgid "Firmware that does not properly support NVRAM boot entries." +msgstr "Phần sụn không hỗ trợ đúng cách các mục khởi động NVRAM." + +#, fuzzy +msgid "Will install to /EFI/BOOT/ (removable location, safe default)" +msgstr "Sẽ cài đặt vào /EFI/BOOT/ (vị trí có thể tháo rời, mặc định an toàn)" + +#, fuzzy +msgid "Will install to custom location with NVRAM entry" +msgstr "Sẽ cài đặt vào vị trí tùy chỉnh với mục nhập NVRAM" + +#, fuzzy +msgid "Firmware that does not properly support NVRAM boot entries like most MSI motherboards," +msgstr "Phần sụn không hỗ trợ đúng cách các mục khởi động NVRAM như hầu hết các bo mạch chủ MSI," + +#, fuzzy +msgid "most Apple Macs, many laptops..." +msgstr "hầu hết các máy Mac của Apple, nhiều máy tính xách tay..." + +msgid "Language" +msgstr "Ngôn ngữ" + +#, fuzzy +msgid "Compression algorithm" +msgstr "Thuật toán nén" + +msgid "" +"Only packages such as base, sudo, linux, linux-firmware, efibootmgr and optional profile packages " +"are installed." +msgstr "" +"Chỉ các gói như base, sudo, linux, linux-firmware, efibootmgr và các gói cấu hình tùy chọn mới được " +"cài đặt." + +#, fuzzy +msgid "Select zram compression algorithm:" +msgstr "Chọn thuật toán nén zram:" + +msgid "Use Network Manager (default backend)" +msgstr "Sử dụng NetworkManager (phụ trợ mặc định)" + +#, fuzzy +msgid "Use Network Manager (iwd backend)" +msgstr "Sử dụng Trình quản lý mạng (phụ trợ iwd)" + +msgid "Firewall" +msgstr "Tường lửa" + +msgid "Additional fonts" +msgstr "Phông chữ bổ sung" + +msgid "Select font packages to install" +msgstr "Chọn gói phông để cài đặt" + +#, fuzzy +msgid "Unicode font coverage for most languages" +msgstr "Phạm vi phông chữ Unicode cho hầu hết các ngôn ngữ" + +#, fuzzy +msgid "color emoji for browsers and apps" +msgstr "biểu tượng cảm xúc màu cho trình duyệt và ứng dụng" + +#, fuzzy +msgid "Chinese, Japanese, Korean characters" +msgstr "Ký tự Trung Quốc, Nhật Bản, Hàn Quốc" + +msgid "Select audio configuration" +msgstr "Chọn cấu hình âm thanh" + +#, fuzzy +msgid "Enter credentials file decryption password" +msgstr "Nhập mật khẩu giải mã tập tin thông tin xác thực" + +msgid "Enter root password" +msgstr "Nhập mật khẩu root" + +msgid "Select bootloader to install" +msgstr "Chọn bootloader để cài đặt" + +msgid "Configuration preview" +msgstr "Xem trước cấu hình" + +msgid "Enter a directory for the configuration(s) to be saved" +msgstr "Nhập thư mục để lưu (các) cấu hình" + +msgid "Select encryption type" +msgstr "Chọn loại mã hóa" + +msgid "Select disks for the installation" +msgstr "Chọn đĩa để cài đặt" + +msgid "Enter a mountpoint" +msgstr "Nhập điểm gắn" + +#, fuzzy, python-brace-format +msgid "Enter a size (default: {}): " +msgstr "Nhập kích thước (mặc định: {} ):" + +msgid "Enter subvolume name" +msgstr "Nhập tên phân vùng con (subvolume)" + +msgid "Enter subvolume mountpoint" +msgstr "Nhập điểm gắn subvolume" + +msgid "Select a disk configuration" +msgstr "Chọn cấu hình đĩa" + +msgid "Enter root mount directory" +msgstr "Nhập thư mục gắn gốc" + +#, fuzzy +msgid "You will use whatever drive-setup is mounted at the specified directory" +msgstr "Bạn sẽ sử dụng bất kỳ thiết lập ổ đĩa nào được gắn vào thư mục đã chỉ định" + +#, fuzzy +msgid "WARNING: Archinstall won't check the suitability of this setup" +msgstr "CẢNH BÁO: Archinstall sẽ không kiểm tra tính phù hợp của thiết lập này" + +msgid "Select main filesystem" +msgstr "Chọn hệ thống tập tin chính" + +msgid "Enter a hostname" +msgstr "Nhập tên máy" + +msgid "Select timezone" +msgstr "Chọn múi giờ" + +#, fuzzy +msgid "Enter the number of parallel downloads to be enabled" +msgstr "Nhập số lượng tải song song sẽ được kích hoạt" + +#, fuzzy, python-brace-format +msgid "Value must be between 1 and {}" +msgstr "Giá trị phải nằm trong khoảng từ 1 đến {}" + +msgid "Select which kernel(s) to install" +msgstr "Chọn (các) nhân để cài đặt" + +msgid "Enter a respository name" +msgstr "Nhập tên kho phần mềm" + +msgid "Enter the repository url" +msgstr "Nhập url kho phần mềm" + +#, fuzzy +msgid "Enter server url" +msgstr "Nhập url máy chủ" + +msgid "Select mirror regions to be enabled" +msgstr "Chọn các máy chủ bản sao sẽ được kích hoạt" + +msgid "Select optional repositories to be enabled" +msgstr "Chọn kho phần mềm bổ sung sẽ được kích hoạt" + +msgid "Select an interface" +msgstr "Chọn một giao diện" + +msgid "Choose network configuration" +msgstr "Chọn cấu hình mạng" + +msgid "Recommended: Network Manager for desktop, Manual for server" +msgstr "Khuyến nghị: Network Manager cho máy tính để bàn, Cấu hình thủ công cho máy chủ" + +#, fuzzy +msgid "" +"Warning: no network configuration selected. Network will need to be set up manually on the " +"installed system." +msgstr "" +"Cảnh báo: chưa chọn cấu hình mạng. Mạng sẽ cần được thiết lập thủ công trên hệ thống đã cài đặt." + +msgid "No packages found" +msgstr "Không tìm thấy gói nào" + +msgid "Select which greeter to install" +msgstr "Chọn màn hình đăng nhập để cài đặt" + +msgid "Select a profile type" +msgstr "Chọn loại cấu hình" + +msgid "Enter new password" +msgstr "Nhập mật khẩu mới" + +msgid "Enter a username" +msgstr "Nhập tên người dùng" + +msgid "Enter a password" +msgstr "Nhập mật khẩu" + +msgid "The password did not match, please try again" +msgstr "Mật khẩu không khớp, vui lòng thử lại" + +msgid "Password strength: Weak" +msgstr "Độ mạnh của mật khẩu: Yếu" + +msgid "Password strength: Moderate" +msgstr "Độ mạnh mật khẩu: Trung bình" + +msgid "Password strength: Strong" +msgstr "Độ mạnh mật khẩu: Mạnh" + +msgid "The selected desktop profile requires a regular user to log in via the greeter" +msgstr "" +"Cấu hình máy tính để bàn đã chọn yêu cầu người dùng thông thường đăng nhập thông qua màn hình đăng " +"nhập" + +#, fuzzy, python-brace-format +msgid "Environment type: {} {}" +msgstr "Loại môi trường: {} {}" + +msgid "Input cannot be empty" +msgstr "Đầu vào không thể trống" + +msgid "Recommended" +msgstr "Khuyến khích" + +msgid "Package" +msgstr "Gói" + +#, fuzzy +msgid "Curated selection of KDE Plasma packages" +msgstr "Lựa chọn các gói KDE Plasma được tuyển chọn" + +#, fuzzy +msgid "Dependencies" +msgstr "phụ thuộc" + +#, fuzzy +msgid "Package group" +msgstr "Nhóm gói" + +#, fuzzy +msgid "Extensive KDE Plasma installation" +msgstr "Cài đặt KDE Plasma mở rộng" + +#, fuzzy +msgid "Packages in group" +msgstr "Gói trong nhóm" + +#, fuzzy +msgid "Minimal KDE Plasma installation" +msgstr "Cài đặt KDE Plasma tối thiểu" + +#, fuzzy +msgid "Description" +msgstr "Sự miêu tả" + +#, fuzzy +msgid "Select a flavor of KDE Plasma to install" +msgstr "Chọn phiên bản KDE Plasma để cài đặt" + +#, fuzzy +msgid "Arial/Times/Courier replacement, Cyrillic support for Steam/games" +msgstr "Thay thế Arial/Time/Courier, hỗ trợ Cyrillic cho Steam/game" + +#, fuzzy +msgid "wide Unicode coverage, good fallback font" +msgstr "vùng phủ sóng Unicode rộng, phông chữ dự phòng tốt" + +#, fuzzy, python-brace-format +msgid "Setting up U2F login: {u2f_config.u2f_login_method.value}" +msgstr "Thiết lập đăng nhập U2F: {u2f_config.u2f_login_method.value}" + +#, fuzzy, python-brace-format +msgid "Default: {DEFAULT_ITER_TIME}ms, Recommended range: 1000-60000" +msgstr "Mặc định: {DEFAULT_ITER_TIME} ms, Phạm vi khuyến nghị: 1000-60000" + +#, fuzzy, python-brace-format +msgid "Setting up U2F login: {}" +msgstr "Thiết lập đăng nhập U2F: {}" + +#, fuzzy, python-brace-format +msgid "Default: {}ms, Recommended range: 1000-60000" +msgstr "Mặc định: {} ms, Phạm vi khuyến nghị: 1000-60000" + +#, python-brace-format +msgid "{} needs access to your seat" +msgstr "{} cần quyền truy cập vào seat của bạn" + +#, fuzzy +msgid "collection of hardware devices i.e. keyboard, mouse" +msgstr "tập hợp các thiết bị phần cứng như bàn phím, chuột" + +#, fuzzy, python-brace-format +msgid "Choose an option how to give {} access to your hardware" +msgstr "Chọn một tùy chọn về cách cấp quyền truy cập {} vào phần cứng của bạn" + +#, python-brace-format +msgid "About to upload \"{}\" to the publicly accessible {}" +msgstr "Sắp tải \"{}\" lên {} có thể truy cập công khai" + +#, fuzzy +msgid "Do you want to continue?" +msgstr "Bạn có muốn tiếp tục không?" + +#, fuzzy, python-brace-format +msgid "Log uploaded successfully. URL: {}" +msgstr "Nhật ký được tải lên thành công. URL: {}" + +#, fuzzy +msgid "Failed to upload log." +msgstr "Không thể tải lên nhật ký." + +#, fuzzy +msgid "ArchInstall Language" +msgstr "Ngôn ngữ cài đặt Arch" + +#, fuzzy +msgid "Version" +msgstr "Phiên bản" + +#, fuzzy +msgid "Installation Script" +msgstr "Tập lệnh cài đặt" + +msgid "Application" +msgstr "Ứng dụng" + +#, fuzzy +msgid "Services" +msgstr "Dịch vụ" + +#, fuzzy +msgid "Custom commands" +msgstr "Lệnh tùy chỉnh" + +#, fuzzy +msgid "Users" +msgstr "Người dùng" + +#, fuzzy +msgid "Root encrypted password" +msgstr "Mật khẩu được mã hóa gốc" + +#, fuzzy +msgid "Zram enabled" +msgstr "Đã bật Zram" + +#, fuzzy, python-brace-format +msgid "Zram algorithm {}" +msgstr "Thuật toán Zram {}" + +#, fuzzy +msgid "Bluetooth enabled" +msgstr "Đã bật Bluetooth" + +#, python-brace-format +msgid "Audio server \"{}\"" +msgstr "Hệ thống âm thanh \"{}\"" + +#, fuzzy, python-brace-format +msgid "Power management \"{}\"" +msgstr "Quản lý nguồn điện \"{}\"" + +#, fuzzy +msgid "Print service enabled" +msgstr "Đã bật dịch vụ in" + +#, fuzzy, python-brace-format +msgid "Firewall \"{}\"" +msgstr "Tường lửa \"{}\"" + +#, python-brace-format +msgid "Extra fonts \"{}\"" +msgstr "Phông chữ bổ sung \"{}\"" + +#, fuzzy +msgid "Root password set" +msgstr "Đã đặt mật khẩu gốc" + +#, fuzzy, python-brace-format +msgid "Configured {} user(s)" +msgstr "(Những) người dùng {} đã định cấu hình" + +#, fuzzy +msgid "U2F set up" +msgstr "thiết lập U2F" + +#, fuzzy, python-brace-format +msgid "Bootloader \"{}\"" +msgstr "Bộ nạp khởi động \"{}\"" + +#, fuzzy +msgid "UKI enabled" +msgstr "Đã bật UKI" + +#, fuzzy +msgid "Default" +msgstr "Mặc định" + +#, fuzzy +msgid "Manual" +msgstr "Thủ công" + +#, fuzzy +msgid "Pre-mount" +msgstr "Gắn trước" + +#, python-brace-format +msgid "{} layout" +msgstr "Bố cục {}" + +#, python-brace-format +msgid "Devices {}" +msgstr "Thiết bị {}" + +msgid "LVM set up" +msgstr "Cấu hình LVM" + +#, python-brace-format +msgid "{} encryption" +msgstr "Mã hóa {}" + +#, python-brace-format +msgid "Btrfs snapshot \"{}\"" +msgstr "Bản sao lưu tức thời Btrfs \"{}\"" + +#, python-brace-format +msgid "Keyboard layout \"{}\"" +msgstr "Bố cục bàn phím \"{}\"" + +#, python-brace-format +msgid "Locale language \"{}\"" +msgstr "Ngôn ngữ hệ thống (Locale) \"{}\"" + +#, python-brace-format +msgid "Locale encoding \"{}\"" +msgstr "Mã hóa ngôn ngữ hệ thống \"{}\"" + +#, python-brace-format +msgid "Console font \"{}\"" +msgstr "Phông chữ Console \"{}\"" + +#, python-brace-format +msgid "Mirror regions \"{}\"" +msgstr "Máy chủ bản sao \"{}\"" + +#, python-brace-format +msgid "Optional repositories \"{}\"" +msgstr "Kho phần mềm bổ sung \"{}\"" + +msgid "Custom servers set up" +msgstr "Cấu hình máy chủ tùy chỉnh" + +msgid "Custom repositories set up" +msgstr "Kho phần mềm tùy chỉnh được thiết lập" + +#, fuzzy +msgid "Use standalone iwd" +msgstr "Sử dụng iwd độc lập" + +#, fuzzy +msgid "Color enabled" +msgstr "Đã bật màu" + +#, fuzzy, python-brace-format +msgid "{} grphics driver" +msgstr "Trình điều khiển đồ họa {}" + +#, python-brace-format +msgid "{} greeter" +msgstr "Màn hình đăng nhập {}" + +msgid "Enter a repository name" +msgstr "Nhập tên kho phần mềm" diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 29573a5f..627a3b75 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -6,7 +6,7 @@ from archinstall.lib.applications.application_handler import ApplicationHandler from archinstall.lib.args import ArchConfig, ArchConfigHandler from archinstall.lib.authentication.authentication_handler import AuthenticationHandler from archinstall.lib.bootloader.utils import validate_bootloader_layout -from archinstall.lib.configuration import ConfigurationOutput +from archinstall.lib.configuration import confirm_config from archinstall.lib.disk.filesystem import FilesystemHandler from archinstall.lib.disk.utils import disk_layouts from archinstall.lib.general.general_menu import PostInstallationAction, select_post_installation @@ -119,6 +119,7 @@ def perform_installation( config.bootloader_config.bootloader, config.bootloader_config.uki, config.bootloader_config.removable, + config.bootloader_config.plymouth, ) if config.network_config: @@ -212,9 +213,8 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None: if not arch_config_handler.args.silent: show_menu(arch_config_handler, mirror_list_handler) - config = ConfigurationOutput(arch_config_handler.config) - config.write_debug() - config.save() + arch_config_handler.config.write_debug() + arch_config_handler.config.save() # Safety net for silent/config-file flow. The TUI menu blocks Install via # GlobalMenu._validate_bootloader() before reaching this point. @@ -230,7 +230,7 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None: if not arch_config_handler.args.silent: aborted = False - res: bool = tui.run(config.confirm_config) + res: bool = tui.run(lambda: confirm_config(arch_config_handler.config)) if not res: debug('Installation aborted') diff --git a/archinstall/scripts/minimal.py b/archinstall/scripts/minimal.py index a58963f2..0ab6da83 100644 --- a/archinstall/scripts/minimal.py +++ b/archinstall/scripts/minimal.py @@ -1,6 +1,6 @@ from archinstall.default_profiles.minimal import MinimalProfile from archinstall.lib.args import ArchConfigHandler -from archinstall.lib.configuration import ConfigurationOutput +from archinstall.lib.configuration import confirm_config from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu from archinstall.lib.disk.filesystem import FilesystemHandler from archinstall.lib.installer import Installer @@ -68,16 +68,15 @@ async def main(arch_config_handler: ArchConfigHandler | None = None) -> None: disk_config = await DiskLayoutConfigurationMenu(disk_layout_config=None).show() arch_config_handler.config.disk_config = disk_config - config = ConfigurationOutput(arch_config_handler.config) - config.write_debug() - config.save() + arch_config_handler.config.write_debug() + arch_config_handler.config.save() if arch_config_handler.args.dry_run: return if not arch_config_handler.args.silent: aborted = False - res: bool = tui.run(config.confirm_config) + res: bool = tui.run(lambda: confirm_config(arch_config_handler.config)) if not res: debug('Installation aborted') diff --git a/archinstall/scripts/only_hd.py b/archinstall/scripts/only_hd.py index c7349c51..6a2374df 100644 --- a/archinstall/scripts/only_hd.py +++ b/archinstall/scripts/only_hd.py @@ -2,7 +2,7 @@ import sys from pathlib import Path from archinstall.lib.args import ArchConfig, ArchConfigHandler -from archinstall.lib.configuration import ConfigurationOutput +from archinstall.lib.configuration import confirm_config from archinstall.lib.disk.filesystem import FilesystemHandler from archinstall.lib.disk.utils import disk_layouts from archinstall.lib.global_menu import GlobalMenu @@ -69,16 +69,15 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None: if not arch_config_handler.args.silent: show_menu(arch_config_handler) - config = ConfigurationOutput(arch_config_handler.config) - config.write_debug() - config.save() + arch_config_handler.config.write_debug() + arch_config_handler.config.save() if arch_config_handler.args.dry_run: return if not arch_config_handler.args.silent: aborted = False - res: bool = tui.run(config.confirm_config) + res: bool = tui.run(lambda: confirm_config(arch_config_handler.config)) if not res: debug('Installation aborted') diff --git a/pyproject.toml b/pyproject.toml index 8a128717..6c6ac0c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "archinstall" -version = "4.3" +version = "4.4" description = "Arch Linux installer - guided, templates etc." authors = [ {name = "Anton Hvornum", email = "anton@hvornum.se"}, @@ -20,8 +20,8 @@ classifiers = [ dependencies = [ "pyparted==3.13.0", "pydantic==2.13.4", - "cryptography==48.0.0", - "textual==8.2.7", + "cryptography==49.0.0", + "textual==8.2.8", "markdown-it-py==4.0.0", "linkify-it-py==2.1.0", ] @@ -32,14 +32,14 @@ Documentation = "https://archinstall.readthedocs.io/" Source = "https://github.com/archlinux/archinstall" [project.optional-dependencies] -log = ["systemd_python==235"] +log = ["systemd_python==236"] dev = [ - "mypy==2.1.0", + "mypy==2.3.0", "flake8==7.3.0", "pre-commit==4.6.0", - "ruff==0.15.16", - "pylint==4.0.5", - "pytest==9.0.3", + "ruff==0.15.21", + "pylint==4.0.6", + "pytest==9.1.1", "hypothesis>=6.152.4", ] doc = ["sphinx"] @@ -72,7 +72,6 @@ python_version = "3.14" files = "." exclude = [ "^build/", - "^test_tooling/", ] disallow_any_explicit = false disallow_any_expr = false diff --git a/test_tooling/qemu/qemu.py b/test_tooling/qemu/qemu.py index c8307771..69e766da 100644 --- a/test_tooling/qemu/qemu.py +++ b/test_tooling/qemu/qemu.py @@ -11,6 +11,8 @@ import sys import time from argparse import ArgumentParser from collections.abc import Iterator +from functools import cache +from pathlib import Path from select import EPOLLHUP, EPOLLIN, epoll from shutil import which from types import TracebackType @@ -25,25 +27,32 @@ class ArgumentError(Exception): pass -def get_master(interface): +def cpu_count() -> int: + count = os.cpu_count() + if not count: + return 1 + + return count - 1 + + +def get_master(interface: str) -> str | None: master_path = pathlib.Path(f'/sys/class/net/{interface}/master') return master_path.readlink().name if master_path.exists() else None -def gray(text): +def gray(text: str) -> str: return f'\033[38;5;246m{text}\033[0m' -def orange(text): +def orange(text: str) -> str: return f'\033[38;5;208m{text}\033[0m' -def red(text): +def red(text: str) -> str: return f'\033[31m{text}\033[0m' -sudo_password = None # Gets populated later -harddrives = {} +harddrives: dict[Path, str] = {} username = getpass.getuser() groupname = grp.getgrgid(os.getgid()).gr_name @@ -71,37 +80,37 @@ hardware = parser.add_argument_group('Hardware', 'General hardware specs for the hardware.add_argument('--bios', action='store_true', help='Disables EFI (edk2/ovmf) and uses BIOS support instead', default=False) hardware.add_argument('--memory', nargs='?', help='Ammount of memory to supply the machine', default=8192) hardware.add_argument('--harddrive', action='append', help='Sets up one or more virtio-scsi-pci, size is defined by --harddrive test.qcow2:15G', type=str) -hardware.add_argument('--cpu', help='Sets the number of cores to allocate (default nproc -1)', type=str, default=os.cpu_count() - 1 if os.cpu_count() else 1) +hardware.add_argument('--cpu', help='Sets the number of cores to allocate (default nproc -1)', type=str, default=cpu_count()) hardware.add_argument('--resolution', help="Sets Qemu's VGA resolution", type=str, default='1920x1107') kernel = parser.add_argument_group('Kernel', '--kernel specific arguments') kernel.add_argument('--initrd', nargs='?', help='Defines which ISO to run (skips build all together)', default=None, type=pathlib.Path) -args, unknowns = parser.parse_known_args() # pylint: disable=redefined-outer-name +cli_args, _ = parser.parse_known_args() -if args.bios and args.uki: +if cli_args.bios and cli_args.uki: raise ArgumentError('Cannot boot a --uki image with --bios mode (at least not that I know of).') -if args.uki is None and args.kernel is None and args.iso is None and args.harddrive is None: +if cli_args.uki is None and cli_args.kernel is None and cli_args.iso is None and cli_args.harddrive is None: raise ArgumentError('Cannot boot this machine, define at least one of: --uki, --kernel, --iso, --harddrive') -if args.bridge is None and args.bridge_master: +if cli_args.bridge is None and cli_args.bridge_master: raise ArgumentError('Cannot use --bridge-master without defining --bridge') -if args.bridge is None and args.bridge_mac: +if cli_args.bridge is None and cli_args.bridge_mac: raise ArgumentError('Cannot use --bridge-mac without defining --bridge') -elif args.bridge and args.bridge_mac is None: - args.bridge_mac = '52:54:00:00:00:1' +elif cli_args.bridge and cli_args.bridge_mac is None: + cli_args.bridge_mac = '52:54:00:00:00:1' -if args.tap and not args.bridge and get_master(args.tap) is None: +if cli_args.tap and not cli_args.bridge and get_master(cli_args.tap) is None: # We'll allow it, because maybe we're tesing what happens without networking, but the NIC exists. # Or the user has some creative iptables/nftables forwarding. print(orange('--tap does not have a master, consider adding --bridge or manual set a master using ip-link(8).')) -if args.tap is None and args.bridge: +if cli_args.tap is None and cli_args.bridge: print(orange("--bridge* arguments will be ignored since there's no --tap defined")) -elif args.tap and args.tap_mac is None: - args.tap_mac = '52:54:00:00:00:2' +elif cli_args.tap and cli_args.tap_mac is None: + cli_args.tap_mac = '52:54:00:00:00:2' class SysCallError(Exception): @@ -176,7 +185,7 @@ class SysCommandWorker: return False - def __iter__(self, *args: str, **kwargs: dict[str, Any]) -> Iterator[bytes]: # pylint: disable=redefined-outer-name + def __iter__(self, *args: str, **kwargs: dict[str, Any]) -> Iterator[bytes]: last_line = self._trace_log.rfind(b'\n') lines = filter(None, self._trace_log[self._trace_log_pos : last_line].splitlines()) for line in lines: @@ -334,81 +343,82 @@ class SysCommandWorker: return self._trace_log.decode(encoding) -def ensure_sudo(): - global sudo_password # pylint: disable=global-statement +@cache +def get_sudo_password() -> str: + sudo_password = getpass.getpass(f'[sudo] password for {username}: ') + if sudo_password == '': + raise ValueError('Certain commands need sudo to work and no sudo password was given.') - if sudo_password is None: - if (sudo_password := getpass.getpass(f'[sudo] password for {username}: ')) == '': - raise ValueError('Certain commands need sudo to work and no sudo password was given.') + return sudo_password -def setup_networking(): - if args.tap: - if pathlib.Path(f'/sys/class/net/{args.tap}').exists() is False: - print(gray(f'Creating {args.tap} for user {username} and group {groupname}')) - handle, pw_prompted = SysCommandWorker(f'sudo ip tuntap add dev {args.tap} mode tap user {username} group {groupname}'), False +def setup_networking() -> None: + if cli_args.tap: + if pathlib.Path(f'/sys/class/net/{cli_args.tap}').exists() is False: + print(gray(f'Creating {cli_args.tap} for user {username} and group {groupname}')) + handle, pw_prompted = SysCommandWorker(f'sudo ip tuntap add dev {cli_args.tap} mode tap user {username} group {groupname}'), False while handle.is_alive(): if b'password for' in handle and pw_prompted is False: - ensure_sudo() - handle.write(bytes(sudo_password, 'UTF-8')) + sudo_pw = get_sudo_password() + handle.write(bytes(sudo_pw, 'UTF-8')) pw_prompted = True - if args.bridge: - if pathlib.Path(f'/sys/class/net/{args.bridge}').exists() is False: - print(gray(f'Creating {args.bridge}')) - handle, pw_prompted = SysCommandWorker(f'sudo ip link add name {args.bridge} type bridge'), False + if cli_args.bridge: + if pathlib.Path(f'/sys/class/net/{cli_args.bridge}').exists() is False: + print(gray(f'Creating {cli_args.bridge}')) + handle, pw_prompted = SysCommandWorker(f'sudo ip link add name {cli_args.bridge} type bridge'), False while handle.is_alive(): if b'password for' in handle and pw_prompted is False: - ensure_sudo() - handle.write(bytes(sudo_password, 'UTF-8')) + sudo_pw = get_sudo_password() + handle.write(bytes(sudo_pw, 'UTF-8')) pw_prompted = True - if args.bridge_mac: - handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {args.bridge} address {args.bridge_mac}'), False - print(gray(f'Setting bridge {args.bridge} MAC address to {args.bridge_mac}')) + if cli_args.bridge_mac: + handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {cli_args.bridge} address {cli_args.bridge_mac}'), False + print(gray(f'Setting bridge {cli_args.bridge} MAC address to {cli_args.bridge_mac}')) while handle.is_alive(): if b'password for' in handle and pw_prompted is False: - ensure_sudo() - handle.write(bytes(sudo_password, 'UTF-8')) + sudo_pw = get_sudo_password() + handle.write(bytes(sudo_pw, 'UTF-8')) pw_prompted = True - if args.bridge_master and get_master(args.bridge) != args.bridge_master: - handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {args.bridge_master} master {args.bridge}'), False - print(gray(f'Setting interface {args.bridge_master} master to {args.bridge}')) + if cli_args.bridge_master and get_master(cli_args.bridge) != cli_args.bridge_master: + handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {cli_args.bridge_master} master {cli_args.bridge}'), False + print(gray(f'Setting interface {cli_args.bridge_master} master to {cli_args}')) while handle.is_alive(): if b'password for' in handle and pw_prompted is False: - ensure_sudo() - handle.write(bytes(sudo_password, 'UTF-8')) + sudo_pw = get_sudo_password() + handle.write(bytes(sudo_pw, 'UTF-8')) pw_prompted = True - print(gray(f'Setting interface {args.tap} master to {args.bridge}')) - handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {args.tap} master {args.bridge}'), False + print(gray(f'Setting interface {cli_args.tap} master to {cli_args.bridge}')) + handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {cli_args.tap} master {cli_args.bridge}'), False while handle.is_alive(): if b'password for' in handle and pw_prompted is False: - ensure_sudo() - handle.write(bytes(sudo_password, 'UTF-8')) + sudo_pw = get_sudo_password() + handle.write(bytes(sudo_pw, 'UTF-8')) pw_prompted = True - print(gray(f'Bringing up bridge {args.bridge}')) - handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {args.bridge} up'), False + print(gray(f'Bringing up bridge {cli_args.bridge}')) + handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {cli_args.bridge} up'), False while handle.is_alive(): if b'password for' in handle and pw_prompted is False: - ensure_sudo() - handle.write(bytes(sudo_password, 'UTF-8')) + sudo_pw = get_sudo_password() + handle.write(bytes(sudo_pw, 'UTF-8')) pw_prompted = True - print(gray(f'Bringing interface {args.tap} up')) - handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {args.tap} up'), False + print(gray(f'Bringing interface {cli_args.tap} up')) + handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {cli_args.tap} up'), False while handle.is_alive(): if b'password for' in handle and pw_prompted is False: - ensure_sudo() - handle.write(bytes(sudo_password, 'UTF-8')) + sudo_pw = get_sudo_password() + handle.write(bytes(sudo_pw, 'UTF-8')) pw_prompted = True -def setup_disks(): - if args.harddrive: - for harddrive_arg in args.harddrive: +def setup_disks() -> None: + if cli_args.harddrive: + for harddrive_arg in cli_args.harddrive: path, size = harddrive_arg.split(':') path = pathlib.Path(path.strip()).expanduser().resolve().absolute() harddrives[path] = size.strip() @@ -425,7 +435,8 @@ def setup_disks(): setup_networking() setup_disks() -if args.uki or args.bios is False: +disk_paths_hash = '' +if cli_args.uki or cli_args.bios is False: disk_paths_hash = hashlib.sha1((''.join(sorted([str(x) for x in harddrives.keys()]))).encode()).hexdigest() shutil.copy2('/usr/share/ovmf/x64/OVMF_CODE.secboot.4m.fd', f'./OVMF_CODE.secboot.4m.fd.{disk_paths_hash}') @@ -439,16 +450,16 @@ qemu += ' -machine q35,accel=kvm' qemu += ' -object rng-random,filename=/dev/urandom,id=rng0' qemu += ' -device virtio-rng-pci,rng=rng0' qemu += ' -global driver=cfi.pflash01,property=secure,value=on' -qemu += f' -smp {args.cpu},sockets=1,dies=1,cores={args.cpu},threads=1' +qemu += f' -smp {cli_args.cpu},sockets=1,dies=1,cores={cli_args.cpu},threads=1' # qemu += f' -vga vga' -qemu += f' -device VGA,edid=on,xres={args.resolution.split("x")[0]},yres={args.resolution.split("x")[1]}' +qemu += f' -device VGA,edid=on,xres={cli_args.resolution.split("x")[0]},yres={cli_args.resolution.split("x")[1]}' qemu += ' -device intel-iommu,device-iotlb=on,caching-mode=on' -qemu += f' -m {args.memory}' -if args.bios is False: +qemu += f' -m {cli_args.memory}' +if cli_args.bios is False: qemu += f' -drive if=pflash,format=raw,readonly=on,file=./OVMF_CODE.secboot.4m.fd.{disk_paths_hash}' qemu += f' -drive if=pflash,format=raw,file=./OVMF_VARS.4m.fd.{disk_paths_hash}' -if args.uki: - qemu += f' -kernel {args.uki}' +if cli_args.uki: + qemu += f' -kernel {cli_args.uki}' boot_index += 1 scsi_index = 0 for scsi_index, hdd in enumerate(harddrives.keys()): @@ -460,18 +471,18 @@ for scsi_index, hdd in enumerate(harddrives.keys()): qemu += f' -blockdev \'{{"driver":"file","filename":"{hdd}","aio":"threads","node-name":"libvirt-{scsi_index}-storage","cache":{{"direct":false,"no-flush":false}},"auto-read-only":true,"discard":"unmap"}}\'' # noqa: E501 qemu += f' -blockdev \'{{"node-name":"libvirt-{scsi_index}-format","read-only":false,"discard":"unmap","cache":{{"direct":true,"no-flush":false}},"driver":"qcow2","file":"libvirt-{scsi_index}-storage","backing":null}}\'' # noqa: E501 boot_index += 1 -if args.iso: +if cli_args.iso: qemu += f' -device virtio-scsi-pci,bus=pcie.0,id=scsi{scsi_index + 1}' qemu += f' -device scsi-cd,drive=cdrom0,bus=scsi{scsi_index + 1}.0,bootindex={boot_index}' - qemu += f' -drive file={args.iso},media=cdrom,if=none,format=raw,cache=none,id=cdrom0' + qemu += f' -drive file={cli_args.iso},media=cdrom,if=none,format=raw,cache=none,id=cdrom0' boot_index += 1 -# if args.vfio: -# qemu += f' -drive file={args.vfio},index=2,media=cdrom' +# if cli_args.vfio: +# qemu += f' -drive file={cli_args.vfio},index=2,media=cdrom' -if args.tap: - qemu += f' -device virtio-net-pci,mac={args.tap_mac},id=network0,netdev=network0.0,status=on,bus=pcie.0' - qemu += f' -netdev tap,ifname={args.tap},id=network0.0,script=no,downscript=no' +if cli_args.tap: + qemu += f' -device virtio-net-pci,mac={cli_args.tap_mac},id=network0,netdev=network0.0,status=on,bus=pcie.0' + qemu += f' -netdev tap,ifname={cli_args.tap},id=network0.0,script=no,downscript=no' print(gray(qemu)) diff --git a/tests/test_args.py b/tests/test_args.py index 4cbad41b..324f5a11 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -50,6 +50,7 @@ def test_default_args(monkeypatch: MonkeyPatch) -> None: offline=False, no_pkg_lookups=False, plugin=None, + plugin_url=None, skip_version_check=False, advanced=False, ) @@ -106,7 +107,8 @@ def test_correct_parsing_args( debug=True, offline=True, no_pkg_lookups=True, - plugin='pytest_plugin.py', + plugin=Path('pytest_plugin.py'), + plugin_url=None, skip_version_check=True, advanced=True, ) diff --git a/tests/test_configuration_output.py b/tests/test_configuration_output.py index 7d9f1f97..cbedc026 100644 --- a/tests/test_configuration_output.py +++ b/tests/test_configuration_output.py @@ -3,8 +3,7 @@ from pathlib import Path from pytest import MonkeyPatch -from archinstall.lib.args import ArchConfigHandler -from archinstall.lib.configuration import ConfigurationOutput +from archinstall.lib.args import USER_CONFIG_FILE, USER_CREDS_FILE, ArchConfigHandler def test_user_config_roundtrip( @@ -20,12 +19,10 @@ def test_user_config_roundtrip( # as there is no version present in the test environment we'll set it manually arch_config.version = '3.0.2' - config_output = ConfigurationOutput(arch_config) - test_out_dir = Path('/tmp/') - test_out_file = test_out_dir / config_output.user_configuration_file + test_out_file = test_out_dir / USER_CONFIG_FILE - config_output.save(test_out_dir) + arch_config.save(test_out_dir) result = json.loads(test_out_file.read_text()) expected = json.loads(config_fixture.read_text()) @@ -55,12 +52,10 @@ def test_creds_roundtrip( handler = ArchConfigHandler() arch_config = handler.config - config_output = ConfigurationOutput(arch_config) - test_out_dir = Path('/tmp/') - test_out_file = test_out_dir / config_output.user_credentials_file + test_out_file = test_out_dir / USER_CREDS_FILE - config_output.save(test_out_dir, creds=True) + arch_config.save(test_out_dir, creds=True) result = json.loads(test_out_file.read_text()) expected = json.loads(creds_fixture.read_text())